diff options
-rw-r--r-- | src/common/windows/common_windows.gyp | 5 | ||||
-rw-r--r-- | src/common/windows/module_info.h | 75 | ||||
-rw-r--r-- | src/common/windows/pdb_source_line_writer.cc | 273 | ||||
-rw-r--r-- | src/common/windows/pdb_source_line_writer.h | 48 | ||||
-rw-r--r-- | src/common/windows/pe_source_line_writer.cpp | 77 | ||||
-rw-r--r-- | src/common/windows/pe_source_line_writer.h | 69 | ||||
-rw-r--r-- | src/common/windows/pe_util.cpp | 418 | ||||
-rw-r--r-- | src/common/windows/pe_util.h | 67 | ||||
-rw-r--r-- | src/tools/windows/dump_syms/dump_syms.cc | 35 | ||||
-rw-r--r-- | src/tools/windows/dump_syms/dump_syms_unittest.cc | 67 | ||||
-rw-r--r-- | src/tools/windows/dump_syms/testdata/pe_only_symbol_test.dll | bin | 0 -> 51200 bytes | |||
-rw-r--r-- | src/tools/windows/dump_syms/testdata/pe_only_symbol_test.sym | 214 |
12 files changed, 1020 insertions, 328 deletions
diff --git a/src/common/windows/common_windows.gyp b/src/common/windows/common_windows.gyp index 6626c576..095ab9e2 100644 --- a/src/common/windows/common_windows.gyp +++ b/src/common/windows/common_windows.gyp @@ -77,11 +77,16 @@ 'guid_string.h', 'http_upload.cc', 'http_upload.h', + 'module_info.h' 'omap.cc', 'omap.h', 'omap_internal.h', 'pdb_source_line_writer.cc', 'pdb_source_line_writer.h', + 'pe_source_line_writer.cc', + 'pe_source_line_writer.h', + 'pe_util.h', + 'pe_util.cc', 'string_utils.cc', 'string_utils-inl.h', 'symbol_collector_client.cc', diff --git a/src/common/windows/module_info.h b/src/common/windows/module_info.h new file mode 100644 index 00000000..3dccc808 --- /dev/null +++ b/src/common/windows/module_info.h @@ -0,0 +1,75 @@ +// Copyright (c) 2019, Google Inc.
+// All rights reserved.
+//
+// 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 Inc. 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.
+
+#ifndef COMMON_WINDOWS_MODULE_INFO_H_
+#define COMMON_WINDOWS_MODULE_INFO_H_
+
+#include <string>
+
+namespace google_breakpad {
+
+using std::wstring;
+// A structure that carries information that identifies a module.
+struct PDBModuleInfo {
+public:
+ // The basename of the pe/pdb file from which information was loaded.
+ wstring debug_file;
+
+ // The module's identifier. For recent pe/pdb files, the identifier consists
+ // of the pe/pdb's guid, in uppercase hexadecimal form without any dashes
+ // or separators, followed immediately by the pe/pdb's age, also in
+ // uppercase hexadecimal form. For older pe/pdb files which have no guid,
+ // the identifier is the pe/pdb's 32-bit signature value, in zero-padded
+ // hexadecimal form, followed immediately by the pe/pdb's age, in lowercase
+ // hexadecimal form.
+ wstring debug_identifier;
+
+ // A string identifying the cpu that the pe/pdb is associated with.
+ // Currently, this may be "x86" or "unknown".
+ wstring cpu;
+};
+
+// A structure that carries information that identifies a PE file,
+// either an EXE or a DLL.
+struct PEModuleInfo {
+ // The basename of the PE file.
+ wstring code_file;
+
+ // The PE file's code identifier, which consists of its timestamp
+ // and file size concatenated together into a single hex string.
+ // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and
+ // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp
+ // documentation.) This is not well documented, if it's documented
+ // at all, but it's what symstore does and what DbgHelp supports.
+ wstring code_identifier;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_MODULE_INFO_H_
diff --git a/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc index b7788ca0..4030a2e9 100644 --- a/src/common/windows/pdb_source_line_writer.cc +++ b/src/common/windows/pdb_source_line_writer.cc @@ -45,6 +45,7 @@ #include "common/windows/dia_util.h" #include "common/windows/guid_string.h" +#include "common/windows/pe_util.h" #include "common/windows/string_utils-inl.h" // This constant may be missing from DbgHelp.h. See the documentation for @@ -53,56 +54,6 @@ #define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union. #endif // UNDNAME_NO_ECSU -/* - * Not defined in WinNT.h for some reason. Definitions taken from: - * http://uninformed.org/index.cgi?v=4&a=1&p=13 - * - */ -typedef unsigned char UBYTE; - -#if !defined(_WIN64) -#define UNW_FLAG_EHANDLER 0x01 -#define UNW_FLAG_UHANDLER 0x02 -#define UNW_FLAG_CHAININFO 0x04 -#endif - -union UnwindCode { - struct { - UBYTE offset_in_prolog; - UBYTE unwind_operation_code : 4; - UBYTE operation_info : 4; - }; - USHORT frame_offset; -}; - -enum UnwindOperationCodes { - UWOP_PUSH_NONVOL = 0, /* info == register number */ - UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ - UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ - UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ - UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ - UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ - // XXX: these are missing from MSDN! - // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm - UWOP_SAVE_XMM, - UWOP_SAVE_XMM_FAR, - UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */ - UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ - UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ -}; - -// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx -// Note: some fields removed as we don't use them. -struct UnwindInfo { - UBYTE version : 3; - UBYTE flags : 5; - UBYTE size_of_prolog; - UBYTE count_of_codes; - UBYTE frame_register : 4; - UBYTE frame_offset : 4; - UnwindCode unwind_code[1]; -}; - namespace google_breakpad { namespace { @@ -157,21 +108,7 @@ void MaybeRecordSymbol(DWORD rva, } } -// A helper class to scope a PLOADED_IMAGE. -class AutoImage { - public: - explicit AutoImage(PLOADED_IMAGE img) : img_(img) {} - ~AutoImage() { - if (img_) - ImageUnload(img_); - } - operator PLOADED_IMAGE() { return img_; } - PLOADED_IMAGE operator->() { return img_; } - - private: - PLOADED_IMAGE img_; -}; bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) { DWORD a_section, a_offset, b_section, b_offset; @@ -223,6 +160,7 @@ PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { } PDBSourceLineWriter::~PDBSourceLineWriter() { + Close(); } bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) { @@ -728,131 +666,7 @@ bool PDBSourceLineWriter::PrintFrameDataUsingEXE() { return false; } - // Convert wchar to native charset because ImageLoad only takes - // a PSTR as input. - string code_file; - if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { - return false; - } - - AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); - if (!img) { - fprintf(stderr, "Failed to load %s\n", code_file.c_str()); - return false; - } - PIMAGE_OPTIONAL_HEADER64 optional_header = - &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader; - if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { - fprintf(stderr, "Not a PE32+ image\n"); - return false; - } - - // Read Exception Directory - DWORD exception_rva = optional_header-> - DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; - DWORD exception_size = optional_header-> - DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; - PIMAGE_RUNTIME_FUNCTION_ENTRY funcs = - static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( - ImageRvaToVa(img->FileHeader, - img->MappedAddress, - exception_rva, - &img->LastRvaSection)); - for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) { - DWORD unwind_rva = funcs[i].UnwindInfoAddress; - // handle chaining - while (unwind_rva & 0x1) { - unwind_rva ^= 0x1; - PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = - static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( - ImageRvaToVa(img->FileHeader, - img->MappedAddress, - unwind_rva, - &img->LastRvaSection)); - unwind_rva = chained_func->UnwindInfoAddress; - } - - UnwindInfo *unwind_info = static_cast<UnwindInfo *>( - ImageRvaToVa(img->FileHeader, - img->MappedAddress, - unwind_rva, - &img->LastRvaSection)); - - DWORD stack_size = 8; // minimal stack size is 8 for RIP - DWORD rip_offset = 8; - do { - for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) { - UnwindCode *unwind_code = &unwind_info->unwind_code[c]; - switch (unwind_code->unwind_operation_code) { - case UWOP_PUSH_NONVOL: { - stack_size += 8; - break; - } - case UWOP_ALLOC_LARGE: { - if (unwind_code->operation_info == 0) { - c++; - if (c < unwind_info->count_of_codes) - stack_size += (unwind_code + 1)->frame_offset * 8; - } else { - c += 2; - if (c < unwind_info->count_of_codes) - stack_size += (unwind_code + 1)->frame_offset | - ((unwind_code + 2)->frame_offset << 16); - } - break; - } - case UWOP_ALLOC_SMALL: { - stack_size += unwind_code->operation_info * 8 + 8; - break; - } - case UWOP_SET_FPREG: - case UWOP_SAVE_XMM: - case UWOP_SAVE_XMM_FAR: - break; - case UWOP_SAVE_NONVOL: - case UWOP_SAVE_XMM128: { - c++; // skip slot with offset - break; - } - case UWOP_SAVE_NONVOL_FAR: - case UWOP_SAVE_XMM128_FAR: { - c += 2; // skip 2 slots with offset - break; - } - case UWOP_PUSH_MACHFRAME: { - if (unwind_code->operation_info) { - stack_size += 88; - } else { - stack_size += 80; - } - rip_offset += 80; - break; - } - } - } - if (unwind_info->flags & UNW_FLAG_CHAININFO) { - PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = - reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( - (unwind_info->unwind_code + - ((unwind_info->count_of_codes + 1) & ~1))); - - unwind_info = static_cast<UnwindInfo *>( - ImageRvaToVa(img->FileHeader, - img->MappedAddress, - chained_func->UnwindInfoAddress, - &img->LastRvaSection)); - } else { - unwind_info = NULL; - } - } while (unwind_info); - fprintf(output_, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n", - funcs[i].BeginAddress, - funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset); - fprintf(output_, "STACK CFI %lx .cfa: $rsp %lu +\n", - funcs[i].BeginAddress, stack_size); - } - - return true; + return PrintPEFrameData(code_file_, output_); } bool PDBSourceLineWriter::PrintFrameData() { @@ -1236,8 +1050,8 @@ next_child: return param_size; } -bool PDBSourceLineWriter::WriteMap(FILE *map_file) { - output_ = map_file; +bool PDBSourceLineWriter::WriteSymbols(FILE *symbol_file) { + output_ = symbol_file; // Load the OMAP information, and disable auto-translation of addresses in // preference of doing it ourselves. @@ -1259,7 +1073,9 @@ bool PDBSourceLineWriter::WriteMap(FILE *map_file) { } void PDBSourceLineWriter::Close() { - session_.Release(); + if (session_ != nullptr) { + session_.Release(); + } } bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { @@ -1284,17 +1100,7 @@ bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { // Instead, it returns one of the IMAGE_FILE_MACHINE values as // defined here: // http://msdn.microsoft.com/en-us/library/ms680313%28VS.85%29.aspx - switch (machine_type) { - case IMAGE_FILE_MACHINE_I386: - info->cpu = L"x86"; - break; - case IMAGE_FILE_MACHINE_AMD64: - info->cpu = L"x86_64"; - break; - default: - info->cpu = L"unknown"; - break; - } + info->cpu = FileHeaderMachineToCpuString(static_cast<WORD>(machine_type)); } else { // Unexpected, but handle gracefully. info->cpu = L"unknown"; @@ -1317,35 +1123,14 @@ bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { return false; } - // Use the same format that the MS symbol server uses in filesystem - // hierarchies. - wchar_t age_string[9]; - swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]), - L"%x", age); - - // remove when VC++7.1 is no longer supported - age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0'; - - info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid); - info->debug_identifier.append(age_string); + info->debug_identifier = GenerateDebugIdentifier(age, guid); } else { DWORD signature; if (FAILED(global->get_signature(&signature))) { return false; } - // Use the same format that the MS symbol server uses in filesystem - // hierarchies. - wchar_t identifier_string[17]; - swprintf(identifier_string, - sizeof(identifier_string) / sizeof(identifier_string[0]), - L"%08X%x", signature, age); - - // remove when VC++7.1 is no longer supported - identifier_string[sizeof(identifier_string) / - sizeof(identifier_string[0]) - 1] = L'\0'; - - info->debug_identifier = identifier_string; + info->debug_identifier = GenerateDebugIdentifier(age, signature); } CComBSTR debug_file_string; @@ -1368,41 +1153,7 @@ bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { return false; } - // Convert wchar to native charset because ImageLoad only takes - // a PSTR as input. - string code_file; - if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { - return false; - } - - AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); - if (!img) { - fprintf(stderr, "Failed to open PE file: %s\n", code_file.c_str()); - return false; - } - - info->code_file = WindowsStringUtils::GetBaseName(code_file_); - - // The date and time that the file was created by the linker. - DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp; - // The size of the file in bytes, including all headers. - DWORD SizeOfImage = 0; - PIMAGE_OPTIONAL_HEADER64 opt = - &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader; - if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { - // 64-bit PE file. - SizeOfImage = opt->SizeOfImage; - } else { - // 32-bit PE file. - SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage; - } - wchar_t code_identifier[32]; - swprintf(code_identifier, - sizeof(code_identifier) / sizeof(code_identifier[0]), - L"%08X%X", TimeDateStamp, SizeOfImage); - info->code_identifier = code_identifier; - - return true; + return ReadPEInfo(code_file_, info); } bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { diff --git a/src/common/windows/pdb_source_line_writer.h b/src/common/windows/pdb_source_line_writer.h index 3ed07361..c0adf29f 100644 --- a/src/common/windows/pdb_source_line_writer.h +++ b/src/common/windows/pdb_source_line_writer.h @@ -35,9 +35,10 @@ #include <atlcomcli.h> -#include <unordered_map> #include <string> +#include <unordered_map> +#include "common/windows/module_info.h" #include "common/windows/omap.h" struct IDiaEnumLineNumbers; @@ -49,41 +50,6 @@ namespace google_breakpad { using std::wstring; using std::unordered_map; -// A structure that carries information that identifies a pdb file. -struct PDBModuleInfo { - public: - // The basename of the pdb file from which information was loaded. - wstring debug_file; - - // The pdb's identifier. For recent pdb files, the identifier consists - // of the pdb's guid, in uppercase hexadecimal form without any dashes - // or separators, followed immediately by the pdb's age, also in - // uppercase hexadecimal form. For older pdb files which have no guid, - // the identifier is the pdb's 32-bit signature value, in zero-padded - // hexadecimal form, followed immediately by the pdb's age, in lowercase - // hexadecimal form. - wstring debug_identifier; - - // A string identifying the cpu that the pdb is associated with. - // Currently, this may be "x86" or "unknown". - wstring cpu; -}; - -// A structure that carries information that identifies a PE file, -// either an EXE or a DLL. -struct PEModuleInfo { - // The basename of the PE file. - wstring code_file; - - // The PE file's code identifier, which consists of its timestamp - // and file size concatenated together into a single hex string. - // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and - // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp - // documentation.) This is not well documented, if it's documented - // at all, but it's what symstore does and what DbgHelp supports. - wstring code_identifier; -}; - class PDBSourceLineWriter { public: enum FileFormat { @@ -101,6 +67,9 @@ class PDBSourceLineWriter { // Returns true on success. bool Open(const wstring &file, FileFormat format); + // Closes the current pdb file and its associated resources. + void Close(); + // Sets the code file full path. This is optional for 32-bit modules. It is // also optional for 64-bit modules when there is an executable file stored // in the same directory as the PDB file. It is only required for 64-bit @@ -110,12 +79,9 @@ class PDBSourceLineWriter { // SetCodeFile() with a different file path and it will return false. bool SetCodeFile(const wstring &exe_file); - // Writes a map file from the current pdb file to the given file stream. + // Writes a Breakpad symbol file from the current pdb file to |symbol_file|. // Returns true on success. - bool WriteMap(FILE *map_file); - - // Closes the current pdb file and its associated resources. - void Close(); + bool WriteSymbols(FILE *symbol_file); // Retrieves information about the module's debugging file. Returns // true on success and false on failure. diff --git a/src/common/windows/pe_source_line_writer.cpp b/src/common/windows/pe_source_line_writer.cpp new file mode 100644 index 00000000..cb6cc713 --- /dev/null +++ b/src/common/windows/pe_source_line_writer.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2019, Google Inc.
+// All rights reserved.
+//
+// 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 Inc. 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.
+
+#include "common/windows/pe_source_line_writer.h"
+
+#include "common/windows/pe_util.h"
+
+namespace google_breakpad {
+PESourceLineWriter::PESourceLineWriter(const wstring& pe_file) :
+ pe_file_(pe_file) {
+}
+
+PESourceLineWriter::~PESourceLineWriter() {
+}
+
+bool PESourceLineWriter::WriteSymbols(FILE* symbol_file) {
+ PDBModuleInfo module_info;
+ if (!GetModuleInfo(&module_info)) {
+ return false;
+ }
+ // Hard-code "windows" for the OS because that's the only thing that makes
+ // sense for PDB files. (This might not be strictly correct for Windows CE
+ // support, but we don't care about that at the moment.)
+ fprintf(symbol_file, "MODULE windows %ws %ws %ws\n",
+ module_info.cpu.c_str(), module_info.debug_identifier.c_str(),
+ module_info.debug_file.c_str());
+
+ PEModuleInfo pe_info;
+ if (!GetPEInfo(&pe_info)) {
+ return false;
+ }
+ fprintf(symbol_file, "INFO CODE_ID %ws %ws\n",
+ pe_info.code_identifier.c_str(),
+ pe_info.code_file.c_str());
+
+ if (!PrintPEFrameData(pe_file_, symbol_file)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool PESourceLineWriter::GetModuleInfo(PDBModuleInfo* info) {
+ return ReadModuleInfo(pe_file_, info);
+}
+
+bool PESourceLineWriter::GetPEInfo(PEModuleInfo* info) {
+ return ReadPEInfo(pe_file_, info);
+}
+
+} // namespace google_breakpad
diff --git a/src/common/windows/pe_source_line_writer.h b/src/common/windows/pe_source_line_writer.h new file mode 100644 index 00000000..2bf1d4fd --- /dev/null +++ b/src/common/windows/pe_source_line_writer.h @@ -0,0 +1,69 @@ +// Copyright (c) 2019, Google Inc.
+// All rights reserved.
+//
+// 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 Inc. 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.
+
+#ifndef COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
+#define COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
+
+#include <string>
+
+#include "common/basictypes.h"
+#include "common/windows/module_info.h"
+
+namespace google_breakpad {
+
+using std::wstring;
+
+// PESourceLineWriter uses a pe file produced by Visual C++ to output
+// a line/address map for use with BasicSourceLineResolver.
+// NOTE: Only supports PE32+ format, ie. a 64bit PE file.
+class PESourceLineWriter {
+public:
+ explicit PESourceLineWriter(const wstring& pe_file);
+ ~PESourceLineWriter();
+
+ // Writes Breakpad symbols from the pe file to |symbol_file|.
+ // Returns true on success.
+ bool WriteSymbols(FILE* symbol_file);
+
+ // Retrieves information about the module. Returns true on success.
+ bool GetModuleInfo(PDBModuleInfo* info);
+
+ // Retrieves information about the module's PE file. Returns + // true on success.
+ bool GetPEInfo(PEModuleInfo* info);
+
+private:
+ const wstring pe_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(PESourceLineWriter);
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
diff --git a/src/common/windows/pe_util.cpp b/src/common/windows/pe_util.cpp new file mode 100644 index 00000000..11181954 --- /dev/null +++ b/src/common/windows/pe_util.cpp @@ -0,0 +1,418 @@ +// Copyright (c) 2019, Google Inc.
+// All rights reserved.
+//
+// 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 Inc. 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.
+
+#include "pe_util.h"
+
+#include <windows.h>
+#include <winnt.h>
+#include <atlbase.h>
+#include <ImageHlp.h>
+
+#include <functional>
+
+#include "common/windows/string_utils-inl.h"
+#include "common/windows/guid_string.h"
+
+namespace {
+
+/*
+ * Not defined in WinNT.h for some reason. Definitions taken from:
+ * http://uninformed.org/index.cgi?v=4&a=1&p=13
+ *
+ */
+typedef unsigned char UBYTE;
+
+#if !defined(_WIN64)
+#define UNW_FLAG_EHANDLER 0x01
+#define UNW_FLAG_UHANDLER 0x02
+#define UNW_FLAG_CHAININFO 0x04
+#endif
+
+union UnwindCode {
+ struct {
+ UBYTE offset_in_prolog;
+ UBYTE unwind_operation_code : 4;
+ UBYTE operation_info : 4;
+ };
+ USHORT frame_offset;
+};
+
+enum UnwindOperationCodes {
+ UWOP_PUSH_NONVOL = 0, /* info == register number */
+ UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
+ UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
+ UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+ UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
+ UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
+ // XXX: these are missing from MSDN!
+ // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
+ UWOP_SAVE_XMM,
+ UWOP_SAVE_XMM_FAR,
+ UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
+ UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
+ UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
+};
+
+// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
+// Note: some fields removed as we don't use them.
+struct UnwindInfo {
+ UBYTE version : 3;
+ UBYTE flags : 5;
+ UBYTE size_of_prolog;
+ UBYTE count_of_codes;
+ UBYTE frame_register : 4;
+ UBYTE frame_offset : 4;
+ UnwindCode unwind_code[1];
+};
+
+struct CV_INFO_PDB70 {
+ ULONG cv_signature;
+ GUID signature;
+ ULONG age;
+ UCHAR pdb_filename[ANYSIZE_ARRAY];
+};
+
+#define CV_SIGNATURE_RSDS 'SDSR'
+
+// A helper class to scope a PLOADED_IMAGE.
+class AutoImage {
+public:
+ explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}
+ ~AutoImage() {
+ if (img_)
+ ImageUnload(img_);
+ }
+
+ operator PLOADED_IMAGE() { return img_; }
+ PLOADED_IMAGE operator->() { return img_; }
+
+private:
+ PLOADED_IMAGE img_;
+};
+} // namespace
+
+namespace google_breakpad {
+
+using std::unique_ptr;
+using google_breakpad::GUIDString;
+
+bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) {
+ info->debug_file = WindowsStringUtils::GetBaseName(pe_file);
+
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string img_file;
+ if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
+ fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
+ pe_file.c_str());
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to load %s\n", img_file.c_str());
+ return false;
+ }
+
+ info->cpu = FileHeaderMachineToCpuString(
+ img->FileHeader->FileHeader.Machine);
+
+ PIMAGE_OPTIONAL_HEADER64 optional_header =
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ fprintf(stderr, "Not a PE32+ image\n");
+ return false;
+ }
+
+ // Search debug directories for a guid signature & age
+ DWORD debug_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ DWORD debug_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ PIMAGE_DEBUG_DIRECTORY debug_directories =
+ static_cast<PIMAGE_DEBUG_DIRECTORY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ debug_rva,
+ &img->LastRvaSection));
+
+ for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) {
+ if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ||
+ debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) {
+ continue;
+ }
+
+ struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa(
+ img->FileHeader,
+ img->MappedAddress,
+ debug_directories[i].AddressOfRawData,
+ &img->LastRvaSection));
+ if (cv_info->cv_signature != CV_SIGNATURE_RSDS) {
+ continue;
+ }
+
+ info->debug_identifier = GenerateDebugIdentifier(cv_info->age,
+ cv_info->signature);
+ return true;
+ }
+
+ fprintf(stderr, "Image is missing debug information.\n");
+ return false;
+}
+
+bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) {
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string img_file;
+ if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
+ fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
+ pe_file.c_str());
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str());
+ return false;
+ }
+
+ info->code_file = WindowsStringUtils::GetBaseName(pe_file);
+
+ // The date and time that the file was created by the linker.
+ DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;
+ // The size of the file in bytes, including all headers.
+ DWORD SizeOfImage = 0;
+ PIMAGE_OPTIONAL_HEADER64 opt =
+ &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;
+ if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ // 64-bit PE file.
+ SizeOfImage = opt->SizeOfImage;
+ }
+ else {
+ // 32-bit PE file.
+ SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;
+ }
+ wchar_t code_identifier[32];
+ swprintf(code_identifier,
+ sizeof(code_identifier) / sizeof(code_identifier[0]),
+ L"%08X%X", TimeDateStamp, SizeOfImage);
+ info->code_identifier = code_identifier;
+
+ return true;
+}
+
+bool PrintPEFrameData(const wstring & pe_file, FILE * out_file)
+{
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string img_file;
+ if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
+ fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
+ pe_file.c_str());
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to load %s\n", img_file.c_str());
+ return false;
+ }
+ PIMAGE_OPTIONAL_HEADER64 optional_header =
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ fprintf(stderr, "Not a PE32+ image\n");
+ return false;
+ }
+
+ // Read Exception Directory
+ DWORD exception_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
+ DWORD exception_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ exception_rva,
+ &img->LastRvaSection));
+ for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
+ DWORD unwind_rva = funcs[i].UnwindInfoAddress;
+ // handle chaining
+ while (unwind_rva & 0x1) {
+ unwind_rva ^= 0x1;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+ unwind_rva = chained_func->UnwindInfoAddress;
+ }
+
+ UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+
+ DWORD stack_size = 8; // minimal stack size is 8 for RIP
+ DWORD rip_offset = 8;
+ do {
+ for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
+ UnwindCode *unwind_code = &unwind_info->unwind_code[c];
+ switch (unwind_code->unwind_operation_code) {
+ case UWOP_PUSH_NONVOL: {
+ stack_size += 8;
+ break;
+ }
+ case UWOP_ALLOC_LARGE: {
+ if (unwind_code->operation_info == 0) {
+ c++;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset * 8;
+ }
+ else {
+ c += 2;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset |
+ ((unwind_code + 2)->frame_offset << 16);
+ }
+ break;
+ }
+ case UWOP_ALLOC_SMALL: {
+ stack_size += unwind_code->operation_info * 8 + 8;
+ break;
+ }
+ case UWOP_SET_FPREG:
+ case UWOP_SAVE_XMM:
+ case UWOP_SAVE_XMM_FAR:
+ break;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128: {
+ c++; // skip slot with offset
+ break;
+ }
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR: {
+ c += 2; // skip 2 slots with offset
+ break;
+ }
+ case UWOP_PUSH_MACHFRAME: {
+ if (unwind_code->operation_info) {
+ stack_size += 88;
+ }
+ else {
+ stack_size += 80;
+ }
+ rip_offset += 80;
+ break;
+ }
+ }
+ }
+ if (unwind_info->flags & UNW_FLAG_CHAININFO) {
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ (unwind_info->unwind_code +
+ ((unwind_info->count_of_codes + 1) & ~1)));
+
+ unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ chained_func->UnwindInfoAddress,
+ &img->LastRvaSection));
+ }
+ else {
+ unwind_info = NULL;
+ }
+ } while (unwind_info);
+ fprintf(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
+ funcs[i].BeginAddress,
+ funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
+ fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n",
+ funcs[i].BeginAddress, stack_size);
+ }
+
+ return true;
+}
+
+wstring GenerateDebugIdentifier(DWORD age, GUID signature)
+{
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ wchar_t age_string[9];
+ swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
+ L"%x", age);
+
+ // remove when VC++7.1 is no longer supported
+ age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
+
+ wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature);
+ debug_identifier.append(age_string);
+
+ return debug_identifier;
+}
+
+wstring GenerateDebugIdentifier(DWORD age, DWORD signature)
+{
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ wchar_t identifier_string[17];
+ swprintf(identifier_string,
+ sizeof(identifier_string) / sizeof(identifier_string[0]),
+ L"%08X%x", signature, age);
+
+ // remove when VC++7.1 is no longer supported
+ identifier_string[sizeof(identifier_string) /
+ sizeof(identifier_string[0]) - 1] = L'\0';
+
+ return wstring(identifier_string);
+}
+
+constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine)
+{
+ {
+ switch (machine) {
+ case IMAGE_FILE_MACHINE_I386:
+ {
+ return L"x86";
+ }
+ case IMAGE_FILE_MACHINE_IA64:
+ case IMAGE_FILE_MACHINE_AMD64:
+ {
+ return L"x86_64";
+ }
+ default:
+ {
+ return L"unknown";
+ }
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/src/common/windows/pe_util.h b/src/common/windows/pe_util.h new file mode 100644 index 00000000..cea40a2e --- /dev/null +++ b/src/common/windows/pe_util.h @@ -0,0 +1,67 @@ +// Copyright (c) 2019, Google Inc.
+// All rights reserved.
+//
+// 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 Inc. 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.
+
+#ifndef COMMON_WINDOWS_PE_UTIL_H_
+#define COMMON_WINDOWS_PE_UTIL_H_
+
+#include <windows.h>
+
+#include "common/windows/module_info.h"
+
+namespace google_breakpad {
+
+using std::wstring;
+
+// Reads |pe_file| and populates |info|. Returns true on success.
+// Only supports PE32+ format, ie. a 64bit PE file.
+// Will fail if |pe_file| does not contain a valid CodeView record.
+bool ReadModuleInfo(const wstring& pe_file, PDBModuleInfo* info);
+
+// Reads |pe_file| and populates |info|. Returns true on success.
+bool ReadPEInfo(const wstring& pe_file, PEModuleInfo* info);
+
+// Reads |pe_file| and prints frame data (aka. unwind info) to |out_file|.
+// Only supports PE32+ format, ie. a 64bit PE file.
+bool PrintPEFrameData(const wstring& pe_file, FILE* out_file);
+
+// Combines a GUID |signature| and DWORD |age| to create a Breakpad debug
+// identifier.
+wstring GenerateDebugIdentifier(DWORD age, GUID signature);
+
+// Combines a DWORD |signature| and DWORD |age| to create a Breakpad debug
+// identifier.
+wstring GenerateDebugIdentifier(DWORD age, DWORD signature);
+
+// Converts |machine| enum value to the corresponding string used by Breakpad.
+// The enum is IMAGE_FILE_MACHINE_*, contained in winnt.h.
+constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine);
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_PE_UTIL_H_
diff --git a/src/tools/windows/dump_syms/dump_syms.cc b/src/tools/windows/dump_syms/dump_syms.cc index 8ea777a5..a61434be 100644 --- a/src/tools/windows/dump_syms/dump_syms.cc +++ b/src/tools/windows/dump_syms/dump_syms.cc @@ -36,27 +36,38 @@ #include <string> #include "common/windows/pdb_source_line_writer.h" +#include "common/windows/pe_source_line_writer.h" -using std::wstring; +using std::wstring;; using google_breakpad::PDBSourceLineWriter; +using google_breakpad::PESourceLineWriter; +using std::unique_ptr; int wmain(int argc, wchar_t **argv) { - if (argc < 2) { - fprintf(stderr, "Usage: %ws <file.[pdb|exe|dll]>\n", argv[0]); + bool success; + if (argc == 2) { + PDBSourceLineWriter pdb_writer; + if (!pdb_writer.Open(wstring(argv[1]), PDBSourceLineWriter::ANY_FILE)) { + fprintf(stderr, "Open failed.\n"); + return 1; + } + success = pdb_writer.WriteSymbols(stdout); + } else if (argc == 3 && wcscmp(argv[1], L"--pe") == 0) { + PESourceLineWriter pe_writer(argv[2]); + success = pe_writer.WriteSymbols(stdout); + } else { + fprintf(stderr, "Usage: %ws [--pe] <file.[pdb|exe|dll]>\n", argv[0]); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "--pe:\tRead debugging information from PE file and do " + "not attempt to locate matching PDB file.\n" + "\tThis is only supported for PE32+ (64 bit) PE files.\n"); return 1; } - PDBSourceLineWriter writer; - if (!writer.Open(wstring(argv[1]), PDBSourceLineWriter::ANY_FILE)) { - fprintf(stderr, "Open failed\n"); + if (!success) { + fprintf(stderr, "WriteSymbols failed.\n"); return 1; } - if (!writer.WriteMap(stdout)) { - fprintf(stderr, "WriteMap failed\n"); - return 1; - } - - writer.Close(); return 0; } diff --git a/src/tools/windows/dump_syms/dump_syms_unittest.cc b/src/tools/windows/dump_syms/dump_syms_unittest.cc index 5ed512e6..766e5c09 100644 --- a/src/tools/windows/dump_syms/dump_syms_unittest.cc +++ b/src/tools/windows/dump_syms/dump_syms_unittest.cc @@ -55,11 +55,15 @@ const wchar_t* kRootNames[] = { // without source data.
L"omap_stretched",
// A PDB file with OMAP data for an image that has been basic block reordered.
- L"omap_reorder_bbs",
+ L"omap_reorder_bbs",
// A 64bit PDB file with no OMAP data.
L"dump_syms_regtest64",
};
+const wchar_t* kPEOnlyRootNames[] = {
+ L"pe_only_symbol_test",
+};
+
void TrimLastComponent(const std::wstring& path,
std::wstring* trimmed,
std::wstring* component) {
@@ -177,29 +181,64 @@ class DumpSymsRegressionTest : public testing::TestWithParam<const wchar_t *> { std::wstring testdata_dir;
};
+class DumpSymsPEOnlyRegressionTest : public testing::TestWithParam<const wchar_t *> {
+public:
+ virtual void SetUp() {
+ std::wstring self_dir;
+ ASSERT_TRUE(GetSelfDirectory(&self_dir));
+ dump_syms_exe = self_dir + L"\\dump_syms.exe";
+
+ TrimLastComponent(self_dir, &testdata_dir, NULL);
+ testdata_dir += L"\\testdata";
+ }
+
+ std::wstring dump_syms_exe;
+ std::wstring testdata_dir;
+};
+
} //namespace
TEST_P(DumpSymsRegressionTest, EnsureDumpedSymbolsMatch) {
- const wchar_t* root_name = GetParam();
- std::wstring root_path = testdata_dir + L"\\" + root_name;
+ const wchar_t* root_name = GetParam();
+ std::wstring root_path = testdata_dir + L"\\" + root_name;
- std::wstring sym_path = root_path + L".sym";
- std::string expected_symbols;
- ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols));
+ std::wstring sym_path = root_path + L".sym";
+ std::string expected_symbols;
+ ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols));
- std::wstring pdb_path = root_path + L".pdb";
- std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" +
- pdb_path + L"\"";
- std::string symbols;
- ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols));
+ std::wstring pdb_path = root_path + L".pdb";
+ std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" +
+ pdb_path + L"\"";
+ std::string symbols;
+ ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols));
- EXPECT_EQ(expected_symbols, symbols);
+ EXPECT_EQ(expected_symbols, symbols);
}
INSTANTIATE_TEST_CASE_P(DumpSyms, DumpSymsRegressionTest,
- testing::ValuesIn(kRootNames));
+ testing::ValuesIn(kRootNames));
+
+TEST_P(DumpSymsPEOnlyRegressionTest, EnsurePEOnlyDumpedSymbolsMatch) {
+ const wchar_t* root_name = GetParam();
+ std::wstring root_path = testdata_dir + L"\\" + root_name;
+
+ std::wstring sym_path = root_path + L".sym";
+ std::string expected_symbols;
+ ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols));
+
+ std::wstring dll_path = root_path + L".dll";
+ std::wstring command_line = L"\"" + dump_syms_exe + L"\" --pe \"" +
+ dll_path + L"\"";
+ std::string symbols;
+ ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols));
+
+ EXPECT_EQ(expected_symbols, symbols);
+}
+
+INSTANTIATE_TEST_CASE_P(PEOnlyDumpSyms, DumpSymsPEOnlyRegressionTest,
+ testing::ValuesIn(kPEOnlyRootNames));
} // namespace dump_syms
} // namespace windows
-} // namespace tools +} // namespace tools
diff --git a/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.dll b/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.dll Binary files differnew file mode 100644 index 00000000..879af40b --- /dev/null +++ b/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.dll diff --git a/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.sym b/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.sym new file mode 100644 index 00000000..24a3ddf4 --- /dev/null +++ b/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.sym @@ -0,0 +1,214 @@ +MODULE windows x86_64 2A5EAB481FAB4A17A9761CDC14FE531A1 pe_only_symbol_test.dll
+INFO CODE_ID 5C8AD05F12000 pe_only_symbol_test.dll
+STACK CFI INIT 1440 39 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1440 .cfa: $rsp 32 +
+STACK CFI INIT 1490 7f .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1490 .cfa: $rsp 128 +
+STACK CFI INIT 1520 41 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1520 .cfa: $rsp 48 +
+STACK CFI INIT 1570 35 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1570 .cfa: $rsp 48 +
+STACK CFI INIT 15b0 3a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 15b0 .cfa: $rsp 48 +
+STACK CFI INIT 1640 8 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1640 .cfa: $rsp 16 +
+STACK CFI INIT 1650 d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1650 .cfa: $rsp 16 +
+STACK CFI INIT 1660 b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1660 .cfa: $rsp 16 +
+STACK CFI INIT 1670 5a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1670 .cfa: $rsp 64 +
+STACK CFI INIT 16e0 97 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 16e0 .cfa: $rsp 112 +
+STACK CFI INIT 17c0 3f .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 17c0 .cfa: $rsp 64 +
+STACK CFI INIT 1810 23 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1810 .cfa: $rsp 64 +
+STACK CFI INIT 1840 16 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1840 .cfa: $rsp 16 +
+STACK CFI INIT 1856 20 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1856 .cfa: $rsp 16 +
+STACK CFI INIT 1876 5 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1876 .cfa: $rsp 16 +
+STACK CFI INIT 1890 1b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1890 .cfa: $rsp 48 +
+STACK CFI INIT 18ab 56 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 18ab .cfa: $rsp 48 +
+STACK CFI INIT 1901 10 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1901 .cfa: $rsp 48 +
+STACK CFI INIT 1940 7 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1940 .cfa: $rsp 64 +
+STACK CFI INIT 1947 1a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1947 .cfa: $rsp 64 +
+STACK CFI INIT 1961 b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1961 .cfa: $rsp 64 +
+STACK CFI INIT 196c 4c .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 196c .cfa: $rsp 64 +
+STACK CFI INIT 19b8 5 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 19b8 .cfa: $rsp 64 +
+STACK CFI INIT 19bd 13 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 19bd .cfa: $rsp 64 +
+STACK CFI INIT 19d0 73 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 19d0 .cfa: $rsp 64 +
+STACK CFI INIT 1a90 3a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1a90 .cfa: $rsp 48 +
+STACK CFI INIT 1ae0 f8 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1ae0 .cfa: $rsp 96 +
+STACK CFI INIT 1c30 21 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1c30 .cfa: $rsp 8 +
+STACK CFI INIT 1c60 87 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1c60 .cfa: $rsp 64 +
+STACK CFI INIT 1d10 13a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1d10 .cfa: $rsp 80 +
+STACK CFI INIT 1ea0 88 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1ea0 .cfa: $rsp 64 +
+STACK CFI INIT 1f50 135 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 1f50 .cfa: $rsp 80 +
+STACK CFI INIT 20e0 4d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 20e0 .cfa: $rsp 64 +
+STACK CFI INIT 2140 2a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2140 .cfa: $rsp 48 +
+STACK CFI INIT 2180 36 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2180 .cfa: $rsp 48 +
+STACK CFI INIT 2290 36 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2290 .cfa: $rsp 96 +
+STACK CFI INIT 22e0 44 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 22e0 .cfa: $rsp 96 +
+STACK CFI INIT 2340 5f .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2340 .cfa: $rsp 544 +
+STACK CFI INIT 239f d9 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 239f .cfa: $rsp 544 +
+STACK CFI INIT 2478 1d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2478 .cfa: $rsp 544 +
+STACK CFI INIT 2560 cf .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2560 .cfa: $rsp 1088 +
+STACK CFI INIT 2670 2d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2670 .cfa: $rsp 80 +
+STACK CFI INIT 269d 6b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 269d .cfa: $rsp 80 +
+STACK CFI INIT 2708 1a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2708 .cfa: $rsp 80 +
+STACK CFI INIT 2770 260 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2770 .cfa: $rsp 3824 +
+STACK CFI INIT 2a70 1f .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2a70 .cfa: $rsp 48 +
+STACK CFI INIT 2aa0 c5 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2aa0 .cfa: $rsp 1088 +
+STACK CFI INIT 2ba0 64 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2ba0 .cfa: $rsp 64 +
+STACK CFI INIT 2c20 25 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2c20 .cfa: $rsp 64 +
+STACK CFI INIT 2c50 35 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2c50 .cfa: $rsp 48 +
+STACK CFI INIT 2ca0 d1 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2ca0 .cfa: $rsp 64 +
+STACK CFI INIT 2db0 13 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2db0 .cfa: $rsp 48 +
+STACK CFI INIT 2dd0 9b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2dd0 .cfa: $rsp 48 +
+STACK CFI INIT 2ea0 10e .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 2ea0 .cfa: $rsp 64 +
+STACK CFI INIT 3000 91 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3000 .cfa: $rsp 128 +
+STACK CFI INIT 30c0 b2 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 30c0 .cfa: $rsp 128 +
+STACK CFI INIT 31a0 be .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 31a0 .cfa: $rsp 80 +
+STACK CFI INIT 3290 74 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3290 .cfa: $rsp 64 +
+STACK CFI INIT 3330 16 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3330 .cfa: $rsp 48 +
+STACK CFI INIT 3350 15 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3350 .cfa: $rsp 48 +
+STACK CFI INIT 3380 45 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3380 .cfa: $rsp 64 +
+STACK CFI INIT 33e0 3b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 33e0 .cfa: $rsp 48 +
+STACK CFI INIT 3430 40 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3430 .cfa: $rsp 48 +
+STACK CFI INIT 34a0 15 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 34a0 .cfa: $rsp 48 +
+STACK CFI INIT 34c0 c6 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 34c0 .cfa: $rsp 64 +
+STACK CFI INIT 35c0 e .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 35c0 .cfa: $rsp 48 +
+STACK CFI INIT 35e0 8a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 35e0 .cfa: $rsp 48 +
+STACK CFI INIT 36a0 62 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 36a0 .cfa: $rsp 80 +
+STACK CFI INIT 3720 2d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3720 .cfa: $rsp 48 +
+STACK CFI INIT 3760 1d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3760 .cfa: $rsp 48 +
+STACK CFI INIT 3790 30 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3790 .cfa: $rsp 48 +
+STACK CFI INIT 37d0 15 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 37d0 .cfa: $rsp 48 +
+STACK CFI INIT 37f0 5b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 37f0 .cfa: $rsp 64 +
+STACK CFI INIT 3870 2e .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3870 .cfa: $rsp 48 +
+STACK CFI INIT 38b0 15 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 38b0 .cfa: $rsp 48 +
+STACK CFI INIT 38d0 49 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 38d0 .cfa: $rsp 48 +
+STACK CFI INIT 3930 10c .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3930 .cfa: $rsp 128 +
+STACK CFI INIT 3a80 8b .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3a80 .cfa: $rsp 96 +
+STACK CFI INIT 3b30 2f .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3b30 .cfa: $rsp 48 +
+STACK CFI INIT 3b70 3f .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3b70 .cfa: $rsp 48 +
+STACK CFI INIT 3bc0 82 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3bc0 .cfa: $rsp 80 +
+STACK CFI INIT 3c70 50 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3c70 .cfa: $rsp 64 +
+STACK CFI INIT 3ce0 33 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3ce0 .cfa: $rsp 64 +
+STACK CFI INIT 3d50 191 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3d50 .cfa: $rsp 1536 +
+STACK CFI INIT 3f50 51 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3f50 .cfa: $rsp 176 +
+STACK CFI INIT 3fc0 e .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3fc0 .cfa: $rsp 48 +
+STACK CFI INIT 3ff0 a6 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 3ff0 .cfa: $rsp 64 +
+STACK CFI INIT 40c0 16 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 40c0 .cfa: $rsp 48 +
+STACK CFI INIT 40f0 72 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 40f0 .cfa: $rsp 64 +
+STACK CFI INIT 4180 42 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4180 .cfa: $rsp 48 +
+STACK CFI INIT 41e0 42 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 41e0 .cfa: $rsp 48 +
+STACK CFI INIT 4250 1e .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4250 .cfa: $rsp 32 +
+STACK CFI INIT 4280 18 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4280 .cfa: $rsp 48 +
+STACK CFI INIT 42a0 37 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 42a0 .cfa: $rsp 64 +
+STACK CFI INIT 4300 145 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4300 .cfa: $rsp 1120 +
+STACK CFI INIT 44a0 2ae .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 44a0 .cfa: $rsp 640 +
+STACK CFI INIT 4800 103 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4800 .cfa: $rsp 1664 +
+STACK CFI INIT 4950 3c5 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4950 .cfa: $rsp 240 +
+STACK CFI INIT 4e10 36d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 4e10 .cfa: $rsp 96 +
+STACK CFI INIT 5270 25 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 5270 .cfa: $rsp 32 +
+STACK CFI INIT 66c0 2 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 66c0 .cfa: $rsp 8 +
+STACK CFI INIT 76d0 1a .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 76d0 .cfa: $rsp 48 +
+STACK CFI INIT 76f0 20 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 76f0 .cfa: $rsp 48 +
+STACK CFI INIT 7720 48 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 7720 .cfa: $rsp 64 +
+STACK CFI INIT 7780 20 .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 7780 .cfa: $rsp 48 +
+STACK CFI INIT 77b0 3d .cfa: $rsp .ra: .cfa 8 - ^
+STACK CFI 77b0 .cfa: $rsp 48 +
|