1 // Copyright 2007 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // ms_symbol_server_converter.cc: Obtain symbol files from a Microsoft
30 // symbol server, and convert them to Breakpad's dumped format.
31 //
32 // See ms_symbol_server_converter.h for documentation.
33 //
34 // Author: Mark Mentovai
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h> // Must come first
38 #endif
39
40 #include <windows.h>
41 #include <dbghelp.h>
42 #include <pathcch.h>
43
44 #include <cassert>
45 #include <cstdio>
46
47 #include "tools/windows/converter/ms_symbol_server_converter.h"
48 #include "common/windows/pdb_source_line_writer.h"
49 #include "common/windows/pe_source_line_writer.h"
50 #include "common/windows/string_utils-inl.h"
51
52 // SYMOPT_NO_PROMPTS is not defined in earlier platform SDKs. Define it
53 // in that case, in the event that this code is used with a newer version
54 // of DbgHelp at runtime that recognizes the option. The presence of this
55 // bit in the symbol options should not harm earlier versions of DbgHelp.
56 #ifndef SYMOPT_NO_PROMPTS
57 #define SYMOPT_NO_PROMPTS 0x00080000
58 #endif // SYMOPT_NO_PROMPTS
59
60 namespace {
61
GetExeDirectory()62 std::wstring GetExeDirectory() {
63 wchar_t directory[MAX_PATH];
64
65 // Get path to this process exe.
66 DWORD result = GetModuleFileName(/*hModule=*/nullptr, directory, MAX_PATH);
67 if (result <= 0 || result == MAX_PATH) {
68 fprintf(stderr,
69 "GetExeDirectory: failed to get path to process exe.\n");
70 return L"";
71 }
72 HRESULT hr = PathCchRemoveFileSpec(directory, result + 1);
73 if (hr != S_OK) {
74 fprintf(stderr,
75 "GetExeDirectory: failed to remove basename from path '%ls'.\n",
76 directory);
77 return L"";
78 }
79
80 return std::wstring(directory);
81 }
82
83 } // namespace
84
85 namespace google_breakpad {
86
87 // Use sscanf_s if it is available, to quench the warning about scanf being
88 // deprecated. Use scanf where sscanf_is not available. Note that the
89 // parameters passed to sscanf and sscanf_s are only compatible as long as
90 // fields of type c, C, s, S, and [ are not used.
91 #if _MSC_VER >= 1400 // MSVC 2005/8
92 #define SSCANF sscanf_s
93 #else // _MSC_VER >= 1400
94 #define SSCANF sscanf
95 #endif // _MSC_VER >= 1400
96
InitializeFromString(const string & identifier)97 bool GUIDOrSignatureIdentifier::InitializeFromString(
98 const string& identifier) {
99 type_ = TYPE_NONE;
100
101 size_t length = identifier.length();
102
103 if (length > 32 && length <= 40) {
104 // GUID
105 if (SSCANF(identifier.c_str(),
106 "%08X%04hX%04hX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%X",
107 &guid_.Data1, &guid_.Data2, &guid_.Data3,
108 &guid_.Data4[0], &guid_.Data4[1],
109 &guid_.Data4[2], &guid_.Data4[3],
110 &guid_.Data4[4], &guid_.Data4[5],
111 &guid_.Data4[6], &guid_.Data4[7],
112 &age_) != 12) {
113 return false;
114 }
115
116 type_ = TYPE_GUID;
117 } else if (length > 8 && length <= 15) {
118 // Signature
119 if (SSCANF(identifier.c_str(), "%08X%x", &signature_, &age_) != 2) {
120 return false;
121 }
122
123 type_ = TYPE_SIGNATURE;
124 } else {
125 return false;
126 }
127
128 return true;
129 }
130
131 #undef SSCANF
132
MSSymbolServerConverter(const string & local_cache,const vector<string> & symbol_servers,bool trace_symsrv)133 MSSymbolServerConverter::MSSymbolServerConverter(
134 const string& local_cache,
135 const vector<string>& symbol_servers,
136 bool trace_symsrv)
137 : symbol_path_(),
138 fail_dns_(false),
139 fail_timeout_(false),
140 fail_http_https_redir_(false),
141 fail_not_found_(false),
142 trace_symsrv_(trace_symsrv) {
143 // Setting local_cache can be done without verifying that it exists because
144 // SymSrv will create it if it is missing - any creation failures will occur
145 // at that time, so there's nothing to check here, making it safe to
146 // assign this in the constructor.
147
148 assert(symbol_servers.size() > 0);
149
150 #if !defined(NDEBUG)
151 // These are characters that are interpreted as having special meanings in
152 // symbol_path_.
153 const char kInvalidCharacters[] = "*;";
154 assert(local_cache.find_first_of(kInvalidCharacters) == string::npos);
155 #endif // !defined(NDEBUG)
156
157 for (vector<string>::const_iterator symbol_server = symbol_servers.begin();
158 symbol_server != symbol_servers.end();
159 ++symbol_server) {
160 // The symbol path format is explained by
161 // http://msdn.microsoft.com/library/en-us/debug/base/using_symsrv.asp .
162 // "srv*" is the same as "symsrv*symsrv.dll*", which means that
163 // symsrv.dll is to be responsible for locating symbols. symsrv.dll
164 // interprets the rest of the string as a series of symbol stores separated
165 // by '*'. "srv*local_cache*symbol_server" means to check local_cache
166 // first for the symbol file, and if it is not found there, to check
167 // symbol_server. Symbol files found on the symbol server will be placed
168 // in the local cache, decompressed.
169 //
170 // Multiple specifications in this format may be presented, separated by
171 // semicolons.
172
173 assert((*symbol_server).find_first_of(kInvalidCharacters) == string::npos);
174 symbol_path_ += "srv*" + local_cache + "*" + *symbol_server + ";";
175 }
176
177 // Strip the trailing semicolon.
178 symbol_path_.erase(symbol_path_.length() - 1);
179 }
180
181 // A stack-based class that manages SymInitialize and SymCleanup calls.
182 class AutoSymSrv {
183 public:
AutoSymSrv()184 AutoSymSrv() : initialized_(false) {}
185
~AutoSymSrv()186 ~AutoSymSrv() {
187 if (!Cleanup()) {
188 // Print the error message here, because destructors have no return
189 // value.
190 fprintf(stderr, "~AutoSymSrv: SymCleanup: error %lu\n", GetLastError());
191 }
192 }
193
Initialize(HANDLE process,char * path,bool invade_process)194 bool Initialize(HANDLE process, char* path, bool invade_process) {
195 process_ = process;
196
197 // TODO(nbilling): Figure out why dbghelp.dll is being loaded from
198 // system32/SysWOW64 before exe folder.
199
200 // Attempt to locate and load dbghelp.dll beside the process exe. This is
201 // somewhat of a workaround to loader delay load behavior that is occurring
202 // when we call into symsrv APIs. dbghelp.dll must be loaded from beside
203 // the process exe so that we are guaranteed to find symsrv.dll alongside
204 // dbghelp.dll (a security requirement of dbghelp.dll) and so that the
205 // symsrv.dll file that is loaded has a symsrv.yes file alongside it (a
206 // requirement of symsrv.dll when accessing Microsoft-owned symbol
207 // servers).
208 // 'static local' because we don't care about the value but we need the
209 // initialization to happen exactly once.
210 static HMODULE dbghelp_module = [] () -> HMODULE {
211 std::wstring exe_directory = GetExeDirectory();
212 if (exe_directory.empty()) {
213 return nullptr;
214 }
215 std::wstring dbghelp_path = exe_directory + L"\\dbghelp.dll";
216 return LoadLibrary(dbghelp_path.c_str());
217 }();
218 if (dbghelp_module == nullptr) {
219 fprintf(stderr,
220 "AutoSymSrv::Initialize: failed to load dbghelp.dll beside exe.");
221 return false;
222 }
223
224 initialized_ = SymInitialize(process, path, invade_process) == TRUE;
225 return initialized_;
226 }
227
Cleanup()228 bool Cleanup() {
229 if (initialized_) {
230 if (SymCleanup(process_)) {
231 initialized_ = false;
232 return true;
233 }
234 return false;
235 }
236
237 return true;
238 }
239
240 private:
241 HANDLE process_;
242 bool initialized_;
243 };
244
245 // A stack-based class that "owns" a pathname and deletes it when destroyed,
246 // unless told not to by having its Release() method called. Early deletions
247 // are supported by calling Delete().
248 class AutoDeleter {
249 public:
AutoDeleter(const string & path)250 explicit AutoDeleter(const string& path) : path_(path) {}
251
~AutoDeleter()252 ~AutoDeleter() {
253 int error;
254 if ((error = Delete()) != 0) {
255 // Print the error message here, because destructors have no return
256 // value.
257 fprintf(stderr, "~AutoDeleter: Delete: error %d for %s\n",
258 error, path_.c_str());
259 }
260 }
261
Delete()262 int Delete() {
263 if (path_.empty())
264 return 0;
265
266 int error = remove(path_.c_str());
267 Release();
268 return error;
269 }
270
Release()271 void Release() {
272 path_.clear();
273 }
274
275 private:
276 string path_;
277 };
278
279 MSSymbolServerConverter::LocateResult
LocateFile(const string & debug_or_code_file,const string & debug_or_code_id,const string & version,string * file_name)280 MSSymbolServerConverter::LocateFile(const string& debug_or_code_file,
281 const string& debug_or_code_id,
282 const string& version,
283 string* file_name) {
284 assert(file_name);
285 file_name->clear();
286
287 GUIDOrSignatureIdentifier identifier;
288 if (!identifier.InitializeFromString(debug_or_code_id)) {
289 fprintf(stderr,
290 "LocateFile: Unparseable identifier for %s %s %s\n",
291 debug_or_code_file.c_str(),
292 debug_or_code_id.c_str(),
293 version.c_str());
294 return LOCATE_FAILURE;
295 }
296
297 HANDLE process = GetCurrentProcess(); // CloseHandle is not needed.
298 AutoSymSrv symsrv;
299 if (!symsrv.Initialize(process,
300 const_cast<char*>(symbol_path_.c_str()),
301 false)) {
302 fprintf(stderr, "LocateFile: SymInitialize: error %lu for %s %s %s\n",
303 GetLastError(),
304 debug_or_code_file.c_str(),
305 debug_or_code_id.c_str(),
306 version.c_str());
307 return LOCATE_FAILURE;
308 }
309
310 if (!SymRegisterCallback64(process, SymCallback,
311 reinterpret_cast<ULONG64>(this))) {
312 fprintf(stderr,
313 "LocateFile: SymRegisterCallback64: error %lu for %s %s %s\n",
314 GetLastError(),
315 debug_or_code_file.c_str(),
316 debug_or_code_id.c_str(),
317 version.c_str());
318 return LOCATE_FAILURE;
319 }
320
321 // SYMOPT_DEBUG arranges for SymCallback to be called with additional
322 // debugging information. This is used to determine the nature of failures.
323 DWORD options = SymGetOptions() | SYMOPT_DEBUG | SYMOPT_NO_PROMPTS |
324 SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_SECURE;
325 SymSetOptions(options);
326
327 // SymCallback will set these as needed inisde the SymFindFileInPath call.
328 fail_dns_ = false;
329 fail_timeout_ = false;
330 fail_not_found_ = false;
331
332 // Do the lookup.
333 char path[MAX_PATH];
334 if (!SymFindFileInPath(
335 process, NULL,
336 const_cast<char*>(debug_or_code_file.c_str()),
337 const_cast<void*>(identifier.guid_or_signature_pointer()),
338 identifier.age(), 0,
339 identifier.type() == GUIDOrSignatureIdentifier::TYPE_GUID ?
340 SSRVOPT_GUIDPTR : SSRVOPT_DWORDPTR,
341 path, SymFindFileInPathCallback, this)) {
342 DWORD error = GetLastError();
343 if (error == ERROR_FILE_NOT_FOUND) {
344 // This can be returned for a number of reasons. Use the crumbs
345 // collected by SymCallback to determine which one is relevant.
346
347 // These errors are possibly transient.
348 if (fail_dns_ || fail_timeout_) {
349 return LOCATE_RETRY;
350 }
351
352 if (fail_http_https_redir_) {
353 return LOCATE_HTTP_HTTPS_REDIR;
354 }
355
356 // This is an authoritiative file-not-found message.
357 if (fail_not_found_) {
358 fprintf(stderr,
359 "LocateFile: SymFindFileInPath: LOCATE_NOT_FOUND error "
360 "for %s %s %s\n",
361 debug_or_code_file.c_str(),
362 debug_or_code_id.c_str(),
363 version.c_str());
364 return LOCATE_NOT_FOUND;
365 }
366
367 // If the error is FILE_NOT_FOUND but none of the known error
368 // conditions are matched, fall through to LOCATE_FAILURE.
369 }
370
371 fprintf(stderr,
372 "LocateFile: SymFindFileInPath: error %lu for %s %s %s\n",
373 error,
374 debug_or_code_file.c_str(),
375 debug_or_code_id.c_str(),
376 version.c_str());
377 return LOCATE_FAILURE;
378 }
379
380 // Making sure path is null-terminated.
381 path[MAX_PATH - 1] = '\0';
382
383 // The AutoDeleter ensures that the file is only kept when returning
384 // LOCATE_SUCCESS.
385 AutoDeleter deleter(path);
386
387 // Do the cleanup here even though it will happen when symsrv goes out of
388 // scope, to allow it to influence the return value.
389 if (!symsrv.Cleanup()) {
390 fprintf(stderr, "LocateFile: SymCleanup: error %lu for %s %s %s\n",
391 GetLastError(),
392 debug_or_code_file.c_str(),
393 debug_or_code_id.c_str(),
394 version.c_str());
395 return LOCATE_FAILURE;
396 }
397
398 deleter.Release();
399
400 printf("Downloaded: %s\n", path);
401 *file_name = path;
402 return LOCATE_SUCCESS;
403 }
404
405
406 MSSymbolServerConverter::LocateResult
LocatePEFile(const MissingSymbolInfo & missing,string * pe_file)407 MSSymbolServerConverter::LocatePEFile(const MissingSymbolInfo& missing,
408 string* pe_file) {
409 return LocateFile(missing.code_file, missing.code_identifier,
410 missing.version, pe_file);
411 }
412
413 MSSymbolServerConverter::LocateResult
LocateSymbolFile(const MissingSymbolInfo & missing,string * symbol_file)414 MSSymbolServerConverter::LocateSymbolFile(const MissingSymbolInfo& missing,
415 string* symbol_file) {
416 return LocateFile(missing.debug_file, missing.debug_identifier,
417 missing.version, symbol_file);
418 }
419
420
421 // static
SymCallback(HANDLE process,ULONG action,ULONG64 data,ULONG64 context)422 BOOL CALLBACK MSSymbolServerConverter::SymCallback(HANDLE process,
423 ULONG action,
424 ULONG64 data,
425 ULONG64 context) {
426 MSSymbolServerConverter* self =
427 reinterpret_cast<MSSymbolServerConverter*>(context);
428
429 switch (action) {
430 case CBA_EVENT: {
431 IMAGEHLP_CBA_EVENT* cba_event =
432 reinterpret_cast<IMAGEHLP_CBA_EVENT*>(data);
433
434 // Put the string into a string object to be able to use string::find
435 // for substring matching. This is important because the not-found
436 // message does not use the entire string but is appended to the URL
437 // that SymSrv attempted to retrieve.
438 string desc(cba_event->desc);
439 if (self->trace_symsrv_) {
440 fprintf(stderr, "LocateFile: SymCallback: action desc '%s'\n",
441 desc.c_str());
442 }
443
444 // desc_action maps strings (in desc) to boolean pointers that are to
445 // be set to true if the string matches.
446 struct desc_action {
447 const char* desc; // The substring to match.
448 bool* action; // On match, this pointer will be set to true.
449 };
450
451 static const desc_action desc_actions[] = {
452 // When a DNS error occurs, it could be indiciative of network
453 // problems.
454 {"SYMSRV: The server name or address could not be resolved\n",
455 &self->fail_dns_},
456
457 // This message is produced if no connection is opened.
458 {"SYMSRV: A connection with the server could not be established\n",
459 &self->fail_timeout_},
460
461 // This message is produced if a connection is established but the
462 // server fails to respond to the HTTP request.
463 {"SYMSRV: The operation timed out\n", &self->fail_timeout_},
464
465 // This message is produced if the server is redirecting us from http
466 // to https. When this happens SymSrv will fail and we need to use
467 // the https URL in our call-- we've made a mistake.
468 {"ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR\n",
469 &self->fail_http_https_redir_},
470
471 // This message is produced when the requested file is not found,
472 // even if one or more of the above messages are also produced.
473 // It's trapped to distinguish between not-found and unknown-failure
474 // conditions. Note that this message will not be produced if a
475 // connection is established and the server begins to respond to the
476 // HTTP request but does not finish transmitting the file.
477 {" not found\n", &self->fail_not_found_}};
478
479 for (int desc_action_index = 0;
480 desc_action_index <
481 static_cast<int>(sizeof(desc_actions) / sizeof(desc_action));
482 ++desc_action_index) {
483 if (desc.find(desc_actions[desc_action_index].desc) != string::npos) {
484 *(desc_actions[desc_action_index].action) = true;
485 break;
486 }
487 }
488
489 break;
490 }
491 }
492
493 // This function is a mere fly on the wall. Treat everything as unhandled.
494 return FALSE;
495 }
496
497 // static
SymFindFileInPathCallback(const char * filename,void * context)498 BOOL CALLBACK MSSymbolServerConverter::SymFindFileInPathCallback(
499 const char* filename, void* context) {
500 // FALSE ends the search, indicating that the located symbol file is
501 // satisfactory.
502 return FALSE;
503 }
504
505 MSSymbolServerConverter::LocateResult
LocateAndConvertSymbolFile(const MissingSymbolInfo & missing,bool keep_symbol_file,bool keep_pe_file,string * converted_symbol_file,string * symbol_file,string * out_pe_file)506 MSSymbolServerConverter::LocateAndConvertSymbolFile(
507 const MissingSymbolInfo& missing,
508 bool keep_symbol_file,
509 bool keep_pe_file,
510 string* converted_symbol_file,
511 string* symbol_file,
512 string* out_pe_file) {
513 assert(converted_symbol_file);
514 converted_symbol_file->clear();
515 if (symbol_file) {
516 symbol_file->clear();
517 }
518
519 string pdb_file;
520 LocateResult result = LocateSymbolFile(missing, &pdb_file);
521 if (result != LOCATE_SUCCESS) {
522 fprintf(stderr, "Fallback to PE-only symbol generation for: %s\n",
523 missing.debug_file.c_str());
524 return LocateAndConvertPEFile(missing, keep_pe_file, converted_symbol_file,
525 out_pe_file);
526 }
527
528 if (symbol_file && keep_symbol_file) {
529 *symbol_file = pdb_file;
530 }
531
532 // The conversion of a symbol file for a Windows 64-bit module requires
533 // loading of the executable file. If there is no executable file, convert
534 // using only the PDB file. Without an executable file, the conversion will
535 // fail for 64-bit modules but it should succeed for 32-bit modules.
536 string pe_file;
537 result = LocatePEFile(missing, &pe_file);
538 if (result != LOCATE_SUCCESS) {
539 fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
540 }
541
542 if (out_pe_file && keep_pe_file) {
543 *out_pe_file = pe_file;
544 }
545
546 // Conversion may fail because the file is corrupt. If a broken file is
547 // kept in the local cache, LocateSymbolFile will not hit the network again
548 // to attempt to locate it. To guard against problems like this, the
549 // symbol file in the local cache will be removed if conversion fails.
550 AutoDeleter pdb_deleter(pdb_file);
551 AutoDeleter pe_deleter(pe_file);
552
553 // Be sure that it's a .pdb file, since we'll be replacing .pdb with .sym
554 // for the converted file's name.
555 string pdb_extension = pdb_file.substr(pdb_file.length() - 4);
556 // strcasecmp is called _stricmp here.
557 if (_stricmp(pdb_extension.c_str(), ".pdb") != 0) {
558 fprintf(stderr, "LocateAndConvertSymbolFile: "
559 "no .pdb extension for %s %s %s %s\n",
560 missing.debug_file.c_str(),
561 missing.debug_identifier.c_str(),
562 missing.version.c_str(),
563 pdb_file.c_str());
564 return LOCATE_FAILURE;
565 }
566
567 PDBSourceLineWriter writer;
568 wstring pe_file_w;
569 if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
570 fprintf(stderr,
571 "LocateAndConvertSymbolFile: "
572 "WindowsStringUtils::safe_mbstowcs failed for %s\n",
573 pe_file.c_str());
574 return LOCATE_FAILURE;
575 }
576 wstring pdb_file_w;
577 if (!WindowsStringUtils::safe_mbstowcs(pdb_file, &pdb_file_w)) {
578 fprintf(stderr,
579 "LocateAndConvertSymbolFile: "
580 "WindowsStringUtils::safe_mbstowcs failed for %ws\n",
581 pdb_file_w.c_str());
582 return LOCATE_FAILURE;
583 }
584 if (!writer.Open(pdb_file_w, PDBSourceLineWriter::PDB_FILE)) {
585 fprintf(stderr,
586 "ERROR: PDBSourceLineWriter::Open failed for %s %s %s %ws\n",
587 missing.debug_file.c_str(), missing.debug_identifier.c_str(),
588 missing.version.c_str(), pdb_file_w.c_str());
589 return LOCATE_FAILURE;
590 }
591 if (!writer.SetCodeFile(pe_file_w)) {
592 fprintf(stderr,
593 "ERROR: PDBSourceLineWriter::SetCodeFile failed for %s %s %s %ws\n",
594 missing.debug_file.c_str(), missing.debug_identifier.c_str(),
595 missing.version.c_str(), pe_file_w.c_str());
596 return LOCATE_FAILURE;
597 }
598
599 *converted_symbol_file = pdb_file.substr(0, pdb_file.length() - 4) + ".sym";
600
601 FILE* converted_output = NULL;
602 #if _MSC_VER >= 1400 // MSVC 2005/8
603 errno_t err;
604 if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
605 != 0) {
606 #else // _MSC_VER >= 1400
607 // fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
608 // environments. Don't use fopen with MSVC8 and later, because it's
609 // deprecated. fopen does not provide reliable error codes, so just use
610 // -1 in the event of a failure.
611 int err;
612 if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
613 err = -1;
614 #endif // _MSC_VER >= 1400
615 fprintf(stderr, "LocateAndConvertSymbolFile: "
616 "fopen_s: error %d for %s %s %s %s\n",
617 err,
618 missing.debug_file.c_str(),
619 missing.debug_identifier.c_str(),
620 missing.version.c_str(),
621 converted_symbol_file->c_str());
622 return LOCATE_FAILURE;
623 }
624
625 AutoDeleter sym_deleter(*converted_symbol_file);
626
627 bool success = writer.WriteSymbols(converted_output);
628 fclose(converted_output);
629
630 if (!success) {
631 fprintf(stderr, "LocateAndConvertSymbolFile: "
632 "PDBSourceLineWriter::WriteMap failed for %s %s %s %s\n",
633 missing.debug_file.c_str(),
634 missing.debug_identifier.c_str(),
635 missing.version.c_str(),
636 pdb_file.c_str());
637 return LOCATE_FAILURE;
638 }
639
640 if (keep_symbol_file) {
641 pdb_deleter.Release();
642 }
643
644 if (keep_pe_file) {
645 pe_deleter.Release();
646 }
647
648 sym_deleter.Release();
649
650 return LOCATE_SUCCESS;
651 }
652
653 MSSymbolServerConverter::LocateResult
654 MSSymbolServerConverter::LocateAndConvertPEFile(
655 const MissingSymbolInfo& missing,
656 bool keep_pe_file,
657 string* converted_symbol_file,
658 string* out_pe_file) {
659 assert(converted_symbol_file);
660 converted_symbol_file->clear();
661
662 string pe_file;
663 MSSymbolServerConverter::LocateResult result = LocatePEFile(missing,
664 &pe_file);
665 if (result != LOCATE_SUCCESS) {
666 fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
667 return result;
668 }
669
670 if (out_pe_file && keep_pe_file) {
671 *out_pe_file = pe_file;
672 }
673
674 // Conversion may fail because the file is corrupt. If a broken file is
675 // kept in the local cache, LocatePEFile will not hit the network again
676 // to attempt to locate it. To guard against problems like this, the
677 // PE file in the local cache will be removed if conversion fails.
678 AutoDeleter pe_deleter(pe_file);
679
680 // Be sure that it's a .exe or .dll file, since we'll be replacing extension
681 // with .sym for the converted file's name.
682 string pe_extension = pe_file.substr(pe_file.length() - 4);
683 // strcasecmp is called _stricmp here.
684 if (_stricmp(pe_extension.c_str(), ".exe") != 0 &&
685 _stricmp(pe_extension.c_str(), ".dll") != 0) {
686 fprintf(stderr, "LocateAndConvertPEFile: "
687 "no .dll/.exe extension for %s %s %s %s\n",
688 missing.debug_file.c_str(),
689 missing.debug_identifier.c_str(),
690 missing.version.c_str(),
691 pe_file.c_str());
692 return LOCATE_FAILURE;
693 }
694
695 *converted_symbol_file = pe_file.substr(0, pe_file.length() - 4) + ".sym";
696
697 FILE* converted_output = NULL;
698 #if _MSC_VER >= 1400 // MSVC 2005/8
699 errno_t err;
700 if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
701 != 0) {
702 #else // _MSC_VER >= 1400
703 // fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
704 // environments. Don't use fopen with MSVC8 and later, because it's
705 // deprecated. fopen does not provide reliable error codes, so just use
706 // -1 in the event of a failure.
707 int err;
708 if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
709 err = -1;
710 #endif // _MSC_VER >= 1400
711 fprintf(stderr, "LocateAndConvertPEFile: "
712 "fopen_s: error %d for %s %s %s %s\n",
713 err,
714 missing.debug_file.c_str(),
715 missing.debug_identifier.c_str(),
716 missing.version.c_str(),
717 converted_symbol_file->c_str());
718 return LOCATE_FAILURE;
719 }
720 AutoDeleter sym_deleter(*converted_symbol_file);
721
722 wstring pe_file_w;
723 if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
724 fprintf(stderr,
725 "LocateAndConvertPEFile: "
726 "WindowsStringUtils::safe_mbstowcs failed for %s\n",
727 pe_file.c_str());
728 return LOCATE_FAILURE;
729 }
730 PESourceLineWriter writer(pe_file_w);
731 PDBModuleInfo module_info;
732 if (!writer.GetModuleInfo(&module_info)) {
733 fprintf(stderr, "LocateAndConvertPEFile: "
734 "PESourceLineWriter::GetModuleInfo failed for %s %s %s %s\n",
735 missing.debug_file.c_str(),
736 missing.debug_identifier.c_str(),
737 missing.version.c_str(),
738 pe_file.c_str());
739 return LOCATE_FAILURE;
740 }
741 if (module_info.cpu.compare(L"x86_64") != 0) {
742 // This module is not x64 so we cannot generate Breakpad symbols from the
743 // PE alone. Don't delete PE-- no need to retry download.
744 pe_deleter.Release();
745 return LOCATE_FAILURE;
746 }
747
748 bool success = writer.WriteSymbols(converted_output);
749 fclose(converted_output);
750
751 if (!success) {
752 fprintf(stderr, "LocateAndConvertPEFile: "
753 "PESourceLineWriter::WriteMap failed for %s %s %s %s\n",
754 missing.debug_file.c_str(),
755 missing.debug_identifier.c_str(),
756 missing.version.c_str(),
757 pe_file.c_str());
758 return LOCATE_FAILURE;
759 }
760
761 if (keep_pe_file) {
762 pe_deleter.Release();
763 }
764
765 sym_deleter.Release();
766
767 return LOCATE_SUCCESS;
768 }
769
770 } // namespace google_breakpad
771