mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-03-12 15:52:57 +00:00
sed -i '' -E -e 's/Copyright (\(c\) )?([0-9-]+),? (Google|The Chromium Authors).*(\r)?$/Copyright \2 Google LLC\4/' -e '/^((\/\/|#| \*) )?All rights reserved\.?\r?$/d' -e 's/name of Google Inc\. nor the/name of Google LLC nor the/' -e 's/POSSIBILITY OF SUCH DAMAGE$/POSSIBILITY OF SUCH DAMAGE./' $(git grep -El 'Copyright (\(c\) )?([0-9-]+),? (Google|The Chromium Authors).*$')
Plus manual fixes for src/processor/disassembler_x86.{cc,h}.
Plus some conversions from CRLF to LF line endings in .cc and .h files.
Bug: chromium:1098010
Change-Id: I8030e804eecd9f5a1ec9d66ae166efd8418c2a67
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3878302
Reviewed-by: Mike Frysinger <vapier@chromium.org>
767 lines
26 KiB
C++
767 lines
26 KiB
C++
// Copyright 2007 Google LLC
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google LLC nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
// ms_symbol_server_converter.cc: Obtain symbol files from a Microsoft
|
|
// symbol server, and convert them to Breakpad's dumped format.
|
|
//
|
|
// See ms_symbol_server_converter.h for documentation.
|
|
//
|
|
// Author: Mark Mentovai
|
|
|
|
#include <windows.h>
|
|
#include <dbghelp.h>
|
|
#include <pathcch.h>
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
|
|
#include "tools/windows/converter/ms_symbol_server_converter.h"
|
|
#include "common/windows/pdb_source_line_writer.h"
|
|
#include "common/windows/pe_source_line_writer.h"
|
|
#include "common/windows/string_utils-inl.h"
|
|
|
|
// SYMOPT_NO_PROMPTS is not defined in earlier platform SDKs. Define it
|
|
// in that case, in the event that this code is used with a newer version
|
|
// of DbgHelp at runtime that recognizes the option. The presence of this
|
|
// bit in the symbol options should not harm earlier versions of DbgHelp.
|
|
#ifndef SYMOPT_NO_PROMPTS
|
|
#define SYMOPT_NO_PROMPTS 0x00080000
|
|
#endif // SYMOPT_NO_PROMPTS
|
|
|
|
namespace {
|
|
|
|
std::wstring GetExeDirectory() {
|
|
wchar_t directory[MAX_PATH];
|
|
|
|
// Get path to this process exe.
|
|
DWORD result = GetModuleFileName(/*hModule=*/nullptr, directory, MAX_PATH);
|
|
if (result <= 0 || result == MAX_PATH) {
|
|
fprintf(stderr,
|
|
"GetExeDirectory: failed to get path to process exe.\n");
|
|
return L"";
|
|
}
|
|
HRESULT hr = PathCchRemoveFileSpec(directory, result + 1);
|
|
if (hr != S_OK) {
|
|
fprintf(stderr,
|
|
"GetExeDirectory: failed to remove basename from path '%ls'.\n",
|
|
directory);
|
|
return L"";
|
|
}
|
|
|
|
return std::wstring(directory);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace google_breakpad {
|
|
|
|
// Use sscanf_s if it is available, to quench the warning about scanf being
|
|
// deprecated. Use scanf where sscanf_is not available. Note that the
|
|
// parameters passed to sscanf and sscanf_s are only compatible as long as
|
|
// fields of type c, C, s, S, and [ are not used.
|
|
#if _MSC_VER >= 1400 // MSVC 2005/8
|
|
#define SSCANF sscanf_s
|
|
#else // _MSC_VER >= 1400
|
|
#define SSCANF sscanf
|
|
#endif // _MSC_VER >= 1400
|
|
|
|
bool GUIDOrSignatureIdentifier::InitializeFromString(
|
|
const string& identifier) {
|
|
type_ = TYPE_NONE;
|
|
|
|
size_t length = identifier.length();
|
|
|
|
if (length > 32 && length <= 40) {
|
|
// GUID
|
|
if (SSCANF(identifier.c_str(),
|
|
"%08X%04hX%04hX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%X",
|
|
&guid_.Data1, &guid_.Data2, &guid_.Data3,
|
|
&guid_.Data4[0], &guid_.Data4[1],
|
|
&guid_.Data4[2], &guid_.Data4[3],
|
|
&guid_.Data4[4], &guid_.Data4[5],
|
|
&guid_.Data4[6], &guid_.Data4[7],
|
|
&age_) != 12) {
|
|
return false;
|
|
}
|
|
|
|
type_ = TYPE_GUID;
|
|
} else if (length > 8 && length <= 15) {
|
|
// Signature
|
|
if (SSCANF(identifier.c_str(), "%08X%x", &signature_, &age_) != 2) {
|
|
return false;
|
|
}
|
|
|
|
type_ = TYPE_SIGNATURE;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#undef SSCANF
|
|
|
|
MSSymbolServerConverter::MSSymbolServerConverter(
|
|
const string& local_cache,
|
|
const vector<string>& symbol_servers,
|
|
bool trace_symsrv)
|
|
: symbol_path_(),
|
|
fail_dns_(false),
|
|
fail_timeout_(false),
|
|
fail_http_https_redir_(false),
|
|
fail_not_found_(false),
|
|
trace_symsrv_(trace_symsrv) {
|
|
// Setting local_cache can be done without verifying that it exists because
|
|
// SymSrv will create it if it is missing - any creation failures will occur
|
|
// at that time, so there's nothing to check here, making it safe to
|
|
// assign this in the constructor.
|
|
|
|
assert(symbol_servers.size() > 0);
|
|
|
|
#if !defined(NDEBUG)
|
|
// These are characters that are interpreted as having special meanings in
|
|
// symbol_path_.
|
|
const char kInvalidCharacters[] = "*;";
|
|
assert(local_cache.find_first_of(kInvalidCharacters) == string::npos);
|
|
#endif // !defined(NDEBUG)
|
|
|
|
for (vector<string>::const_iterator symbol_server = symbol_servers.begin();
|
|
symbol_server != symbol_servers.end();
|
|
++symbol_server) {
|
|
// The symbol path format is explained by
|
|
// http://msdn.microsoft.com/library/en-us/debug/base/using_symsrv.asp .
|
|
// "srv*" is the same as "symsrv*symsrv.dll*", which means that
|
|
// symsrv.dll is to be responsible for locating symbols. symsrv.dll
|
|
// interprets the rest of the string as a series of symbol stores separated
|
|
// by '*'. "srv*local_cache*symbol_server" means to check local_cache
|
|
// first for the symbol file, and if it is not found there, to check
|
|
// symbol_server. Symbol files found on the symbol server will be placed
|
|
// in the local cache, decompressed.
|
|
//
|
|
// Multiple specifications in this format may be presented, separated by
|
|
// semicolons.
|
|
|
|
assert((*symbol_server).find_first_of(kInvalidCharacters) == string::npos);
|
|
symbol_path_ += "srv*" + local_cache + "*" + *symbol_server + ";";
|
|
}
|
|
|
|
// Strip the trailing semicolon.
|
|
symbol_path_.erase(symbol_path_.length() - 1);
|
|
}
|
|
|
|
// A stack-based class that manages SymInitialize and SymCleanup calls.
|
|
class AutoSymSrv {
|
|
public:
|
|
AutoSymSrv() : initialized_(false) {}
|
|
|
|
~AutoSymSrv() {
|
|
if (!Cleanup()) {
|
|
// Print the error message here, because destructors have no return
|
|
// value.
|
|
fprintf(stderr, "~AutoSymSrv: SymCleanup: error %lu\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
bool Initialize(HANDLE process, char* path, bool invade_process) {
|
|
process_ = process;
|
|
|
|
// TODO(nbilling): Figure out why dbghelp.dll is being loaded from
|
|
// system32/SysWOW64 before exe folder.
|
|
|
|
// Attempt to locate and load dbghelp.dll beside the process exe. This is
|
|
// somewhat of a workaround to loader delay load behavior that is occurring
|
|
// when we call into symsrv APIs. dbghelp.dll must be loaded from beside
|
|
// the process exe so that we are guaranteed to find symsrv.dll alongside
|
|
// dbghelp.dll (a security requirement of dbghelp.dll) and so that the
|
|
// symsrv.dll file that is loaded has a symsrv.yes file alongside it (a
|
|
// requirement of symsrv.dll when accessing Microsoft-owned symbol
|
|
// servers).
|
|
// 'static local' because we don't care about the value but we need the
|
|
// initialization to happen exactly once.
|
|
static HMODULE dbghelp_module = [] () -> HMODULE {
|
|
std::wstring exe_directory = GetExeDirectory();
|
|
if (exe_directory.empty()) {
|
|
return nullptr;
|
|
}
|
|
std::wstring dbghelp_path = exe_directory + L"\\dbghelp.dll";
|
|
return LoadLibrary(dbghelp_path.c_str());
|
|
}();
|
|
if (dbghelp_module == nullptr) {
|
|
fprintf(stderr,
|
|
"AutoSymSrv::Initialize: failed to load dbghelp.dll beside exe.");
|
|
return false;
|
|
}
|
|
|
|
initialized_ = SymInitialize(process, path, invade_process) == TRUE;
|
|
return initialized_;
|
|
}
|
|
|
|
bool Cleanup() {
|
|
if (initialized_) {
|
|
if (SymCleanup(process_)) {
|
|
initialized_ = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
HANDLE process_;
|
|
bool initialized_;
|
|
};
|
|
|
|
// A stack-based class that "owns" a pathname and deletes it when destroyed,
|
|
// unless told not to by having its Release() method called. Early deletions
|
|
// are supported by calling Delete().
|
|
class AutoDeleter {
|
|
public:
|
|
explicit AutoDeleter(const string& path) : path_(path) {}
|
|
|
|
~AutoDeleter() {
|
|
int error;
|
|
if ((error = Delete()) != 0) {
|
|
// Print the error message here, because destructors have no return
|
|
// value.
|
|
fprintf(stderr, "~AutoDeleter: Delete: error %d for %s\n",
|
|
error, path_.c_str());
|
|
}
|
|
}
|
|
|
|
int Delete() {
|
|
if (path_.empty())
|
|
return 0;
|
|
|
|
int error = remove(path_.c_str());
|
|
Release();
|
|
return error;
|
|
}
|
|
|
|
void Release() {
|
|
path_.clear();
|
|
}
|
|
|
|
private:
|
|
string path_;
|
|
};
|
|
|
|
MSSymbolServerConverter::LocateResult
|
|
MSSymbolServerConverter::LocateFile(const string& debug_or_code_file,
|
|
const string& debug_or_code_id,
|
|
const string& version,
|
|
string* file_name) {
|
|
assert(file_name);
|
|
file_name->clear();
|
|
|
|
GUIDOrSignatureIdentifier identifier;
|
|
if (!identifier.InitializeFromString(debug_or_code_id)) {
|
|
fprintf(stderr,
|
|
"LocateFile: Unparseable identifier for %s %s %s\n",
|
|
debug_or_code_file.c_str(),
|
|
debug_or_code_id.c_str(),
|
|
version.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
HANDLE process = GetCurrentProcess(); // CloseHandle is not needed.
|
|
AutoSymSrv symsrv;
|
|
if (!symsrv.Initialize(process,
|
|
const_cast<char*>(symbol_path_.c_str()),
|
|
false)) {
|
|
fprintf(stderr, "LocateFile: SymInitialize: error %lu for %s %s %s\n",
|
|
GetLastError(),
|
|
debug_or_code_file.c_str(),
|
|
debug_or_code_id.c_str(),
|
|
version.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
if (!SymRegisterCallback64(process, SymCallback,
|
|
reinterpret_cast<ULONG64>(this))) {
|
|
fprintf(stderr,
|
|
"LocateFile: SymRegisterCallback64: error %lu for %s %s %s\n",
|
|
GetLastError(),
|
|
debug_or_code_file.c_str(),
|
|
debug_or_code_id.c_str(),
|
|
version.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
// SYMOPT_DEBUG arranges for SymCallback to be called with additional
|
|
// debugging information. This is used to determine the nature of failures.
|
|
DWORD options = SymGetOptions() | SYMOPT_DEBUG | SYMOPT_NO_PROMPTS |
|
|
SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_SECURE;
|
|
SymSetOptions(options);
|
|
|
|
// SymCallback will set these as needed inisde the SymFindFileInPath call.
|
|
fail_dns_ = false;
|
|
fail_timeout_ = false;
|
|
fail_not_found_ = false;
|
|
|
|
// Do the lookup.
|
|
char path[MAX_PATH];
|
|
if (!SymFindFileInPath(
|
|
process, NULL,
|
|
const_cast<char*>(debug_or_code_file.c_str()),
|
|
const_cast<void*>(identifier.guid_or_signature_pointer()),
|
|
identifier.age(), 0,
|
|
identifier.type() == GUIDOrSignatureIdentifier::TYPE_GUID ?
|
|
SSRVOPT_GUIDPTR : SSRVOPT_DWORDPTR,
|
|
path, SymFindFileInPathCallback, this)) {
|
|
DWORD error = GetLastError();
|
|
if (error == ERROR_FILE_NOT_FOUND) {
|
|
// This can be returned for a number of reasons. Use the crumbs
|
|
// collected by SymCallback to determine which one is relevant.
|
|
|
|
// These errors are possibly transient.
|
|
if (fail_dns_ || fail_timeout_) {
|
|
return LOCATE_RETRY;
|
|
}
|
|
|
|
if (fail_http_https_redir_) {
|
|
return LOCATE_HTTP_HTTPS_REDIR;
|
|
}
|
|
|
|
// This is an authoritiative file-not-found message.
|
|
if (fail_not_found_) {
|
|
fprintf(stderr,
|
|
"LocateFile: SymFindFileInPath: LOCATE_NOT_FOUND error "
|
|
"for %s %s %s\n",
|
|
debug_or_code_file.c_str(),
|
|
debug_or_code_id.c_str(),
|
|
version.c_str());
|
|
return LOCATE_NOT_FOUND;
|
|
}
|
|
|
|
// If the error is FILE_NOT_FOUND but none of the known error
|
|
// conditions are matched, fall through to LOCATE_FAILURE.
|
|
}
|
|
|
|
fprintf(stderr,
|
|
"LocateFile: SymFindFileInPath: error %lu for %s %s %s\n",
|
|
error,
|
|
debug_or_code_file.c_str(),
|
|
debug_or_code_id.c_str(),
|
|
version.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
// Making sure path is null-terminated.
|
|
path[MAX_PATH - 1] = '\0';
|
|
|
|
// The AutoDeleter ensures that the file is only kept when returning
|
|
// LOCATE_SUCCESS.
|
|
AutoDeleter deleter(path);
|
|
|
|
// Do the cleanup here even though it will happen when symsrv goes out of
|
|
// scope, to allow it to influence the return value.
|
|
if (!symsrv.Cleanup()) {
|
|
fprintf(stderr, "LocateFile: SymCleanup: error %lu for %s %s %s\n",
|
|
GetLastError(),
|
|
debug_or_code_file.c_str(),
|
|
debug_or_code_id.c_str(),
|
|
version.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
deleter.Release();
|
|
|
|
printf("Downloaded: %s\n", path);
|
|
*file_name = path;
|
|
return LOCATE_SUCCESS;
|
|
}
|
|
|
|
|
|
MSSymbolServerConverter::LocateResult
|
|
MSSymbolServerConverter::LocatePEFile(const MissingSymbolInfo& missing,
|
|
string* pe_file) {
|
|
return LocateFile(missing.code_file, missing.code_identifier,
|
|
missing.version, pe_file);
|
|
}
|
|
|
|
MSSymbolServerConverter::LocateResult
|
|
MSSymbolServerConverter::LocateSymbolFile(const MissingSymbolInfo& missing,
|
|
string* symbol_file) {
|
|
return LocateFile(missing.debug_file, missing.debug_identifier,
|
|
missing.version, symbol_file);
|
|
}
|
|
|
|
|
|
// static
|
|
BOOL CALLBACK MSSymbolServerConverter::SymCallback(HANDLE process,
|
|
ULONG action,
|
|
ULONG64 data,
|
|
ULONG64 context) {
|
|
MSSymbolServerConverter* self =
|
|
reinterpret_cast<MSSymbolServerConverter*>(context);
|
|
|
|
switch (action) {
|
|
case CBA_EVENT: {
|
|
IMAGEHLP_CBA_EVENT* cba_event =
|
|
reinterpret_cast<IMAGEHLP_CBA_EVENT*>(data);
|
|
|
|
// Put the string into a string object to be able to use string::find
|
|
// for substring matching. This is important because the not-found
|
|
// message does not use the entire string but is appended to the URL
|
|
// that SymSrv attempted to retrieve.
|
|
string desc(cba_event->desc);
|
|
if (self->trace_symsrv_) {
|
|
fprintf(stderr, "LocateFile: SymCallback: action desc '%s'\n",
|
|
desc.c_str());
|
|
}
|
|
|
|
// desc_action maps strings (in desc) to boolean pointers that are to
|
|
// be set to true if the string matches.
|
|
struct desc_action {
|
|
const char* desc; // The substring to match.
|
|
bool* action; // On match, this pointer will be set to true.
|
|
};
|
|
|
|
static const desc_action desc_actions[] = {
|
|
// When a DNS error occurs, it could be indiciative of network
|
|
// problems.
|
|
{"SYMSRV: The server name or address could not be resolved\n",
|
|
&self->fail_dns_},
|
|
|
|
// This message is produced if no connection is opened.
|
|
{"SYMSRV: A connection with the server could not be established\n",
|
|
&self->fail_timeout_},
|
|
|
|
// This message is produced if a connection is established but the
|
|
// server fails to respond to the HTTP request.
|
|
{"SYMSRV: The operation timed out\n", &self->fail_timeout_},
|
|
|
|
// This message is produced if the server is redirecting us from http
|
|
// to https. When this happens SymSrv will fail and we need to use
|
|
// the https URL in our call-- we've made a mistake.
|
|
{"ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR\n",
|
|
&self->fail_http_https_redir_},
|
|
|
|
// This message is produced when the requested file is not found,
|
|
// even if one or more of the above messages are also produced.
|
|
// It's trapped to distinguish between not-found and unknown-failure
|
|
// conditions. Note that this message will not be produced if a
|
|
// connection is established and the server begins to respond to the
|
|
// HTTP request but does not finish transmitting the file.
|
|
{" not found\n", &self->fail_not_found_}};
|
|
|
|
for (int desc_action_index = 0;
|
|
desc_action_index <
|
|
static_cast<int>(sizeof(desc_actions) / sizeof(desc_action));
|
|
++desc_action_index) {
|
|
if (desc.find(desc_actions[desc_action_index].desc) != string::npos) {
|
|
*(desc_actions[desc_action_index].action) = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This function is a mere fly on the wall. Treat everything as unhandled.
|
|
return FALSE;
|
|
}
|
|
|
|
// static
|
|
BOOL CALLBACK MSSymbolServerConverter::SymFindFileInPathCallback(
|
|
const char* filename, void* context) {
|
|
// FALSE ends the search, indicating that the located symbol file is
|
|
// satisfactory.
|
|
return FALSE;
|
|
}
|
|
|
|
MSSymbolServerConverter::LocateResult
|
|
MSSymbolServerConverter::LocateAndConvertSymbolFile(
|
|
const MissingSymbolInfo& missing,
|
|
bool keep_symbol_file,
|
|
bool keep_pe_file,
|
|
string* converted_symbol_file,
|
|
string* symbol_file,
|
|
string* out_pe_file) {
|
|
assert(converted_symbol_file);
|
|
converted_symbol_file->clear();
|
|
if (symbol_file) {
|
|
symbol_file->clear();
|
|
}
|
|
|
|
string pdb_file;
|
|
LocateResult result = LocateSymbolFile(missing, &pdb_file);
|
|
if (result != LOCATE_SUCCESS) {
|
|
fprintf(stderr, "Fallback to PE-only symbol generation for: %s\n",
|
|
missing.debug_file.c_str());
|
|
return LocateAndConvertPEFile(missing, keep_pe_file, converted_symbol_file,
|
|
out_pe_file);
|
|
}
|
|
|
|
if (symbol_file && keep_symbol_file) {
|
|
*symbol_file = pdb_file;
|
|
}
|
|
|
|
// The conversion of a symbol file for a Windows 64-bit module requires
|
|
// loading of the executable file. If there is no executable file, convert
|
|
// using only the PDB file. Without an executable file, the conversion will
|
|
// fail for 64-bit modules but it should succeed for 32-bit modules.
|
|
string pe_file;
|
|
result = LocatePEFile(missing, &pe_file);
|
|
if (result != LOCATE_SUCCESS) {
|
|
fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
|
|
}
|
|
|
|
if (out_pe_file && keep_pe_file) {
|
|
*out_pe_file = pe_file;
|
|
}
|
|
|
|
// Conversion may fail because the file is corrupt. If a broken file is
|
|
// kept in the local cache, LocateSymbolFile will not hit the network again
|
|
// to attempt to locate it. To guard against problems like this, the
|
|
// symbol file in the local cache will be removed if conversion fails.
|
|
AutoDeleter pdb_deleter(pdb_file);
|
|
AutoDeleter pe_deleter(pe_file);
|
|
|
|
// Be sure that it's a .pdb file, since we'll be replacing .pdb with .sym
|
|
// for the converted file's name.
|
|
string pdb_extension = pdb_file.substr(pdb_file.length() - 4);
|
|
// strcasecmp is called _stricmp here.
|
|
if (_stricmp(pdb_extension.c_str(), ".pdb") != 0) {
|
|
fprintf(stderr, "LocateAndConvertSymbolFile: "
|
|
"no .pdb extension for %s %s %s %s\n",
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
pdb_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
PDBSourceLineWriter writer;
|
|
wstring pe_file_w;
|
|
if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
|
|
fprintf(stderr,
|
|
"LocateAndConvertSymbolFile: "
|
|
"WindowsStringUtils::safe_mbstowcs failed for %s\n",
|
|
pe_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
wstring pdb_file_w;
|
|
if (!WindowsStringUtils::safe_mbstowcs(pdb_file, &pdb_file_w)) {
|
|
fprintf(stderr,
|
|
"LocateAndConvertSymbolFile: "
|
|
"WindowsStringUtils::safe_mbstowcs failed for %ws\n",
|
|
pdb_file_w.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
if (!writer.Open(pdb_file_w, PDBSourceLineWriter::PDB_FILE)) {
|
|
fprintf(stderr,
|
|
"ERROR: PDBSourceLineWriter::Open failed for %s %s %s %ws\n",
|
|
missing.debug_file.c_str(), missing.debug_identifier.c_str(),
|
|
missing.version.c_str(), pdb_file_w.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
if (!writer.SetCodeFile(pe_file_w)) {
|
|
fprintf(stderr,
|
|
"ERROR: PDBSourceLineWriter::SetCodeFile failed for %s %s %s %ws\n",
|
|
missing.debug_file.c_str(), missing.debug_identifier.c_str(),
|
|
missing.version.c_str(), pe_file_w.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
*converted_symbol_file = pdb_file.substr(0, pdb_file.length() - 4) + ".sym";
|
|
|
|
FILE* converted_output = NULL;
|
|
#if _MSC_VER >= 1400 // MSVC 2005/8
|
|
errno_t err;
|
|
if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
|
|
!= 0) {
|
|
#else // _MSC_VER >= 1400
|
|
// fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
|
|
// environments. Don't use fopen with MSVC8 and later, because it's
|
|
// deprecated. fopen does not provide reliable error codes, so just use
|
|
// -1 in the event of a failure.
|
|
int err;
|
|
if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
|
|
err = -1;
|
|
#endif // _MSC_VER >= 1400
|
|
fprintf(stderr, "LocateAndConvertSymbolFile: "
|
|
"fopen_s: error %d for %s %s %s %s\n",
|
|
err,
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
converted_symbol_file->c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
AutoDeleter sym_deleter(*converted_symbol_file);
|
|
|
|
bool success = writer.WriteSymbols(converted_output);
|
|
fclose(converted_output);
|
|
|
|
if (!success) {
|
|
fprintf(stderr, "LocateAndConvertSymbolFile: "
|
|
"PDBSourceLineWriter::WriteMap failed for %s %s %s %s\n",
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
pdb_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
if (keep_symbol_file) {
|
|
pdb_deleter.Release();
|
|
}
|
|
|
|
if (keep_pe_file) {
|
|
pe_deleter.Release();
|
|
}
|
|
|
|
sym_deleter.Release();
|
|
|
|
return LOCATE_SUCCESS;
|
|
}
|
|
|
|
MSSymbolServerConverter::LocateResult
|
|
MSSymbolServerConverter::LocateAndConvertPEFile(
|
|
const MissingSymbolInfo& missing,
|
|
bool keep_pe_file,
|
|
string* converted_symbol_file,
|
|
string* out_pe_file) {
|
|
assert(converted_symbol_file);
|
|
converted_symbol_file->clear();
|
|
|
|
string pe_file;
|
|
MSSymbolServerConverter::LocateResult result = LocatePEFile(missing,
|
|
&pe_file);
|
|
if (result != LOCATE_SUCCESS) {
|
|
fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
|
|
return result;
|
|
}
|
|
|
|
if (out_pe_file && keep_pe_file) {
|
|
*out_pe_file = pe_file;
|
|
}
|
|
|
|
// Conversion may fail because the file is corrupt. If a broken file is
|
|
// kept in the local cache, LocatePEFile will not hit the network again
|
|
// to attempt to locate it. To guard against problems like this, the
|
|
// PE file in the local cache will be removed if conversion fails.
|
|
AutoDeleter pe_deleter(pe_file);
|
|
|
|
// Be sure that it's a .exe or .dll file, since we'll be replacing extension
|
|
// with .sym for the converted file's name.
|
|
string pe_extension = pe_file.substr(pe_file.length() - 4);
|
|
// strcasecmp is called _stricmp here.
|
|
if (_stricmp(pe_extension.c_str(), ".exe") != 0 &&
|
|
_stricmp(pe_extension.c_str(), ".dll") != 0) {
|
|
fprintf(stderr, "LocateAndConvertPEFile: "
|
|
"no .dll/.exe extension for %s %s %s %s\n",
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
pe_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
*converted_symbol_file = pe_file.substr(0, pe_file.length() - 4) + ".sym";
|
|
|
|
FILE* converted_output = NULL;
|
|
#if _MSC_VER >= 1400 // MSVC 2005/8
|
|
errno_t err;
|
|
if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
|
|
!= 0) {
|
|
#else // _MSC_VER >= 1400
|
|
// fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
|
|
// environments. Don't use fopen with MSVC8 and later, because it's
|
|
// deprecated. fopen does not provide reliable error codes, so just use
|
|
// -1 in the event of a failure.
|
|
int err;
|
|
if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
|
|
err = -1;
|
|
#endif // _MSC_VER >= 1400
|
|
fprintf(stderr, "LocateAndConvertPEFile: "
|
|
"fopen_s: error %d for %s %s %s %s\n",
|
|
err,
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
converted_symbol_file->c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
AutoDeleter sym_deleter(*converted_symbol_file);
|
|
|
|
wstring pe_file_w;
|
|
if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
|
|
fprintf(stderr,
|
|
"LocateAndConvertPEFile: "
|
|
"WindowsStringUtils::safe_mbstowcs failed for %s\n",
|
|
pe_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
PESourceLineWriter writer(pe_file_w);
|
|
PDBModuleInfo module_info;
|
|
if (!writer.GetModuleInfo(&module_info)) {
|
|
fprintf(stderr, "LocateAndConvertPEFile: "
|
|
"PESourceLineWriter::GetModuleInfo failed for %s %s %s %s\n",
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
pe_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
if (module_info.cpu.compare(L"x86_64") != 0) {
|
|
// This module is not x64 so we cannot generate Breakpad symbols from the
|
|
// PE alone. Don't delete PE-- no need to retry download.
|
|
pe_deleter.Release();
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
bool success = writer.WriteSymbols(converted_output);
|
|
fclose(converted_output);
|
|
|
|
if (!success) {
|
|
fprintf(stderr, "LocateAndConvertPEFile: "
|
|
"PESourceLineWriter::WriteMap failed for %s %s %s %s\n",
|
|
missing.debug_file.c_str(),
|
|
missing.debug_identifier.c_str(),
|
|
missing.version.c_str(),
|
|
pe_file.c_str());
|
|
return LOCATE_FAILURE;
|
|
}
|
|
|
|
if (keep_pe_file) {
|
|
pe_deleter.Release();
|
|
}
|
|
|
|
sym_deleter.Release();
|
|
|
|
return LOCATE_SUCCESS;
|
|
}
|
|
|
|
} // namespace google_breakpad
|