diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -248,6 +248,7 @@ // OS uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); +uptr ReadBinaryDir(/*out*/ char *buf, uptr buf_len); uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len); const char *GetProcessName(); void UpdateProcessName(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp @@ -274,6 +274,14 @@ return name_len; } +uptr ReadBinaryDir(/*out*/ char *buf, uptr buf_len) { + ReadBinaryNameCached(buf, buf_len); + const char *exec_name_pos = StripModuleName(buf); + uptr name_len = exec_name_pos - buf; + buf[name_len] = '\0'; + return name_len; +} + #if !SANITIZER_GO void PrintCmdline() { char **argv = GetArgv(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc @@ -26,7 +26,7 @@ COMMON_FLAG( const char *, external_symbolizer_path, nullptr, "Path to external symbolizer. If empty, the tool will search $PATH for " - "the symbolizer.") + "the symbolizer. @BINARY_DIR expands to the path to the current binary.") COMMON_FLAG( bool, allow_addr2line, false, "If set, allows online symbolizer to run addr2line binary to symbolize " diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp @@ -400,6 +400,17 @@ static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) { const char *path = common_flags()->external_symbolizer_path; + + static const char kPrefix[] = "@BINARY_DIR"; + uptr prefixLen = ARRAY_SIZE(kPrefix) - 1; + if (internal_strncmp(path, kPrefix, prefixLen) == 0) { + InternalScopedString buffer(kMaxPathLength); + if (ReadBinaryDir(buffer.data(), kMaxPathLength)) { + internal_strncat(buffer.data(), path + prefixLen, buffer.size()); + path = buffer.data(); + } + } + const char *binary_name = path ? StripModuleName(path) : ""; if (path && path[0] == '\0') { VReport(2, "External symbolizer is explicitly disabled.\n"); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cpp @@ -288,8 +288,19 @@ return; } - // Add llvm-symbolizer in case the binary has dwarf. + // Add llvm-symbolizer. const char *user_path = common_flags()->external_symbolizer_path; + + static const char kPrefix[] = "@BINARY_DIR"; + uptr prefixLen = ARRAY_SIZE(kPrefix) - 1; + if (internal_strncmp(user_path, kPrefix, prefixLen) == 0) { + InternalScopedString buffer(kMaxPathLength); + if (ReadBinaryDir(buffer.data(), kMaxPathLength)) { + internal_strncat(buffer.data(), user_path + prefixLen, buffer.size()); + user_path = buffer.data(); + } + } + const char *path = user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe"); if (path) { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_win.cpp @@ -1049,10 +1049,16 @@ } uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { - // FIXME: Actually implement this function. - CHECK_GT(buf_len, 0); - buf[0] = 0; - return 0; + // Get the UTF-16 path and convert to UTF-8. + wchar_t binname_utf16[kMaxPathLength]; + int binname_utf16_len = + GetModuleFileNameW(NULL, binname_utf16, kMaxPathLength); + if (binname_utf16_len == 0) + binname_utf16[0] = '\0'; + int binary_name_len = + ::WideCharToMultiByte(CP_UTF8, 0, binname_utf16, binname_utf16_len + 1, + &buf[0], kMaxPathLength, NULL, NULL); + return binary_name_len; } uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp @@ -461,12 +461,9 @@ EXPECT_DEATH(address_range.Unmap(base_addr + (PageSize * 2), PageSize), ".*"); } -// Windows has no working ReadBinaryName. -#if !SANITIZER_WINDOWS TEST(SanitizerCommon, ReadBinaryNameCached) { char buf[256]; EXPECT_NE((uptr)0, ReadBinaryNameCached(buf, sizeof(buf))); } -#endif } // namespace __sanitizer diff --git a/compiler-rt/test/sanitizer_common/TestCases/external_symbolizer_path.cpp b/compiler-rt/test/sanitizer_common/TestCases/external_symbolizer_path.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/external_symbolizer_path.cpp @@ -0,0 +1,31 @@ +// REQUIRES: shell +// RUN: rm -rf %t.bin +// RUN: mkdir %t.bin +// RUN: cp $(which llvm-symbolizer) %t.bin +// RUN: rm -rf %t.dir +// RUN: mkdir %t.dir +// RUN: %clangxx -O0 %s -o %t && cd %t.dir && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=CHECK,FOUND +// RUN: rm -rf %t.bin/llvm-symbolizer +// RUN: cd .. +// RUN: %clangxx -O0 %s -o %t && cd %t.dir && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=CHECK,NOT-FOUND + +#include + +extern "C" __attribute__((visibility("default"))) +const char * +__asan_default_options() { + return "symbolize=1 external_symbolizer_path=" + "@BINARY_DIR/external_symbolizer_path.cpp.tmp.bin/llvm-symbolizer"; +} + +int main() { + char *x = (char *)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // FOUND: {{ #0 0x.* in main}} + // NOT-FOUND: WARNING: invalid path to external symbolizer! +}