/*
* Copyright (c) 2024, Sönke Holz <sholz8530@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/EnumBits.h>
#include <AK/StringView.h>
#include <AK/Types.h>
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
// NOTE: All struct definitions in this file assume little endian.
// Only PE32+ (64 bit) images are supported.
// The PE file offset to the value containing the offset of the PE magic.
static constexpr size_t PE_MAGIC_OFFSET_OFFSET = 0x3c;
static constexpr Array<u8, 2> DOS_MAGIC = { 'M', 'Z' };
static constexpr Array<u8, 4> PE_MAGIC = { 'P', 'E', '\0', '\0' };
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
struct [[gnu::packed]] COFFHeader {
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
enum class Machine : u16 {
AMD64 = 0x8664,
ARM64 = 0xaa64,
RISCV64 = 0x5064,
};
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics
enum class Characteristics : u16 {
RELOCS_STRIPPED = 0x0001,
EXECUTABLE_IMAGE = 0x0002,
LINE_NUMS_STRIPPED = 0x0004,
LOCAL_SYMS_STRIPPED = 0x0008,
AGGRESSIVE_WS_TRIM = 0x0010,
LARGE_ADDRESS_AWARE = 0x0020,
BYTES_REVERSED_LO = 0x0080,
_32BIT_MACHINE = 0x0100,
DEBUG_STRIPPED = 0x0200,
REMOVABLE_RUN_FROM_SWAP = 0x0400,
NET_RUN_FROM_SWAP = 0x0800,
IMAGE_FILE_SYSTEM = 0x1000,
DLL = 0x2000,
UP_SYSTEM_ONLY = 0x4000,
BYTES_REVERSED_HI = 0x8000,
};
Machine machine;
u16 number_of_sections;
u32 time_date_stamp; // 32-bit time_t :^(
u32 pointer_to_symbol_table;
u32 number_of_symbols;
u16 size_of_optional_header;
Characteristics characteristics;
};
static_assert(AssertSize<COFFHeader, 20>());
AK_ENUM_BITWISE_OPERATORS(COFFHeader::Characteristics)
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only
struct [[gnu::packed]] OptionalHeader {
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only
struct [[gnu::packed]] StandardFields {
enum class Magic : u16 {
PE32 = 0x10b,
PE32Plus = 0x20b,
};
Magic magic;
u8 major_linker_version;
u8 minor_linker_version;
u32 size_of_code;
u32 size_of_initialized_data;
u32 size_of_uninitialized_data;
u32 address_of_entry_point;
u32 base_of_code;
};
static_assert(AssertSize<StandardFields, 24>());
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-windows-specific-fields-image-only
struct [[gnu::packed]] WindowsSpecificFields {
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#windows-subsystem
enum class Subsystem : u16 {
EFI_APPLICATION = 10,
};
u64 image_base;
u32 section_alignment;
u32 file_alignment;
u16 major_operating_system_version;
u16 minor_operating_system_version;
u16 major_image_version;
u16 minor_image_version;
u16 major_subsystem_version;
u16 minor_subsystem_version;
u32 win32_version_value;
u32 size_of_image;
u32 size_of_headers;
u32 checksum;
Subsystem subsystem;
u16 dll_characteristics;
u64 size_of_stack_reserve;
u64 size_of_stack_commit;
u64 size_of_heap_reserve;
u64 size_of_heap_commit;
u32 loader_flags;
u32 number_of_rva_and_size;
};
static_assert(AssertSize<WindowsSpecificFields, 112 - 24>());
struct [[gnu::packed]] DataDirectory {
u32 virtual_address;
u32 size;
};
static_assert(AssertSize<DataDirectory, 8>());
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-data-directories-image-only
struct [[gnu::packed]] DataDirectories {
DataDirectory export_table;
DataDirectory import_table;
DataDirectory resource_table;
DataDirectory exception_table;
DataDirectory certificate_table;
DataDirectory base_relocation_table;
DataDirectory debug;
DataDirectory architecture;
DataDirectory global_ptr;
DataDirectory tls_table;
DataDirectory load_config_table;
DataDirectory bound_import;
DataDirectory iat;
DataDirectory delay_import_descriptor;
DataDirectory clr_runtime_header;
DataDirectory reserved;
};
static_assert(AssertSize<DataDirectories, 240 - 112>());
StandardFields standard_fields;
WindowsSpecificFields windows_specific_fields;
DataDirectories data_directories;
};
static_assert(AssertSize<OptionalHeader, 240>());
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers
struct [[gnu::packed]] SectionHeader {
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-flags
enum class Characteristics : u32 {
NONE = 0x00000000,
TYPE_NO_PAD = 0x00000008,
CNT_CODE = 0x00000020,
CNT_INITIALIZED_DATA = 0x00000040,
CNT_UNINITIALIZED_DATA = 0x00000080,
LNK_OTHER = 0x00000100,
LNK_INFO = 0x00000200,
LNK_REMOVE = 0x00000800,
LNK_COMDAT = 0x00001000,
GPREL = 0x00008000,
MEM_PURGEABLE = 0x00020000,
MEM_16BIT = 0x00020000,
MEM_LOCKED = 0x00040000,
MEM_PRELOAD = 0x00080000,
ALIGN_1BYTES = 0x00100000,
ALIGN_2BYTES = 0x00200000,
ALIGN_4BYTES = 0x00300000,
ALIGN_8BYTES = 0x00400000,
ALIGN_16BYTES = 0x00500000,
ALIGN_32BYTES = 0x00600000,
ALIGN_64BYTES = 0x00700000,
ALIGN_128BYTES = 0x00800000,
ALIGN_256BYTES = 0x00900000,
ALIGN_512BYTES = 0x00a00000,
ALIGN_1024BYTES = 0x00b00000,
ALIGN_2048BYTES = 0x00c00000,
ALIGN_4096BYTES = 0x00d00000,
ALIGN_8192BYTES = 0x00e00000,
LNK_NRELOC_OVFL = 0x01000000,
MEM_DISCARDABLE = 0x02000000,
MEM_NOT_CACHED = 0x04000000,
MEM_NOT_PAGED = 0x08000000,
MEM_SHARED = 0x10000000,
MEM_EXECUTE = 0x20000000,
MEM_READ = 0x40000000,
MEM_WRITE = 0x80000000,
};
Array<char, 8> name;
u32 virtual_size;
u32 virtual_address;
u32 size_of_raw_data;
u32 pointer_to_raw_data;
u32 pointer_to_relocations;
u32 pointer_to_line_numbers;
u16 number_of_relocations;
u16 number_of_line_numbers;
Characteristics characteristics;
};
static_assert(AssertSize<SectionHeader, 40>());
AK_ENUM_BITWISE_OPERATORS(SectionHeader::Characteristics)
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#special-sections
struct SpecialSection {
StringView name;
SectionHeader::Characteristics characteristics;
};
// This array only includes special sections that may be in the Prekernel ELF.
static constexpr Array SPECIAL_PE_SECTIONS = to_array<SpecialSection>({
{ ".bss"sv, SectionHeader::Characteristics::CNT_UNINITIALIZED_DATA | SectionHeader::Characteristics::MEM_READ | SectionHeader::Characteristics::MEM_WRITE },
{ ".data"sv, SectionHeader::Characteristics::CNT_INITIALIZED_DATA | SectionHeader::Characteristics::MEM_READ | SectionHeader::Characteristics::MEM_WRITE },
{ ".rdata"sv, SectionHeader::Characteristics::CNT_INITIALIZED_DATA | SectionHeader::Characteristics::MEM_READ },
{ ".text"sv, SectionHeader::Characteristics::CNT_CODE | SectionHeader::Characteristics::MEM_EXECUTE | SectionHeader::Characteristics::MEM_READ },
});
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#base-relocation-block
struct [[gnu::packed]] BaseRelocationBlockHeader {
u32 page_rva;
u32 block_size;
};
static_assert(AssertSize<BaseRelocationBlockHeader, 8>());
struct [[gnu::packed]] BaseRelocationBlockEntry {
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#base-relocation-types
enum class Type : u16 {
ABSOLUTE = 0,
DIR64 = 10,
};
u16 offset : 12;
Type type : 4;
};
static_assert(AssertSize<BaseRelocationBlockEntry, 2>());
template<>
struct AK::Traits<COFFHeader> : public AK::DefaultTraits<COFFHeader> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct AK::Traits<OptionalHeader> : public AK::DefaultTraits<OptionalHeader> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct AK::Traits<SectionHeader> : public AK::DefaultTraits<SectionHeader> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct AK::Traits<BaseRelocationBlockHeader> : public AK::DefaultTraits<BaseRelocationBlockHeader> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct AK::Traits<BaseRelocationBlockEntry> : public AK::DefaultTraits<BaseRelocationBlockEntry> {
static constexpr bool is_trivially_serializable() { return true; }
};