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"
15 #define WIN32_LEAN_AND_MEAN
18 #pragma comment(lib, "dbghelp.lib")
20 #include "sanitizer_symbolizer_internal.h"
22 namespace __sanitizer
{
26 class WinSymbolizerTool
: public SymbolizerTool
{
28 bool SymbolizePC(uptr addr
, SymbolizedStack
*stack
) override
;
29 bool SymbolizeData(uptr addr
, DataInfo
*info
) override
{
32 const char *Demangle(const char *name
) override
;
35 bool is_dbghelp_initialized
= false;
37 bool TrySymInitialize() {
38 SymSetOptions(SYMOPT_DEFERRED_LOADS
| SYMOPT_UNDNAME
| SYMOPT_LOAD_LINES
);
39 return SymInitialize(GetCurrentProcess(), 0, TRUE
);
40 // FIXME: We don't call SymCleanup() on exit yet - should we?
43 // Initializes DbgHelp library, if it's not yet initialized. Calls to this
44 // function should be synchronized with respect to other calls to DbgHelp API
45 // (e.g. from WinSymbolizerTool).
46 void InitializeDbgHelpIfNeeded() {
47 if (is_dbghelp_initialized
)
49 if (!TrySymInitialize()) {
50 // OK, maybe the client app has called SymInitialize already.
51 // That's a bit unfortunate for us as all the DbgHelp functions are
52 // single-threaded and we can't coordinate with the app.
53 // FIXME: Can we stop the other threads at this point?
54 // Anyways, we have to reconfigure stuff to make sure that SymInitialize
55 // has all the appropriate options set.
56 // Cross our fingers and reinitialize DbgHelp.
57 Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
58 Report("*** Most likely this means that the app is already ***\n");
59 Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
60 Report("*** Due to technical reasons, symbolization might crash ***\n");
61 Report("*** or produce wrong results. ***\n");
62 SymCleanup(GetCurrentProcess());
65 is_dbghelp_initialized
= true;
67 // When an executable is run from a location different from the one where it
68 // was originally built, we may not see the nearby PDB files.
69 // To work around this, let's append the directory of the main module
70 // to the symbol search path. All the failures below are not fatal.
71 const size_t kSymPathSize
= 2048;
72 static wchar_t path_buffer
[kSymPathSize
+ 1 + MAX_PATH
];
73 if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer
, kSymPathSize
)) {
74 Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
77 size_t sz
= wcslen(path_buffer
);
79 CHECK_EQ(0, wcscat_s(path_buffer
, L
";"));
82 DWORD res
= GetModuleFileNameW(NULL
, path_buffer
+ sz
, MAX_PATH
);
83 if (res
== 0 || res
== MAX_PATH
) {
84 Report("*** WARNING: Failed to getting the EXE directory ***\n");
87 // Write the zero character in place of the last backslash to get the
88 // directory of the main module at the end of path_buffer.
89 wchar_t *last_bslash
= wcsrchr(path_buffer
+ sz
, L
'\\');
90 CHECK_NE(last_bslash
, 0);
92 if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer
)) {
93 Report("*** WARNING: Failed to SymSetSearchPathW\n");
100 bool WinSymbolizerTool::SymbolizePC(uptr addr
, SymbolizedStack
*frame
) {
101 InitializeDbgHelpIfNeeded();
103 // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
104 char buffer
[sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
* sizeof(CHAR
)];
105 PSYMBOL_INFO symbol
= (PSYMBOL_INFO
)buffer
;
106 symbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
107 symbol
->MaxNameLen
= MAX_SYM_NAME
;
109 BOOL got_objname
= SymFromAddr(GetCurrentProcess(),
110 (DWORD64
)addr
, &offset
, symbol
);
115 IMAGEHLP_LINE64 line_info
;
116 line_info
.SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
117 BOOL got_fileline
= SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64
)addr
,
118 &unused
, &line_info
);
119 frame
->info
.function
= internal_strdup(symbol
->Name
);
120 frame
->info
.function_offset
= (uptr
)offset
;
122 frame
->info
.file
= internal_strdup(line_info
.FileName
);
123 frame
->info
.line
= line_info
.LineNumber
;
125 // Only consider this a successful symbolization attempt if we got file info.
126 // Otherwise, try llvm-symbolizer.
130 const char *WinSymbolizerTool::Demangle(const char *name
) {
131 CHECK(is_dbghelp_initialized
);
132 static char demangle_buffer
[1000];
133 if (name
[0] == '\01' &&
134 UnDecorateSymbolName(name
+ 1, demangle_buffer
, sizeof(demangle_buffer
),
136 return demangle_buffer
;
141 const char *Symbolizer::PlatformDemangle(const char *name
) {
145 void Symbolizer::PlatformPrepareForSandboxing() {
150 struct ScopedHandle
{
151 ScopedHandle() : h_(nullptr) {}
152 explicit ScopedHandle(HANDLE h
) : h_(h
) {}
157 HANDLE
get() { return h_
; }
158 HANDLE
*receive() { return &h_
; }
168 bool SymbolizerProcess::StartSymbolizerSubprocess() {
169 // Create inherited pipes for stdin and stdout.
170 ScopedHandle stdin_read
, stdin_write
;
171 ScopedHandle stdout_read
, stdout_write
;
172 SECURITY_ATTRIBUTES attrs
;
173 attrs
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
174 attrs
.bInheritHandle
= TRUE
;
175 attrs
.lpSecurityDescriptor
= nullptr;
176 if (!::CreatePipe(stdin_read
.receive(), stdin_write
.receive(), &attrs
, 0) ||
177 !::CreatePipe(stdout_read
.receive(), stdout_write
.receive(), &attrs
, 0)) {
178 VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
179 SanitizerToolName
, path_
, GetLastError());
183 // Don't inherit the writing end of stdin or the reading end of stdout.
184 if (!SetHandleInformation(stdin_write
.get(), HANDLE_FLAG_INHERIT
, 0) ||
185 !SetHandleInformation(stdout_read
.get(), HANDLE_FLAG_INHERIT
, 0)) {
186 VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
187 SanitizerToolName
, path_
, GetLastError());
191 // Compute the command line. Wrap double quotes around everything.
192 const char *argv
[kArgVMax
];
193 GetArgV(path_
, argv
);
194 InternalScopedString
command_line(kMaxPathLength
* 3);
195 for (int i
= 0; argv
[i
]; i
++) {
196 const char *arg
= argv
[i
];
197 int arglen
= internal_strlen(arg
);
198 // Check that tool command lines are simple and that complete escaping is
200 CHECK(!internal_strchr(arg
, '"') && "quotes in args unsupported");
201 CHECK(!internal_strstr(arg
, "\\\\") &&
202 "double backslashes in args unsupported");
203 CHECK(arglen
> 0 && arg
[arglen
- 1] != '\\' &&
204 "args ending in backslash and empty args unsupported");
205 command_line
.append("\"%s\" ", arg
);
207 VReport(3, "Launching symbolizer command: %s\n", command_line
.data());
209 // Launch llvm-symbolizer with stdin and stdout redirected.
211 memset(&si
, 0, sizeof(si
));
213 si
.dwFlags
|= STARTF_USESTDHANDLES
;
214 si
.hStdInput
= stdin_read
.get();
215 si
.hStdOutput
= stdout_write
.get();
216 PROCESS_INFORMATION pi
;
217 memset(&pi
, 0, sizeof(pi
));
218 if (!CreateProcessA(path_
, // Executable
219 command_line
.data(), // Command line
220 nullptr, // Process handle not inheritable
221 nullptr, // Thread handle not inheritable
222 TRUE
, // Set handle inheritance to TRUE
224 nullptr, // Use parent's environment block
225 nullptr, // Use parent's starting directory
227 VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
228 SanitizerToolName
, path_
, GetLastError());
232 // Process creation succeeded, so transfer handle ownership into the fields.
233 input_fd_
= stdout_read
.release();
234 output_fd_
= stdin_write
.release();
236 // The llvm-symbolizer process is responsible for quitting itself when the
237 // stdin pipe is closed, so we don't need these handles. Close them to prevent
238 // leaks. If we ever want to try to kill the symbolizer process from the
239 // parent, we'll want to hang on to these handles.
240 CloseHandle(pi
.hProcess
);
241 CloseHandle(pi
.hThread
);
245 static void ChooseSymbolizerTools(IntrusiveList
<SymbolizerTool
> *list
,
246 LowLevelAllocator
*allocator
) {
247 if (!common_flags()->symbolize
) {
248 VReport(2, "Symbolizer is disabled.\n");
252 // Add llvm-symbolizer in case the binary has dwarf.
253 const char *user_path
= common_flags()->external_symbolizer_path
;
255 user_path
? user_path
: FindPathToBinary("llvm-symbolizer.exe");
257 VReport(2, "Using llvm-symbolizer at %spath: %s\n",
258 user_path
? "user-specified " : "", path
);
259 list
->push_back(new(*allocator
) LLVMSymbolizer(path
, allocator
));
261 if (user_path
&& user_path
[0] == '\0') {
262 VReport(2, "External symbolizer is explicitly disabled.\n");
264 VReport(2, "External symbolizer is not present.\n");
268 // Add the dbghelp based symbolizer.
269 list
->push_back(new(*allocator
) WinSymbolizerTool());
272 Symbolizer
*Symbolizer::PlatformInit() {
273 IntrusiveList
<SymbolizerTool
> list
;
275 ChooseSymbolizerTools(&list
, &symbolizer_allocator_
);
277 return new(symbolizer_allocator_
) Symbolizer(list
);
280 } // namespace __sanitizer