diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp @@ -31,6 +31,9 @@ Dl_info info; int result = dladdr((const void *)addr, &info); if (!result) return false; + + CHECK(addr >= reinterpret_cast(info.dli_saddr)); + stack->info.function_offset = addr - reinterpret_cast(info.dli_saddr); const char *demangled = DemangleSwiftAndCXX(info.dli_sname); if (!demangled) return false; stack->info.function = internal_strdup(demangled); @@ -145,12 +148,29 @@ const char *buf = process_->SendCommand(command); if (!buf) return false; uptr line; + uptr start_address = AddressInfo::kUnknown; if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module, - &stack->info.file, &line, nullptr)) { + &stack->info.file, &line, &start_address)) { process_ = nullptr; return false; } stack->info.line = (int)line; + + if (start_address == AddressInfo::kUnknown) { + // Fallback to dladdr() to get function start address if atos doesn't report + // it. + Dl_info info; + int result = dladdr((const void *)addr, &info); + if (result) + start_address = reinterpret_cast(info.dli_saddr); + } + + // Only assig to `function_offset` if we were able to get the function's + // start address. + if (start_address != AddressInfo::kUnknown) { + CHECK(addr >= start_address); + stack->info.function_offset = addr - start_address; + } return true; } diff --git a/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-partial-report-no-external-symbolizer.cpp b/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-partial-report-no-external-symbolizer.cpp --- a/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-partial-report-no-external-symbolizer.cpp +++ b/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-partial-report-no-external-symbolizer.cpp @@ -17,8 +17,8 @@ // source location is not and instead module name and offset are // printed. // CHECK-PS: WRITE of size 4 -// CHECK-PS: #0 0x{{.+}} in foo ({{.+}}.executable:{{.+}}+0x{{.+}}) -// CHECK-PS: #1 0x{{.+}} in main ({{.+}}.executable:{{.+}}+0x{{.+}}) +// CHECK-PS: #0 0x{{.+}} in foo{{(\+0x[0-9a-f]+)?}} ({{.+}}.executable:{{.+}}+0x{{.+}}) +// CHECK-PS: #1 0x{{.+}} in main{{(\+0x[0-9a-f]+)?}} ({{.+}}.executable:{{.+}}+0x{{.+}}) // CHECK-FS: WRITE of size 4 diff --git a/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-atos.cpp b/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-atos.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-atos.cpp @@ -0,0 +1,43 @@ +// RUN: %clangxx %s -g -O0 -o %t-with-debug + +// With debug info atos reports the source location, but no function offset. We fallback to dladdr() to retrieve the function offset. +// RUN: %env_tool_opts=verbosity=2,stack_trace_format='"function_name:%f function_offset:%q"' %run %t-with-debug > %t-with-debug.output 2>&1 +// RUN: FileCheck -input-file=%t-with-debug.output %s + +// Without debug info atos reports the function offset and so dladdr() fallback is not used. +// RUN: rm -rf %t-with-debug.dSYM +// RUN: %env_tool_opts=verbosity=2,stack_trace_format='"function_name:%f function_offset:%q"' %run %t-with-debug > %t-no-debug.output 2>&1 +// RUN: FileCheck -input-file=%t-no-debug.output %s + +#include +#include + +void baz() { + printf("Do stuff in baz\n"); + __sanitizer_print_stack_trace(); +} + +void bar() { + printf("Do stuff in bar\n"); + baz(); +} + +void foo() { + printf("Do stuff in foo\n"); + bar(); +} + +int main() { + printf("Do stuff in main\n"); + foo(); + return 0; +} + +// CHECK: Using atos found at: + +// These `function_offset` patterns are designed to disallow `0x0` which is the +// value printed for `kUnknown`. +// CHECK: function_name:baz{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} +// CHECK: function_name:bar{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} +// CHECK: function_name:foo{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} +// CHECK: function_name:main{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} diff --git a/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp b/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp @@ -0,0 +1,41 @@ +// UNSUPPORTED: lsan +// This test fails with LSan enabled because the dladdr symbolizer actually leaks +// memory because the call to `__sanitizer::DemangleCXXABI` leaks memory which LSan +// detects (rdar://problem/42868950). + +// RUN: %clangxx %s -O0 -o %t +// RUN: %env_tool_opts=verbosity=2,external_symbolizer_path=,stack_trace_format='"function_name:%f function_offset:%q"' %run %t > %t.output 2>&1 +// RUN: FileCheck -input-file=%t.output %s +#include +#include + +void baz() { + printf("Do stuff in baz\n"); + __sanitizer_print_stack_trace(); +} + +void bar() { + printf("Do stuff in bar\n"); + baz(); +} + +void foo() { + printf("Do stuff in foo\n"); + bar(); +} + +int main() { + printf("Do stuff in main\n"); + foo(); + return 0; +} + +// CHECK: External symbolizer is explicitly disabled +// CHECK: Using dladdr symbolizer + +// These `function_offset` patterns are designed to disallow `0x0` which is the +// value printed for `kUnknown`. +// CHECK: function_name:baz{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} +// CHECK: function_name:bar{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} +// CHECK: function_name:foo{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}} +// CHECK: function_name:main{{(\(\))?}} function_offset:0x{{0*[1-9a-f][0-9a-f]*$}}