13cab2bb3Spatrick //===-- sanitizer_symbolizer_win.cpp --------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is shared between AddressSanitizer and ThreadSanitizer
103cab2bb3Spatrick // run-time libraries.
113cab2bb3Spatrick // Windows-specific implementation of symbolizer parts.
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick
143cab2bb3Spatrick #include "sanitizer_platform.h"
153cab2bb3Spatrick #if SANITIZER_WINDOWS
163cab2bb3Spatrick
173cab2bb3Spatrick #include "sanitizer_dbghelp.h"
183cab2bb3Spatrick #include "sanitizer_symbolizer_internal.h"
193cab2bb3Spatrick
203cab2bb3Spatrick namespace __sanitizer {
213cab2bb3Spatrick
223cab2bb3Spatrick decltype(::StackWalk64) *StackWalk64;
233cab2bb3Spatrick decltype(::SymCleanup) *SymCleanup;
243cab2bb3Spatrick decltype(::SymFromAddr) *SymFromAddr;
253cab2bb3Spatrick decltype(::SymFunctionTableAccess64) *SymFunctionTableAccess64;
263cab2bb3Spatrick decltype(::SymGetLineFromAddr64) *SymGetLineFromAddr64;
273cab2bb3Spatrick decltype(::SymGetModuleBase64) *SymGetModuleBase64;
283cab2bb3Spatrick decltype(::SymGetSearchPathW) *SymGetSearchPathW;
293cab2bb3Spatrick decltype(::SymInitialize) *SymInitialize;
303cab2bb3Spatrick decltype(::SymSetOptions) *SymSetOptions;
313cab2bb3Spatrick decltype(::SymSetSearchPathW) *SymSetSearchPathW;
323cab2bb3Spatrick decltype(::UnDecorateSymbolName) *UnDecorateSymbolName;
333cab2bb3Spatrick
343cab2bb3Spatrick namespace {
353cab2bb3Spatrick
36d89ec533Spatrick class WinSymbolizerTool final : public SymbolizerTool {
373cab2bb3Spatrick public:
383cab2bb3Spatrick // The constructor is provided to avoid synthesized memsets.
WinSymbolizerTool()393cab2bb3Spatrick WinSymbolizerTool() {}
403cab2bb3Spatrick
413cab2bb3Spatrick bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
SymbolizeData(uptr addr,DataInfo * info)423cab2bb3Spatrick bool SymbolizeData(uptr addr, DataInfo *info) override {
433cab2bb3Spatrick return false;
443cab2bb3Spatrick }
453cab2bb3Spatrick const char *Demangle(const char *name) override;
463cab2bb3Spatrick };
473cab2bb3Spatrick
483cab2bb3Spatrick bool is_dbghelp_initialized = false;
493cab2bb3Spatrick
TrySymInitialize()503cab2bb3Spatrick bool TrySymInitialize() {
513cab2bb3Spatrick SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
523cab2bb3Spatrick return SymInitialize(GetCurrentProcess(), 0, TRUE);
533cab2bb3Spatrick // FIXME: We don't call SymCleanup() on exit yet - should we?
543cab2bb3Spatrick }
553cab2bb3Spatrick
563cab2bb3Spatrick } // namespace
573cab2bb3Spatrick
583cab2bb3Spatrick // Initializes DbgHelp library, if it's not yet initialized. Calls to this
593cab2bb3Spatrick // function should be synchronized with respect to other calls to DbgHelp API
603cab2bb3Spatrick // (e.g. from WinSymbolizerTool).
InitializeDbgHelpIfNeeded()613cab2bb3Spatrick void InitializeDbgHelpIfNeeded() {
623cab2bb3Spatrick if (is_dbghelp_initialized)
633cab2bb3Spatrick return;
643cab2bb3Spatrick
653cab2bb3Spatrick HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
663cab2bb3Spatrick CHECK(dbghelp && "failed to load dbghelp.dll");
673cab2bb3Spatrick
683cab2bb3Spatrick #define DBGHELP_IMPORT(name) \
693cab2bb3Spatrick do { \
703cab2bb3Spatrick name = \
713cab2bb3Spatrick reinterpret_cast<decltype(::name) *>(GetProcAddress(dbghelp, #name)); \
723cab2bb3Spatrick CHECK(name != nullptr); \
733cab2bb3Spatrick } while (0)
743cab2bb3Spatrick DBGHELP_IMPORT(StackWalk64);
753cab2bb3Spatrick DBGHELP_IMPORT(SymCleanup);
763cab2bb3Spatrick DBGHELP_IMPORT(SymFromAddr);
773cab2bb3Spatrick DBGHELP_IMPORT(SymFunctionTableAccess64);
783cab2bb3Spatrick DBGHELP_IMPORT(SymGetLineFromAddr64);
793cab2bb3Spatrick DBGHELP_IMPORT(SymGetModuleBase64);
803cab2bb3Spatrick DBGHELP_IMPORT(SymGetSearchPathW);
813cab2bb3Spatrick DBGHELP_IMPORT(SymInitialize);
823cab2bb3Spatrick DBGHELP_IMPORT(SymSetOptions);
833cab2bb3Spatrick DBGHELP_IMPORT(SymSetSearchPathW);
843cab2bb3Spatrick DBGHELP_IMPORT(UnDecorateSymbolName);
853cab2bb3Spatrick #undef DBGHELP_IMPORT
863cab2bb3Spatrick
873cab2bb3Spatrick if (!TrySymInitialize()) {
883cab2bb3Spatrick // OK, maybe the client app has called SymInitialize already.
893cab2bb3Spatrick // That's a bit unfortunate for us as all the DbgHelp functions are
903cab2bb3Spatrick // single-threaded and we can't coordinate with the app.
913cab2bb3Spatrick // FIXME: Can we stop the other threads at this point?
923cab2bb3Spatrick // Anyways, we have to reconfigure stuff to make sure that SymInitialize
933cab2bb3Spatrick // has all the appropriate options set.
943cab2bb3Spatrick // Cross our fingers and reinitialize DbgHelp.
953cab2bb3Spatrick Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
963cab2bb3Spatrick Report("*** Most likely this means that the app is already ***\n");
973cab2bb3Spatrick Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
983cab2bb3Spatrick Report("*** Due to technical reasons, symbolization might crash ***\n");
993cab2bb3Spatrick Report("*** or produce wrong results. ***\n");
1003cab2bb3Spatrick SymCleanup(GetCurrentProcess());
1013cab2bb3Spatrick TrySymInitialize();
1023cab2bb3Spatrick }
1033cab2bb3Spatrick is_dbghelp_initialized = true;
1043cab2bb3Spatrick
1053cab2bb3Spatrick // When an executable is run from a location different from the one where it
1063cab2bb3Spatrick // was originally built, we may not see the nearby PDB files.
1073cab2bb3Spatrick // To work around this, let's append the directory of the main module
1083cab2bb3Spatrick // to the symbol search path. All the failures below are not fatal.
1093cab2bb3Spatrick const size_t kSymPathSize = 2048;
1103cab2bb3Spatrick static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
1113cab2bb3Spatrick if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
1123cab2bb3Spatrick Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
1133cab2bb3Spatrick return;
1143cab2bb3Spatrick }
1153cab2bb3Spatrick size_t sz = wcslen(path_buffer);
1163cab2bb3Spatrick if (sz) {
1173cab2bb3Spatrick CHECK_EQ(0, wcscat_s(path_buffer, L";"));
1183cab2bb3Spatrick sz++;
1193cab2bb3Spatrick }
1203cab2bb3Spatrick DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
1213cab2bb3Spatrick if (res == 0 || res == MAX_PATH) {
1223cab2bb3Spatrick Report("*** WARNING: Failed to getting the EXE directory ***\n");
1233cab2bb3Spatrick return;
1243cab2bb3Spatrick }
1253cab2bb3Spatrick // Write the zero character in place of the last backslash to get the
1263cab2bb3Spatrick // directory of the main module at the end of path_buffer.
1273cab2bb3Spatrick wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
1283cab2bb3Spatrick CHECK_NE(last_bslash, 0);
1293cab2bb3Spatrick *last_bslash = L'\0';
1303cab2bb3Spatrick if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
1313cab2bb3Spatrick Report("*** WARNING: Failed to SymSetSearchPathW\n");
1323cab2bb3Spatrick return;
1333cab2bb3Spatrick }
1343cab2bb3Spatrick }
1353cab2bb3Spatrick
SymbolizePC(uptr addr,SymbolizedStack * frame)1363cab2bb3Spatrick bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
1373cab2bb3Spatrick InitializeDbgHelpIfNeeded();
1383cab2bb3Spatrick
139d89ec533Spatrick // See https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-symbol-information-by-address
140d89ec533Spatrick InternalMmapVector<char> buffer(sizeof(SYMBOL_INFO) +
141d89ec533Spatrick MAX_SYM_NAME * sizeof(CHAR));
142d89ec533Spatrick PSYMBOL_INFO symbol = (PSYMBOL_INFO)&buffer[0];
1433cab2bb3Spatrick symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
1443cab2bb3Spatrick symbol->MaxNameLen = MAX_SYM_NAME;
1453cab2bb3Spatrick DWORD64 offset = 0;
1463cab2bb3Spatrick BOOL got_objname = SymFromAddr(GetCurrentProcess(),
1473cab2bb3Spatrick (DWORD64)addr, &offset, symbol);
1483cab2bb3Spatrick if (!got_objname)
1493cab2bb3Spatrick return false;
1503cab2bb3Spatrick
1513cab2bb3Spatrick DWORD unused;
1523cab2bb3Spatrick IMAGEHLP_LINE64 line_info;
1533cab2bb3Spatrick line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
1543cab2bb3Spatrick BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
1553cab2bb3Spatrick &unused, &line_info);
1563cab2bb3Spatrick frame->info.function = internal_strdup(symbol->Name);
1573cab2bb3Spatrick frame->info.function_offset = (uptr)offset;
1583cab2bb3Spatrick if (got_fileline) {
1593cab2bb3Spatrick frame->info.file = internal_strdup(line_info.FileName);
1603cab2bb3Spatrick frame->info.line = line_info.LineNumber;
1613cab2bb3Spatrick }
1623cab2bb3Spatrick // Only consider this a successful symbolization attempt if we got file info.
1633cab2bb3Spatrick // Otherwise, try llvm-symbolizer.
1643cab2bb3Spatrick return got_fileline;
1653cab2bb3Spatrick }
1663cab2bb3Spatrick
Demangle(const char * name)1673cab2bb3Spatrick const char *WinSymbolizerTool::Demangle(const char *name) {
1683cab2bb3Spatrick CHECK(is_dbghelp_initialized);
1693cab2bb3Spatrick static char demangle_buffer[1000];
1703cab2bb3Spatrick if (name[0] == '\01' &&
1713cab2bb3Spatrick UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
1723cab2bb3Spatrick UNDNAME_NAME_ONLY))
1733cab2bb3Spatrick return demangle_buffer;
1743cab2bb3Spatrick else
1753cab2bb3Spatrick return name;
1763cab2bb3Spatrick }
1773cab2bb3Spatrick
PlatformDemangle(const char * name)1783cab2bb3Spatrick const char *Symbolizer::PlatformDemangle(const char *name) {
1793cab2bb3Spatrick return name;
1803cab2bb3Spatrick }
1813cab2bb3Spatrick
1823cab2bb3Spatrick namespace {
1833cab2bb3Spatrick struct ScopedHandle {
ScopedHandle__sanitizer::__anon689dacdf0211::ScopedHandle1843cab2bb3Spatrick ScopedHandle() : h_(nullptr) {}
ScopedHandle__sanitizer::__anon689dacdf0211::ScopedHandle1853cab2bb3Spatrick explicit ScopedHandle(HANDLE h) : h_(h) {}
~ScopedHandle__sanitizer::__anon689dacdf0211::ScopedHandle1863cab2bb3Spatrick ~ScopedHandle() {
1873cab2bb3Spatrick if (h_)
1883cab2bb3Spatrick ::CloseHandle(h_);
1893cab2bb3Spatrick }
get__sanitizer::__anon689dacdf0211::ScopedHandle1903cab2bb3Spatrick HANDLE get() { return h_; }
receive__sanitizer::__anon689dacdf0211::ScopedHandle1913cab2bb3Spatrick HANDLE *receive() { return &h_; }
release__sanitizer::__anon689dacdf0211::ScopedHandle1923cab2bb3Spatrick HANDLE release() {
1933cab2bb3Spatrick HANDLE h = h_;
1943cab2bb3Spatrick h_ = nullptr;
1953cab2bb3Spatrick return h;
1963cab2bb3Spatrick }
1973cab2bb3Spatrick HANDLE h_;
1983cab2bb3Spatrick };
1993cab2bb3Spatrick } // namespace
2003cab2bb3Spatrick
StartSymbolizerSubprocess()2013cab2bb3Spatrick bool SymbolizerProcess::StartSymbolizerSubprocess() {
2023cab2bb3Spatrick // Create inherited pipes for stdin and stdout.
2033cab2bb3Spatrick ScopedHandle stdin_read, stdin_write;
2043cab2bb3Spatrick ScopedHandle stdout_read, stdout_write;
2053cab2bb3Spatrick SECURITY_ATTRIBUTES attrs;
2063cab2bb3Spatrick attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
2073cab2bb3Spatrick attrs.bInheritHandle = TRUE;
2083cab2bb3Spatrick attrs.lpSecurityDescriptor = nullptr;
2093cab2bb3Spatrick if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
2103cab2bb3Spatrick !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
2113cab2bb3Spatrick VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
2123cab2bb3Spatrick SanitizerToolName, path_, GetLastError());
2133cab2bb3Spatrick return false;
2143cab2bb3Spatrick }
2153cab2bb3Spatrick
2163cab2bb3Spatrick // Don't inherit the writing end of stdin or the reading end of stdout.
2173cab2bb3Spatrick if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
2183cab2bb3Spatrick !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
2193cab2bb3Spatrick VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
2203cab2bb3Spatrick SanitizerToolName, path_, GetLastError());
2213cab2bb3Spatrick return false;
2223cab2bb3Spatrick }
2233cab2bb3Spatrick
2243cab2bb3Spatrick // Compute the command line. Wrap double quotes around everything.
2253cab2bb3Spatrick const char *argv[kArgVMax];
2263cab2bb3Spatrick GetArgV(path_, argv);
227d89ec533Spatrick InternalScopedString command_line;
2283cab2bb3Spatrick for (int i = 0; argv[i]; i++) {
2293cab2bb3Spatrick const char *arg = argv[i];
2303cab2bb3Spatrick int arglen = internal_strlen(arg);
2313cab2bb3Spatrick // Check that tool command lines are simple and that complete escaping is
2323cab2bb3Spatrick // unnecessary.
2333cab2bb3Spatrick CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
2343cab2bb3Spatrick CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
2353cab2bb3Spatrick "args ending in backslash and empty args unsupported");
2363cab2bb3Spatrick command_line.append("\"%s\" ", arg);
2373cab2bb3Spatrick }
2383cab2bb3Spatrick VReport(3, "Launching symbolizer command: %s\n", command_line.data());
2393cab2bb3Spatrick
2403cab2bb3Spatrick // Launch llvm-symbolizer with stdin and stdout redirected.
2413cab2bb3Spatrick STARTUPINFOA si;
2423cab2bb3Spatrick memset(&si, 0, sizeof(si));
2433cab2bb3Spatrick si.cb = sizeof(si);
2443cab2bb3Spatrick si.dwFlags |= STARTF_USESTDHANDLES;
2453cab2bb3Spatrick si.hStdInput = stdin_read.get();
2463cab2bb3Spatrick si.hStdOutput = stdout_write.get();
2473cab2bb3Spatrick PROCESS_INFORMATION pi;
2483cab2bb3Spatrick memset(&pi, 0, sizeof(pi));
2493cab2bb3Spatrick if (!CreateProcessA(path_, // Executable
2503cab2bb3Spatrick command_line.data(), // Command line
2513cab2bb3Spatrick nullptr, // Process handle not inheritable
2523cab2bb3Spatrick nullptr, // Thread handle not inheritable
2533cab2bb3Spatrick TRUE, // Set handle inheritance to TRUE
2543cab2bb3Spatrick 0, // Creation flags
2553cab2bb3Spatrick nullptr, // Use parent's environment block
2563cab2bb3Spatrick nullptr, // Use parent's starting directory
2573cab2bb3Spatrick &si, &pi)) {
2583cab2bb3Spatrick VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
2593cab2bb3Spatrick SanitizerToolName, path_, GetLastError());
2603cab2bb3Spatrick return false;
2613cab2bb3Spatrick }
2623cab2bb3Spatrick
2633cab2bb3Spatrick // Process creation succeeded, so transfer handle ownership into the fields.
2643cab2bb3Spatrick input_fd_ = stdout_read.release();
2653cab2bb3Spatrick output_fd_ = stdin_write.release();
2663cab2bb3Spatrick
2673cab2bb3Spatrick // The llvm-symbolizer process is responsible for quitting itself when the
2683cab2bb3Spatrick // stdin pipe is closed, so we don't need these handles. Close them to prevent
2693cab2bb3Spatrick // leaks. If we ever want to try to kill the symbolizer process from the
2703cab2bb3Spatrick // parent, we'll want to hang on to these handles.
2713cab2bb3Spatrick CloseHandle(pi.hProcess);
2723cab2bb3Spatrick CloseHandle(pi.hThread);
2733cab2bb3Spatrick return true;
2743cab2bb3Spatrick }
2753cab2bb3Spatrick
ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> * list,LowLevelAllocator * allocator)2763cab2bb3Spatrick static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
2773cab2bb3Spatrick LowLevelAllocator *allocator) {
2783cab2bb3Spatrick if (!common_flags()->symbolize) {
2793cab2bb3Spatrick VReport(2, "Symbolizer is disabled.\n");
2803cab2bb3Spatrick return;
2813cab2bb3Spatrick }
2823cab2bb3Spatrick
283d89ec533Spatrick // Add llvm-symbolizer.
2843cab2bb3Spatrick const char *user_path = common_flags()->external_symbolizer_path;
285d89ec533Spatrick
286d89ec533Spatrick if (user_path && internal_strchr(user_path, '%')) {
287d89ec533Spatrick char *new_path = (char *)InternalAlloc(kMaxPathLength);
288d89ec533Spatrick SubstituteForFlagValue(user_path, new_path, kMaxPathLength);
289d89ec533Spatrick user_path = new_path;
290d89ec533Spatrick }
291d89ec533Spatrick
2923cab2bb3Spatrick const char *path =
2933cab2bb3Spatrick user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
2943cab2bb3Spatrick if (path) {
2953cab2bb3Spatrick VReport(2, "Using llvm-symbolizer at %spath: %s\n",
2963cab2bb3Spatrick user_path ? "user-specified " : "", path);
2973cab2bb3Spatrick list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
2983cab2bb3Spatrick } else {
2993cab2bb3Spatrick if (user_path && user_path[0] == '\0') {
3003cab2bb3Spatrick VReport(2, "External symbolizer is explicitly disabled.\n");
3013cab2bb3Spatrick } else {
3023cab2bb3Spatrick VReport(2, "External symbolizer is not present.\n");
3033cab2bb3Spatrick }
3043cab2bb3Spatrick }
3053cab2bb3Spatrick
3063cab2bb3Spatrick // Add the dbghelp based symbolizer.
3073cab2bb3Spatrick list->push_back(new(*allocator) WinSymbolizerTool());
3083cab2bb3Spatrick }
3093cab2bb3Spatrick
PlatformInit()3103cab2bb3Spatrick Symbolizer *Symbolizer::PlatformInit() {
3113cab2bb3Spatrick IntrusiveList<SymbolizerTool> list;
3123cab2bb3Spatrick list.clear();
3133cab2bb3Spatrick ChooseSymbolizerTools(&list, &symbolizer_allocator_);
3143cab2bb3Spatrick
3153cab2bb3Spatrick return new(symbolizer_allocator_) Symbolizer(list);
3163cab2bb3Spatrick }
3173cab2bb3Spatrick
LateInitialize()3183cab2bb3Spatrick void Symbolizer::LateInitialize() {
319*810390e3Srobert Symbolizer::GetOrInit();
3203cab2bb3Spatrick }
3213cab2bb3Spatrick
3223cab2bb3Spatrick } // namespace __sanitizer
3233cab2bb3Spatrick
3243cab2bb3Spatrick #endif // _WIN32
325