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 (path && internal_strncmp(path, kPrefix, prefixLen) == 0) { + InternalScopedString buffer(kMaxPathLength); + if (ReadBinaryDir(buffer.data(), kMaxPathLength)) { + internal_strncat(buffer.data(), path + prefixLen + 1, buffer.size()); + path = internal_strdup(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,52 @@ +// 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 && %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=FOUND +// RUN: rm -rf %t.bin/llvm-symbolizer +// RUN: cd .. +// RUN: %clangxx -O0 %s -o %t && cd %t.dir && %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=NOT-FOUND + +#include +#include + +static const char sanitizer_options[] = + "external_symbolizer_path=@BINARY_DIR/external_symbolizer_path.cpp.tmp.bin/" + "llvm-symbolizer"; + +extern "C" __attribute__((visibility("default"))) +const char * +__asan_default_options() { return sanitizer_options; } + +extern "C" __attribute__((visibility("default"))) +const char * +__tsan_default_options() { return sanitizer_options; } + +extern "C" __attribute__((visibility("default"))) +const char * +__lsan_default_options() { return sanitizer_options; } + +extern "C" __attribute__((visibility("default"))) +const char * +__msan_default_options() { return sanitizer_options; } + +extern "C" __attribute__((visibility("default"))) +const char * +__ubsan_default_options() { return sanitizer_options; } + +static void Symbolize() { + char buffer[100]; + __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", buffer, + sizeof(buffer)); + printf("%s\n", buffer); +} + +int main() { + // FOUND: {{0x.* in main}} + // NOT-FOUND: WARNING: invalid path to external symbolizer! + Symbolize(); +}