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 + +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(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( - 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( - ImageRvaToVa(img->FileHeader, - img->MappedAddress, - unwind_rva, - &img->LastRvaSection)); - unwind_rva = chained_func->UnwindInfoAddress; - } - - UnwindInfo *unwind_info = static_cast( - 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( - (unwind_info->unwind_code + - ((unwind_info->count_of_codes + 1) & ~1))); - - unwind_info = static_cast( - 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(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 -#include #include +#include +#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 + +#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 +#include +#include +#include + +#include + +#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(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( + 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(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(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( + 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( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + unwind_rva, + &img->LastRvaSection)); + unwind_rva = chained_func->UnwindInfoAddress; + } + + UnwindInfo *unwind_info = static_cast( + 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( + (unwind_info->unwind_code + + ((unwind_info->count_of_codes + 1) & ~1))); + + unwind_info = static_cast( + 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 + +#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 #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 \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] \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 { std::wstring testdata_dir; }; +class DumpSymsPEOnlyRegressionTest : public testing::TestWithParam { +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 new file mode 100644 index 00000000..879af40b Binary files /dev/null and b/src/tools/windows/dump_syms/testdata/pe_only_symbol_test.dll differ 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 +