Commit c84e5ee878

Jakub Konka <kubkon@jakubkonka.com>
2022-08-22 19:48:14
coff: improve default COFF/PE object parser
We now do not allocate memory for headers and other metadata unless requested by the caller. Instead, we read-in the entire contents of the image into memory and operate on pointers and casts wherever possible. I have a left a TODO to hook up Windows' memory-mapped API here in-place of standard `readToEndAlloc` which should be more memory proof on memory constrained hosts. This commit also supplements our `std.coff` with a lot missing basic extern structs required to make our COFF linker.
1 parent 9043e66
Changed files (3)
lib
src
lib/std/coff.zig
@@ -1,14 +1,731 @@
 const std = @import("std.zig");
+const assert = std.debug.assert;
 const io = std.io;
 const mem = std.mem;
 const os = std.os;
-const File = std.fs.File;
+const fs = std.fs;
 
-// CoffHeader.machine values
-// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
-const IMAGE_FILE_MACHINE_I386 = 0x014c;
-const IMAGE_FILE_MACHINE_IA64 = 0x0200;
-const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
+pub const CoffHeaderFlags = packed struct {
+    /// Image only, Windows CE, and Microsoft Windows NT and later.
+    /// This indicates that the file does not contain base relocations
+    /// and must therefore be loaded at its preferred base address.
+    /// If the base address is not available, the loader reports an error.
+    /// The default behavior of the linker is to strip base relocations
+    /// from executable (EXE) files.
+    RELOCS_STRIPPED: u1 = 0,
+
+    /// Image only. This indicates that the image file is valid and can be run.
+    /// If this flag is not set, it indicates a linker error.
+    EXECUTABLE_IMAGE: u1 = 0,
+
+    /// COFF line numbers have been removed. This flag is deprecated and should be zero.
+    LINE_NUMS_STRIPPED: u1 = 0,
+
+    /// COFF symbol table entries for local symbols have been removed.
+    /// This flag is deprecated and should be zero.
+    LOCAL_SYMS_STRIPPED: u1 = 0,
+
+    /// Obsolete. Aggressively trim working set.
+    /// This flag is deprecated for Windows 2000 and later and must be zero.
+    AGGRESSIVE_WS_TRIM: u1 = 0,
+
+    /// Application can handle > 2-GB addresses.
+    LARGE_ADDRESS_AWARE: u1 = 0,
+
+    /// This flag is reserved for future use.
+    RESERVED: u1 = 0,
+
+    /// Little endian: the least significant bit (LSB) precedes the
+    /// most significant bit (MSB) in memory. This flag is deprecated and should be zero.
+    BYTES_REVERSED_LO: u1 = 0,
+
+    /// Machine is based on a 32-bit-word architecture.
+    @"32BIT_MACHINE": u1 = 0,
+
+    /// Debugging information is removed from the image file.
+    DEBUG_STRIPPED: u1 = 0,
+
+    /// If the image is on removable media, fully load it and copy it to the swap file.
+    REMOVABLE_RUN_FROM_SWAP: u1 = 0,
+
+    /// If the image is on network media, fully load it and copy it to the swap file.
+    NET_RUN_FROM_SWAP: u1 = 0,
+
+    /// The image file is a system file, not a user program.
+    SYSTEM: u1 = 0,
+
+    /// The image file is a dynamic-link library (DLL).
+    /// Such files are considered executable files for almost all purposes,
+    /// although they cannot be directly run.
+    DLL: u1 = 0,
+
+    /// The file should be run only on a uniprocessor machine.
+    UP_SYSTEM_ONLY: u1 = 0,
+
+    /// Big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero.
+    BYTES_REVERSED_HI: u1 = 0,
+};
+
+pub const CoffHeader = extern struct {
+    /// The number that identifies the type of target machine.
+    machine: MachineType,
+
+    /// The number of sections. This indicates the size of the section table, which immediately follows the headers.
+    number_of_sections: u16,
+
+    /// The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value),
+    /// which indicates when the file was created.
+    time_date_stamp: u32,
+
+    /// The file offset of the COFF symbol table, or zero if no COFF symbol table is present.
+    /// This value should be zero for an image because COFF debugging information is deprecated.
+    pointer_to_symbol_table: u32,
+
+    /// The number of entries in the symbol table.
+    /// This data can be used to locate the string table, which immediately follows the symbol table.
+    /// This value should be zero for an image because COFF debugging information is deprecated.
+    number_of_symbols: u32,
+
+    /// The size of the optional header, which is required for executable files but not for object files.
+    /// This value should be zero for an object file. For a description of the header format, see Optional Header (Image Only).
+    size_of_optional_header: u16,
+
+    /// The flags that indicate the attributes of the file.
+    flags: CoffHeaderFlags,
+};
+
+// OptionalHeader.magic values
+// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
+pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
+pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
+
+pub const DllFlags = packed struct {
+    _reserved_0: u5 = 0,
+
+    /// Image can handle a high entropy 64-bit virtual address space.
+    HIGH_ENTROPY_VA: u1 = 0,
+
+    /// DLL can be relocated at load time.
+    DYNAMIC_BASE: u1 = 0,
+
+    /// Code Integrity checks are enforced.
+    FORCE_INTEGRITY: u1 = 0,
+
+    /// Image is NX compatible.
+    NX_COMPAT: u1 = 0,
+
+    /// Isolation aware, but do not isolate the image.
+    NO_ISOLATION: u1 = 0,
+
+    /// Does not use structured exception (SE) handling. No SE handler may be called in this image.
+    NO_SEH: u1 = 0,
+
+    /// Do not bind the image.
+    NO_BIND: u1 = 0,
+
+    /// Image must execute in an AppContainer.
+    APPCONTAINER: u1 = 0,
+
+    /// A WDM driver.
+    WDM_DRIVER: u1 = 0,
+
+    /// Image supports Control Flow Guard.
+    GUARD_CF: u1 = 0,
+
+    /// Terminal Server aware.
+    TERMINAL_SERVER_AWARE: u1 = 0,
+};
+
+pub const Subsystem = enum(u16) {
+    /// An unknown subsystem
+    UNKNOWN = 0,
+
+    /// Device drivers and native Windows processes
+    NATIVE = 1,
+
+    /// The Windows graphical user interface (GUI) subsystem
+    WINDOWS_GUI = 2,
+
+    /// The Windows character subsystem
+    WINDOWS_CUI = 3,
+
+    /// The OS/2 character subsystem
+    OS2_CUI = 5,
+
+    /// The Posix character subsystem
+    POSIX_CUI = 7,
+
+    /// Native Win9x driver
+    NATIVE_WINDOWS = 8,
+
+    /// Windows CE
+    WINDOWS_CE_GUI = 9,
+
+    /// An Extensible Firmware Interface (EFI) application
+    EFI_APPLICATION = 10,
+
+    /// An EFI driver with boot services
+    EFI_BOOT_SERVICE_DRIVER = 11,
+
+    /// An EFI driver with run-time services
+    EFI_RUNTIME_DRIVER = 12,
+
+    /// An EFI ROM image
+    EFI_ROM = 13,
+
+    /// XBOX
+    XBOX = 14,
+
+    /// Windows boot application
+    WINDOWS_BOOT_APPLICATION = 16,
+};
+
+pub const OptionalHeader = extern struct {
+    magic: u16,
+    major_linker_version: u8,
+    minor_linker_version: u8,
+    size_of_code: u32,
+    size_of_initialized_data: u32,
+    size_of_uninitialized_data: u32,
+    address_of_entry_point: u32,
+    base_of_code: u32,
+};
+
+pub const OptionalHeaderPE32 = extern struct {
+    magic: u16,
+    major_linker_version: u8,
+    minor_linker_version: u8,
+    size_of_code: u32,
+    size_of_initialized_data: u32,
+    size_of_uninitialized_data: u32,
+    address_of_entry_point: u32,
+    base_of_code: u32,
+    base_of_data: u32,
+    image_base: u32,
+    section_alignment: u32,
+    file_alignment: u32,
+    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: u16,
+    win32_version_value: u32,
+    size_of_image: u32,
+    size_of_headers: u32,
+    checksum: u32,
+    subsystem: Subsystem,
+    dll_flags: DllFlags,
+    size_of_stack_reserve: u32,
+    size_of_stack_commit: u32,
+    size_of_heap_reserve: u32,
+    size_of_heap_commit: u32,
+    loader_flags: u32,
+    number_of_rva_and_sizes: u32,
+};
+
+pub const OptionalHeaderPE64 = extern struct {
+    magic: u16,
+    major_linker_version: u8,
+    minor_linker_version: u8,
+    size_of_code: u32,
+    size_of_initialized_data: u32,
+    size_of_uninitialized_data: u32,
+    address_of_entry_point: u32,
+    base_of_code: u32,
+    image_base: u64,
+    section_alignment: u32,
+    file_alignment: u32,
+    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: u16,
+    win32_version_value: u32,
+    size_of_image: u32,
+    size_of_headers: u32,
+    checksum: u32,
+    subsystem: Subsystem,
+    dll_flags: DllFlags,
+    size_of_stack_reserve: u64,
+    size_of_stack_commit: u64,
+    size_of_heap_reserve: u64,
+    size_of_heap_commit: u64,
+    loader_flags: u32,
+    number_of_rva_and_sizes: u32,
+};
+
+pub const DebugDirectoryEntry = extern struct {
+    characteristiccs: u32,
+    time_date_stamp: u32,
+    major_version: u16,
+    minor_version: u16,
+    @"type": u32,
+    size_of_data: u32,
+    address_of_raw_data: u32,
+    pointer_to_raw_data: u32,
+};
+
+pub const ImageDataDirectory = extern struct {
+    virtual_address: u32,
+    size: u32,
+};
+
+pub const SectionHeader = extern struct {
+    name: [8]u8,
+    virtual_size: u32,
+    virtual_address: u32,
+    size_of_raw_data: u32,
+    pointer_to_raw_data: u32,
+    pointer_to_relocations: u32,
+    pointer_to_linenumbers: u32,
+    number_of_relocations: u16,
+    number_of_linenumbers: u16,
+    flags: SectionHeaderFlags,
+
+    pub fn getName(self: *align(1) const SectionHeader) ?[]const u8 {
+        if (self.name[0] == '/') return null;
+        const len = std.mem.indexOfScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len;
+        return self.name[0..len];
+    }
+
+    pub fn getNameOffset(self: SectionHeader) ?u32 {
+        if (self.name[0] != '/') return null;
+        const len = std.mem.indexOfScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len;
+        const offset = std.fmt.parseInt(u32, self.name[1..len], 10) catch unreachable;
+        return offset;
+    }
+
+    /// Applicable only to section headers in COFF objects.
+    pub fn getAlignment(self: SectionHeader) ?u16 {
+        if (self.flags.ALIGN == 0) return null;
+        return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable;
+    }
+
+    pub fn isComdat(self: SectionHeader) bool {
+        return self.flags.LNK_COMDAT == 0b1;
+    }
+};
+
+pub const SectionHeaderFlags = packed struct {
+    _reserved_0: u3 = 0,
+
+    /// The section should not be padded to the next boundary.
+    /// This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES.
+    /// This is valid only for object files.
+    TYPE_NO_PAD: u1 = 0,
+
+    _reserved_1: u1 = 0,
+
+    /// The section contains executable code.
+    CNT_CODE: u1 = 0,
+
+    /// The section contains initialized data.
+    CNT_INITIALIZED_DATA: u1 = 0,
+
+    /// The section contains uninitialized data.
+    CNT_UNINITIALIZED_DATA: u1 = 0,
+
+    /// Reserved for future use.
+    LNK_OTHER: u1 = 0,
+
+    /// The section contains comments or other information.
+    /// The .drectve section has this type.
+    /// This is valid for object files only.
+    LNK_INFO: u1 = 0,
+
+    _reserverd_2: u1 = 0,
+
+    /// The section will not become part of the image.
+    /// This is valid only for object files.
+    LNK_REMOVE: u1 = 0,
+
+    /// The section contains COMDAT data.
+    /// For more information, see COMDAT Sections (Object Only).
+    /// This is valid only for object files.
+    LNK_COMDAT: u1 = 0,
+
+    _reserved_3: u2 = 0,
+
+    /// The section contains data referenced through the global pointer (GP).
+    GPREL: u1 = 0,
+
+    /// Reserved for future use.
+    MEM_PURGEABLE: u1 = 0,
+
+    /// Reserved for future use.
+    MEM_16BIT: u1 = 0,
+
+    /// Reserved for future use.
+    MEM_LOCKED: u1 = 0,
+
+    /// Reserved for future use.
+    MEM_PRELOAD: u1 = 0,
+
+    /// Takes on multiple values according to flags:
+    /// pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x100000;
+    /// pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x200000;
+    /// pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x300000;
+    /// pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x400000;
+    /// pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x500000;
+    /// pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x600000;
+    /// pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x700000;
+    /// pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x800000;
+    /// pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x900000;
+    /// pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0xA00000;
+    /// pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0xB00000;
+    /// pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0xC00000;
+    /// pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0xD00000;
+    /// pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0xE00000;
+    ALIGN: u4 = 0,
+
+    /// The section contains extended relocations.
+    LNK_NRELOC_OVFL: u1 = 0,
+
+    /// The section can be discarded as needed.
+    MEM_DISCARDABLE: u1 = 0,
+
+    /// The section cannot be cached.
+    MEM_NOT_CACHED: u1 = 0,
+
+    /// The section is not pageable.
+    MEM_NOT_PAGED: u1 = 0,
+
+    /// The section can be shared in memory.
+    MEM_SHARED: u1 = 0,
+
+    /// The section can be executed as code.
+    MEM_EXECUTE: u1 = 0,
+
+    /// The section can be read.
+    MEM_READ: u1 = 0,
+
+    /// The section can be written to.
+    MEM_WRITE: u1 = 0,
+};
+
+pub const Symbol = struct {
+    name: [8]u8,
+    value: u32,
+    section_number: SectionNumber,
+    @"type": SymType,
+    storage_class: StorageClass,
+    number_of_aux_symbols: u8,
+
+    pub fn sizeOf() usize {
+        return 18;
+    }
+
+    pub fn getName(self: *const Symbol) ?[]const u8 {
+        if (std.mem.eql(u8, self.name[0..4], "\x00\x00\x00\x00")) return null;
+        const len = std.mem.indexOfScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len;
+        return self.name[0..len];
+    }
+
+    pub fn getNameOffset(self: Symbol) ?u32 {
+        if (!std.mem.eql(u8, self.name[0..4], "\x00\x00\x00\x00")) return null;
+        const offset = std.mem.readIntLittle(u32, self.name[4..8]);
+        return offset;
+    }
+};
+
+pub const SectionNumber = enum(u16) {
+    /// The symbol record is not yet assigned a section.
+    /// A value of zero indicates that a reference to an external symbol is defined elsewhere.
+    /// A value of non-zero is a common symbol with a size that is specified by the value.
+    UNDEFINED = 0,
+
+    /// The symbol has an absolute (non-relocatable) value and is not an address.
+    ABSOLUTE = 0xffff,
+
+    /// The symbol provides general type or debugging information but does not correspond to a section.
+    /// Microsoft tools use this setting along with .file records (storage class FILE).
+    DEBUG = 0xfffe,
+    _,
+};
+
+pub const SymType = packed struct {
+    complex_type: ComplexType,
+    base_type: BaseType,
+};
+
+pub const BaseType = enum(u8) {
+    /// No type information or unknown base type. Microsoft tools use this setting
+    NULL = 0,
+
+    /// No valid type; used with void pointers and functions
+    VOID = 1,
+
+    /// A character (signed byte)
+    CHAR = 2,
+
+    /// A 2-byte signed integer
+    SHORT = 3,
+
+    /// A natural integer type (normally 4 bytes in Windows)
+    INT = 4,
+
+    /// A 4-byte signed integer
+    LONG = 5,
+
+    /// A 4-byte floating-point number
+    FLOAT = 6,
+
+    /// An 8-byte floating-point number
+    DOUBLE = 7,
+
+    /// A structure
+    STRUCT = 8,
+
+    /// A union
+    UNION = 9,
+
+    /// An enumerated type
+    ENUM = 10,
+
+    /// A member of enumeration (a specified value)
+    MOE = 11,
+
+    /// A byte; unsigned 1-byte integer
+    BYTE = 12,
+
+    /// A word; unsigned 2-byte integer
+    WORD = 13,
+
+    /// An unsigned integer of natural size (normally, 4 bytes)
+    UINT = 14,
+
+    /// An unsigned 4-byte integer
+    DWORD = 15,
+};
+
+pub const ComplexType = enum(u8) {
+    /// No derived type; the symbol is a simple scalar variable.
+    NULL = 0,
+
+    /// The symbol is a pointer to base type.
+    POINTER = 16,
+
+    /// The symbol is a function that returns a base type.
+    FUNCTION = 32,
+
+    /// The symbol is an array of base type.
+    ARRAY = 48,
+};
+
+pub const StorageClass = enum(u8) {
+    /// A special symbol that represents the end of function, for debugging purposes.
+    END_OF_FUNCTION = 0xff,
+
+    /// No assigned storage class.
+    NULL = 0,
+
+    /// The automatic (stack) variable. The Value field specifies the stack frame offset.
+    AUTOMATIC = 1,
+
+    /// A value that Microsoft tools use for external symbols.
+    /// The Value field indicates the size if the section number is IMAGE_SYM_UNDEFINED (0).
+    /// If the section number is not zero, then the Value field specifies the offset within the section.
+    EXTERNAL = 2,
+
+    /// The offset of the symbol within the section.
+    /// If the Value field is zero, then the symbol represents a section name.
+    STATIC = 3,
+
+    /// A register variable.
+    /// The Value field specifies the register number.
+    REGISTER = 4,
+
+    /// A symbol that is defined externally.
+    EXTERNAL_DEF = 5,
+
+    /// A code label that is defined within the module.
+    /// The Value field specifies the offset of the symbol within the section.
+    LABEL = 6,
+
+    /// A reference to a code label that is not defined.
+    UNDEFINED_LABEL = 7,
+
+    /// The structure member. The Value field specifies the n th member.
+    MEMBER_OF_STRUCT = 8,
+
+    /// A formal argument (parameter) of a function. The Value field specifies the n th argument.
+    ARGUMENT = 9,
+
+    /// The structure tag-name entry.
+    STRUCT_TAG = 10,
+
+    /// A union member. The Value field specifies the n th member.
+    MEMBER_OF_UNION = 11,
+
+    /// The Union tag-name entry.
+    UNION_TAG = 12,
+
+    /// A Typedef entry.
+    TYPE_DEFINITION = 13,
+
+    /// A static data declaration.
+    UNDEFINED_STATIC = 14,
+
+    /// An enumerated type tagname entry.
+    ENUM_TAG = 15,
+
+    /// A member of an enumeration. The Value field specifies the n th member.
+    MEMBER_OF_ENUM = 16,
+
+    /// A register parameter.
+    REGISTER_PARAM = 17,
+
+    /// A bit-field reference. The Value field specifies the n th bit in the bit field.
+    BIT_FIELD = 18,
+
+    /// A .bb (beginning of block) or .eb (end of block) record.
+    /// The Value field is the relocatable address of the code location.
+    BLOCK = 100,
+
+    /// A value that Microsoft tools use for symbol records that define the extent of a function: begin function (.bf ), end function ( .ef ), and lines in function ( .lf ).
+    /// For .lf records, the Value field gives the number of source lines in the function.
+    /// For .ef records, the Value field gives the size of the function code.
+    FUNCTION = 101,
+
+    /// An end-of-structure entry.
+    END_OF_STRUCT = 102,
+
+    /// A value that Microsoft tools, as well as traditional COFF format, use for the source-file symbol record.
+    /// The symbol is followed by auxiliary records that name the file.
+    FILE = 103,
+
+    /// A definition of a section (Microsoft tools use STATIC storage class instead).
+    SECTION = 104,
+
+    /// A weak external. For more information, see Auxiliary Format 3: Weak Externals.
+    WEAK_EXTERNAL = 105,
+
+    /// A CLR token symbol. The name is an ASCII string that consists of the hexadecimal value of the token.
+    /// For more information, see CLR Token Definition (Object Only).
+    CLR_TOKEN = 107,
+};
+
+pub const FunctionDefinition = struct {
+    /// The symbol-table index of the corresponding .bf (begin function) symbol record.
+    tag_index: u32,
+
+    /// The size of the executable code for the function itself.
+    /// If the function is in its own section, the SizeOfRawData in the section header is greater or equal to this field,
+    /// depending on alignment considerations.
+    total_size: u32,
+
+    /// The file offset of the first COFF line-number entry for the function, or zero if none exists.
+    pointer_to_linenumber: u32,
+
+    /// The symbol-table index of the record for the next function.
+    /// If the function is the last in the symbol table, this field is set to zero.
+    pointer_to_next_function: u32,
+
+    unused: [2]u8,
+};
+
+pub const SectionDefinition = struct {
+    /// The size of section data; the same as SizeOfRawData in the section header.
+    length: u32,
+
+    /// The number of relocation entries for the section.
+    number_of_relocations: u16,
+
+    /// The number of line-number entries for the section.
+    number_of_linenumbers: u16,
+
+    /// The checksum for communal data. It is applicable if the IMAGE_SCN_LNK_COMDAT flag is set in the section header.
+    checksum: u32,
+
+    /// One-based index into the section table for the associated section. This is used when the COMDAT selection setting is 5.
+    number: u16,
+
+    /// The COMDAT selection number. This is applicable if the section is a COMDAT section.
+    selection: ComdatSelection,
+
+    unused: [3]u8,
+};
+
+pub const FileDefinition = struct {
+    /// An ANSI string that gives the name of the source file.
+    /// This is padded with nulls if it is less than the maximum length.
+    file_name: [18]u8,
+
+    pub fn getFileName(self: *const FileDefinition) []const u8 {
+        const len = std.mem.indexOfScalar(u8, &self.file_name, @as(u8, 0)) orelse self.file_name.len;
+        return self.file_name[0..len];
+    }
+};
+
+pub const WeakExternalDefinition = struct {
+    /// The symbol-table index of sym2, the symbol to be linked if sym1 is not found.
+    tag_index: u32,
+
+    /// A value of IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY indicates that no library search for sym1 should be performed.
+    /// A value of IMAGE_WEAK_EXTERN_SEARCH_LIBRARY indicates that a library search for sym1 should be performed.
+    /// A value of IMAGE_WEAK_EXTERN_SEARCH_ALIAS indicates that sym1 is an alias for sym2.
+    flag: WeakExternalFlag,
+
+    unused: [10]u8,
+};
+
+// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/km/ntimage.h
+pub const WeakExternalFlag = enum(u32) {
+    SEARCH_NOLIBRARY = 1,
+    SEARCH_LIBRARY = 2,
+    SEARCH_ALIAS = 3,
+    ANTI_DEPENDENCY = 4,
+};
+
+pub const ComdatSelection = enum(u8) {
+    /// Not a COMDAT section.
+    NONE = 0,
+
+    /// If this symbol is already defined, the linker issues a "multiply defined symbol" error.
+    NODUPLICATES = 1,
+
+    /// Any section that defines the same COMDAT symbol can be linked; the rest are removed.
+    ANY = 2,
+
+    /// The linker chooses an arbitrary section among the definitions for this symbol.
+    /// If all definitions are not the same size, a "multiply defined symbol" error is issued.
+    SAME_SIZE = 3,
+
+    /// The linker chooses an arbitrary section among the definitions for this symbol.
+    /// If all definitions do not match exactly, a "multiply defined symbol" error is issued.
+    EXACT_MATCH = 4,
+
+    /// The section is linked if a certain other COMDAT section is linked.
+    /// This other section is indicated by the Number field of the auxiliary symbol record for the section definition.
+    /// This setting is useful for definitions that have components in multiple sections
+    /// (for example, code in one and data in another), but where all must be linked or discarded as a set.
+    /// The other section this section is associated with must be a COMDAT section, which can be another
+    /// associative COMDAT section. An associative COMDAT section's section association chain can't form a loop.
+    /// The section association chain must eventually come to a COMDAT section that doesn't have IMAGE_COMDAT_SELECT_ASSOCIATIVE set.
+    ASSOCIATIVE = 5,
+
+    /// The linker chooses the largest definition from among all of the definitions for this symbol.
+    /// If multiple definitions have this size, the choice between them is arbitrary.
+    LARGEST = 6,
+};
+
+pub const DebugInfoDefinition = struct {
+    unused_1: [4]u8,
+
+    /// The actual ordinal line number (1, 2, 3, and so on) within the source file, corresponding to the .bf or .ef record.
+    linenumber: u16,
+
+    unused_2: [6]u8,
+
+    /// The symbol-table index of the next .bf symbol record.
+    /// If the function is the last in the symbol table, this field is set to zero.
+    /// It is not used for .ef records.
+    pointer_to_next_function: u32,
+
+    unused_3: [2]u8,
+};
 
 pub const MachineType = enum(u16) {
     Unknown = 0x0,
@@ -77,25 +794,6 @@ pub const MachineType = enum(u16) {
     }
 };
 
