Merge branch 'master' into dwarf5
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..4e34e72
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,2 @@
+

+BasedOnStyle: google

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 893dafe..868d557 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,7 @@
 project (Bloaty VERSION 1.1)
 include(CTest)
 set(CMAKE_CXX_STANDARD 17)
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)  # Group projects in visual studio
 
 # Options we define for users.
 option(BLOATY_ENABLE_ASAN "Enable address sanitizer." OFF)
@@ -13,39 +14,35 @@
 
 if(UNIX)
 find_package(PkgConfig)
-if(${PKG_CONFIG_FOUND})
+find_package(ZLIB)
 if(BLOATY_ENABLE_RE2)
   pkg_search_module(RE2 re2)
-endif(BLOATY_ENABLE_RE2)
+endif()
 pkg_search_module(CAPSTONE capstone)
 pkg_search_module(PROTOBUF protobuf)
-pkg_search_module(ZLIB zlib)
 if(BLOATY_ENABLE_RE2)
-  if(${RE2_FOUND})
+  if(RE2_FOUND)
     MESSAGE(STATUS "System re2 found, using")
-  else(${RE2_FOUND})
+  else()
     MESSAGE(STATUS "System re2 not found, using bundled version")
-  endif(${RE2_FOUND})
-endif(BLOATY_ENABLE_RE2)
-if(${CAPSTONE_FOUND})
+  endif()
+endif()
+if(CAPSTONE_FOUND)
   MESSAGE(STATUS "System capstone found, using")
-else(${CAPSTONE_FOUND})
+else()
   MESSAGE(STATUS "System capstone not found, using bundled version")
-endif(${CAPSTONE_FOUND})
-if(${PROTOBUF_FOUND})
+endif()
+if(PROTOBUF_FOUND)
   MESSAGE(STATUS "System protobuf found, using")
-else(${PROTOBUF_FOUND})
+else()
   MESSAGE(STATUS "System protobuf not found, using bundled version")
-endif(${PROTOBUF_FOUND})
-if (${ZLIB_FOUND})
+endif()
+if (ZLIB_FOUND)
   MESSAGE(STATUS "System zlib found, using")
-else(${ZLIB_FOUND})
+else()
   MESSAGE(STATUS "System zlib not found, using bundled version")
-endif(${ZLIB_FOUND})
-else(${PKG_CONFIG_FOUND})
-  MESSAGE(STATUS "pkg-config not found, using bundled dependencies")
-endif(${PKG_CONFIG_FOUND})
-endif(UNIX)
+endif()
+endif()
 
 # Set default build type.
 if(NOT CMAKE_BUILD_TYPE)
@@ -66,7 +63,7 @@
 add_definitions(-D_LIBCXXABI_FUNC_VIS=)  # For Demumble.
 if(BLOATY_ENABLE_RE2)
   add_definitions(-DUSE_RE2)
-endif(BLOATY_ENABLE_RE2)
+endif()
 
 # Set MSVC runtime before including thirdparty libraries
 if(MSVC)
@@ -77,55 +74,58 @@
   foreach(flag_var
       CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
       CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
-    if(${flag_var} MATCHES "/MD")
+    if (flag_var MATCHES "/MD")
       string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
-    endif(${flag_var} MATCHES "/MD")
-  endforeach(flag_var)
+    endif()
+  endforeach()
 endif()
 
+set(THREADS_PREFER_PTHREAD_FLAG TRUE)
+find_package(Threads REQUIRED)
+
 if(UNIX)
   if(BLOATY_ENABLE_RE2)
-    if(${RE2_FOUND})
+    if(RE2_FOUND)
       include_directories(${RE2_INCLUDE_DIRS})
-    else(${RE2_FOUND})
+    else()
       set(RE2_BUILD_TESTING OFF CACHE BOOL "enable testing for RE2" FORCE)
       add_subdirectory(third_party/re2)
       include_directories(third_party/re2)
-    endif(${RE2_FOUND})
-  endif(BLOATY_ENABLE_RE2)
-  if(${CAPSTONE_FOUND})
+    endif()
+  endif()
+  if(CAPSTONE_FOUND)
     include_directories(${CAPSTONE_INCLUDE_DIRS})
-  else(${CAPSTONE_FOUND})
+  else()
     set(CAPSTONE_BUILD_SHARED OFF CACHE BOOL "Build shared library" FORCE)
     set(CAPSTONE_BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE)
     add_subdirectory(third_party/capstone)
     include_directories(third_party/capstone/include)
-  endif(${CAPSTONE_FOUND})
-  if(${PROTOBUF_FOUND})
+  endif()
+  if(PROTOBUF_FOUND)
     include_directories(${PROTOBUF_INCLUDE_DIRS})
-  else(${PROTOBUF_FOUND})
+  else()
     set(protobuf_BUILD_TESTS OFF CACHE BOOL "enable tests for proto2" FORCE)
     set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "enable shared libs for proto2" FORCE)
     add_subdirectory(third_party/protobuf/cmake)
     include_directories(SYSTEM third_party/protobuf/src)
-  endif(${PROTOBUF_FOUND})
-  if(${ZLIB_FOUND})
-    include_directories(${ZLIB_INCLUDE_DIRS})
-  else(${ZLIB_FOUND})
+  endif()
+  if(NOT ZLIB_FOUND)
     add_subdirectory(third_party/zlib)
     include_directories(third_party/zlib)
-  endif(${ZLIB_FOUND})
-else(UNIX)
+  endif()
+else()
   if(BLOATY_ENABLE_RE2)
     set(RE2_BUILD_TESTING OFF CACHE BOOL "enable testing for RE2" FORCE)
     add_subdirectory(third_party/re2)
     include_directories(third_party/re2)
-  endif(BLOATY_ENABLE_RE2)
+    set_property(TARGET re2 PROPERTY FOLDER "third_party")
+  endif()
 
   set(CAPSTONE_BUILD_SHARED OFF CACHE BOOL "Build shared library" FORCE)
   set(CAPSTONE_BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE)
   add_subdirectory(third_party/capstone)
   include_directories(third_party/capstone/include)
+  set_property(TARGET capstone-static PROPERTY FOLDER "third_party")
 
   set(protobuf_BUILD_TESTS OFF CACHE BOOL "enable tests for proto2" FORCE)
   set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "enable shared libs for proto2" FORCE)
@@ -134,7 +134,16 @@
 
   add_subdirectory(third_party/zlib)
   include_directories(third_party/zlib)
-endif(UNIX)
+  include_directories(${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib)
+  set_property(TARGET example PROPERTY FOLDER "third_party")
+  set_property(TARGET minigzip PROPERTY FOLDER "third_party")
+  set_property(TARGET zlib PROPERTY FOLDER "third_party")
+  set_property(TARGET zlibstatic PROPERTY FOLDER "third_party")
+  set_property(TARGET libprotobuf PROPERTY FOLDER "third_party")
+  set_property(TARGET libprotobuf-lite PROPERTY FOLDER "third_party")
+  set_property(TARGET libprotoc PROPERTY FOLDER "third_party")
+  set_property(TARGET protoc PROPERTY FOLDER "third_party")
+endif()
 
 include_directories(.)
 include_directories(src)
@@ -144,8 +153,8 @@
 # Baseline build flags.
 if(MSVC)
   set(CMAKE_CXX_FLAGS "/EHsc /wd4018 /D_CRT_SECURE_NO_WARNINGS /DNOMINMAX")
-else(MSVC)
-  set(CMAKE_CXX_FLAGS "-std=c++11 -W -Wall -Wno-sign-compare")
+else()
+  set(CMAKE_CXX_FLAGS "-W -Wall -Wno-sign-compare")
   set(CMAKE_CXX_FLAGS_DEBUG "-g1")
   set(CMAKE_CXX_FLAGS_RELEASE "-O2")
   set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g1")
@@ -156,7 +165,7 @@
 elseif(UNIX)
   if(BLOATY_ENABLE_BUILDID)
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--build-id")
-  endif(BLOATY_ENABLE_BUILDID)
+  endif()
 endif()
 
 # When using Ninja, compiler output won't be colorized without this.
@@ -182,7 +191,7 @@
 endif()
 
 file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src)
