Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_symbolizer_win.cc ---------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is shared between AddressSanitizer and ThreadSanitizer
     11 // run-time libraries.
     12 // Windows-specific implementation of symbolizer parts.
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "sanitizer_platform.h"
     16 #if SANITIZER_WINDOWS
     17 #define WIN32_LEAN_AND_MEAN
     18 #include <windows.h>
     19 #include <dbghelp.h>
     20 #pragma comment(lib, "dbghelp.lib")
     21 
     22 #include "sanitizer_symbolizer_internal.h"
     23 
     24 namespace __sanitizer {
     25 
     26 namespace {
     27 
     28 class WinSymbolizerTool : public SymbolizerTool {
     29  public:
     30   bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
     31   bool SymbolizeData(uptr addr, DataInfo *info) override {
     32     return false;
     33   }
     34   const char *Demangle(const char *name) override;
     35 };
     36 
     37 bool is_dbghelp_initialized = false;
     38 
     39 bool TrySymInitialize() {
     40   SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
     41   return SymInitialize(GetCurrentProcess(), 0, TRUE);
     42   // FIXME: We don't call SymCleanup() on exit yet - should we?
     43 }
     44 
     45 // Initializes DbgHelp library, if it's not yet initialized. Calls to this
     46 // function should be synchronized with respect to other calls to DbgHelp API
     47 // (e.g. from WinSymbolizerTool).
     48 void InitializeDbgHelpIfNeeded() {
     49   if (is_dbghelp_initialized)
     50     return;
     51   if (!TrySymInitialize()) {
     52     // OK, maybe the client app has called SymInitialize already.
     53     // That's a bit unfortunate for us as all the DbgHelp functions are
     54     // single-threaded and we can't coordinate with the app.
     55     // FIXME: Can we stop the other threads at this point?
     56     // Anyways, we have to reconfigure stuff to make sure that SymInitialize
     57     // has all the appropriate options set.
     58     // Cross our fingers and reinitialize DbgHelp.
     59     Report("*** WARNING: Failed to initialize DbgHelp!              ***\n");
     60     Report("*** Most likely this means that the app is already      ***\n");
     61     Report("*** using DbgHelp, possibly with incompatible flags.    ***\n");
     62     Report("*** Due to technical reasons, symbolization might crash ***\n");
     63     Report("*** or produce wrong results.                           ***\n");
     64     SymCleanup(GetCurrentProcess());
     65     TrySymInitialize();
     66   }
     67   is_dbghelp_initialized = true;
     68 
     69   // When an executable is run from a location different from the one where it
     70   // was originally built, we may not see the nearby PDB files.
     71   // To work around this, let's append the directory of the main module
     72   // to the symbol search path.  All the failures below are not fatal.
     73   const size_t kSymPathSize = 2048;
     74   static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
     75   if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
     76     Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
     77     return;
     78   }
     79   size_t sz = wcslen(path_buffer);
     80   if (sz) {
     81     CHECK_EQ(0, wcscat_s(path_buffer, L";"));
     82     sz++;
     83   }
     84   DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
     85   if (res == 0 || res == MAX_PATH) {
     86     Report("*** WARNING: Failed to getting the EXE directory ***\n");
     87     return;
     88   }
     89   // Write the zero character in place of the last backslash to get the
     90   // directory of the main module at the end of path_buffer.
     91   wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
     92   CHECK_NE(last_bslash, 0);
     93   *last_bslash = L'\0';
     94   if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
     95     Report("*** WARNING: Failed to SymSetSearchPathW\n");
     96     return;
     97   }
     98 }
     99 
    100 }  // namespace
    101 
    102 bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
    103   InitializeDbgHelpIfNeeded();
    104 
    105   // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
    106   char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
    107   PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
    108   symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    109   symbol->MaxNameLen = MAX_SYM_NAME;
    110   DWORD64 offset = 0;
    111   BOOL got_objname = SymFromAddr(GetCurrentProcess(),
    112                                  (DWORD64)addr, &offset, symbol);
    113   if (!got_objname)
    114     return false;
    115 
    116   DWORD unused;
    117   IMAGEHLP_LINE64 line_info;
    118   line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    119   BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
    120                                            &unused, &line_info);
    121   frame->info.function = internal_strdup(symbol->Name);
    122   frame->info.function_offset = (uptr)offset;
    123   if (got_fileline) {
    124     frame->info.file = internal_strdup(line_info.FileName);
    125     frame->info.line = line_info.LineNumber;
    126   }
    127   // Only consider this a successful symbolization attempt if we got file info.
    128   // Otherwise, try llvm-symbolizer.
    129   return got_fileline;
    130 }
    131 
    132 const char *WinSymbolizerTool::Demangle(const char *name) {
    133   CHECK(is_dbghelp_initialized);
    134   static char demangle_buffer[1000];
    135   if (name[0] == '\01' &&
    136       UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
    137                            UNDNAME_NAME_ONLY))
    138     return demangle_buffer;
    139   else
    140     return name;
    141 }
    142 
    143 const char *Symbolizer::PlatformDemangle(const char *name) {
    144   return name;
    145 }
    146 
    147 void Symbolizer::PlatformPrepareForSandboxing() {
    148   // Do nothing.
    149 }
    150 
    151 namespace {
    152 struct ScopedHandle {
    153   ScopedHandle() : h_(nullptr) {}
    154   explicit ScopedHandle(HANDLE h) : h_(h) {}
    155   ~ScopedHandle() {
    156     if (h_)
    157       ::CloseHandle(h_);
    158   }
    159   HANDLE get() { return h_; }
    160   HANDLE *receive() { return &h_; }
    161   HANDLE release() {
    162     HANDLE h = h_;
    163     h_ = nullptr;
    164     return h;
    165   }
    166   HANDLE h_;
    167 };
    168 } // namespace
    169 
    170 bool SymbolizerProcess::StartSymbolizerSubprocess() {
    171   // Create inherited pipes for stdin and stdout.
    172   ScopedHandle stdin_read, stdin_write;
    173   ScopedHandle stdout_read, stdout_write;
    174   SECURITY_ATTRIBUTES attrs;
    175   attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
    176   attrs.bInheritHandle = TRUE;
    177   attrs.lpSecurityDescriptor = nullptr;
    178   if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
    179       !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
    180     VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
    181             SanitizerToolName, path_, GetLastError());
    182     return false;
    183   }
    184 
    185   // Don't inherit the writing end of stdin or the reading end of stdout.
    186   if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
    187       !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
    188     VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
    189             SanitizerToolName, path_, GetLastError());
    190     return false;
    191   }
    192 
    193   // Compute the command line. Wrap double quotes around everything.
    194   const char *argv[kArgVMax];
    195   GetArgV(path_, argv);
    196   InternalScopedString command_line(kMaxPathLength * 3);
    197   for (int i = 0; argv[i]; i++) {
    198     const char *arg = argv[i];
    199     int arglen = internal_strlen(arg);
    200     // Check that tool command lines are simple and that complete escaping is
    201     // unnecessary.
    202     CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
    203     CHECK(!internal_strstr(arg, "\\\\") &&
    204           "double backslashes in args unsupported");
    205     CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
    206           "args ending in backslash and empty args unsupported");
    207     command_line.append("\"%s\" ", arg);
    208   }
    209   VReport(3, "Launching symbolizer command: %s\n", command_line.data());
    210 
    211   // Launch llvm-symbolizer with stdin and stdout redirected.
    212   STARTUPINFOA si;
    213   memset(&si, 0, sizeof(si));
    214   si.cb = sizeof(si);
    215   si.dwFlags |= STARTF_USESTDHANDLES;
    216   si.hStdInput = stdin_read.get();
    217   si.hStdOutput = stdout_write.get();
    218   PROCESS_INFORMATION pi;
    219   memset(&pi, 0, sizeof(pi));
    220   if (!CreateProcessA(path_,               // Executable
    221                       command_line.data(), // Command line
    222                       nullptr,             // Process handle not inheritable
    223                       nullptr,             // Thread handle not inheritable
    224                       TRUE,                // Set handle inheritance to TRUE
    225                       0,                   // Creation flags
    226                       nullptr,             // Use parent's environment block
    227                       nullptr,             // Use parent's starting directory
    228                       &si, &pi)) {
    229     VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
    230             SanitizerToolName, path_, GetLastError());
    231     return false;
    232   }
    233 
    234   // Process creation succeeded, so transfer handle ownership into the fields.
    235   input_fd_ = stdout_read.release();
    236   output_fd_ = stdin_write.release();
    237 
    238   // The llvm-symbolizer process is responsible for quitting itself when the
    239   // stdin pipe is closed, so we don't need these handles. Close them to prevent
    240   // leaks. If we ever want to try to kill the symbolizer process from the
    241   // parent, we'll want to hang on to these handles.
    242   CloseHandle(pi.hProcess);
    243   CloseHandle(pi.hThread);
    244   return true;
    245 }
    246 
    247 static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
    248                                   LowLevelAllocator *allocator) {
    249   if (!common_flags()->symbolize) {
    250     VReport(2, "Symbolizer is disabled.\n");
    251     return;
    252   }
    253 
    254   // Add llvm-symbolizer in case the binary has dwarf.
    255   const char *user_path = common_flags()->external_symbolizer_path;
    256   const char *path =
    257       user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
    258   if (path) {
    259     VReport(2, "Using llvm-symbolizer at %spath: %s\n",
    260             user_path ? "user-specified " : "", path);
    261     list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
    262   } else {
    263     if (user_path && user_path[0] == '\0') {
    264       VReport(2, "External symbolizer is explicitly disabled.\n");
    265     } else {
    266       VReport(2, "External symbolizer is not present.\n");
    267     }
    268   }
    269 
    270   // Add the dbghelp based symbolizer.
    271   list->push_back(new(*allocator) WinSymbolizerTool());
    272 }
    273 
    274 Symbolizer *Symbolizer::PlatformInit() {
    275   IntrusiveList<SymbolizerTool> list;
    276   list.clear();
    277   ChooseSymbolizerTools(&list, &symbolizer_allocator_);
    278 
    279   return new(symbolizer_allocator_) Symbolizer(list);
    280 }
    281 
    282 void Symbolizer::LateInitialize() {
    283   Symbolizer::GetOrInit();
    284 }
    285 
    286 }  // namespace __sanitizer
    287 
    288 #endif  // _WIN32
    289