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