-if(${PROTOC_FOUND})
+if(PROTOC_FOUND)
 add_custom_command(
   OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc
   DEPENDS protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty.proto
@@ -190,27 +199,34 @@
       --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src
       -I${CMAKE_CURRENT_SOURCE_DIR}/src
 )
-else(${PROTOC_FOUND})
+else()
 add_custom_command(
   OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc
   COMMAND protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty.proto
       --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src
       -I${CMAKE_CURRENT_SOURCE_DIR}/src
 )
-endif(${PROTOC_FOUND})
+endif()
 
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty_package.bloaty
      DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
 
 add_library(libbloaty STATIC
     src/bloaty.cc
+    src/bloaty.h
     src/disassemble.cc
     ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc
     src/dwarf.cc
+    src/dwarf_constants.h
     src/elf.cc
     src/macho.cc
+    src/pe.cc
+    third_party/lief_pe/pe_structures.h
     src/range_map.cc
+    src/range_map.h
+    src/re.h
     src/util.cc
+    src/util.h
     src/webassembly.cc
     # Until Abseil has a proper CMake build system
     third_party/abseil-cpp/absl/base/internal/raw_logging.cc # Grrrr...
@@ -235,73 +251,65 @@
     # One source file, no special build system needed.
     third_party/demumble/third_party/libcxxabi/cxa_demangle.cpp
     )
+set_property(TARGET libbloaty PROPERTY FOLDER "bloaty")
 
 if(UNIX)
   set(LIBBLOATY_LIBS libbloaty)
-  if(${PROTOBUF_FOUND})
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} ${PROTOBUF_LIBRARIES})
-  else(${PROTOBUF_FOUND})
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} libprotoc)
-  endif(${PROTOBUF_FOUND})
+  if(PROTOBUF_FOUND)
+    list(APPEND LIBBLOATY_LIBS ${PROTOBUF_LIBRARIES})
+  else()
+    list(APPEND LIBBLOATY_LIBS libprotoc)
+  endif()
   if(BLOATY_ENABLE_RE2)
-    if(${RE2_FOUND})
-      set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} ${RE2_LIBRARIES})
-    else(${RE2_FOUND})
-      set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} re2)
-    endif(${RE2_FOUND})
-  endif(BLOATY_ENABLE_RE2)
-  if(${CAPSTONE_FOUND})
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} ${CAPSTONE_LIBRARIES})
-  else(${CAPSTONE_FOUND})
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} capstone-static)
-  endif(${CAPSTONE_FOUND})
-  if(${ZLIB_FOUND})
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} ${ZLIB_LIBRARIES})
-  else(${ZLIB_FOUND})
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} zlib)
-  endif(${ZLIB_FOUND})
-else(UNIX)
+    if(RE2_FOUND)
+      list(APPEND LIBBLOATY_LIBS ${RE2_LIBRARIES})
+    else()
+      list(APPEND LIBBLOATY_LIBS re2)
+    endif()
+  endif()
+  if(CAPSTONE_FOUND)
+    list(APPEND LIBBLOATY_LIBS ${CAPSTONE_LIBRARIES})
+  else()
+    list(APPEND LIBBLOATY_LIBS capstone-static)
+  endif()
+  if(ZLIB_FOUND)
+    list(APPEND LIBBLOATY_LIBS ZLIB::ZLIB)
+  else()
+    list(APPEND LIBBLOATY_LIBS zlib)
+  endif()
+else()
   set(LIBBLOATY_LIBS libbloaty libprotoc capstone-static)
   if(BLOATY_ENABLE_RE2)
-    set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} re2)
-  endif(BLOATY_ENABLE_RE2)
-  set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} zlib)
-endif(UNIX)
+    list(APPEND LIBBLOATY_LIBS  re2)
+  endif()
+  list(APPEND LIBBLOATY_LIBS zlibstatic)
+endif()
 
 if(UNIX)
   if(BLOATY_ENABLE_RE2)
-    if(${RE2_FOUND})
+    if(RE2_FOUND)
       link_directories(${RE2_LIBRARY_DIRS})
-    endif(${RE2_FOUND})
-  endif(BLOATY_ENABLE_RE2)
-  if(${CAPSTONE_FOUND})
+    endif()
+  endif()
+  if(CAPSTONE_FOUND)
     link_directories(${CAPSTONE_LIBRARY_DIRS})
-  endif(${CAPSTONE_FOUND})
-  if(${PROTOBUF_FOUND})
+  endif()
+  if(PROTOBUF_FOUND)
     link_directories(${PROTOBUF_LIBRARY_DIRS})
-  endif(${PROTOBUF_FOUND})
-  if(${ZLIB_FOUND})
-    link_directories(${ZLIB_LIBRARY_DIRS})
-  endif(${ZLIB_FOUND})
-endif(UNIX)
+  endif()
+endif()
+
+list(APPEND LIBBLOATY_LIBS Threads::Threads)
 
 if(DEFINED ENV{LIB_FUZZING_ENGINE})
   message("LIB_FUZZING_ENGINE set, building fuzz_target instead of Bloaty")
   add_executable(fuzz_target tests/fuzz_target.cc)
-  target_link_libraries(fuzz_target "${LIBBLOATY_LIBS}" "${CMAKE_THREAD_LIBS_INIT}" $ENV{LIB_FUZZING_ENGINE})
+  target_link_libraries(fuzz_target ${LIBBLOATY_LIBS} $ENV{LIB_FUZZING_ENGINE})
 else()
   add_executable(bloaty src/main.cc)
-  target_link_libraries(bloaty "${LIBBLOATY_LIBS}")
+  target_link_libraries(bloaty ${LIBBLOATY_LIBS})
 
