blob: 1ccf1eb8d0bafecceb6691bde93968d1a0d38cfa [file] [log] [blame]
From 6d7e6014d2bb1b0f883cdaeb8e13861ae0f57c7c Mon Sep 17 00:00:00 2001
From: Jon Turney <jon.turney@dronecode.org.uk>
Date: Tue, 4 Feb 2014 00:57:13 +0000
Subject: [PATCH 05/29] 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 <jon.turney@dronecode.org.uk>
---
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 ++++++
src/tools/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 fcff8ea7..8d816efc 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
@@ -368,6 +373,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
@@ -768,6 +779,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 ec194ddd..4e92b291 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include <iostream>
+#include <set>
+#include <utility>
+#include <vector>
+
+#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<typename ObjectFileReader>
+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<Module::Line> *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<typename ObjectFileReader>
+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<const uint8_t *>(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<const uint8_t*, uint64>& 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<string>* 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<typename ObjectFileReader>
+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<string> register_names;
+ if (!DwarfCFIRegisterNames(architecture, &register_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<const uint8_t *>(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<typename ObjectFileReader>
+string ReadDebugLink(const char* debuglink,
+ size_t debuglink_size,
+ const string& obj_file,
+ const std::vector<string>& 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<string>::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<typename ObjectFileReader>
+class LoadSymbolsInfo {
+ public:
+ typedef typename ObjectFileReader::Addr Addr;
+
+ explicit LoadSymbolsInfo(const std::vector<string>& 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 &section) {
+ 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<string>& 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<string>& 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<string> loaded_sections_; // Tracks the Loaded sections
+ // between calls to LoadSymbols().
+};
+
+template<typename ObjectFileReader>
+bool LoadSymbols(const string& obj_file,
+ const bool big_endian,
+ const typename ObjectFileReader::ObjectFileBase header,
+ const bool read_gnu_debug_link,
+ LoadSymbolsInfo<ObjectFileReader>* 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<ObjectFileReader>(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<ObjectFileReader>(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<ObjectFileReader>(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<ObjectFileReader>(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<const char *>
+ (ObjectFileReader::GetSectionPointer(header, gnu_debuglink_section));
+ string debuglink_file
+ = ReadDebugLink<ObjectFileReader>(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<typename ObjectFileReader>
+bool ReadSymbolDataFromObjectFile(
+ const typename ObjectFileReader::ObjectFileBase header,
+ const string& obj_filename,
+ const std::vector<string>& 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<ObjectFileReader> info(debug_dirs);
+ scoped_ptr<Module> module(new Module(name, os, architecture, id));
+ if (!LoadSymbols<ObjectFileReader>(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<const void**>(&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<ObjectFileReader>(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<string>& 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<PeCoffClass32>(
+ reinterpret_cast<const PeCoffClass32::ObjectFileBase>(obj_file), obj_filename, debug_dirs,
+ options, module);
+ }
+ if (peclass == PE32PLUS) {
+ return ReadSymbolDataFromObjectFile<PeCoffClass64>(
+ reinterpret_cast<const PeCoffClass64::ObjectFileBase>(obj_file), obj_filename, debug_dirs,
+ options, module);
+ }
+
+ return false;
+}
+
+bool WriteSymbolFile(const string &obj_file,
+ const std::vector<string>& 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<string>& 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<const uint8_t*>(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 <iostream>
+#include <string>
+#include <vector>
+
+#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<string>& 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<string>& 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 <stdint.h>
+
+#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<const uint8_t *>(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<const uint8_t *>(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 <stddef.h>
+#include <stdint.h>
+
+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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#else
+#include <winsock.h>
+#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<const uint32_t*>(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<const PeHeader*>(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<const uint32_t*>(obj_base +
+ IMAGE_FILE_HEADER_OFFSET);
+ const PeHeader* peHeader = reinterpret_cast<const PeHeader*>(obj_base+*peOffsetPtr);
+ const uint16_t* peOptionalHeader = reinterpret_cast<const uint16_t*>
+ (reinterpret_cast<const uint8_t*>(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<typename PeCoffClassTraits>
+const char* PeCoffObjectFileReader<PeCoffClassTraits>::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<typename PeCoffClassTraits>
+bool PeCoffObjectFileReader<PeCoffClassTraits>::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 PeCoffClassTraits>
+typename PeCoffObjectFileReader<PeCoffClassTraits>::Addr
+PeCoffObjectFileReader<PeCoffClassTraits>::GetLoadingAddress(
+ ObjectFileBase obj_base) {
+ const PeOptionalHeader* peOptionalHeader = GetOptionalHeader(obj_base);
+ return peOptionalHeader->mImageBase;
+}
+
+//
+// Section enumeration and location
+//
+
+template<typename PeCoffClassTraits>
+int PeCoffObjectFileReader<PeCoffClassTraits>::GetNumberOfSections(
+ ObjectFileBase obj_base) {
+ const PeHeader* peHeader = GetHeader(obj_base);
+ return peHeader->mNumberOfSections;
+}
+
+template<typename PeCoffClassTraits>
+const typename PeCoffObjectFileReader<PeCoffClassTraits>::Section
+PeCoffObjectFileReader<PeCoffClassTraits>::FindSectionByIndex(
+ ObjectFileBase obj_base, int i) {
+ const PeSectionHeader* section_table = GetSectionTable(obj_base);
+ return reinterpret_cast<const Section>(&(section_table[i]));
+}
+
+template<typename PeCoffClassTraits>
+const typename PeCoffObjectFileReader<PeCoffClassTraits>::Section
+PeCoffObjectFileReader<PeCoffClassTraits>::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<const uint32_t*>(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<const Section>(&(section_table[s]));
+ }
+ }
+
+ // nothing found
+ return NULL;
+}
+
+//
+// Section information
+//
+
+template<typename PeCoffClassTraits>
+const uint8_t*
+PeCoffObjectFileReader<PeCoffClassTraits>::GetSectionPointer(
+ ObjectFileBase obj_base, Section section) {
+ return reinterpret_cast<const uint8_t*>(obj_base) + reinterpret_cast<const PeSectionHeader*>(section)->mPointerToRawData;
+}
+
+template<typename PeCoffClassTraits>
+typename PeCoffObjectFileReader<PeCoffClassTraits>::Offset
+PeCoffObjectFileReader<PeCoffClassTraits>::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<const PeSectionHeader*>(section)->mVirtualSize) <
+ (reinterpret_cast<const PeSectionHeader*>(section)->mSizeOfRawData))
+ return reinterpret_cast<const PeSectionHeader*>(section)->mVirtualSize;
+
+ return reinterpret_cast<const PeSectionHeader*>(section)->mSizeOfRawData;
+}
+
+template<typename PeCoffClassTraits>
+typename PeCoffObjectFileReader<PeCoffClassTraits>::Offset
+PeCoffObjectFileReader<PeCoffClassTraits>::GetSectionRVA(
+ ObjectFileBase obj_base, Section section) {
+ return reinterpret_cast<const PeSectionHeader*>(section)->mVirtualAddress;
+}
+
+template<typename PeCoffClassTraits>
+const char* PeCoffObjectFileReader<PeCoffClassTraits>::GetSectionName(
+ ObjectFileBase obj_base,Section section) {
+ const char* string_table = GetStringTable(obj_base);
+ uint32_t string_table_length = *(reinterpret_cast<const uint32_t*>(string_table));
+ const char* name = reinterpret_cast<const PeSectionHeader*>(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<class PeCoffClassTraits>
+bool PeCoffObjectFileReader<PeCoffClassTraits>::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<const PeExportTable*>(ConvertRVAToPointer(obj_base, data_directory_export_entry->mVirtualAddress));
+ const uint32_t* eat = reinterpret_cast<const uint32_t*>(ConvertRVAToPointer(obj_base, export_table->mExportAddressTableRVA));
+ const uint32_t* enpt = reinterpret_cast<const uint32_t*>(ConvertRVAToPointer(obj_base, export_table->mNamePointerRVA));
+ const uint16_t* eot = reinterpret_cast<const uint16_t*>(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<const char*>(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<class PeCoffClassTraits>
+string
+PeCoffObjectFileReader<PeCoffClassTraits>::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<uint32_t*>(identifier_swapped);
+ *data1 = htonl(*data1);
+ uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
+ *data2 = htons(*data2);
+ uint16_t* data3 = reinterpret_cast<uint16_t*>(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<typename PeCoffClassTraits>
+bool PeCoffObjectFileReader<PeCoffClassTraits>::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<const PeDebugDirectory*>(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<const CvInfoPbd70*>
+ (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<typename PeCoffClassTraits>
+bool PeCoffObjectFileReader<PeCoffClassTraits>::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<typename PeCoffClassTraits>
+const PeHeader* PeCoffObjectFileReader<PeCoffClassTraits>::GetHeader(
+ ObjectFileBase obj_base) {
+ const uint32_t* peOffsetPtr = reinterpret_cast<const uint32_t*>(obj_base +
+ IMAGE_FILE_HEADER_OFFSET);
+ const PeHeader* peHeader = reinterpret_cast<const PeHeader*>(obj_base+*peOffsetPtr);
+ return peHeader;
+}
+
+template<typename PeCoffClassTraits>
+const typename PeCoffObjectFileReader<PeCoffClassTraits>::PeOptionalHeader*
+PeCoffObjectFileReader<PeCoffClassTraits>::GetOptionalHeader(
+ ObjectFileBase obj_base) {
+ const PeHeader* peHeader = GetHeader(obj_base);
+ PeOptionalHeader* peOptionalHeader = (PeOptionalHeader*) ((uint32_t*)peHeader + 6);
+ return peOptionalHeader;
+}
+
+template<typename PeCoffClassTraits>
+const PeSectionHeader*
+PeCoffObjectFileReader<PeCoffClassTraits>::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<const PeSectionHeader*>
+ (reinterpret_cast<const uint8_t*>(peOptionalHeader) + peHeader->mSizeOfOptionalHeader);
+ return section_table;
+}
+
+template<typename PeCoffClassTraits>
+const char* PeCoffObjectFileReader<PeCoffClassTraits>::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<const char*>(obj_base) + string_table_offset;
+ return string_table;
+}
+
+template<class PeCoffClassTraits>
+const PeDataDirectory*
+PeCoffObjectFileReader<PeCoffClassTraits>::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<const PeDataDirectory*>(&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<typename PeCoffClassTraits>
+const uint8_t*
+PeCoffObjectFileReader<PeCoffClassTraits>::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<PeCoffClass32Traits>;
+template class PeCoffObjectFileReader<PeCoffClass64Traits>;
+
+}
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 <typename PeCoffClassTraits>
+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<PeCoffClass32Traits> { };
+class PeCoffClass64 : public PeCoffObjectFileReader<PeCoffClass64Traits> { };
+
+} // 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 <stdio.h>
+
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "common/pecoff/dump_symbols.h"
+
+using google_breakpad::WriteSymbolFile;
+
+int usage(const char* self) {
+ fprintf(stderr, "Usage: %s [OPTION] <binary-with-dwarf-debugging-info> "
+ "[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<string> 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.15.0