From 663cc2bf819d810b818cd16cbea6e7b2dbaaa812 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Tue, 4 Feb 2014 00:57:13 +0000 Subject: [PATCH 04/19] Add support for DWARF in PECOFF as used by Cygwin and MinGW For cygwin and mingw targets, build a dump_syms tool which can read DWARF symbols contained in a PE/COFF executable. I felt bad about making another copy of dump_symbols.cc, so this one has the beginnings of being parameterized by an image file reader class, so we can apply it to a pecoff-format image file reading class Write pecoff format image file reading class which - knows how to read PE32 and PE32+ image files - makes PECOFF file id by fetching from a CV record or by hashing - can read PECOFF exports as a fallback if there is no DWARF information v2: Don't include arpa/inet.h on MinGW v3: Use AM_CPPFLAGS for NO_STABS_SUPPORT v4: Fixes for -Werror=sign-compare errors arising due to signedness of size_t Update use of Module::Extern() for change in r1415 Fix EOT fallback to match reality rather than PE/COFF spec. v5: Add needed include of winsock.h for htons etc. v6: Update for char -> uint8_t changes in commit bc44efdc v7: Update for "Add debug fission support" changes What's this? We now build elf_reader.cc into our COFF symbols dumping tool? But why is that? Because dwarf2reader now contains an entirely separate ELF reader, for reading .dwo/.dwp files... Signed-off-by: Jon Turney --- Makefile.am | 30 + configure.ac | 5 + src/common/pecoff/dump_symbols-inl.h | 705 ++++++++++++++++++ src/common/pecoff/dump_symbols.cc | 90 +++ src/common/pecoff/dump_symbols.h | 80 ++ src/common/pecoff/pecoff.h | 262 +++++++ src/common/pecoff/pecoff_file_id.cc | 83 +++ src/common/pecoff/pecoff_file_id.h | 52 ++ src/common/pecoff/pecoffutils.cc | 494 ++++++++++++ src/common/pecoff/pecoffutils.h | 167 +++++ .../windows/dump_syms_dwarf/dump_syms.cc | 88 +++ 11 files changed, 2056 insertions(+) create mode 100644 src/common/pecoff/dump_symbols-inl.h create mode 100644 src/common/pecoff/dump_symbols.cc create mode 100644 src/common/pecoff/dump_symbols.h create mode 100644 src/common/pecoff/pecoff.h create mode 100644 src/common/pecoff/pecoff_file_id.cc create mode 100644 src/common/pecoff/pecoff_file_id.h create mode 100644 src/common/pecoff/pecoffutils.cc create mode 100644 src/common/pecoff/pecoffutils.h create mode 100644 src/tools/windows/dump_syms_dwarf/dump_syms.cc diff --git a/Makefile.am b/Makefile.am index ea82edcd..0f6388f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,11 @@ AM_CFLAGS += -fPIC AM_CXXFLAGS += -fPIC endif +if WINDOWS_HOST +# don't have stab.h, don't build stabs support +AM_CPPFLAGS += -DNO_STABS_SUPPORT +endif + # Specify include paths for ac macros ACLOCAL_AMFLAGS = -I m4 @@ -370,6 +375,12 @@ endif endif endif LINUX_HOST +if WINDOWS_HOST +if !DISABLE_TOOLS +bin_PROGRAMS += \ + src/tools/windows/dump_syms_dwarf/dump_syms +endif +endif ## Tests if !DISABLE_PROCESSOR @@ -782,6 +793,25 @@ src_tools_linux_md2core_minidump_2_core_unittest_LDADD = \ endif LINUX_HOST +if WINDOWS_HOST +if !DISABLE_TOOLS +src_tools_windows_dump_syms_dwarf_dump_syms_SOURCES = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/language.cc \ + src/common/module.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc \ + src/common/pecoff/dump_symbols.cc \ + src/common/pecoff/pecoffutils.cc \ + src/common/pecoff/pecoff_file_id.cc \ + src/tools/windows/dump_syms_dwarf/dump_syms.cc +endif +endif + if !DISABLE_PROCESSOR src_processor_address_map_unittest_SOURCES = \ src/processor/address_map_unittest.cc diff --git a/configure.ac b/configure.ac index 492d0903..63b32bdd 100644 --- a/configure.ac +++ b/configure.ac @@ -110,8 +110,13 @@ case $host in *-*-linux* | *-android* ) LINUX_HOST=true ;; + *-*-cygwin* ) + WINDOWS_HOST=true + ;; esac + AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue) +AM_CONDITIONAL(WINDOWS_HOST, test x$WINDOWS_HOST = xtrue) # Only use Android support headers when compiling for Android case $host in diff --git a/src/common/pecoff/dump_symbols-inl.h b/src/common/pecoff/dump_symbols-inl.h new file mode 100644 index 00000000..23955f6a --- /dev/null +++ b/src/common/pecoff/dump_symbols-inl.h @@ -0,0 +1,705 @@ +// Copyright (c) 2011 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. + +// dump_symbols-inl.h: implement google_breakpad::WriteSymbolFile: +// Find all the debugging info in a file and dump it as a Breakpad symbol file. + +#ifndef COMMON_PECOFF_DUMP_SYMBOLS_INL_H +#define COMMON_PECOFF_DUMP_SYMBOLS_INL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/dwarf_cfi_to_module.h" +#include "common/dwarf_cu_to_module.h" +#include "common/dwarf_line_to_module.h" +#include "common/module.h" +#include "common/scoped_ptr.h" +#ifndef NO_STABS_SUPPORT +#include "common/stabs_reader.h" +#include "common/stabs_to_module.h" +#endif +#include "common/using_std_string.h" + +// This namespace contains helper functions. +namespace { + +using google_breakpad::DumpOptions; +using google_breakpad::DwarfCFIToModule; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::Module; +#ifndef NO_STABS_SUPPORT +using google_breakpad::StabsToModule; +#endif +using google_breakpad::scoped_ptr; + +// +// FDWrapper +// +// Wrapper class to make sure opened file is closed. +// +class FDWrapper { + public: + explicit FDWrapper(int fd) : + fd_(fd) {} + ~FDWrapper() { + if (fd_ != -1) + close(fd_); + } + int get() { + return fd_; + } + int release() { + int fd = fd_; + fd_ = -1; + return fd; + } + private: + int fd_; +}; + +// +// MmapWrapper +// +// Wrapper class to make sure mapped regions are unmapped. +// +class MmapWrapper { + public: + MmapWrapper() : is_set_(false) {} + ~MmapWrapper() { + if (is_set_ && base_ != NULL) { + assert(size_ > 0); + munmap(base_, size_); + } + } + void set(void *mapped_address, size_t mapped_size) { + is_set_ = true; + base_ = mapped_address; + size_ = mapped_size; + } + void release() { + assert(is_set_); + is_set_ = false; + base_ = NULL; + size_ = 0; + } + + private: + bool is_set_; + void *base_; + size_t size_; +}; + +#ifndef NO_STABS_SUPPORT +template +bool LoadStabs(const typename ObjectFileReader::ObjectFileBase header, + const typename ObjectFileReader::Section stab_section, + const typename ObjectFileReader::Section stabstr_section, + const bool big_endian, + Module* module) { + // A callback object to handle data from the STABS reader. + StabsToModule handler(module); + // Find the addresses of the STABS data, and create a STABS reader object. + // On Linux, STABS entries always have 32-bit values, regardless of the + // address size of the architecture whose code they're describing, and + // the strings are always "unitized". + const uint8_t* stabs = ObjectFileReader::GetSectionPointer(header, + stab_section); + const uint8_t* stabstr = ObjectFileReader::GetSectionPointer(header, + stabstr_section); + google_breakpad::StabsReader reader(stabs, + ObjectFileReader::GetSectionSize(header, stab_section), + stabstr, + ObjectFileReader::GetSectionSize(header, stabstr_section), + big_endian, 4, true, &handler); + // Read the STABS data, and do post-processing. + if (!reader.Process()) + return false; + handler.Finalize(); + return true; +} +#endif // NO_STABS_SUPPORT + +// A line-to-module loader that accepts line number info parsed by +// dwarf2reader::LineInfo and populates a Module and a line vector +// with the results. +class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { + public: + // Create a line-to-module converter using BYTE_READER. + explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader) + : byte_reader_(byte_reader) { } + void StartCompilationUnit(const string& compilation_dir) { + compilation_dir_ = compilation_dir; + } + void ReadProgram(const uint8_t *program, uint64 length, + Module *module, std::vector *lines) { + DwarfLineToModule handler(module, compilation_dir_, lines); + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); + parser.Start(); + } + private: + string compilation_dir_; + dwarf2reader::ByteReader *byte_reader_; +}; + +template +bool LoadDwarf(const string& dwarf_filename, + const typename ObjectFileReader::ObjectFileBase header, + const bool big_endian, + bool handle_inter_cu_refs, + Module* module) { + typedef typename ObjectFileReader::Section Shdr; + + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + dwarf2reader::ByteReader byte_reader(endianness); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(dwarf_filename, + module, + handle_inter_cu_refs); + + // Build a map of the file's sections. + int num_sections = ObjectFileReader::GetNumberOfSections(header); + for (int i = 0; i < num_sections; ++i) { + const Shdr section = ObjectFileReader::FindSectionByIndex(header, i); + string name = ObjectFileReader::GetSectionName(header, section); + const uint8_t* contents = reinterpret_cast(ObjectFileReader::GetSectionPointer(header, section)); + file_context.AddSectionToSectionMap(name, contents, + ObjectFileReader::GetSectionSize(header, section)); + } + + // Parse all the compilation units in the .debug_info section. + DumperLineToModule line_to_module(&byte_reader); + dwarf2reader::SectionMap::const_iterator debug_info_entry = + file_context.section_map().find(".debug_info"); + assert(debug_info_entry != file_context.section_map().end()); + const std::pair& debug_info_section = + debug_info_entry->second; + // This should never have been called if the file doesn't have a + // .debug_info section. + assert(debug_info_section.first); + uint64 debug_info_length = debug_info_section.second; + for (uint64 offset = 0; offset < debug_info_length;) { + // Make a handler for the root DIE that populates MODULE with the + // data that was found. + DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); + DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); + // Make a Dwarf2Handler that drives the DIEHandler. + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + dwarf2reader::CompilationUnit reader(dwarf_filename, + file_context.section_map(), + offset, + &byte_reader, + &die_dispatcher); + // Process the entire compilation unit; get the offset of the next. + offset += reader.Start(); + } + return true; +} + +// Fill REGISTER_NAMES with the register names appropriate to the +// machine architecture, indexed by the register +// numbers used in DWARF call frame information. Return true on +// success, or false if HEADER's machine architecture is not +// supported. +bool DwarfCFIRegisterNames(const char *architecture, + std::vector* register_names) { + if (strcmp(architecture, "x86" ) == 0) + *register_names = DwarfCFIToModule::RegisterNames::I386(); + else if (strcmp(architecture, "arm" ) == 0) + *register_names = DwarfCFIToModule::RegisterNames::ARM(); + else if (strcmp(architecture, "mips" ) == 0) + *register_names = DwarfCFIToModule::RegisterNames::MIPS(); + else if (strcmp(architecture, "x86_64" ) == 0) + *register_names = DwarfCFIToModule::RegisterNames::X86_64(); + else + return false; + + return true; +} + +template +bool LoadDwarfCFI(const string& dwarf_filename, + const typename ObjectFileReader::ObjectFileBase header, + const char* section_name, + const typename ObjectFileReader::Section section, + const bool eh_frame, + const typename ObjectFileReader::Section got_section, + const typename ObjectFileReader::Section text_section, + const bool big_endian, + Module* module) { + // Find the appropriate set of register names for this file's + // architecture. + const char *architecture = ObjectFileReader::Architecture(header); + std::vector register_names; + if (!DwarfCFIRegisterNames(architecture, ®ister_names)) { + return false; + } + + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + + // Find the call frame information and its size. + const uint8_t* cfi = reinterpret_cast(ObjectFileReader::GetSectionPointer(header, section)); + size_t cfi_size = ObjectFileReader::GetSectionSize(header, section); + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(endianness); + + byte_reader.SetAddressSize(ObjectFileReader::kAddrSize); + + // Provide the base addresses for .eh_frame encoded pointers, if + // possible. + byte_reader.SetCFIDataBase(ObjectFileReader::GetSectionRVA(header, section) + + ObjectFileReader::GetLoadingAddress(header), + cfi); + if (got_section) + byte_reader.SetDataBase(ObjectFileReader::GetSectionRVA(header, got_section) + + ObjectFileReader::GetLoadingAddress(header)); + if (text_section) + byte_reader.SetTextBase(ObjectFileReader::GetSectionRVA(header, text_section) + + ObjectFileReader::GetLoadingAddress(header)); + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, + section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + parser.Start(); + return true; +} + +bool LoadFile(const string& obj_file, MmapWrapper* map_wrapper, + const void** header) { + int obj_fd = open(obj_file.c_str(), O_RDONLY); + if (obj_fd < 0) { + fprintf(stderr, "Failed to open file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + FDWrapper obj_fd_wrapper(obj_fd); + struct stat st; + if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { + fprintf(stderr, "Unable to fstat file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + void *obj_base = mmap(NULL, st.st_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); + if (obj_base == MAP_FAILED) { + fprintf(stderr, "Failed to mmap file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + map_wrapper->set(obj_base, st.st_size); + *header = obj_base; + return true; +} + +// Read the .gnu_debuglink and get the debug file name. If anything goes +// wrong, return an empty string. +template +string ReadDebugLink(const char* debuglink, + size_t debuglink_size, + const string& obj_file, + const std::vector& debug_dirs) { + size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32. + debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes. + + // Sanity check. + if (debuglink_len != debuglink_size) { + fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " + "%zx %zx\n", debuglink_len, debuglink_size); + return ""; + } + + bool found = false; + int debuglink_fd = -1; + string debuglink_path; + std::vector::const_iterator it; + for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { + const string& debug_dir = *it; + debuglink_path = debug_dir + "/" + debuglink; + debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); + if (debuglink_fd >= 0) { + found = true; + break; + } + } + + if (!found) { + fprintf(stderr, "Failed to find debug file for '%s' after trying:\n", + obj_file.c_str()); + for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { + const string debug_dir = *it; + fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink); + } + return ""; + } + + FDWrapper debuglink_fd_wrapper(debuglink_fd); + // TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink + // section. + + return debuglink_path; +} + +// +// LoadSymbolsInfo +// +// Holds the state between the two calls to LoadSymbols() in case it's necessary +// to follow the .gnu_debuglink section and load debug information from a +// different file. +// +template +class LoadSymbolsInfo { + public: + typedef typename ObjectFileReader::Addr Addr; + + explicit LoadSymbolsInfo(const std::vector& dbg_dirs) : + debug_dirs_(dbg_dirs), + has_loading_addr_(false) {} + + // Keeps track of which sections have been loaded so sections don't + // accidentally get loaded twice from two different files. + void LoadedSection(const string §ion) { + if (loaded_sections_.count(section) == 0) { + loaded_sections_.insert(section); + } else { + fprintf(stderr, "Section %s has already been loaded.\n", + section.c_str()); + } + } + + // The file and linked debug file are expected to have the same preferred + // loading address. + void set_loading_addr(Addr addr, const string &filename) { + if (!has_loading_addr_) { + loading_addr_ = addr; + loaded_file_ = filename; + return; + } + + if (addr != loading_addr_) { + fprintf(stderr, + "file '%s' and debug file '%s' " + "have different load addresses.\n", + loaded_file_.c_str(), filename.c_str()); + assert(false); + } + } + + // Setters and getters + const std::vector& debug_dirs() const { + return debug_dirs_; + } + + string debuglink_file() const { + return debuglink_file_; + } + void set_debuglink_file(string file) { + debuglink_file_ = file; + } + + private: + const std::vector& debug_dirs_; // Directories in which to + // search for the debug file. + + string debuglink_file_; // Full path to the debug file. + + bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid. + + Addr loading_addr_; // Saves the preferred loading address from the + // first call to LoadSymbols(). + + string loaded_file_; // Name of the file loaded from the first call to + // LoadSymbols(). + + std::set loaded_sections_; // Tracks the Loaded sections + // between calls to LoadSymbols(). +}; + +template +bool LoadSymbols(const string& obj_file, + const bool big_endian, + const typename ObjectFileReader::ObjectFileBase header, + const bool read_gnu_debug_link, + LoadSymbolsInfo* info, + const DumpOptions& options, + Module* module) { + typedef typename ObjectFileReader::Addr Addr; + typedef typename ObjectFileReader::Section Shdr; + + Addr loading_addr = ObjectFileReader::GetLoadingAddress(header); + module->SetLoadAddress(loading_addr); + info->set_loading_addr(loading_addr, obj_file); + + bool found_debug_info_section = false; + bool found_usable_info = false; + + if (options.symbol_data != ONLY_CFI) { +#ifndef NO_STABS_SUPPORT + // Look for STABS debugging information, and load it if present. + const Shdr stab_section = + ObjectFileReader::FindSectionByName(".stab", header); + if (stab_section) { + const Shdr stabstr_section = ObjectFileReader::FindLinkedSection(header, stab_section); + if (stabstr_section) { + found_debug_info_section = true; + found_usable_info = true; + info->LoadedSection(".stab"); + if (!LoadStabs(header, stab_section, stabstr_section, + big_endian, module)) { + fprintf(stderr, "%s: \".stab\" section found, but failed to load" + " STABS debugging information\n", obj_file.c_str()); + } + } + } +#endif // NO_STABS_SUPPORT + + // Look for DWARF debugging information, and load it if present. + const Shdr dwarf_section = + ObjectFileReader::FindSectionByName(".debug_info", header); + if (dwarf_section) { + found_debug_info_section = true; + found_usable_info = true; + info->LoadedSection(".debug_info"); + if (!LoadDwarf(obj_file, header, big_endian, + options.handle_inter_cu_refs, module)) { + fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " + "DWARF debugging information\n", obj_file.c_str()); + } + } + } + + if (options.symbol_data != NO_CFI) { + // Dwarf Call Frame Information (CFI) is actually independent from + // the other DWARF debugging information, and can be used alone. + const Shdr dwarf_cfi_section = + ObjectFileReader::FindSectionByName(".debug_frame", header); + if (dwarf_cfi_section) { + // Ignore the return value of this function; even without call frame + // information, the other debugging information could be perfectly + // useful. + info->LoadedSection(".debug_frame"); + bool result = + LoadDwarfCFI(obj_file, header, ".debug_frame", + dwarf_cfi_section, false, 0, 0, big_endian, + module); + found_usable_info = found_usable_info || result; + } + + // Linux C++ exception handling information can also provide + // unwinding data. + const Shdr eh_frame_section = + ObjectFileReader::FindSectionByName(".eh_frame", header); + if (eh_frame_section) { + // Pointers in .eh_frame data may be relative to the base addresses of + // certain sections. Provide those sections if present. + const Shdr got_section = + ObjectFileReader::FindSectionByName(".got", header); + const Shdr text_section = + ObjectFileReader::FindSectionByName(".text", header); + info->LoadedSection(".eh_frame"); + // As above, ignore the return value of this function. + bool result = + LoadDwarfCFI(obj_file, header, ".eh_frame", + eh_frame_section, true, + got_section, text_section, big_endian, module); + found_usable_info = found_usable_info || result; + } + } + + if (!found_debug_info_section) { + fprintf(stderr, "%s: file contains no debugging information" + " (no \".stab\" or \".debug_info\" sections)\n", + obj_file.c_str()); + + // Failed, but maybe there's a .gnu_debuglink section? + if (read_gnu_debug_link) { + const Shdr gnu_debuglink_section + = ObjectFileReader::FindSectionByName(".gnu_debuglink", header); + if (gnu_debuglink_section) { + if (!info->debug_dirs().empty()) { + const char* debuglink_contents = reinterpret_cast + (ObjectFileReader::GetSectionPointer(header, gnu_debuglink_section)); + string debuglink_file + = ReadDebugLink(debuglink_contents, + ObjectFileReader::GetSectionSize(header, gnu_debuglink_section), + obj_file, info->debug_dirs()); + info->set_debuglink_file(debuglink_file); + } else { + fprintf(stderr, ".gnu_debuglink section found in '%s', " + "but no debug path specified.\n", obj_file.c_str()); + } + } else { + fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", + obj_file.c_str()); + } + } else { + if (options.symbol_data != ONLY_CFI) { + // The caller doesn't want to consult .gnu_debuglink. + // See if there are export symbols available. + bool result = ObjectFileReader::ExportedSymbolsToModule(header, module); + found_usable_info = found_usable_info || result; + } + + // Return true if some usable information was found, since + // the caller doesn't want to use .gnu_debuglink. + return found_usable_info; + } + + // No debug info was found, let the user try again with .gnu_debuglink + // if present. + return false; + } + + return true; +} + +// Return the non-directory portion of FILENAME: the portion after the +// last slash, or the whole filename if there are no slashes. +string BaseFileName(const string &filename) { + // Lots of copies! basename's behavior is less than ideal. + char *c_filename = strdup(filename.c_str()); + string base = basename(c_filename); + free(c_filename); + return base; +} + +template +bool ReadSymbolDataFromObjectFile( + const typename ObjectFileReader::ObjectFileBase header, + const string& obj_filename, + const std::vector& debug_dirs, + const DumpOptions& options, + Module** out_module) { + + *out_module = NULL; + + string identifier = ObjectFileReader::FileIdentifierFromMappedFile(header); + if (identifier.empty()) { + fprintf(stderr, "%s: unable to generate file identifier\n", + obj_filename.c_str()); + return false; + } + + const char *architecture = ObjectFileReader::Architecture(header); + if (!architecture) { + return false; + } + + // Figure out what endianness this file is. + bool big_endian; + if (!ObjectFileReader::Endianness(header, &big_endian)) + return false; + + string name = BaseFileName(obj_filename); + string os = "windows"; + string id = identifier; + + LoadSymbolsInfo info(debug_dirs); + scoped_ptr module(new Module(name, os, architecture, id)); + if (!LoadSymbols(obj_filename, big_endian, header, + !debug_dirs.empty(), &info, + options, module.get())) { + const string debuglink_file = info.debuglink_file(); + if (debuglink_file.empty()) + return false; + + // Load debuglink file. + fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); + MmapWrapper debug_map_wrapper; + typename ObjectFileReader::ObjectFileBase debug_header = NULL; + if (!LoadFile(debuglink_file, &debug_map_wrapper, + reinterpret_cast(&debug_header))) + return false; + + if (!ObjectFileReader::IsValid(debug_header)) { + fprintf(stderr, "Not a valid file: %s\n", debuglink_file.c_str()); + return false; + } + + // Sanity checks to make sure everything matches up. + const char *debug_architecture = + ObjectFileReader::Architecture(debug_header); + if (!debug_architecture) { + return false; + } + if (strcmp(architecture, debug_architecture)) { + fprintf(stderr, "%s with machine architecture %s does not match " + "%s with architecture %s\n", + debuglink_file.c_str(), debug_architecture, + obj_filename.c_str(), architecture); + return false; + } + + bool debug_big_endian; + if (!ObjectFileReader::Endianness(debug_header, &debug_big_endian)) + return false; + if (debug_big_endian != big_endian) { + fprintf(stderr, "%s and %s does not match in endianness\n", + obj_filename.c_str(), debuglink_file.c_str()); + return false; + } + + if (!LoadSymbols(debuglink_file, debug_big_endian, + debug_header, false, &info, + options, module.get())) { + return false; + } + } + + *out_module = module.release(); + return true; +} + +} // namespace + +#endif // COMMON_PECOFF_DUMP_SYMBOLS_INL_H diff --git a/src/common/pecoff/dump_symbols.cc b/src/common/pecoff/dump_symbols.cc new file mode 100644 index 00000000..47be9408 --- /dev/null +++ b/src/common/pecoff/dump_symbols.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2011 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/pecoff/dump_symbols.h" + +#include "common/pecoff/pecoffutils.h" +#include "common/pecoff/dump_symbols-inl.h" + +namespace google_breakpad { + +// Not explicitly exported, but not static so it can be used in unit tests. +bool ReadSymbolDataInternal(const uint8_t* obj_file, + const string& obj_filename, + const std::vector& debug_dirs, + const DumpOptions& options, + Module** module) { + if (!IsValidPeCoff(obj_file)) { + fprintf(stderr, "Not a valid PE/COFF file: %s\n", obj_filename.c_str()); + return false; + } + + int peclass = PeCoffClass(obj_file); + if (peclass == PE32) { + return ReadSymbolDataFromObjectFile( + reinterpret_cast(obj_file), obj_filename, debug_dirs, + options, module); + } + if (peclass == PE32PLUS) { + return ReadSymbolDataFromObjectFile( + reinterpret_cast(obj_file), obj_filename, debug_dirs, + options, module); + } + + return false; +} + +bool WriteSymbolFile(const string &obj_file, + const std::vector& debug_dirs, + const DumpOptions& options, + std::ostream &sym_stream) { + Module* module; + if (!ReadSymbolData(obj_file, debug_dirs, options, &module)) + return false; + + bool result = module->Write(sym_stream, options.symbol_data); + delete module; + return result; +} + +bool ReadSymbolData(const string& obj_file, + const std::vector& debug_dirs, + const DumpOptions& options, + Module** module) { + MmapWrapper map_wrapper; + const void* pe_header = NULL; + + if (!LoadFile(obj_file, &map_wrapper, &pe_header)) + return false; + + return ReadSymbolDataInternal(reinterpret_cast(pe_header), + obj_file, debug_dirs, options, module); +} + +} // namespace google_breakpad diff --git a/src/common/pecoff/dump_symbols.h b/src/common/pecoff/dump_symbols.h new file mode 100644 index 00000000..675a4895 --- /dev/null +++ b/src/common/pecoff/dump_symbols.h @@ -0,0 +1,80 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, 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. + +// dump_symbols.h: Read debugging information from a PECOFF file, and write +// it out as a Breakpad symbol file. + +#ifndef COMMON_PECOFF_DUMP_SYMBOLS_H__ +#define COMMON_PECOFF_DUMP_SYMBOLS_H__ + +#include +#include +#include + +#include "common/symbol_data.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +class Module; + +struct DumpOptions { + DumpOptions(SymbolData symbol_data, bool handle_inter_cu_refs) + : symbol_data(symbol_data), + handle_inter_cu_refs(handle_inter_cu_refs) { + } + + SymbolData symbol_data; + bool handle_inter_cu_refs; +}; + +// Find all the debugging information in OBJ_FILE, an PECOFF executable +// or shared library, and write it to SYM_STREAM in the Breakpad symbol +// file format. +// If OBJ_FILE has been stripped but contains a .gnu_debuglink section, +// then look for the debug file in DEBUG_DIRS. +// SYMBOL_DATA allows limiting the type of symbol data written. +bool WriteSymbolFile(const string &obj_file, + const std::vector& debug_dirs, + const DumpOptions& options, + std::ostream &sym_stream); + +// As above, but simply return the debugging information in MODULE +// instead of writing it to a stream. The caller owns the resulting +// Module object and must delete it when finished. +bool ReadSymbolData(const string& obj_file, + const std::vector& debug_dirs, + const DumpOptions& options, + Module** module); + +} // namespace google_breakpad + +#endif // COMMON_PECOFF_DUMP_SYMBOLS_H__ diff --git a/src/common/pecoff/pecoff.h b/src/common/pecoff/pecoff.h new file mode 100644 index 00000000..9ac32ef3 --- /dev/null +++ b/src/common/pecoff/pecoff.h @@ -0,0 +1,262 @@ +// Copyright (c) 2014 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. + +// pecoff.h: PECOFF file format +// + +#ifndef COMMON_PECOFF_PECOFF_H__ +#define COMMON_PECOFF_PECOFF_H__ + +#include + +#define IMAGE_FILE_HEADER_OFFSET 0x3c + +#define IMAGE_FILE_MAGIC 0x00004550 // "PE\0\0" + +#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000 +#define IMAGE_FILE_MACHINE_ALPHA 0x0184 +#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 +#define IMAGE_FILE_MACHINE_AM33 0x01d3 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_ARMV7 0x01c4 +#define IMAGE_FILE_MACHINE_CEE 0xc0ee +#define IMAGE_FILE_MACHINE_CEF 0x0cef +#define IMAGE_FILE_MACHINE_EBC 0x0ebc +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_M32R 0x9041 +#define IMAGE_FILE_MACHINE_M68K 0x0268 +#define IMAGE_FILE_MACHINE_MIPS16 0x0266 +#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 +#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 +#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 +#define IMAGE_FILE_MACHINE_R10000 0x0168 +#define IMAGE_FILE_MACHINE_R3000 0x0162 +#define IMAGE_FILE_MACHINE_R4000 0x0166 +#define IMAGE_FILE_MACHINE_SH3 0x01a2 +#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 +#define IMAGE_FILE_MACHINE_SH3E 0x01a4 +#define IMAGE_FILE_MACHINE_SH4 0x01a6 +#define IMAGE_FILE_MACHINE_SH5 0x01a8 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_TRICORE 0x0520 +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 + +struct PeHeader { + uint32_t mMagic; // IMAGE_FILE_MAGIC + uint16_t mMachine; // IMAGE_FILE_MACHINE_* values + uint16_t mNumberOfSections; + uint32_t mTimeDateStamp; + uint32_t mPointerToSymbolTable; + uint32_t mNumberOfSymbols; + uint16_t mSizeOfOptionalHeader; + uint16_t mCharacteristics; +}; + +enum PeMagic { + PE32 = 0x010b, // 32 bit + PE32PLUS = 0x020b, // 64 bit address space, 2GB image size limit +}; + +struct PeDataDirectory { + uint32_t mVirtualAddress; + uint32_t mSize; +}; + +struct Pe32OptionalHeader { + uint16_t mMagic; // PeMagic + uint8_t mMajorLinkerVersion; + uint8_t mMinorLinkerVersion; + uint32_t mSizeOfCode; + uint32_t mSizeOfInitializedData; + uint32_t mSizeOfUninitializedData; + uint32_t mAddressOfEntryPoint; + uint32_t mBaseOfCode; + uint32_t mBaseOfData; + uint32_t mImageBase; + uint32_t mSectionAlignment; + uint32_t mFileAlignment; + uint16_t mMajorOperatingSystemVersion; + uint16_t mMinorOperatingSystemVersion; + uint16_t mMajorImageVersion; + uint16_t mMinorImageVersion; + uint16_t mMajorSubsystemVersion; + uint16_t mMinorSubsystemVersion; + uint32_t mWin32VersionValue; + uint32_t mSizeOfImage; + uint32_t mSizeOfHeaders; + uint32_t mCheckSum; + uint16_t mSubsystem; + uint16_t mDllCharacteristics; + uint32_t mSizeOfStackReserve; + uint32_t mSizeOfStackCommit; + uint32_t mSizeOfHeapReserve; + uint32_t mSizeOfHeapCommit; + uint32_t mLoaderFlags; + uint32_t mNumberOfRvaAndSizes; + PeDataDirectory mDataDirectory[0]; +}; + +struct Pe32PlusOptionalHeader { + uint16_t mMagic; // PeMagic + uint8_t mMajorLinkerVersion; + uint8_t mMinorLinkerVersion; + uint32_t mSizeOfCode; + uint32_t mSizeOfInitializedData; + uint32_t mSizeOfUninitializedData; + uint32_t mAddressOfEntryPoint; + uint32_t mBaseOfCode; + uint64_t mImageBase; + uint32_t mSectionAlignment; + uint32_t mFileAlignment; + uint16_t mMajorOperatingSystemVersion; + uint16_t mMinorOperatingSystemVersion; + uint16_t mMajorImageVersion; + uint16_t mMinorImageVersion; + uint16_t mMajorSubsystemVersion; + uint16_t mMinorSubsystemVersion; + uint32_t mWin32VersionValue; + uint32_t mSizeOfImage; + uint32_t mSizeOfHeaders; + uint32_t mCheckSum; + uint16_t mSubsystem; + uint16_t mDllCharacteristics; + uint64_t mSizeOfStackReserve; + uint64_t mSizeOfStackCommit; + uint64_t mSizeOfHeapReserve; + uint64_t mSizeOfHeapCommit; + uint32_t mLoaderFlags; + uint32_t mNumberOfRvaAndSizes; + PeDataDirectory mDataDirectory[0]; +}; + +#define PE_EXPORT_TABLE 0 +#define PE_IMPORT_TABLE 1 +#define PE_RESOURCE_TABLE 2 +#define PE_EXCEPTION_TABLE 3 +#define PE_CERTIFICATE_TABLE 4 +#define PE_BASE_RELOCATION_TABLE 5 +#define PE_DEBUG_DATA 6 +#define PE_ARCHITECTURE 7 +#define PE_GLOBAL_PTR 8 +#define PE_TLS_TABLE 9 +#define PE_LOAD_CONFIG_TABLE 10 +#define PE_BOUND_IMPORT_TABLE 11 +#define PE_IMPORT_ADDRESS_TABLE 12 +#define PE_DELAY_IMPORT_DESCRIPTOR 13 +#define PE_CLR_RUNTIME_HEADER 14 + +struct PeDebugDirectory { + uint32_t mCharacteristics; + uint32_t mTimeDateStamp; + uint16_t mMajorVersion; + uint16_t mMinorVersion; + uint32_t mType; + uint32_t mSizeOfData; + uint32_t mAddressOfRawData; + uint32_t mPointerToRawData; +}; + +#define IMAGE_DEBUG_TYPE_UNKNOWN 0 +#define IMAGE_DEBUG_TYPE_COFF 1 +#define IMAGE_DEBUG_TYPE_CODEVIEW 2 +#define IMAGE_DEBUG_TYPE_FPO 3 +#define IMAGE_DEBUG_TYPE_MISC 4 +#define IMAGE_DEBUG_TYPE_EXCEPTION 5 +#define IMAGE_DEBUG_TYPE_FIXUP 6 +#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7 +#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8 +#define IMAGE_DEBUG_TYPE_BORLAND 9 +#define IMAGE_DEBUG_TYPE_RESERVED10 10 +#define IMAGE_DEBUG_TYPE_CLSID 11 + +struct CvInfoPbd70 +{ + uint32_t mCvSignature; + uint8_t mSignature[16]; + uint32_t mAge; + uint8_t mPdbFileName[]; +}; + +#define CODEVIEW_PDB70_CVSIGNATURE 0x53445352 // "RSDS" +#define CODEVIEW_PDB20_CVSIGNATURE 0x3031424e // "NB10" +#define CODEVIEW_CV50_CVSIGNATURE 0x3131424e // "NB11" +#define CODEVIEW_CV41_CVSIGNATURE 0x3930424e // “NB09" + +struct PeSectionHeader { + char mName[8]; + union { + uint32_t mPhysicalAddress; + uint32_t mVirtualSize; + } ; + uint32_t mVirtualAddress; + uint32_t mSizeOfRawData; + uint32_t mPointerToRawData; + uint32_t mPointerToRelocations; + uint32_t mPointerToLinenumbers; + uint16_t mNumberOfRelocations; + uint16_t mNumberOfLinenumbers; + uint32_t mCharacteristics; +}; + +struct __attribute__ ((__packed__)) PeSymbol +{ + union { + char mName[8]; // Symbol Name + struct { + uint32_t mFirst4Bytes; + uint32_t mSecond4Bytes; + }; + }; + + uint32_t mValue; // Value of Symbol + uint16_t mScNum; // Section Number + uint16_t mType; // Symbol Type + uint8_t mSClass; // Storage Class + uint8_t mNumAux; // Auxiliary Count +}; + +struct PeExportTable { + uint32_t mFlags; + uint32_t mTimeDateStamp; + uint16_t mMajorVersion; + uint16_t mMinorVErsion; + uint32_t mNameRVA; + uint32_t mOrdinalBase; + uint32_t mAddressTableEntries; + uint32_t mNumberofNamePointers; + uint32_t mExportAddressTableRVA; + uint32_t mNamePointerRVA; + uint32_t mOrdinalTableRVA; +}; + +#endif// COMMON_PECOFF_PECOFF_H__ diff --git a/src/common/pecoff/pecoff_file_id.cc b/src/common/pecoff/pecoff_file_id.cc new file mode 100644 index 00000000..47c2763f --- /dev/null +++ b/src/common/pecoff/pecoff_file_id.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2006, 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. +// +// pecoff_file_id.cc: Return a unique identifier for a file +// + +#include "common/pecoff/pecoff_file_id.h" + +#include "common/pecoff/pecoffutils.h" + +namespace google_breakpad { + +// Attempt to locate a CodeView build-id section in a PECOFF binary +// and copy as many bytes of it as will fit into |identifier|. +static bool FindPeCoffBuildID(const uint8_t* mapped_base, + uint8_t identifier[kMDGUIDSize], + uint32_t *age) { + int peclass = PeCoffClass(mapped_base); + if (peclass == PE32) + return PeCoffClass32::GetBuildID(mapped_base, identifier, age); + if (peclass == PE32PLUS) + return PeCoffClass64::GetBuildID(mapped_base, identifier, age); + + return false; +} + +// Attempt to locate the .text section of a binary and generate +// a simple hash by XORing the first page worth of bytes into |identifier|. +static bool HashPeCoffTextSection(const uint8_t* mapped_base, + uint8_t identifier[kMDGUIDSize]) { + int peclass = PeCoffClass(mapped_base); + if (peclass == PE32) + return PeCoffClass32::HashTextSection(mapped_base, identifier); + if (peclass == PE32PLUS) + return PeCoffClass64::HashTextSection(mapped_base, identifier); + + return false; +} + +bool PeCoffFileID::PeCoffFileIdentifierFromMappedFile(const void* base, + uint8_t identifier[kMDGUIDSize], + uint32_t *age) { + *age = 0; + + // Look for a build id first. + if (FindPeCoffBuildID(reinterpret_cast(base), identifier, + age)) + return true; + + // Fall back on hashing the first page of the text section. + // (This is of questionable value as the Windows Minidump writer doesn't have + // this feature) + return HashPeCoffTextSection(reinterpret_cast(base), + identifier); +} + +} // namespace google_breakpad diff --git a/src/common/pecoff/pecoff_file_id.h b/src/common/pecoff/pecoff_file_id.h new file mode 100644 index 00000000..fd2ea103 --- /dev/null +++ b/src/common/pecoff/pecoff_file_id.h @@ -0,0 +1,52 @@ +// Copyright (c) 2006, 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. +// +// pecoff_file_id.h: Return a unique identifier for a file +// + +#ifndef COMMON_PECOFF_PECOFF_FILE_ID_H__ +#define COMMON_PECOFF_PECOFF_FILE_ID_H__ + +#include +#include + +namespace google_breakpad { + +static const size_t kMDGUIDSize = 16; + +class PeCoffFileID { + public: + static bool PeCoffFileIdentifierFromMappedFile(const void* base, + uint8_t identifier[kMDGUIDSize], + uint32_t* age); +}; + +} // namespace google_breakpad + +#endif // COMMON_PECOFF_PECOFF_FILE_ID_H__ diff --git a/src/common/pecoff/pecoffutils.cc b/src/common/pecoff/pecoffutils.cc new file mode 100644 index 00000000..f097cc61 --- /dev/null +++ b/src/common/pecoff/pecoffutils.cc @@ -0,0 +1,494 @@ +// Copyright (c) 2014 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. + +// pecoffutils.c: Utilities for dealing with PECOFF files +// + +#include "common/pecoff/pecoffutils.h" + +#include +#include +#include + +#ifndef _WIN32 +#include +#else +#include +#endif + +namespace google_breakpad { + +bool IsValidPeCoff(const uint8_t* obj_base) { + // at offset 0x3c, find the offset to PE signature + const uint32_t* peOffsetPtr = reinterpret_cast(obj_base + + IMAGE_FILE_HEADER_OFFSET); + + // TODO: ideally we want to check that the offset is less than the size of the + // mapped file, but we don't have that information at the moment + // + // if (*peOffsetPtr > size) return FALSE; + + // check PE signature + const PeHeader* peHeader = reinterpret_cast(obj_base+*peOffsetPtr); + if (peHeader->mMagic != IMAGE_FILE_MAGIC) + return false; + + return true; +} + +int PeCoffClass(const uint8_t* obj_base) { + const uint32_t* peOffsetPtr = reinterpret_cast(obj_base + + IMAGE_FILE_HEADER_OFFSET); + const PeHeader* peHeader = reinterpret_cast(obj_base+*peOffsetPtr); + const uint16_t* peOptionalHeader = reinterpret_cast + (reinterpret_cast(peHeader) + sizeof(PeHeader)); + + // We need to read the magic before we know if this a Pe32OptionalHeader or + // Pe32PlusOptionalHeader, so we don't use those types here. + return *peOptionalHeader; +} + +// +// Header information +// + +template +const char* PeCoffObjectFileReader::Architecture( + ObjectFileBase obj_base) { + const PeHeader* peHeader = GetHeader(obj_base); + uint16_t arch = peHeader->mMachine; + switch (arch) { + case IMAGE_FILE_MACHINE_I386: + return "x86"; + case IMAGE_FILE_MACHINE_ARM: + return "arm"; + case IMAGE_FILE_MACHINE_MIPS16: + case IMAGE_FILE_MACHINE_MIPSFPU: + case IMAGE_FILE_MACHINE_MIPSFPU16: + case IMAGE_FILE_MACHINE_WCEMIPSV2: + return "mips"; + case IMAGE_FILE_MACHINE_POWERPC: + case IMAGE_FILE_MACHINE_POWERPCFP: + return "ppc"; + case IMAGE_FILE_MACHINE_AMD64: + return "x86_64"; + default: + fprintf(stderr, "unrecognized machine architecture: %d\n", + peHeader->mMachine); + return NULL; + } +} + +template +bool PeCoffObjectFileReader::Endianness( + ObjectFileBase obj_base, + bool* big_endian) { + // TODO: Not sure what big-endian PECOFF looks like: characteristics flag + // IMAGE_FILE_BYTES_REVERSED_HI and/or certain machine types are big-endian + *big_endian = false; + return true; +} + +template +typename PeCoffObjectFileReader::Addr +PeCoffObjectFileReader::GetLoadingAddress( + ObjectFileBase obj_base) { + const PeOptionalHeader* peOptionalHeader = GetOptionalHeader(obj_base); + return peOptionalHeader->mImageBase; +} + +// +// Section enumeration and location +// + +template +int PeCoffObjectFileReader::GetNumberOfSections( + ObjectFileBase obj_base) { + const PeHeader* peHeader = GetHeader(obj_base); + return peHeader->mNumberOfSections; +} + +template +const typename PeCoffObjectFileReader::Section +PeCoffObjectFileReader::FindSectionByIndex( + ObjectFileBase obj_base, int i) { + const PeSectionHeader* section_table = GetSectionTable(obj_base); + return reinterpret_cast(&(section_table[i])); +} + +template +const typename PeCoffObjectFileReader::Section +PeCoffObjectFileReader::FindSectionByName( + const char* section_name, ObjectFileBase obj_base) { + const PeHeader* peHeader = GetHeader(obj_base); + const PeSectionHeader* section_table = GetSectionTable(obj_base); + const char* string_table = GetStringTable(obj_base); + uint32_t string_table_length = *(reinterpret_cast(string_table)); + + for (int s = 0; s < peHeader->mNumberOfSections; s++) { + const char* name = section_table[s].mName; + + // look up long section names in string table + if (name[0] == '/') { + unsigned int offset = ::strtoul(section_table[s].mName+1, NULL, 10); + + if (offset > string_table_length) + fprintf(stderr, "section name offset %d exceeds string table length", + offset); + else + name = string_table + offset; + } + + if (::strcmp(section_name, name) == 0) { + return reinterpret_cast(&(section_table[s])); + } + } + + // nothing found + return NULL; +} + +// +// Section information +// + +template +const uint8_t* +PeCoffObjectFileReader::GetSectionPointer( + ObjectFileBase obj_base, Section section) { + return reinterpret_cast(obj_base) + reinterpret_cast(section)->mPointerToRawData; +} + +template +typename PeCoffObjectFileReader::Offset +PeCoffObjectFileReader::GetSectionSize( + ObjectFileBase obj_base, Section section) { + + // There are mSizeOfRawData bytes of data for the section in the mapped image + // file. Return mVirtualSize if it's smaller. + // This doesn't handle the case where mVirtualSize is larger and the section + // should be zero padded, because we have nowhere to do that. + if ((reinterpret_cast(section)->mVirtualSize) < + (reinterpret_cast(section)->mSizeOfRawData)) + return reinterpret_cast(section)->mVirtualSize; + + return reinterpret_cast(section)->mSizeOfRawData; +} + +template +typename PeCoffObjectFileReader::Offset +PeCoffObjectFileReader::GetSectionRVA( + ObjectFileBase obj_base, Section section) { + return reinterpret_cast(section)->mVirtualAddress; +} + +template +const char* PeCoffObjectFileReader::GetSectionName( + ObjectFileBase obj_base,Section section) { + const char* string_table = GetStringTable(obj_base); + uint32_t string_table_length = *(reinterpret_cast(string_table)); + const char* name = reinterpret_cast(section)->mName; + + // look up long section names in string table + if (name[0] == '/') { + unsigned int offset = ::strtoul(name+1, NULL, 10); + + if (offset > string_table_length) + fprintf(stderr, "section name offset %d exceeds string table length", + offset); + else + name = string_table + offset; + } + + return name; +} + +// +// +// + +template +bool PeCoffObjectFileReader::ExportedSymbolsToModule( + ObjectFileBase obj_base, Module* module) { + // locate the export table, if present + const PeDataDirectory* data_directory_export_entry = GetDataDirectoryEntry(obj_base, PE_EXPORT_TABLE); + if (data_directory_export_entry && data_directory_export_entry->mSize != 0) { + const PeExportTable* export_table = reinterpret_cast(ConvertRVAToPointer(obj_base, data_directory_export_entry->mVirtualAddress)); + const uint32_t* eat = reinterpret_cast(ConvertRVAToPointer(obj_base, export_table->mExportAddressTableRVA)); + const uint32_t* enpt = reinterpret_cast(ConvertRVAToPointer(obj_base, export_table->mNamePointerRVA)); + const uint16_t* eot = reinterpret_cast(ConvertRVAToPointer(obj_base, export_table->mOrdinalTableRVA)); + + // process the export name pointer table + for (uint32_t i = 0; i < export_table->mNumberofNamePointers; i++) { + // look up the name for the export + uint32_t export_name_rva = enpt[i]; + if (export_name_rva == 0) + continue; + const char* export_name = reinterpret_cast(ConvertRVAToPointer(obj_base, export_name_rva)); + + // get the corresponding ordinal + // (the PE/COFF specification seems to claim that EOT entries are not + // biased by ordinalbase, but that doesn't seem to match reality...) + uint16_t export_ordinal = eot[i] + export_table->mOrdinalBase; + if ((export_ordinal < export_table->mOrdinalBase) || + (export_ordinal >= (export_table->mOrdinalBase + export_table->mAddressTableEntries))) { + fprintf(stderr, "exported ordinal %d out of range for EAT!\n", export_ordinal); + continue; + } + + // find the corresponding export address table entry + uint32_t eat_index = export_ordinal - export_table->mOrdinalBase; + uint32_t export_rva = eat[eat_index]; + + // if the export's address lies inside the export table, it's a forwarded + // export, which we can ignore + if ((export_rva >= data_directory_export_entry->mVirtualAddress) && + (export_rva < (data_directory_export_entry->mVirtualAddress + data_directory_export_entry->mSize))) + continue; + + Module::Extern* ext = new Module::Extern(export_rva + GetLoadingAddress(obj_base)); + ext->name = export_name; + module->AddExtern(ext); + } + + return true; + } + + // report if a COFF symbol table exists, but we don't use it (yet) + // According to the PECOFF spec. COFF debugging information is deprecated. + // We don't know of any tools which produce that and don't produce DWARF or + // MS CodeView debug information. + const PeHeader* peHeader = GetHeader(obj_base); + if (peHeader->mPointerToSymbolTable) { + fprintf(stderr, "COFF debug symbols present but are not implemented\n"); + } + + return false; +} + +template +string +PeCoffObjectFileReader::FileIdentifierFromMappedFile( + ObjectFileBase obj_file) { + uint8_t identifier[kMDGUIDSize]; + uint32_t age; + + if (!PeCoffFileID::PeCoffFileIdentifierFromMappedFile(obj_file, identifier, &age)) + return ""; + + // Endian-ness swap to match dump processor expectation. + uint8_t identifier_swapped[kMDGUIDSize]; + memcpy(identifier_swapped, identifier, kMDGUIDSize); + uint32_t* data1 = reinterpret_cast(identifier_swapped); + *data1 = htonl(*data1); + uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); + *data2 = htons(*data2); + uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); + *data3 = htons(*data3); + + // Format the file identifier in IDENTIFIER as a UUID with the + // dashes removed. + char identifier_str[40]; + int buffer_idx = 0; + for (unsigned int idx = 0; idx < kMDGUIDSize; ++idx) { + int hi = (identifier_swapped[idx] >> 4) & 0x0F; + int lo = (identifier_swapped[idx]) & 0x0F; + + identifier_str[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; + identifier_str[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; + } + identifier_str[buffer_idx] = 0; + string id = identifier_str; + + // Append age + char age_string[9]; + snprintf(age_string, sizeof(age_string) / sizeof(age_string[0]), "%X", age); + id += age_string; + + return id; +} + +// +// Helper functions for PeCoffFileID +// + +template +bool PeCoffObjectFileReader::GetBuildID( + ObjectFileBase obj_base, + uint8_t identifier[kMDGUIDSize], + uint32_t* age) { + // locate the debug directory, if present + const PeDataDirectory* data_directory_debug_entry = GetDataDirectoryEntry(obj_base, PE_DEBUG_DATA); + if (!data_directory_debug_entry) + return false; + + uint32_t debug_directory_size = data_directory_debug_entry->mSize; + if (debug_directory_size == 0) + return false; + + const PeDebugDirectory* debug_directory = reinterpret_cast(ConvertRVAToPointer(obj_base, data_directory_debug_entry->mVirtualAddress)); + if (debug_directory == NULL) { + fprintf(stderr, "No section containing the debug directory VMA could be found\n"); + return false; + } + + // search the debug directory for a codeview entry + for (unsigned int i = 0; i < debug_directory_size/sizeof(PeDebugDirectory); i++) { + if (debug_directory[i].mType == IMAGE_DEBUG_TYPE_CODEVIEW) { + // interpret the codeview record to get build-id + const CvInfoPbd70* codeview_record = reinterpret_cast + (obj_base + debug_directory[i].mPointerToRawData); + if ((codeview_record->mCvSignature) == CODEVIEW_PDB70_CVSIGNATURE) { + memcpy(identifier, codeview_record->mSignature, kMDGUIDSize); + *age = codeview_record->mAge; + return true; + } else { + fprintf(stderr, "Unhandled codeview signature %x\n", + codeview_record->mCvSignature); + } + } + } + + fprintf(stderr, "No codeview entry in debug directory\n"); + return false; +} + +template +bool PeCoffObjectFileReader::HashTextSection( + ObjectFileBase obj_base, + uint8_t identifier[kMDGUIDSize]) { + Section text_section; + Offset text_size; + + if (!(text_section = FindSectionByName(".text", obj_base)) || + ((text_size = GetSectionSize(obj_base, text_section)) == 0)) + return false; + + memset(identifier, 0, kMDGUIDSize); + const uint8_t* ptr = GetSectionPointer(obj_base, text_section); + const uint8_t* ptr_end = ptr + std::min(text_size, 4096U); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) + identifier[i] ^= ptr[i]; + ptr += kMDGUIDSize; + } + return true; +} + +// +// Private implementation helper functions +// + +template +const PeHeader* PeCoffObjectFileReader::GetHeader( + ObjectFileBase obj_base) { + const uint32_t* peOffsetPtr = reinterpret_cast(obj_base + + IMAGE_FILE_HEADER_OFFSET); + const PeHeader* peHeader = reinterpret_cast(obj_base+*peOffsetPtr); + return peHeader; +} + +template +const typename PeCoffObjectFileReader::PeOptionalHeader* +PeCoffObjectFileReader::GetOptionalHeader( + ObjectFileBase obj_base) { + const PeHeader* peHeader = GetHeader(obj_base); + PeOptionalHeader* peOptionalHeader = (PeOptionalHeader*) ((uint32_t*)peHeader + 6); + return peOptionalHeader; +} + +template +const PeSectionHeader* +PeCoffObjectFileReader::GetSectionTable( + ObjectFileBase obj_base) { + const PeHeader* peHeader = GetHeader(obj_base); + const PeOptionalHeader* peOptionalHeader = GetOptionalHeader(obj_base); + + // section table immediately follows optional header + const PeSectionHeader* section_table = reinterpret_cast + (reinterpret_cast(peOptionalHeader) + peHeader->mSizeOfOptionalHeader); + return section_table; +} + +template +const char* PeCoffObjectFileReader::GetStringTable( + ObjectFileBase obj_base) { + const PeHeader* peHeader = GetHeader(obj_base); + + // string table immediately follows symbol table + uint32_t string_table_offset = peHeader->mPointerToSymbolTable + peHeader->mNumberOfSymbols*sizeof(PeSymbol); + const char* string_table = reinterpret_cast(obj_base) + string_table_offset; + return string_table; +} + +template +const PeDataDirectory* +PeCoffObjectFileReader::GetDataDirectoryEntry( + ObjectFileBase obj_base, unsigned int entry) { + const PeOptionalHeader* peOptionalHeader = GetOptionalHeader(obj_base); + + // the data directory immediately follows the optional header + const PeDataDirectory* data_directory = reinterpret_cast(&peOptionalHeader->mDataDirectory[0]); + uint32_t data_directory_size = peOptionalHeader->mNumberOfRvaAndSizes; + + // locate the required directory entry, if present + if (data_directory_size < entry) + return NULL; + + return &data_directory[entry]; +} + +template +const uint8_t* +PeCoffObjectFileReader::ConvertRVAToPointer( + ObjectFileBase obj_base, + Offset rva) { + // find which section contains the rva to compute it's mapped address + const PeSectionHeader* section_table = GetSectionTable(obj_base); + for (int s = 0; s < GetNumberOfSections(obj_base); s++) { + const PeSectionHeader* section = &(section_table[s]); + + if ((rva >= section->mVirtualAddress) && + (rva < (section->mVirtualAddress + section->mSizeOfRawData))) + { + uint32_t offset = rva - section->mVirtualAddress; + const uint8_t* pointer = GetSectionPointer(obj_base, (Section)section) + offset; + return pointer; + } + } + + fprintf(stderr, "No section could be found containing RVA %x\n", rva); + return NULL; +} + +// instantiation of templated classes +template class PeCoffObjectFileReader; +template class PeCoffObjectFileReader; + +} diff --git a/src/common/pecoff/pecoffutils.h b/src/common/pecoff/pecoffutils.h new file mode 100644 index 00000000..3e2d7b34 --- /dev/null +++ b/src/common/pecoff/pecoffutils.h @@ -0,0 +1,167 @@ +// Copyright (c) 2014 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. + +// pecoffutils.h: Utilities for dealing with PECOFF files +// + +#ifndef COMMON_PECOFF_PECOFFUTILS_H__ +#define COMMON_PECOFF_PECOFFUTILS_H__ + +#include "common/pecoff/pecoff.h" +#include "common/pecoff/pecoff_file_id.h" +#include "common/module.h" + +namespace google_breakpad { + +bool IsValidPeCoff(const uint8_t* obj_file); +int PeCoffClass(const uint8_t* obj_file); + +class PeCoffClass32Traits { + public: + typedef uint32_t Addr; + typedef Pe32OptionalHeader PeOptionalHeader; + static const int kClass = PE32; + static const size_t kAddrSize = 4; +}; + +class PeCoffClass64Traits { + public: + typedef uint64_t Addr; + typedef Pe32PlusOptionalHeader PeOptionalHeader; + static const int kClass = PE32PLUS; + static const size_t kAddrSize = 8; +}; +// Offset isn't part of the traits as although PE32+ uses 64-bit address space, +// it still uses 32-bit RVAs and offsets + +template +class PeCoffObjectFileReader { + public: + typedef const uint8_t* ObjectFileBase; + typedef const uint8_t* Section; + typedef uint32_t Offset; + typedef typename PeCoffClassTraits::Addr Addr; + static const int kClass = PeCoffClassTraits::kClass; + static const size_t kAddrSize = PeCoffClassTraits::kAddrSize; + + static bool IsValid(ObjectFileBase obj_file) { + return IsValidPeCoff(obj_file); + }; + + // + // Header information + // + + // Return the breakpad symbol file identifier for the architecture + static const char* Architecture(ObjectFileBase obj_base); + + // Get the endianness. If it's invalid, return false. + static bool Endianness(ObjectFileBase obj_base, bool* big_endian); + + // Find the preferred loading address of the binary. + static Addr GetLoadingAddress(ObjectFileBase obj_base); + + // + // Section enumeration and location + // + + static int GetNumberOfSections(ObjectFileBase obj_base); + static const Section FindSectionByIndex(ObjectFileBase obj_base, int i); + // Attempt to find a section named |section_name| + static const Section FindSectionByName(const char* section_name, + ObjectFileBase obj_base); + + // + // Section information + // + + // Convert a section into a pointer to the mapped address in the current + // process. + static const uint8_t* GetSectionPointer(ObjectFileBase obj_base, + Section section); + + // Get the size of a section + static Offset GetSectionSize(ObjectFileBase obj_base, Section section); + + // Get relative virtual address (RVA) of a section + static Offset GetSectionRVA(ObjectFileBase obj_base, Section section); + + // Get name of a section + static const char* GetSectionName(ObjectFileBase obj_base,Section section); + + // Find any linked section + static const Section FindLinkedSection(ObjectFileBase obj_base, + Section section) { + return 0; // PECOFF doesn't have the concept of linked sections + } + + // + // + // + + // Load symbols from the object file's exported symbol table + static bool ExportedSymbolsToModule(ObjectFileBase obj_base, Module* module); + + // Return the identifier for the file mapped into memory. + // Return an empty string if the identifier could not be created + // for the file. + static string FileIdentifierFromMappedFile(ObjectFileBase obj_base); + + // + // Helpers for PeCoffFileID + // + + // Get the build-id + static bool GetBuildID(ObjectFileBase obj_base, + uint8_t identifier[kMDGUIDSize], uint32_t* age); + // Hash the text section + static bool HashTextSection(ObjectFileBase obj_base, + uint8_t identifier[kMDGUIDSize]); + + private: + typedef typename PeCoffClassTraits::PeOptionalHeader PeOptionalHeader; + + // + // Private implementation helper functions + // + static const PeHeader* GetHeader(ObjectFileBase obj_base); + static const PeOptionalHeader* GetOptionalHeader(ObjectFileBase obj_base); + static const PeSectionHeader* GetSectionTable(ObjectFileBase obj_base); + static const char* GetStringTable(ObjectFileBase obj_base); + static const PeDataDirectory* GetDataDirectoryEntry(ObjectFileBase obj_base, + unsigned int entry); + static const uint8_t* ConvertRVAToPointer(ObjectFileBase obj_base, Offset rva); +}; + +class PeCoffClass32 : public PeCoffObjectFileReader { }; +class PeCoffClass64 : public PeCoffObjectFileReader { }; + +} // namespace google_breakpad + +#endif // COMMON_PECOFF_PECOFFUTILS_H__ diff --git a/src/tools/windows/dump_syms_dwarf/dump_syms.cc b/src/tools/windows/dump_syms_dwarf/dump_syms.cc new file mode 100644 index 00000000..2d7f70a6 --- /dev/null +++ b/src/tools/windows/dump_syms_dwarf/dump_syms.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011, 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 + +#include +#include +#include +#include + +#include "common/pecoff/dump_symbols.h" + +using google_breakpad::WriteSymbolFile; + +int usage(const char* self) { + fprintf(stderr, "Usage: %s [OPTION] " + "[directories-for-debug-file]\n\n", self); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -c Do not generate CFI section\n"); + fprintf(stderr, " -r Do not handle inter-compilation unit references\n"); + return 1; +} + +int main(int argc, char **argv) { + if (argc < 2) + return usage(argv[0]); + + bool cfi = true; + bool handle_inter_cu_refs = true; + int arg_index = 1; + while (arg_index < argc && strlen(argv[arg_index]) > 0 && + argv[arg_index][0] == '-') { + if (strcmp("-c", argv[arg_index]) == 0) { + cfi = false; + } else if (strcmp("-r", argv[arg_index]) == 0) { + handle_inter_cu_refs = false; + } else { + return usage(argv[0]); + } + ++arg_index; + } + if (arg_index == argc) + return usage(argv[0]); + + const char* binary; + std::vector debug_dirs; + binary = argv[arg_index]; + for (int debug_dir_index = arg_index + 1; + debug_dir_index < argc; + ++debug_dir_index) { + debug_dirs.push_back(argv[debug_dir_index]); + } + + SymbolData symbol_data = cfi ? ALL_SYMBOL_DATA : NO_CFI; + google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs); + if (!WriteSymbolFile(binary, debug_dirs, options, std::cout)) { + fprintf(stderr, "Failed to write symbol file.\n"); + return 1; + } + + return 0; +} -- 2.28.0