-  # All of this is to add -pthread, which is required by re2 (not us).
-  find_package(Threads REQUIRED)
-  if(THREADS_HAVE_PTHREAD_ARG)
-    set_property(TARGET bloaty PROPERTY COMPILE_OPTIONS "-pthread")
-    set_property(TARGET bloaty PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
-  endif()
-  if(CMAKE_THREAD_LIBS_INIT)
-    target_link_libraries(bloaty "${CMAKE_THREAD_LIBS_INIT}")
-  endif()
+  set_property(TARGET bloaty PROPERTY FOLDER "bloaty")
 
   if(BLOATY_ENABLE_CMAKETARGETS)
     install(
@@ -309,12 +317,12 @@
       EXPORT ${PROJECT_NAME}Targets
       RUNTIME DESTINATION bin
     )
-  else(BLOATY_ENABLE_CMAKETARGETS)
+  else()
     install(
       TARGETS bloaty
       RUNTIME DESTINATION bin
     )
-  endif(BLOATY_ENABLE_CMAKETARGETS)
+  endif()
 
   if (IS_DIRECTORY "${PROJECT_SOURCE_DIR}/tests")
     enable_testing()
@@ -327,23 +335,32 @@
 
       set(TEST_TARGETS
           bloaty_test
+          bloaty_test_pe
           bloaty_misc_test
           range_map_test
           )
 
       foreach(target ${TEST_TARGETS})
         add_executable(${target} tests/${target}.cc)
-        target_link_libraries(${target} "${LIBBLOATY_LIBS}" gtest_main gmock "${CMAKE_THREAD_LIBS_INIT}")
+        target_link_libraries(${target} ${LIBBLOATY_LIBS} gtest_main gmock)
+        set_property(TARGET ${target} PROPERTY FOLDER "tests")
       endforeach(target)
 
       add_executable(fuzz_test tests/fuzz_target.cc tests/fuzz_driver.cc)
-      target_link_libraries(fuzz_test "${LIBBLOATY_LIBS}" "${CMAKE_THREAD_LIBS_INIT}")
+      target_link_libraries(fuzz_test ${LIBBLOATY_LIBS})
+      set_property(TARGET fuzz_test PROPERTY FOLDER "tests")
+
+      foreach(testlib gmock gmock_main gtest gtest_main)
+        set_property(TARGET ${testlib} PROPERTY FOLDER "tests/libs")
+      endforeach(testlib)
 
       file(GLOB fuzz_corpus tests/testdata/fuzz_corpus/*)
 
       add_test(NAME range_map_test COMMAND range_map_test)
       add_test(NAME bloaty_test_x86-64 COMMAND bloaty_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86_64)
       add_test(NAME bloaty_test_x86 COMMAND bloaty_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86)
+      add_test(NAME bloaty_test_pe_x64 COMMAND bloaty_test_pe WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/PE/x64)
+      add_test(NAME bloaty_test_pe_x86 COMMAND bloaty_test_pe WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/PE/x86)
       add_test(NAME bloaty_misc_test COMMAND bloaty_misc_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/misc)
       add_test(NAME fuzz_test COMMAND fuzz_test ${fuzz_corpus} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/fuzz_corpus)
     endif()
@@ -351,5 +368,5 @@
 
   if(BLOATY_ENABLE_CMAKETARGETS)
     install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
-  endif(BLOATY_ENABLE_CMAKETARGETS)
+  endif()
 endif()
diff --git a/src/bloaty.cc b/src/bloaty.cc
index 59a4544..78147ea 100644
--- a/src/bloaty.cc
+++ b/src/bloaty.cc
@@ -1516,6 +1516,10 @@
   }
 
   if (!object_file.get()) {
+    object_file = TryOpenPEFile(file);
+  }
+
+  if (!object_file.get()) {
     THROWF("unknown file type for file '$0'", filename.c_str());
   }
 
diff --git a/src/bloaty.h b/src/bloaty.h
index 4c1e6e5..e5a1128 100644
--- a/src/bloaty.h
+++ b/src/bloaty.h
@@ -288,6 +288,7 @@
 std::unique_ptr<ObjectFile> TryOpenELFFile(std::unique_ptr<InputFile>& file);
 std::unique_ptr<ObjectFile> TryOpenMachOFile(std::unique_ptr<InputFile>& file);
 std::unique_ptr<ObjectFile> TryOpenWebAssemblyFile(std::unique_ptr<InputFile>& file);
+std::unique_ptr<ObjectFile> TryOpenPEFile(std::unique_ptr<InputFile>& file);
 
 namespace dwarf {
 
diff --git a/src/pe.cc b/src/pe.cc
new file mode 100644
index 0000000..d5e01d3
--- /dev/null
+++ b/src/pe.cc
@@ -0,0 +1,280 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "bloaty.h"
+#include "absl/strings/substitute.h"
+#include "util.h"
+
+using absl::string_view;
+
+namespace bloaty {
+namespace pe {
+const uint16_t dos_magic = 0x5A4D;  // MZ
+
+//! Sizes in bytes of various things in the COFF format.
+namespace STRUCT_SIZES {
+enum {
+  Header16Size = 20,
+  Header32Size = 56,
+  NameSize = 8,
+  Symbol16Size = 18,
+  Symbol32Size = 20,
+  SectionSize = 40,
+  RelocationSize = 10,
+  BaseRelocationBlockSize = 8,
+  ImportDirectoryTableEntrySize = 20,
+  ResourceDirectoryTableSize = 16,
+  ResourceDirectoryEntriesSize = 8,
+  ResourceDataEntrySize = 16
+};
+}
+
+#include "third_party/lief_pe/pe_structures.h"
+
+static_assert(STRUCT_SIZES::SectionSize == sizeof(pe_section), "Compiler options broke LIEF struct layout");
+static_assert(STRUCT_SIZES::RelocationSize == sizeof(pe_relocation), "Compiler options broke LIEF struct layout");
+static_assert(STRUCT_SIZES::BaseRelocationBlockSize ==
+                  sizeof(pe_base_relocation_block),
+              "Compiler options broke LIEF struct layout");
+static_assert(STRUCT_SIZES::ImportDirectoryTableEntrySize == sizeof(pe_import),
+              "Compiler options broke LIEF struct layout");
+static_assert(STRUCT_SIZES::ResourceDirectoryTableSize ==
+                  sizeof(pe_resource_directory_table),
+              "Compiler options broke LIEF struct layout");
+static_assert(STRUCT_SIZES::ResourceDirectoryEntriesSize ==
+                  sizeof(pe_resource_directory_entries),
+              "Compiler options broke LIEF struct layout");
+static_assert(STRUCT_SIZES::ResourceDataEntrySize ==
+                  sizeof(pe_resource_data_entry),
+              "Compiler options broke LIEF struct layout");
+
+class PeFile {
+ public:
+  PeFile(string_view data) : data_(data) { ok_ = Initialize(); }
+
+  bool IsOpen() const { return ok_; }
+
+  string_view header_region() const { return header_region_; }
+
+  uint32_t section_count() const { return section_count_; }
+  string_view section_headers() const { return section_headers_; }
+  string_view section_header(size_t n) const {
+    return StrictSubstr(section_headers_, n * sizeof(pe_section),
+                        sizeof(pe_section));
+  }
+
+ private:
+  bool Initialize();
+
+  string_view GetRegion(uint64_t start, uint64_t n) const {
+    return StrictSubstr(data_, start, n);
+  }
+
+  bool ok_;
+  bool is_64bit_;
+  string_view data_;
+
+  pe_dos_header dos_header_;
+  pe_header pe_header_;
+  string_view header_region_;
+  uint32_t section_count_;
+  string_view section_headers_;
+};
+
+bool PeFile::Initialize() {
+  if (data_.size() < sizeof(dos_header_)) {
+    return false;
+  }
+
+  memcpy(&dos_header_, data_.data(), sizeof(dos_header_));
+
+  if (dos_header_.Magic != dos_magic) {
+    // Not a PE file.
+    return false;
+  }
+
+  if ((dos_header_.AddressOfNewExeHeader + sizeof(pe_header)) > data_.size()) {
+    // Cannot fit the headers
+    return false;
+  }
+
+  memcpy(&pe_header_, data_.data() + dos_header_.AddressOfNewExeHeader,
+         sizeof(pe_header_));
+
+  if (!std::equal(pe_header_.signature, pe_header_.signature + sizeof(PE_Magic),
+                  std::begin(PE_Magic))) {
+    // Not a PE file.
+    return false;
+  }
+
+  // TODO(mj): Parse PE header further to determine this
+  is_64bit_ = false;
+
+  section_count_ = pe_header_.NumberOfSections;
+
+  const uint32_t sections_offset = dos_header_.AddressOfNewExeHeader +
+                                   sizeof(pe_header) +
+                                   pe_header_.SizeOfOptionalHeader;
+
+  auto sections_size = CheckedMul(section_count_, sizeof(pe_section));
+  if ((sections_offset + sections_size) > data_.size()) {
+    // Cannot fit the headers
+    return false;
+  }
+
+  header_region_ = GetRegion(0, sections_offset);
+  section_headers_ = GetRegion(sections_offset, sections_size);
+
+  return true;
+}
+
+class Section {
+ public:
+  std::string name;
+  string_view data;
+
+  uint32_t raw_offset() const { return header_.PointerToRawData; }
+  uint32_t raw_size() const { return header_.SizeOfRawData; }
+
+  uint32_t virtual_addr() const { return header_.VirtualAddress; }
+  uint32_t virtual_size() const { return header_.VirtualSize; }
+
+  Section(string_view header_data) {
+    assert(header_data.size() == sizeof(header_));
+    memcpy(&header_, header_data.data(), sizeof(header_));
+    data = header_data;
+
+    // TODO(mj): Handle long section names:
+    // For longer names, this member contains a forward slash (/) followed by an
+    // ASCII representation of a decimal number that is an offset into the
+    // string table.
+    name = std::string(header_.Name,
+                       strnlen(header_.Name, STRUCT_SIZES::NameSize));
+  }
+
+ private:
+  pe_section header_;
+};
+
+template <class Func>
+void ForEachSection(const PeFile& pe, Func&& section_func) {
+  for (auto n = 0; n < pe.section_count(); ++n) {
+    Section section(pe.section_header(n));
+    section_func(section);
+  }
+}
+
+void ParseSections(const PeFile& pe, RangeSink* sink) {
+  assert(pe.IsOpen());
+  ForEachSection(pe, [sink](const Section& section) {
+    uint64_t vmaddr = section.virtual_addr();
+    uint64_t vmsize = section.virtual_size();
+
+    uint64_t fileoff = section.raw_offset();
+    uint64_t filesize = section.raw_size();
+
+    sink->AddRange("pe_sections", section.name, vmaddr, vmsize, fileoff,
+                   filesize);
+  });
+}
+
+void AddCatchAll(const PeFile& pe, RangeSink* sink) {
+  // The last-line fallback to make sure we cover the entire VM space.
+  assert(pe.IsOpen());
+
+  auto begin = pe.header_region().data() - sink->input_file().data().data();
+  sink->AddRange("pe_catchall", "[PE Headers]", begin,
+                 pe.header_region().size(), pe.header_region());
+  begin = pe.section_headers().data() - sink->input_file().data().data();
+  sink->AddRange("pe_catchall", "[PE Headers]", begin,
+                 pe.section_headers().size(), pe.section_headers());
+
+  // The last-line fallback to make sure we cover the entire file.
+  sink->AddFileRange("pe_catchall", "[Unmapped]", sink->input_file().data());
+}
+
+class PEObjectFile : public ObjectFile {
+ public:
+  PEObjectFile(std::unique_ptr<InputFile> file_data,
+               std::unique_ptr<pe::PeFile> pe)
+      : ObjectFile(std::move(file_data)), pe_file(std::move(pe)) {}
+
+  std::string GetBuildId() const override {
+    // TODO(mj): Read from pe_pdb_??
+    return std::string();
+  }
+
+  void ProcessFile(const std::vector<RangeSink*>& sinks) const override {
+    for (auto sink : sinks) {
+      switch (sink->data_source()) {
+        case DataSource::kSegments:
+          // TODO(mj): sections: list out imports and other stuff!
+        case DataSource::kSections:
+          ParseSections(*pe_file, sink);
+          break;
+        case DataSource::kSymbols:
+        case DataSource::kRawSymbols:
+        case DataSource::kShortSymbols:
+        case DataSource::kFullSymbols:
+          // TODO(mj): Generate symbols from debug info, exports and other known
+          // structures
+        case DataSource::kArchiveMembers:
+        case DataSource::kCompileUnits:
+        case DataSource::kInlines:
+        default:
+          THROW("PE doesn't support this data source");
+      }
+      AddCatchAll(*pe_file, sink);
+    }
+  }
+
+  bool GetDisassemblyInfo(absl::string_view /*symbol*/,
+                          DataSource /*symbol_source*/,
+                          DisassemblyInfo* /*info*/) const override {
+    WARN("PE files do not support disassembly yet");
+    return false;
+  }
+
+ protected:
+  std::unique_ptr<pe::PeFile> pe_file;
+};
+
+bool ReadMagic(const string_view& data) {
+  // If the size is smaller than a dos header, it cannot be a PE file, right?
+  if (data.size() < sizeof(pe_dos_header)) {
+    return false;
+  }
+
+  uint16_t magic;
+  memcpy(&magic, data.data(), sizeof(magic));
+
+  return magic == dos_magic;
+}
+}  // namespace pe
+
+std::unique_ptr<ObjectFile> TryOpenPEFile(std::unique_ptr<InputFile>& file) {
+  // Do not bother creating an object if the first magic is not even there
+  if (pe::ReadMagic(file->data())) {
+    std::unique_ptr<pe::PeFile> pe(new pe::PeFile(file->data()));
+
+    if (pe->IsOpen()) {
+      return std::unique_ptr<ObjectFile>(
+          new pe::PEObjectFile(std::move(file), std::move(pe)));
+    }
+  }
+
+  return nullptr;
+}
+
+}  // namespace bloaty
diff --git a/tests/bloaty_test_pe.cc b/tests/bloaty_test_pe.cc
new file mode 100644
index 0000000..14a247f
--- /dev/null
+++ b/tests/bloaty_test_pe.cc
@@ -0,0 +1,127 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test.h"
+
+struct BloatyTestEntry
+{
+  std::string name;
+  std::vector<std::string> commandline;
+  std::string input_file;
+  std::string result_file;
+};
+
+std::string TestEntryName(const testing::TestParamInfo<struct BloatyTestEntry>& entry) {
+  return entry.param.name;
+}
+
+std::ostream& operator<<(std::ostream& os, const BloatyTestEntry& entry) {
+  os << "{ ";
+  for (const auto& str: entry.commandline) {
+    os << str << ", ";
+  }
+  os << entry.input_file << ", " << entry.result_file << " }";
+  return os;
+}
+
+// Strip all trailing whitespace (including \r)
+void Normalize(std::string& contents) {
+  std::stringstream buffer(contents);
+  contents.clear();
+  std::string tmp;
+  while (std::getline(buffer, tmp)) {
+    auto end = tmp.find_last_not_of("\t \r");
+    if (end != std::string::npos) {
+      tmp = tmp.substr(0, end + 1);
+    }
+    else {
+      tmp.clear();
+    }
+    if (!contents.empty()) {
+      contents += "\n";
+    }
+    contents += tmp;
+  }
+}
+
+inline bool GetFileContents(const std::string& filename, std::string& contents) {
+  FILE* file = fopen(filename.c_str(), "rb");
+  if (!file) {
+    std::cerr << "Couldn't get file size for: " << filename << "\n";
+    return false;
+  }
+  fseek(file, 0L, SEEK_END);
+  size_t size = ftell(file);
+  fseek(file, 0L, SEEK_SET);
+  contents.resize(size);
+  size_t result = fread(&contents[0], 1, size, file);
+  fclose(file);
+  contents.resize(result);
+  Normalize(contents);
+  return result == size;
+}
+
+class BloatyOutputTest: public BloatyTest,
+  public testing::WithParamInterface<BloatyTestEntry>
+{
+public:
+  BloatyOutputTest()
+    : commandline(GetParam().commandline)
+    , input_file(GetParam().input_file)
+    , result_file(GetParam().result_file)
+  {
+  }
+
+  const std::vector<std::string>& commandline;
+  const std::string& input_file;
+  const std::string& result_file;
+};
+
+
+TEST_P(BloatyOutputTest, CheckOutput) {
+  uint64_t size;
+  ASSERT_TRUE(GetFileSize(input_file, &size));
+  std::string expect_result;
+  ASSERT_TRUE(GetFileContents(result_file, expect_result));
+
+  std::vector<std::string> cmdline = { "bloaty" };
+  cmdline.insert(cmdline.end(), commandline.begin(), commandline.end());
+  cmdline.push_back(input_file);
+  RunBloaty(cmdline);
+
+  bloaty::OutputOptions output_options;
+  std::stringstream output_stream;
+  output_options.output_format = bloaty::OutputFormat::kTSV;
+  output_->Print(output_options, &output_stream);
+  std::string tmp = output_stream.str();
+  Normalize(tmp);
+  EXPECT_EQ(tmp, expect_result);
+}
+
+static BloatyTestEntry  tests[] = {
+  { "MSVCR15DLL", {}, "msvc-15.0-foo-bar.dll", "msvc-15.0-foo-bar.dll.txt" },
+  { "MSVCR15DLLSEG", {"-d", "segments"}, "msvc-15.0-foo-bar.dll", "msvc-15.0-foo-bar.dll.seg.txt" },
+  { "MSVC15EXE", {}, "msvc-15.0-foo-bar-main-cv.bin", "msvc-15.0-foo-bar-main-cv.bin.txt" },
+  { "MSVC15EXESEG", {"-d", "segments"}, "msvc-15.0-foo-bar-main-cv.bin", "msvc-15.0-foo-bar-main-cv.bin.seg.txt" },
+
+  { "MSVCR16DLL", {}, "msvc-16.0-foo-bar.dll", "msvc-16.0-foo-bar.dll.txt" },
+  { "MSVCR16DLLSEG", {"-d", "segments"}, "msvc-16.0-foo-bar.dll", "msvc-16.0-foo-bar.dll.seg.txt" },
+  { "MSVC16EXE", {}, "msvc-16.0-foo-bar-main-cv.bin", "msvc-16.0-foo-bar-main-cv.bin.txt" },
+  { "MSVC16EXESEG", {"-d", "segments"}, "msvc-16.0-foo-bar-main-cv.bin", "msvc-16.0-foo-bar-main-cv.bin.seg.txt" },
+};
+
+INSTANTIATE_TEST_SUITE_P(BloatyTest,
+  BloatyOutputTest,
+  testing::ValuesIn(tests),
+  TestEntryName);
diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin
new file mode 100644
index 0000000..8b386c7
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin
Binary files differ
diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin.seg.txt b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin.seg.txt
new file mode 100644
index 0000000..c340143
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin.seg.txt
@@ -0,0 +1,8 @@
+segments	vmsize	filesize