-// OptionalHeader.magic values
-// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
-const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
-const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
-
-// Image Characteristics
-pub const IMAGE_FILE_RELOCS_STRIPPED = 0x1;
-pub const IMAGE_FILE_DEBUG_STRIPPED = 0x200;
-pub const IMAGE_FILE_EXECUTABLE_IMAGE = 0x2;
-pub const IMAGE_FILE_32BIT_MACHINE = 0x100;
-pub const IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x20;
-
-// Section flags
-pub const IMAGE_SCN_CNT_INITIALIZED_DATA = 0x40;
-pub const IMAGE_SCN_MEM_READ = 0x40000000;
-pub const IMAGE_SCN_CNT_CODE = 0x20;
-pub const IMAGE_SCN_MEM_EXECUTE = 0x20000000;
-pub const IMAGE_SCN_MEM_WRITE = 0x80000000;
-
 const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
 const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
 const DEBUG_DIRECTORY = 6;
@@ -104,166 +802,90 @@ pub const CoffError = error{
     InvalidPEMagic,
     InvalidPEHeader,
     InvalidMachine,
+    MissingPEHeader,
     MissingCoffSection,
     MissingStringTable,
 };
 
 // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
 pub const Coff = struct {
-    in_file: File,
     allocator: mem.Allocator,
+    data: []const u8 = undefined,
+    is_image: bool = false,
+    coff_header_offset: usize = 0,
 
-    coff_header: CoffHeader,
-    pe_header: OptionalHeader,
-    sections: std.ArrayListUnmanaged(Section) = .{},
-
-    guid: [16]u8,
-    age: u32,
-
-    pub fn init(allocator: mem.Allocator, in_file: File) Coff {
-        return Coff{
-            .in_file = in_file,
-            .allocator = allocator,
-            .coff_header = undefined,
-            .pe_header = undefined,
-            .guid = undefined,
-            .age = undefined,
-        };
-    }
+    guid: [16]u8 = undefined,
+    age: u32 = undefined,
 
     pub fn deinit(self: *Coff) void {
-        self.sections.deinit(self.allocator);
+        self.allocator.free(self.data);
     }
 
-    pub fn loadHeader(self: *Coff) !void {
-        const pe_pointer_offset = 0x3C;
-
-        const in = self.in_file.reader();
-
-        var magic: [2]u8 = undefined;
-        try in.readNoEof(magic[0..]);
-        if (!mem.eql(u8, &magic, "MZ"))
-            return error.InvalidPEMagic;
-
-        // Seek to PE File Header (coff header)
-        try self.in_file.seekTo(pe_pointer_offset);
-        const pe_magic_offset = try in.readIntLittle(u32);
-        try self.in_file.seekTo(pe_magic_offset);
-
-        var pe_header_magic: [4]u8 = undefined;
-        try in.readNoEof(pe_header_magic[0..]);
-        if (!mem.eql(u8, &pe_header_magic, &[_]u8{ 'P', 'E', 0, 0 }))
-            return error.InvalidPEHeader;
-
-        self.coff_header = CoffHeader{
-            .machine = try in.readIntLittle(u16),
-            .number_of_sections = try in.readIntLittle(u16),
-            .timedate_stamp = try in.readIntLittle(u32),
-            .pointer_to_symbol_table = try in.readIntLittle(u32),
-            .number_of_symbols = try in.readIntLittle(u32),
-            .size_of_optional_header = try in.readIntLittle(u16),
-            .characteristics = try in.readIntLittle(u16),
-        };
+    /// Takes ownership of `data`.
+    pub fn parse(self: *Coff, data: []const u8) !void {
+        self.data = data;
 
-        switch (self.coff_header.machine) {
-            IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 => {},
-            else => return error.InvalidMachine,
-        }
+        const pe_pointer_offset = 0x3C;
+        const pe_magic = "PE\x00\x00";
 
-        try self.loadOptionalHeader();
-    }
+        var stream = std.io.fixedBufferStream(self.data);
+        const reader = stream.reader();
+        try stream.seekTo(pe_pointer_offset);
+        const coff_header_offset = try reader.readByte();
+        try stream.seekTo(coff_header_offset);
+        var buf: [4]u8 = undefined;
+        try reader.readNoEof(&buf);
+        self.is_image = mem.eql(u8, pe_magic, &buf);
 
-    fn readStringFromTable(self: *Coff, offset: usize, buf: []u8) ![]const u8 {
-        if (self.coff_header.pointer_to_symbol_table == 0) {
-            // No symbol table therefore no string table
-            return error.MissingStringTable;
-        }
-        // The string table is at the end of the symbol table and symbols are 18 bytes long
-        const string_table_offset = self.coff_header.pointer_to_symbol_table + (self.coff_header.number_of_symbols * 18) + offset;
-        const in = self.in_file.reader();
-        const old_pos = try self.in_file.getPos();
-
-        try self.in_file.seekTo(string_table_offset);
-        defer {
-            self.in_file.seekTo(old_pos) catch unreachable;
+        // Do some basic validation upfront
+        if (self.is_image) {
+            self.coff_header_offset = coff_header_offset + 4;
+            const coff_header = self.getCoffHeader();
+            if (coff_header.size_of_optional_header == 0) {
+                std.log.err("Required PE header missing for image file", .{});
+                return error.MissingPEHeader;
+            }
         }
 
-        const str = try in.readUntilDelimiterOrEof(buf, 0);
-        return str orelse "";
-    }
-
-    fn loadOptionalHeader(self: *Coff) !void {
-        const in = self.in_file.reader();
-        const opt_header_pos = try self.in_file.getPos();
-
-        self.pe_header.magic = try in.readIntLittle(u16);
-        try self.in_file.seekTo(opt_header_pos + 16);
-        self.pe_header.entry_addr = try in.readIntLittle(u32);
-        try self.in_file.seekTo(opt_header_pos + 20);
-        self.pe_header.code_base = try in.readIntLittle(u32);
-
-        // The header structure is different for 32 or 64 bit
-        var num_rva_pos: u64 = undefined;
-        if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
-            num_rva_pos = opt_header_pos + 92;
-
-            try self.in_file.seekTo(opt_header_pos + 28);
-            const image_base32 = try in.readIntLittle(u32);
-            self.pe_header.image_base = image_base32;
-        } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
-            num_rva_pos = opt_header_pos + 108;
-
-            try self.in_file.seekTo(opt_header_pos + 24);
-            self.pe_header.image_base = try in.readIntLittle(u64);
-        } else return error.InvalidPEMagic;
-
-        try self.in_file.seekTo(num_rva_pos);
-
-        const number_of_rva_and_sizes = try in.readIntLittle(u32);
-        if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
-            return error.InvalidPEHeader;
-
-        for (self.pe_header.data_directory) |*data_dir| {
-            data_dir.* = OptionalHeader.DataDirectory{
-                .virtual_address = try in.readIntLittle(u32),
-                .size = try in.readIntLittle(u32),
-            };
-        }
+        // JK: we used to check for architecture here and throw an error if not x86 or derivative.
+        // However I am willing to take a leap of faith and let aarch64 have a shot also.
     }
 
     pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
-        try self.loadSections();
+        assert(self.is_image);
 
         const header = blk: {
-            if (self.getSection(".buildid")) |section| {
-                break :blk section.header;
-            } else if (self.getSection(".rdata")) |section| {
-                break :blk section.header;
+            if (self.getSectionByName(".buildid")) |hdr| {
+                break :blk hdr;
+            } else if (self.getSectionByName(".rdata")) |hdr| {
+                break :blk hdr;
             } else {
                 return error.MissingCoffSection;
             }
         };
 
-        const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
+        const data_dirs = self.getDataDirectories();
+        const debug_dir = data_dirs[DEBUG_DIRECTORY];
         const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
 
-        const in = self.in_file.reader();
-        try self.in_file.seekTo(file_offset);
+        var stream = std.io.fixedBufferStream(self.data);
+        const reader = stream.reader();
+        try stream.seekTo(file_offset);
 
         // Find the correct DebugDirectoryEntry, and where its data is stored.
         // It can be in any section.
         const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
         var i: u32 = 0;
         blk: while (i < debug_dir_entry_count) : (i += 1) {
-            const debug_dir_entry = try in.readStruct(DebugDirectoryEntry);
+            const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
             if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) {
-                for (self.sections.items) |*section| {
-                    const section_start = section.header.virtual_address;
-                    const section_size = section.header.misc.virtual_size;
+                for (self.getSectionHeaders()) |*section| {
+                    const section_start = section.virtual_address;
+                    const section_size = section.virtual_size;
                     const rva = debug_dir_entry.address_of_raw_data;
                     const offset = rva - section_start;
                     if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) {
-                        try self.in_file.seekTo(section.header.pointer_to_raw_data + offset);
+                        try stream.seekTo(section.pointer_to_raw_data + offset);
                         break :blk;
                     }
                 }
@@ -271,19 +893,19 @@ pub const Coff = struct {
         }
 
         var cv_signature: [4]u8 = undefined; // CodeView signature
-        try in.readNoEof(cv_signature[0..]);
+        try reader.readNoEof(cv_signature[0..]);
         // 'RSDS' indicates PDB70 format, used by lld.
         if (!mem.eql(u8, &cv_signature, "RSDS"))
             return error.InvalidPEMagic;
-        try in.readNoEof(self.guid[0..]);
-        self.age = try in.readIntLittle(u32);
+        try reader.readNoEof(self.guid[0..]);
+        self.age = try reader.readIntLittle(u32);
 
         // Finally read the null-terminated string.
-        var byte = try in.readByte();
+        var byte = try reader.readByte();
         i = 0;
         while (byte != 0 and i < buffer.len) : (i += 1) {
             buffer[i] = byte;
-            byte = try in.readByte();
+            byte = try reader.readByte();
         }
 
         if (byte != 0 and i == buffer.len)
@@ -292,126 +914,232 @@ pub const Coff = struct {
         return @as(usize, i);
     }
 
-    pub fn loadSections(self: *Coff) !void {
-        if (self.sections.items.len == self.coff_header.number_of_sections)
-            return;
+    pub fn getCoffHeader(self: Coff) CoffHeader {
+        return @ptrCast(*align(1) CoffHeader, self.data[self.coff_header_offset..][0..@sizeOf(CoffHeader)]).*;
+    }
 
-        try self.sections.ensureTotalCapacityPrecise(self.allocator, self.coff_header.number_of_sections);
+    pub fn getOptionalHeader(self: Coff) OptionalHeader {
+        assert(self.is_image);
+        const offset = self.coff_header_offset + @sizeOf(CoffHeader);
+        return @ptrCast(*align(1) OptionalHeader, self.data[offset..][0..@sizeOf(OptionalHeader)]).*;
+    }
 
-        const in = self.in_file.reader();
+    pub fn getOptionalHeader32(self: Coff) OptionalHeaderPE32 {
+        assert(self.is_image);
+        const offset = self.coff_header_offset + @sizeOf(CoffHeader);
+        return @ptrCast(*align(1) OptionalHeaderPE32, self.data[offset..][0..@sizeOf(OptionalHeaderPE32)]).*;
+    }
 
-        var name: [32]u8 = undefined;
+    pub fn getOptionalHeader64(self: Coff) OptionalHeaderPE64 {
+        assert(self.is_image);
+        const offset = self.coff_header_offset + @sizeOf(CoffHeader);
+        return @ptrCast(*align(1) OptionalHeaderPE64, self.data[offset..][0..@sizeOf(OptionalHeaderPE64)]).*;
+    }
 
-        var i: u16 = 0;
-        while (i < self.coff_header.number_of_sections) : (i += 1) {
-            try in.readNoEof(name[0..8]);
+    pub fn getImageBase(self: Coff) u64 {
+        const hdr = self.getOptionalHeader();
+        return switch (hdr.magic) {
+            IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().image_base,
+            IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().image_base,
+            else => unreachable, // We assume we have validated the header already
+        };
+    }
 
-            if (name[0] == '/') {
-                // This is a long name and stored in the string table
-                const offset_len = mem.indexOfScalar(u8, name[1..], 0) orelse 7;
+    pub fn getNumberOfDataDirectories(self: Coff) u32 {
+        const hdr = self.getOptionalHeader();
+        return switch (hdr.magic) {
+            IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().number_of_rva_and_sizes,
+            IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().number_of_rva_and_sizes,
+            else => unreachable, // We assume we have validated the header already
+        };
+    }
 
-                const str_offset = try std.fmt.parseInt(u32, name[1 .. offset_len + 1], 10);
-                const str = try self.readStringFromTable(str_offset, &name);
-                std.mem.set(u8, name[str.len..], 0);
-            } else {
-                std.mem.set(u8, name[8..], 0);
-            }
+    pub fn getDataDirectories(self: *const Coff) []align(1) const ImageDataDirectory {
+        const hdr = self.getOptionalHeader();
+        const size: usize = switch (hdr.magic) {
+            IMAGE_NT_OPTIONAL_HDR32_MAGIC => @sizeOf(OptionalHeaderPE32),
+            IMAGE_NT_OPTIONAL_HDR64_MAGIC => @sizeOf(OptionalHeaderPE64),
+            else => unreachable, // We assume we have validated the header already
+        };
+        const offset = self.coff_header_offset + @sizeOf(CoffHeader) + size;
+        return @ptrCast([*]align(1) ImageDataDirectory, self.data[offset..])[0..self.getNumberOfDataDirectories()];
+    }
 
-            self.sections.appendAssumeCapacity(Section{
-                .header = SectionHeader{
-                    .name = name,
-                    .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) },
-                    .virtual_address = try in.readIntLittle(u32),
-                    .size_of_raw_data = try in.readIntLittle(u32),
-                    .pointer_to_raw_data = try in.readIntLittle(u32),
-                    .pointer_to_relocations = try in.readIntLittle(u32),
-                    .pointer_to_line_numbers = try in.readIntLittle(u32),
-                    .number_of_relocations = try in.readIntLittle(u16),
-                    .number_of_line_numbers = try in.readIntLittle(u16),
-                    .characteristics = try in.readIntLittle(u32),
-                },
-            });
-        }
+    pub fn getSymtab(self: *const Coff) ?Symtab {
+        const coff_header = self.getCoffHeader();
+        if (coff_header.pointer_to_symbol_table == 0) return null;
+
+        const offset = coff_header.pointer_to_symbol_table;
+        const size = coff_header.number_of_symbols * Symbol.sizeOf();
+        return .{ .buffer = self.data[offset..][0..size] };
+    }
+
+    pub fn getStrtab(self: *const Coff) ?Strtab {
+        const coff_header = self.getCoffHeader();
+        if (coff_header.pointer_to_symbol_table == 0) return null;
+
+        const offset = coff_header.pointer_to_symbol_table + Symbol.sizeOf() * coff_header.number_of_symbols;
+        const size = mem.readIntLittle(u32, self.data[offset..][0..4]);
+        return .{ .buffer = self.data[offset..][0..size] };
+    }
+
+    pub fn getSectionHeaders(self: *const Coff) []align(1) const SectionHeader {
+        const coff_header = self.getCoffHeader();
+        const offset = self.coff_header_offset + @sizeOf(CoffHeader) + coff_header.size_of_optional_header;
+        return @ptrCast([*]align(1) SectionHeader, self.data.ptr + offset)[0..coff_header.number_of_sections];
     }
 
-    pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
-        for (self.sections.items) |*sec| {
-            if (mem.eql(u8, sec.header.name[0..name.len], name)) {
-                return sec;
+    pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) []const u8 {
+        const name = sect_hdr.getName() orelse blk: {
+            const strtab = self.getStrtab().?;
+            const name_offset = sect_hdr.getNameOffset().?;
+            break :blk strtab.get(name_offset);
+        };
+        return name;
+    }
+
+    pub fn getSectionByName(self: *const Coff, comptime name: []const u8) ?*align(1) const SectionHeader {
+        for (self.getSectionHeaders()) |*sect| {
+            if (mem.eql(u8, self.getSectionName(sect), name)) {
+                return sect;
             }
         }
         return null;
     }
 
     // Return an owned slice full of the section data
-    pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 {
-        const sec = for (self.sections.items) |*sec| {
-            if (mem.eql(u8, sec.header.name[0..name.len], name)) {
-                break sec;
-            }
-        } else {
-            return error.MissingCoffSection;
-        };
-        const in = self.in_file.reader();
-        try self.in_file.seekTo(sec.header.pointer_to_raw_data);
-        const out_buff = try allocator.alloc(u8, sec.header.misc.virtual_size);
-        try in.readNoEof(out_buff);
+    pub fn getSectionDataAlloc(self: *const Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 {
+        const sec = self.getSectionByName(name) orelse return error.MissingCoffSection;
+        const out_buff = try allocator.alloc(u8, sec.virtual_size);
+        mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]);
         return out_buff;
     }
-};
 
-const CoffHeader = struct {
-    machine: u16,
-    number_of_sections: u16,
-    timedate_stamp: u32,
-    pointer_to_symbol_table: u32,
-    number_of_symbols: u32,
-    size_of_optional_header: u16,
-    characteristics: u16,
-};
+    pub const Symtab = struct {
+        buffer: []const u8,
 
-const OptionalHeader = struct {
-    const DataDirectory = struct {
-        virtual_address: u32,
-        size: u32,
-    };
+        fn len(self: Symtab) usize {
+            return @divExact(self.buffer.len, Symbol.sizeOf());
+        }
 
-    magic: u16,
-    data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
-    entry_addr: u32,
-    code_base: u32,
-    image_base: u64,
-};
+        const Tag = enum {
+            symbol,
+            func_def,
+            debug_info,
+            weak_ext,
+            file_def,
+            sect_def,
+        };
 
-const DebugDirectoryEntry = extern struct {
-    characteristiccs: u32,
-    time_date_stamp: u32,
-    major_version: u16,
-    minor_version: u16,
-    @"type": u32,
-    size_of_data: u32,
-    address_of_raw_data: u32,
-    pointer_to_raw_data: u32,
-};
+        const Record = union(Tag) {
+            symbol: Symbol,
+            debug_info: DebugInfoDefinition,
+            func_def: FunctionDefinition,
+            weak_ext: WeakExternalDefinition,
+            file_def: FileDefinition,
+            sect_def: SectionDefinition,
+        };
 
-pub const Section = struct {
-    header: SectionHeader,
-};
+        /// Lives as long as Symtab instance.
+        fn at(self: Symtab, index: usize, tag: Tag) Record {
+            const offset = index * Symbol.sizeOf();
+            const raw = self.buffer[offset..][0..Symbol.sizeOf()];
+            return switch (tag) {
+                .symbol => .{ .symbol = asSymbol(raw) },
+                .debug_info => .{ .debug_info = asDebugInfo(raw) },
+                .func_def => .{ .func_def = asFuncDef(raw) },
+                .weak_ext => .{ .weak_ext = asWeakExtDef(raw) },
+                .file_def => .{ .file_def = asFileDef(raw) },
+                .sect_def => .{ .sect_def = asSectDef(raw) },
+            };
+        }
 
-const SectionHeader = struct {
-    const Misc = union {
-        physical_address: u32,
-        virtual_size: u32,
+        fn asSymbol(raw: []const u8) Symbol {
+            return .{
+                .name = raw[0..8].*,
+                .value = mem.readIntLittle(u32, raw[8..12]),
+                .section_number = @intToEnum(SectionNumber, mem.readIntLittle(u16, raw[12..14])),
+                .@"type" = @bitCast(SymType, mem.readIntLittle(u16, raw[14..16])),
+                .storage_class = @intToEnum(StorageClass, raw[16]),
+                .number_of_aux_symbols = raw[17],
+            };
+        }
+
+        fn asDebugInfo(raw: []const u8) DebugInfoDefinition {
+            return .{
+                .unused_1 = raw[0..4].*,
+                .linenumber = mem.readIntLittle(u16, raw[4..6]),
+                .unused_2 = raw[6..12].*,
+                .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]),
+                .unused_3 = raw[16..18].*,
+            };
+        }
+
+        fn asFuncDef(raw: []const u8) FunctionDefinition {
+            return .{
+                .tag_index = mem.readIntLittle(u32, raw[0..4]),
+                .total_size = mem.readIntLittle(u32, raw[4..8]),
+                .pointer_to_linenumber = mem.readIntLittle(u32, raw[8..12]),
+                .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]),
+                .unused = raw[16..18].*,
+            };
+        }
+
+        fn asWeakExtDef(raw: []const u8) WeakExternalDefinition {
+            return .{
+                .tag_index = mem.readIntLittle(u32, raw[0..4]),
+                .flag = @intToEnum(WeakExternalFlag, mem.readIntLittle(u32, raw[4..8])),
+                .unused = raw[8..18].*,
+            };
+        }
+
+        fn asFileDef(raw: []const u8) FileDefinition {
+            return .{
+                .file_name = raw[0..18].*,
+            };
+        }
+
+        fn asSectDef(raw: []const u8) SectionDefinition {
+            return .{
+                .length = mem.readIntLittle(u32, raw[0..4]),
+                .number_of_relocations = mem.readIntLittle(u16, raw[4..6]),
+                .number_of_linenumbers = mem.readIntLittle(u16, raw[6..8]),
+                .checksum = mem.readIntLittle(u32, raw[8..12]),
+                .number = mem.readIntLittle(u16, raw[12..14]),
+                .selection = @intToEnum(ComdatSelection, raw[14]),
+                .unused = raw[15..18].*,
+            };
+        }
+
+        const Slice = struct {
+            buffer: []const u8,
+            num: usize,
+            count: usize = 0,
+
+            /// Lives as long as Symtab instance.
+            fn next(self: *Slice) ?Symbol {
+                if (self.count >= self.num) return null;
+                const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]);
+                self.count += 1;
+                self.buffer = self.buffer[Symbol.sizeOf()..];
+                return sym;
+            }
+        };
+
+        fn slice(self: Symtab, start: usize, end: ?usize) Slice {
+            const offset = start * Symbol.sizeOf();
+            const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len;
+            const num = @divExact(llen - offset, Symbol.sizeOf());
+            return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num };
+        }
     };
 
