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