+.rdata	7840	8192

+.data	5608	512

+.text	5000	5120

+.pdata	552	1024

+[PE Headers]	696	696

+.reloc	40	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin.txt b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin.txt
new file mode 100644
index 0000000..f56e9fd
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.bin.txt
@@ -0,0 +1,8 @@
+sections	vmsize	filesize

+.rdata	7840	8192

+.data	5608	512

+.text	5000	5120

+.pdata	552	1024

+[PE Headers]	696	696

+.reloc	40	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.pdb b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.pdb
new file mode 100644
index 0000000..b467213
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar-main-cv.pdb
Binary files differ
diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll b/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll
new file mode 100644
index 0000000..ca757e3
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll
Binary files differ
diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll.seg.txt b/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll.seg.txt
new file mode 100644
index 0000000..ebdc542
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll.seg.txt
@@ -0,0 +1,8 @@
+segments	vmsize	filesize

+.text	3587	4096

+.rdata	2520	2560

+.data	1592	512

+[PE Headers]	696	696

+.pdata	408	512

+.reloc	24	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll.txt b/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll.txt
new file mode 100644
index 0000000..9ad01d6
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-15.0-foo-bar.dll.txt
@@ -0,0 +1,8 @@
+sections	vmsize	filesize

