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 @@ -33,8 +33,15 @@ 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); + // Compute offset if possible. `dladdr()` doesn't always ensure that `addr >= + // sym_addr` so only compute the offset when this holds. Failure to find the + // function offset is not treated as a failure because it might still be + // possible to get the symbol name. + uptr sym_addr = reinterpret_cast(info.dli_saddr); + if (addr >= sym_addr) { + stack->info.function_offset = addr - sym_addr; + } + const char *demangled = DemangleSwiftAndCXX(info.dli_sname); if (!demangled) return false; stack->info.function = internal_strdup(demangled); @@ -219,10 +226,10 @@ 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); + // Only assign to `function_offset` if we were able to get the function's + // start address and we got a sensible `start_address` (dladdr doesn't always + // ensure that `addr >= sym_addr`). + if (start_address != AddressInfo::kUnknown && addr >= start_address) { stack->info.function_offset = addr - start_address; } return true; diff --git a/compiler-rt/test/asan/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp b/compiler-rt/test/asan/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp @@ -0,0 +1,44 @@ +// FIXME(dliew): Duplicated from `test/sanitizer_common/TestCases/Darwin/symbolizer-function-offset-dladdr.cpp`. +// This case can be dropped once sanitizer_common tests work on iOS devices (rdar://problem/47333049). + +// NOTE: `detect_leaks=0` is necessary because with LSan enabled the dladdr +// symbolizer actually leaks memory because the call to +// `__sanitizer::DemangleCXXABI` leaks memory which LSan detects +// (rdar://problem/42868950). + +// RUN: %clangxx_asan %s -O0 -o %t +// RUN: %env_asan_opts=detect_leaks=0,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]*$}}