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