+.text	3587	4096

+.rdata	2520	2560

+.data	1592	512

+[PE Headers]	696	696

+.pdata	408	512

+.reloc	24	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin
new file mode 100644
index 0000000..5ee1e64
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin
Binary files differ
diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin.seg.txt b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin.seg.txt
new file mode 100644
index 0000000..e91f2df
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin.seg.txt
@@ -0,0 +1,8 @@
+segments	vmsize	filesize

+.rdata	8236	8704

+.text	5340	5632

+.data	5624	512

+.pdata	588	1024

+[PE Headers]	696	696

+.reloc	52	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin.txt b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin.txt
new file mode 100644
index 0000000..e18bde9
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.bin.txt
@@ -0,0 +1,8 @@
+sections	vmsize	filesize

+.rdata	8236	8704

+.text	5340	5632

+.data	5624	512

+.pdata	588	1024

+[PE Headers]	696	696

+.reloc	52	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.pdb b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.pdb
new file mode 100644
index 0000000..12fd795
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar-main-cv.pdb
Binary files differ
diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll b/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll
new file mode 100644
index 0000000..e2b8dea
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll
Binary files differ
diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll.seg.txt b/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll.seg.txt
new file mode 100644
index 0000000..053ef6a
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll.seg.txt
@@ -0,0 +1,8 @@
+segments	vmsize	filesize

+.text	3592	4096

+.rdata	2680	3072

+.data	1608	512

+[PE Headers]	696	696

+.pdata	432	512

+.reloc	36	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll.txt b/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll.txt
new file mode 100644
index 0000000..e996d4b
--- /dev/null
+++ b/tests/testdata/PE/x64/msvc-16.0-foo-bar.dll.txt
@@ -0,0 +1,8 @@
+sections	vmsize	filesize

+.text	3592	4096

+.rdata	2680	3072

+.data	1608	512

+[PE Headers]	696	696

+.pdata	432	512

+.reloc	36	512

+[Unmapped]	0	328

diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin
new file mode 100644
index 0000000..94e93c8
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin
Binary files differ
diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin.seg.txt b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin.seg.txt
new file mode 100644
index 0000000..3984cdd
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin.seg.txt
@@ -0,0 +1,7 @@
+segments	vmsize	filesize

+.rdata	6724	7168

+.data	4912	512

+.text	4268	4608

+[PE Headers]	640	640

+.reloc	488	512

+[Unmapped]	0	384

diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin.txt b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin.txt
new file mode 100644
index 0000000..c58f6fe
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.bin.txt
@@ -0,0 +1,7 @@
+sections	vmsize	filesize

+.rdata	6724	7168

+.data	4912	512

+.text	4268	4608

+[PE Headers]	640	640

+.reloc	488	512

+[Unmapped]	0	384

diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.pdb b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.pdb
new file mode 100644
index 0000000..76b0695
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar-main-cv.pdb
Binary files differ
diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll b/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll
new file mode 100644
index 0000000..2477499
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll
Binary files differ
diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll.seg.txt b/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll.seg.txt
new file mode 100644
index 0000000..470bad6
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll.seg.txt
@@ -0,0 +1,7 @@
+segments	vmsize	filesize

+.text	3172	3584

+.rdata	1772	2048

+.data	908	512

+[PE Headers]	656	656

+.reloc	296	512

+[Unmapped]	0	368

diff --git a/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll.txt b/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll.txt
new file mode 100644
index 0000000..6d47fc3
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-15.0-foo-bar.dll.txt
@@ -0,0 +1,7 @@
+sections	vmsize	filesize

+.text	3172	3584

+.rdata	1772	2048

+.data	908	512

+[PE Headers]	656	656

+.reloc	296	512

+[Unmapped]	0	368

diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin
new file mode 100644
index 0000000..98c7191
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin
Binary files differ
diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin.seg.txt b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin.seg.txt
new file mode 100644
index 0000000..8844551
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin.seg.txt
@@ -0,0 +1,7 @@
+segments	vmsize	filesize

+.rdata	6944	7168

+.text	4716	5120

+.data	4912	512

+.reloc	544	1024

+[PE Headers]	648	648

+[Unmapped]	0	376

diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin.txt b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin.txt
new file mode 100644
index 0000000..68b42bf
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.bin.txt
@@ -0,0 +1,7 @@
+sections	vmsize	filesize

+.rdata	6944	7168

+.text	4716	5120

+.data	4912	512

+.reloc	544	1024

+[PE Headers]	648	648