-    name: [32]u8,
-    misc: Misc,
-    virtual_address: u32,
-    size_of_raw_data: u32,
-    pointer_to_raw_data: u32,
-    pointer_to_relocations: u32,
-    pointer_to_line_numbers: u32,
-    number_of_relocations: u16,
-    number_of_line_numbers: u16,
-    characteristics: u32,
+    pub const Strtab = struct {
+        buffer: []const u8,
+
+        fn get(self: Strtab, off: u32) []const u8 {
+            assert(off < self.buffer.len);
+            return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0);
+        }
+    };
 };
lib/std/debug.zig
@@ -816,11 +816,11 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) anyerror!DebugInfo {
 /// TODO it's weird to take ownership even on error, rework this code.
 fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo {
     nosuspend {
-        errdefer coff_file.close();
+        defer coff_file.close();
 
         const coff_obj = try allocator.create(coff.Coff);
         errdefer allocator.destroy(coff_obj);
-        coff_obj.* = coff.Coff.init(allocator, coff_file);
+        coff_obj.* = .{ .allocator = allocator };
 
         var di = ModuleDebugInfo{
             .base_address = undefined,
@@ -828,18 +828,21 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo
             .debug_data = undefined,
         };
 
-        try di.coff.loadHeader();
-        try di.coff.loadSections();
-        if (di.coff.getSection(".debug_info")) |sec| {
+        // TODO convert to Windows' memory-mapped file API
+        const file_len = math.cast(usize, try coff_file.getEndPos()) orelse math.maxInt(usize);
+        const data = try coff_file.readToEndAlloc(allocator, file_len);
+        try di.coff.parse(data);
+
+        if (di.coff.getSectionByName(".debug_info")) |sec| {
             // This coff file has embedded DWARF debug info
             _ = sec;
             // TODO: free the section data slices
-            const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null;
-            const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null;
-            const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null;
-            const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null;
-            const debug_line_str_data = di.coff.getSectionData(".debug_line_str", allocator) catch null;
-            const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null;
+            const debug_info_data = di.coff.getSectionDataAlloc(".debug_info", allocator) catch null;
+            const debug_abbrev_data = di.coff.getSectionDataAlloc(".debug_abbrev", allocator) catch null;
+            const debug_str_data = di.coff.getSectionDataAlloc(".debug_str", allocator) catch null;
+            const debug_line_data = di.coff.getSectionDataAlloc(".debug_line", allocator) catch null;
+            const debug_line_str_data = di.coff.getSectionDataAlloc(".debug_line_str", allocator) catch null;
+            const debug_ranges_data = di.coff.getSectionDataAlloc(".debug_ranges", allocator) catch null;
 
             var dwarf = DW.DwarfInfo{
                 .endian = native_endian,
@@ -1628,7 +1631,7 @@ pub const ModuleDebugInfo = switch (native_os) {
 
             switch (self.debug_data) {
                 .dwarf => |*dwarf| {
-                    const dwarf_address = relocated_address + self.coff.pe_header.image_base;
+                    const dwarf_address = relocated_address + self.coff.getImageBase();
                     return getSymbolFromDwarf(allocator, dwarf_address, dwarf);
                 },
                 .pdb => {
@@ -1636,13 +1639,14 @@ pub const ModuleDebugInfo = switch (native_os) {
                 },
             }
 
-            var coff_section: *coff.Section = undefined;
+            var coff_section: *align(1) const coff.SectionHeader = undefined;
             const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
-                if (sect_contrib.Section > self.coff.sections.items.len) continue;
+                const sections = self.coff.getSectionHeaders();
+                if (sect_contrib.Section > sections.len) continue;
                 // Remember that SectionContribEntry.Section is 1-based.
-                coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
+                coff_section = &sections[sect_contrib.Section - 1];
 
-                const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
+                const vaddr_start = coff_section.virtual_address + sect_contrib.Offset;
                 const vaddr_end = vaddr_start + sect_contrib.Size;
                 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
                     break sect_contrib.ModuleIndex;
@@ -1658,11 +1662,11 @@ pub const ModuleDebugInfo = switch (native_os) {
 
             const symbol_name = self.debug_data.pdb.getSymbolName(
                 module,
-                relocated_address - coff_section.header.virtual_address,
+                relocated_address - coff_section.virtual_address,
             ) orelse "???";
             const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
                 module,
-                relocated_address - coff_section.header.virtual_address,
+                relocated_address - coff_section.virtual_address,
             );
 
             return SymbolInfo{
src/link/Coff.zig
@@ -204,15 +204,18 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
     index += 2;
 
     // Characteristics
-    var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
+    var characteristics: std.coff.CoffHeaderFlags = .{
+        .DEBUG_STRIPPED = 1, // TODO remove debug info stripped flag when necessary
+        .RELOCS_STRIPPED = 1,
+    };
     if (options.output_mode == .Exe) {
-        characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
+        characteristics.EXECUTABLE_IMAGE = 1;
     }
     switch (self.ptr_width) {
-        .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
-        .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
+        .p32 => characteristics.@"32BIT_MACHINE" = 1,
+        .p64 => characteristics.LARGE_ADDRESS_AWARE = 1,
     }
-    mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
+    mem.writeIntLittle(u16, hdr_data[index..][0..2], @bitCast(u16, characteristics));
     index += 2;
 
     assert(index == 20);
@@ -352,7 +355,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
     mem.set(u8, hdr_data[index..][0..12], 0);
     index += 12;
     // Section flags
-    mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
+    mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
+        .CNT_INITIALIZED_DATA = 1,
+        .MEM_READ = 1,
+    }));
     index += 4;
     // Then, the .text section
     hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
@@ -378,11 +384,12 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
     mem.set(u8, hdr_data[index..][0..12], 0);
     index += 12;
     // Section flags
-    mem.writeIntLittle(
-        u32,
-        hdr_data[index..][0..4],
-        std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
-    );
+    mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
+        .CNT_CODE = 1,
+        .MEM_EXECUTE = 1,
+        .MEM_READ = 1,
+        .MEM_WRITE = 1,
+    }));
     index += 4;
 
     assert(index == optional_header_size + section_table_size);