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)