+[Unmapped]	0	376

diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.pdb b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.pdb
new file mode 100644
index 0000000..c4ef096
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar-main-cv.pdb
Binary files differ
diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll b/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll
new file mode 100644
index 0000000..4bec075
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll
Binary files differ
diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll.seg.txt b/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll.seg.txt
new file mode 100644
index 0000000..e52851a
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll.seg.txt
@@ -0,0 +1,7 @@
+segments	vmsize	filesize

+.text	3360	3584

+.rdata	1812	2048

+.data	912	512

+[PE Headers]	648	648

+.reloc	312	512

+[Unmapped]	0	376

diff --git a/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll.txt b/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll.txt
new file mode 100644
index 0000000..1599574
--- /dev/null
+++ b/tests/testdata/PE/x86/msvc-16.0-foo-bar.dll.txt
@@ -0,0 +1,7 @@
+sections	vmsize	filesize

+.text	3360	3584

+.rdata	1812	2048

+.data	912	512

+[PE Headers]	648	648

+.reloc	312	512

+[Unmapped]	0	376

diff --git a/tests/testdata/bar.c b/tests/testdata/bar.c
new file mode 100644
index 0000000..0c835fd
--- /dev/null
+++ b/tests/testdata/bar.c
@@ -0,0 +1,5 @@
+
+int bar_x[1000] = {1};
+int bar_y = 1;
+int bar_z = 0;
+int bar_func() { return bar_y / 17; }
diff --git a/tests/testdata/foo.c b/tests/testdata/foo.c
new file mode 100644
index 0000000..9add3b6
--- /dev/null
+++ b/tests/testdata/foo.c
@@ -0,0 +1,4 @@
+
+int foo_x[1000] = {0};
+int foo_y = 0;
+int foo_func() { return foo_y / 17; }
diff --git a/tests/testdata/main.c b/tests/testdata/main.c
new file mode 100644
index 0000000..c272dab
--- /dev/null
+++ b/tests/testdata/main.c
@@ -0,0 +1 @@
+int main() {}
\ No newline at end of file
diff --git a/tests/testdata/make_all_msvc_test_files.bat b/tests/testdata/make_all_msvc_test_files.bat
new file mode 100644
index 0000000..edcef32
--- /dev/null
+++ b/tests/testdata/make_all_msvc_test_files.bat
@@ -0,0 +1,17 @@
+

+@if not defined _echo echo off

+

+set VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"

+

+for /f "usebackq delims=" %%i in (`%VSWHERE% -version [15.0^,17.0^) -property installationPath`) do (

+    if exist "%%i\Common7\Tools\vsdevcmd.bat" (

+        for %%A in (x86 x64) do (

+            SETLOCAL

+            call "%%i\Common7\Tools\vsdevcmd.bat" -arch=%%A

+            ECHO Building VS %VisualStudioVersion% %%A

+            call make_msvc_test_files.bat PE\%%A

+            ENDLOCAL

+        )

+    )

+)

+

diff --git a/tests/testdata/make_msvc_test_files.bat b/tests/testdata/make_msvc_test_files.bat
new file mode 100644
index 0000000..dea7167
--- /dev/null
+++ b/tests/testdata/make_msvc_test_files.bat
@@ -0,0 +1,44 @@
+@echo off

+

+if "%1"=="" goto noargs

+

+pushd %1

+

+@REM /Gm- disables minimal rebuild, /O1 favor size, /MD selects external runtime,

+@REM /GL enable cross-module optimization

+set CL_COMMON_FLAGS=/nologo /Gm- /O1 /MD /GL

+

+

+call :make_obj ..\..\foo.c

+call :make_obj ..\..\bar.c

+call :make_obj ..\..\main.c

+

+call :make_dll msvc-%VisualStudioVersion%-foo-bar.dll foo.obj bar.obj

+call :make_binary_with_pdb msvc-%VisualStudioVersion%-foo-bar-main-cv.bin msvc-%VisualStudioVersion%-foo-bar-main-cv.pdb foo.obj bar.obj main.obj

+

+goto cleanup

+

+:make_dll:

+for /f "tokens=1,* delims= " %%a in ("%*") do set ALL_BUT_FIRST=%%b

+cl %CL_COMMON_FLAGS% /LD %ALL_BUT_FIRST% /link /OUT:%1

+exit /B 0

+

+:make_binary_with_pdb:

+for /f "tokens=2,* delims= " %%a in ("%*") do set ALL_BUT_FIRST=%%b

+cl %CL_COMMON_FLAGS% %ALL_BUT_FIRST% /link /OUT:%1 /PDB:%2 /DEBUG

+exit /B 0

+

+:make_obj:

+cl %CL_COMMON_FLAGS% /c %1

+exit /B 0

+

+:noargs:

+echo Usage: make_test_files.bat ^<output dir^>

+

+:cleanup:

+

+del foo.obj

+del bar.obj

+del main.obj

+

+popd

