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

