• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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