diff --git a/third_party/lief_pe/LICENSE b/third_party/lief_pe/LICENSE
new file mode 100644
index 0000000..87b30a9
--- /dev/null
+++ b/third_party/lief_pe/LICENSE
@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2017 - 2021 R. Thomas
+   Copyright 2017 - 2021 Quarkslab
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/lief_pe/METADATA b/third_party/lief_pe/METADATA
new file mode 100644
index 0000000..f0cd410
--- /dev/null
+++ b/third_party/lief_pe/METADATA
@@ -0,0 +1,25 @@
+name: "LIEF: Library to Instrument Executable Formats (PE header)"
+description:
+    "This contains structures as defined by the LIEF project to parse PE headers."
+    "They only contain struct and constant definitions (no code) and their"
+    "contents are almost entirely derivable from the ELF standard."
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://lief.quarkslab.com/"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/lief-project/LIEF"
+  }
+  version: "1a4a93c36fab9b0d198e85459b6afe0fb2b0157b"
+  last_upgrade_date {
+    year: 2021
+    month: 4
+    day: 3
+  }
+  local_modifications:
+      "Renamed include/LIEF/PE/structures.inc to third_party/lief_pe/pe_structures.h"
+      "Added license header to the file."
+}
diff --git a/third_party/lief_pe/pe_structures.h b/third_party/lief_pe/pe_structures.h
new file mode 100644
index 0000000..71b7e61
--- /dev/null
+++ b/third_party/lief_pe/pe_structures.h
@@ -0,0 +1,516 @@
+/* Copyright 2017 - 2021 R. Thomas
+ * Copyright 2017 - 2021 Quarkslab
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+//! The maximum number of sections that a COFF object can have (inclusive).
+static const int32_t MaxNumberOfSections16 = 65279;
+
+//! The PE signature bytes that follows the DOS stub header.
+static const char PE_Magic[] = { 'P', 'E', '\0', '\0' };
+
+static const char Rich_Magic[] = {'R', 'i', 'c', 'h'};
+static const char DanS_Magic[] = {'D', 'a', 'n', 'S'};
+
+static const uint32_t DanS_Magic_number = 0x536E6144;
+
+static const char BigObjMagic[] = {
+  '\xc7', '\xa1', '\xba', '\xd1', '\xee', '\xba', '\xa9', '\x4b',
+  '\xaf', '\x20', '\xfa', '\xf6', '\x6a', '\xa4', '\xdc', '\xb8',
+};
+
+static const uint8_t DEFAULT_NUMBER_DATA_DIRECTORIES = 15;
+
+#pragma pack(push,1)
+struct pe_header {
+  char     signature[sizeof(PE_Magic)];
+  uint16_t Machine;
+  uint16_t NumberOfSections;
+  uint32_t TimeDateStamp;
+  uint32_t PointerToSymbolTable;
+  uint32_t NumberOfSymbols;
+  uint16_t SizeOfOptionalHeader;
+  uint16_t Characteristics;
+};
+
+
+struct pe_relocation {
+  uint32_t VirtualAddress;
+  uint32_t SymbolTableIndex;
+  uint16_t Type;
+};
+
+struct pe_base_relocation_block {
+  uint32_t PageRVA;
+  uint32_t BlockSize;
+};
+
+
+struct pe_symbol {
+  union {
+    char ShortName[8];
+    struct
+    {
+      uint32_t Zeroes;
+      uint32_t Offset;
+    } Name;
+  } Name;
+  uint32_t Value;
+  int16_t  SectionNumber;
+  uint16_t Type;
+  uint8_t  StorageClass;
+  uint8_t  NumberOfAuxSymbols;
+};
+
+
+struct pe_section {
+  char     Name[8];
+  uint32_t VirtualSize;
+  uint32_t VirtualAddress;
+  uint32_t SizeOfRawData;
+  uint32_t PointerToRawData;
+  uint32_t PointerToRelocations;
+  uint32_t PointerToLineNumbers;
+  uint16_t NumberOfRelocations;
+  uint16_t NumberOfLineNumbers;
+  uint32_t Characteristics;
+};
+
+struct AuxiliaryFunctionDefinition {
+  uint32_t TagIndex;
+  uint32_t TotalSize;
+  uint32_t PointerToLinenumber;
+  uint32_t PointerToNextFunction;
+  char     unused[2];
+};
+
+struct AuxiliarybfAndefSymbol {
+  uint8_t  unused1[4];
+  uint16_t Linenumber;
+  uint8_t  unused2[6];
+  uint32_t PointerToNextFunction;
+  uint8_t  unused3[2];
+};
+
+struct AuxiliaryWeakExternal {
+  uint32_t TagIndex;
+  uint32_t Characteristics;
+  uint8_t  unused[10];
+};
+
+
+struct AuxiliarySectionDefinition {
+  uint32_t Length;
+  uint16_t NumberOfRelocations;
+  uint16_t NumberOfLinenumbers;
+  uint32_t CheckSum;
+  uint32_t Number;
+  uint8_t  Selection;
+  char     unused;
+};
+
+struct AuxiliaryCLRToken {
+  uint8_t  AuxType;
+  uint8_t  unused1;
+  uint32_t SymbolTableIndex;
+  char     unused2[12];
+};
+
+union Auxiliary {
+  AuxiliaryFunctionDefinition FunctionDefinition;
+  AuxiliarybfAndefSymbol      bfAndefSymbol;
+  AuxiliaryWeakExternal       WeakExternal;
+  AuxiliarySectionDefinition  SectionDefinition;
+};
+
+
+/// The Import Directory Table.
+///
+/// There is a single array of these and one entry per imported DLL.
+struct pe_import {
+  uint32_t ImportLookupTableRVA;
+  uint32_t TimeDateStamp;
+  uint32_t ForwarderChain;
+  uint32_t NameRVA;
+  uint32_t ImportAddressTableRVA;
+};
+
+
+struct ImportLookupTableEntry32 {
+  uint32_t data;
+};
+
+struct ImportLookupTableEntry64 {
+  uint64_t data;
+};
+
+
+struct pe32_tls {
+  uint32_t RawDataStartVA;
+  uint32_t RawDataEndVA;
+  uint32_t AddressOfIndex;
+  uint32_t AddressOfCallback;
+  uint32_t SizeOfZeroFill;
+  uint32_t Characteristics;
+};
+
+
+struct pe64_tls {
+  uint64_t RawDataStartVA;
+  uint64_t RawDataEndVA;
+  uint64_t AddressOfIndex;
+  uint64_t AddressOfCallback;
+  uint32_t SizeOfZeroFill;
+  uint32_t Characteristics;
+};
+
+
+/// The DOS compatible header at the front of all PEs.
+struct pe_dos_header {
+  uint16_t Magic;
+  uint16_t UsedBytesInTheLastPage;
+  uint16_t FileSizeInPages;
+  uint16_t NumberOfRelocationItems;
+  uint16_t HeaderSizeInParagraphs;
+  uint16_t MinimumExtraParagraphs;
+  uint16_t MaximumExtraParagraphs;
+  uint16_t InitialRelativeSS;
+  uint16_t InitialSP;
+  uint16_t Checksum;
+  uint16_t InitialIP;
+  uint16_t InitialRelativeCS;
+  uint16_t AddressOfRelocationTable;
+  uint16_t OverlayNumber;
+  uint16_t Reserved[4];
+  uint16_t OEMid;
+  uint16_t OEMinfo;
+  uint16_t Reserved2[10];
+  uint32_t AddressOfNewExeHeader;
+};
+
+struct pe64_optional_header {
+  uint16_t Magic;
+  uint8_t  MajorLinkerVersion;
+  uint8_t  MinorLinkerVersion;
+  uint32_t SizeOfCode;
+  uint32_t SizeOfInitializedData;
+  uint32_t SizeOfUninitializedData;
+  uint32_t AddressOfEntryPoint; // RVA
+  uint32_t BaseOfCode; // RVA
+  //uint32_t BaseOfData; // RVA
+  uint64_t ImageBase;
+  uint32_t SectionAlignment;
+  uint32_t FileAlignment;
+  uint16_t MajorOperatingSystemVersion;
+  uint16_t MinorOperatingSystemVersion;
+  uint16_t MajorImageVersion;
+  uint16_t MinorImageVersion;
+  uint16_t MajorSubsystemVersion;
+  uint16_t MinorSubsystemVersion;
+  uint32_t Win32VersionValue;
+  uint32_t SizeOfImage;
+  uint32_t SizeOfHeaders;
+  uint32_t CheckSum;
+  uint16_t Subsystem;
+  uint16_t DLLCharacteristics;
+  uint64_t SizeOfStackReserve;
+  uint64_t SizeOfStackCommit;
+  uint64_t SizeOfHeapReserve;
+  uint64_t SizeOfHeapCommit;
+  uint32_t LoaderFlags;
+  uint32_t NumberOfRvaAndSize;
+};
+
+
+struct pe32_optional_header {
+  uint16_t Magic;
+  uint8_t  MajorLinkerVersion;
+  uint8_t  MinorLinkerVersion;
+  uint32_t SizeOfCode;
+  uint32_t SizeOfInitializedData;
+  uint32_t SizeOfUninitializedData;
+  uint32_t AddressOfEntryPoint; // RVA
+  uint32_t BaseOfCode; // RVA
+  uint32_t BaseOfData; // RVA
+  uint32_t ImageBase;
+  uint32_t SectionAlignment;
+  uint32_t FileAlignment;
+  uint16_t MajorOperatingSystemVersion;
+  uint16_t MinorOperatingSystemVersion;
+  uint16_t MajorImageVersion;
+  uint16_t MinorImageVersion;
+  uint16_t MajorSubsystemVersion;
+  uint16_t MinorSubsystemVersion;
+  uint32_t Win32VersionValue;
+  uint32_t SizeOfImage;
+  uint32_t SizeOfHeaders;
+  uint32_t CheckSum;
+  uint16_t Subsystem;
+  uint16_t DLLCharacteristics;
+  uint32_t SizeOfStackReserve;
+  uint32_t SizeOfStackCommit;
+  uint32_t SizeOfHeapReserve;
+  uint32_t SizeOfHeapCommit;
+  uint32_t LoaderFlags;
+  uint32_t NumberOfRvaAndSize;
+};
+
+
+struct pe_data_directory {
+  uint32_t RelativeVirtualAddress;
+  uint32_t Size;
+};
+
+
+struct pe_debug {
+  uint32_t Characteristics;
+  uint32_t TimeDateStamp;
+  uint16_t MajorVersion;
+  uint16_t MinorVersion;
+  uint32_t Type;
+  uint32_t SizeOfData;
+  uint32_t AddressOfRawData;
+  uint32_t PointerToRawData;
+};
+
+
+struct pe_pdb_70 {
+  uint32_t cv_signature;
+  uint8_t  signature[16];
+  uint32_t age;
+  char*    filename;
+};
+
+struct pe_pdb_20 {
+  uint32_t cv_signature;
+  uint32_t offset;
+  uint32_t signature;
+  uint32_t age;
+  char*    filename;
+};
+
+struct pe_pogo {
+  uint32_t start_rva;
+  uint32_t size;
+  char     name[1];
+};
+
+
+struct pe_resource_directory_table {
+  uint32_t Characteristics;
+  uint32_t TimeDateStamp;
+  uint16_t MajorVersion;
+  uint16_t MinorVersion;
+  uint16_t NumberOfNameEntries;
+  uint16_t NumberOfIDEntries;
+};
+
+struct pe_resource_directory_entries {
+  union {
+    uint32_t NameRVA;
+    uint32_t IntegerID;
+  } NameID;
+  uint32_t RVA;
+};
+
+struct pe_resource_data_entry {
+  uint32_t DataRVA;
+  uint32_t Size;
+  uint32_t Codepage;
+  uint32_t Reserved;
+};
+
+struct pe_resource_string {
+  int16_t Length;
+  uint16_t Name[1];
+};
+
+struct pe_resource_acceltableentry {
+  int16_t fFlags;
+  int16_t wAnsi;
+  int16_t wId;
+  int16_t padding;
+};
+
+//
+// Export structures
+//
+struct pe_export_directory_table {
+  uint32_t ExportFlags;           ///< Reserverd must be 0
+  uint32_t Timestamp;             ///< The time and date that the export data was created
+  uint16_t MajorVersion;          ///< The Major version number
+  uint16_t MinorVersion;          ///< The Minor version number
+  uint32_t NameRVA;               ///< The address of the ASCII DLL's name (RVA)
+  uint32_t OrdinalBase;           ///< The starting ordinal number for exports. (Usually 1)
+  uint32_t AddressTableEntries;   ///< Number of entries in the export address table
+  uint32_t NumberOfNamePointers;  ///< Number of entries in the name pointer table
+  uint32_t ExportAddressTableRVA; ///< Address of the export address table (RVA)
+  uint32_t NamePointerRVA;        ///< Address of the name pointer table (RVA)
+  uint32_t OrdinalTableRVA;       ///< Address of the ordinal table (RVA)
+};
+
+
+struct pe_resource_fixed_file_info {
+  uint32_t signature;          // e.g.  0xfeef04bd
+  uint32_t struct_version;      // e.g.  0x00000042 = "0.42"
+  uint32_t file_version_MS;    // e.g.  0x00030075 = "3.75"
+  uint32_t file_version_LS;    // e.g.  0x00000031 = "0.31"
+  uint32_t product_version_MS; // e.g.  0x00030010 = "3.10"
+  uint32_t product_version_LS; // e.g.  0x00000031 = "0.31"
+  uint32_t file_flags_mask;    // = 0x3F for version "0.42"
+  uint32_t file_flags;         // e.g.  VFF_DEBUG | VFF_PRERELEASE
+  uint32_t file_OS;            // e.g.  VOS_DOS_WINDOWS16
+  uint32_t file_type;          // e.g.  VFT_DRIVER
+  uint32_t file_subtype;       // e.g.  VFT2_DRV_KEYBOARD
+  uint32_t file_date_MS;       // e.g.  0
+  uint32_t file_date_LS;       // e.g.  0
+};
+
+
+struct pe_resource_version_info {
+  uint16_t length;
+  uint16_t sizeof_value;
+  uint16_t type;
+  char16_t key[16];
+  // uint16_t padding;
+  //
+  // uint16_t padding;
+  // uint16_t children
+};
+
+//! Resource icons directory structure
+//! Based on https://docs.microsoft.com/en-us/windows/win32/menurc/resources-reference
+//!
+//! This is the begining of the RESOURCE_TYPES::GROUP_ICON content
+struct pe_resource_icon_dir {
+  uint16_t reserved; ///< Reserved
+  uint16_t type;     ///< Resource type (1 for icons)
+  uint16_t count;    ///< Number of icons
+};
+
+
+//! Structure that follows pe_resource_icon_dir in a resource entry
+struct pe_resource_icon_group {
+  uint8_t width;        ///< Width, in pixels, of the image
+  uint8_t height;       ///< Height, in pixels, of the image
+  uint8_t color_count;  ///< Number of colors in image (0 if >=8bpp)
+  uint8_t reserved;     ///< Reserved (must be 0)
+  uint16_t planes;      ///< Color Planes
+  uint16_t bit_count;   ///< Bits per pixel
+  uint32_t size;        ///< Size of the image in bytes
+  uint16_t ID;          ///< The associated ID
+};
+
+//! Structure that follows pe_resource_icon_dir in a icon **file**
+struct pe_icon_header {
+  uint8_t width;        ///< Width, in pixels, of the image
+  uint8_t height;       ///< Height, in pixels, of the image
+  uint8_t color_count;  ///< Number of colors in image (0 if >=8bpp)
+  uint8_t reserved;     ///< Reserved (must be 0)
+  uint16_t planes;      ///< Color Planes
+  uint16_t bit_count;   ///< Bits per pixel
+  uint32_t size;        ///< Size of the image in bytes
+  uint32_t offset;      ///< Offset to the pixels
+};
+
+//! Extended dialog box template
+struct pe_dialog_template_ext {
+  uint16_t version;
+  uint16_t signature;
+  uint32_t help_id;
+  uint32_t ext_style;
+  uint32_t style;
+  uint16_t nbof_items;
+  int16_t x;
+  int16_t y;
+  int16_t cx;
+  int16_t cy;
+  // sz_Or_Ord menu;
+  // sz_Or_Ord windowClass;
+  // char16_t  title[titleLen];
+  // uint16_t  pointsize;
+  // uint16_t  weight;
+  // uint8_t   italic;
+  // uint8_t   charset;
+  // char16_t  typeface[stringLen];
+};
+
+//! Dialog box template
+struct pe_dialog_template {
+  uint32_t style;
+  uint32_t ext_style;
+  uint16_t nbof_items;
+  int16_t x;
+  int16_t y;
+  int16_t cx;
+  int16_t cy;
+};
+
+
+//! Extended dialog box template item
+struct pe_dialog_item_template_ext {
+  uint32_t help_id;
+  uint32_t ext_style;
+  uint32_t style;
+  int16_t x;
+  int16_t y;
+  int16_t cx;
+  int16_t cy;
+  uint32_t id;
+  // sz_Or_Ord windowClass;
+  // sz_Or_Ord title;
+  // uint16_t extra_count;
+};
+
+
+//! Dialog box template item
+struct pe_dialog_item_template {
+  uint32_t style;
+  uint32_t ext_style;
+  int16_t x;
+  int16_t y;
+  int16_t cx;
+  int16_t cy;
+  uint16_t id;
+};
+
+struct pe_code_integrity {
+  uint16_t Flags;
+  uint16_t Catalog;
+  uint32_t CatalogOffset;
+  uint32_t Reserved;
+};
+
+struct pe_exception_entry_x64 {
+  uint32_t address_start_rva;
+  uint32_t address_end_rva;
+  uint32_t unwind_info_rva;
+};
+
+
+struct pe_exception_entry_mips {
+  uint32_t address_start_va;
+  uint32_t address_end_va;
+  uint32_t exception_handler;
+  uint32_t handler_data;
+  uint32_t prolog_end_address;
+};
+
+struct pe_exception_entry_arm {
+  uint32_t address_start_va;
+  uint32_t data;
+};
+
+#pragma pack(pop)