mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 16:45:27 +00:00
be56c67b14
Sources:
* https://learn.microsoft.com/en-us/windows/win32/sysinfo/image-file-machine-constants
* 88a28aa575/include/winnt.h (L2720-L2758)
1618 lines
52 KiB
Zig
1618 lines
52 KiB
Zig
const std = @import("std.zig");
|
|
const assert = std.debug.assert;
|
|
const mem = std.mem;
|
|
|
|
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 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
|
|
|
|
pub const DirectoryEntry = enum(u16) {
|
|
/// Export Directory
|
|
EXPORT = 0,
|
|
|
|
/// Import Directory
|
|
IMPORT = 1,
|
|
|
|
/// Resource Directory
|
|
RESOURCE = 2,
|
|
|
|
/// Exception Directory
|
|
EXCEPTION = 3,
|
|
|
|
/// Security Directory
|
|
SECURITY = 4,
|
|
|
|
/// Base Relocation Table
|
|
BASERELOC = 5,
|
|
|
|
/// Debug Directory
|
|
DEBUG = 6,
|
|
|
|
/// Architecture Specific Data
|
|
ARCHITECTURE = 7,
|
|
|
|
/// RVA of GP
|
|
GLOBALPTR = 8,
|
|
|
|
/// TLS Directory
|
|
TLS = 9,
|
|
|
|
/// Load Configuration Directory
|
|
LOAD_CONFIG = 10,
|
|
|
|
/// Bound Import Directory in headers
|
|
BOUND_IMPORT = 11,
|
|
|
|
/// Import Address Table
|
|
IAT = 12,
|
|
|
|
/// Delay Load Import Descriptors
|
|
DELAY_IMPORT = 13,
|
|
|
|
/// COM Runtime descriptor
|
|
COM_DESCRIPTOR = 14,
|
|
|
|
_,
|
|
};
|
|
|
|
pub const ImageDataDirectory = extern struct {
|
|
virtual_address: u32,
|
|
size: u32,
|
|
};
|
|
|
|
pub const BaseRelocationDirectoryEntry = extern struct {
|
|
/// The image base plus the page RVA is added to each offset to create the VA where the base relocation must be applied.
|
|
page_rva: u32,
|
|
|
|
/// The total number of bytes in the base relocation block, including the Page RVA and Block Size fields and the Type/Offset fields that follow.
|
|
block_size: u32,
|
|
};
|
|
|
|
pub const BaseRelocation = packed struct {
|
|
/// Stored in the remaining 12 bits of the WORD, an offset from the starting address that was specified in the Page RVA field for the block.
|
|
/// This offset specifies where the base relocation is to be applied.
|
|
offset: u12,
|
|
|
|
/// Stored in the high 4 bits of the WORD, a value that indicates the type of base relocation to be applied.
|
|
type: BaseRelocationType,
|
|
};
|
|
|
|
pub const BaseRelocationType = enum(u4) {
|
|
/// The base relocation is skipped. This type can be used to pad a block.
|
|
ABSOLUTE = 0,
|
|
|
|
/// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word.
|
|
HIGH = 1,
|
|
|
|
/// The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word.
|
|
LOW = 2,
|
|
|
|
/// The base relocation applies all 32 bits of the difference to the 32-bit field at offset.
|
|
HIGHLOW = 3,
|
|
|
|
/// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset.
|
|
/// The 16-bit field represents the high value of a 32-bit word.
|
|
/// The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation.
|
|
/// This means that this base relocation occupies two slots.
|
|
HIGHADJ = 4,
|
|
|
|
/// When the machine type is MIPS, the base relocation applies to a MIPS jump instruction.
|
|
MIPS_JMPADDR = 5,
|
|
|
|
/// This relocation is meaningful only when the machine type is ARM or Thumb.
|
|
/// The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair.
|
|
// ARM_MOV32 = 5,
|
|
|
|
/// This relocation is only meaningful when the machine type is RISC-V.
|
|
/// The base relocation applies to the high 20 bits of a 32-bit absolute address.
|
|
// RISCV_HIGH20 = 5,
|
|
|
|
/// Reserved, must be zero.
|
|
RESERVED = 6,
|
|
|
|
/// This relocation is meaningful only when the machine type is Thumb.
|
|
/// The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair.
|
|
THUMB_MOV32 = 7,
|
|
|
|
/// This relocation is only meaningful when the machine type is RISC-V.
|
|
/// The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format.
|
|
// RISCV_LOW12I = 7,
|
|
|
|
/// This relocation is only meaningful when the machine type is RISC-V.
|
|
/// The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format.
|
|
RISCV_LOW12S = 8,
|
|
|
|
/// This relocation is only meaningful when the machine type is LoongArch 32-bit.
|
|
/// The base relocation applies to a 32-bit absolute address formed in two consecutive instructions.
|
|
// LOONGARCH32_MARK_LA = 8,
|
|
|
|
/// This relocation is only meaningful when the machine type is LoongArch 64-bit.
|
|
/// The base relocation applies to a 64-bit absolute address formed in four consecutive instructions.
|
|
// LOONGARCH64_MARK_LA = 8,
|
|
|
|
/// The relocation is only meaningful when the machine type is MIPS.
|
|
/// The base relocation applies to a MIPS16 jump instruction.
|
|
MIPS_JMPADDR16 = 9,
|
|
|
|
/// The base relocation applies the difference to the 64-bit field at offset.
|
|
DIR64 = 10,
|
|
|
|
_,
|
|
};
|
|
|
|
pub const DebugDirectoryEntry = extern struct {
|
|
characteristics: u32,
|
|
time_date_stamp: u32,
|
|
major_version: u16,
|
|
minor_version: u16,
|
|
type: DebugType,
|
|
size_of_data: u32,
|
|
address_of_raw_data: u32,
|
|
pointer_to_raw_data: u32,
|
|
};
|
|
|
|
pub const DebugType = enum(u32) {
|
|
UNKNOWN = 0,
|
|
COFF = 1,
|
|
CODEVIEW = 2,
|
|
FPO = 3,
|
|
MISC = 4,
|
|
EXCEPTION = 5,
|
|
FIXUP = 6,
|
|
OMAP_TO_SRC = 7,
|
|
OMAP_FROM_SRC = 8,
|
|
BORLAND = 9,
|
|
RESERVED10 = 10,
|
|
VC_FEATURE = 12,
|
|
POGO = 13,
|
|
ILTCG = 14,
|
|
MPX = 15,
|
|
REPRO = 16,
|
|
EX_DLLCHARACTERISTICS = 20,
|
|
|
|
_,
|
|
};
|
|
|
|
pub const ImportDirectoryEntry = extern struct {
|
|
/// The RVA of the import lookup table.
|
|
/// This table contains a name or ordinal for each import.
|
|
/// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.)
|
|
import_lookup_table_rva: u32,
|
|
|
|
/// The stamp that is set to zero until the image is bound.
|
|
/// After the image is bound, this field is set to the time/data stamp of the DLL.
|
|
time_date_stamp: u32,
|
|
|
|
/// The index of the first forwarder reference.
|
|
forwarder_chain: u32,
|
|
|
|
/// The address of an ASCII string that contains the name of the DLL.
|
|
/// This address is relative to the image base.
|
|
name_rva: u32,
|
|
|
|
/// The RVA of the import address table.
|
|
/// The contents of this table are identical to the contents of the import lookup table until the image is bound.
|
|
import_address_table_rva: u32,
|
|
};
|
|
|
|
pub const ImportLookupEntry32 = struct {
|
|
pub const ByName = packed struct {
|
|
name_table_rva: u31,
|
|
flag: u1 = 0,
|
|
};
|
|
|
|
pub const ByOrdinal = packed struct {
|
|
ordinal_number: u16,
|
|
unused: u15 = 0,
|
|
flag: u1 = 1,
|
|
};
|
|
|
|
const mask = 0x80000000;
|
|
|
|
pub fn getImportByName(raw: u32) ?ByName {
|
|
if (mask & raw != 0) return null;
|
|
return @as(ByName, @bitCast(raw));
|
|
}
|
|
|
|
pub fn getImportByOrdinal(raw: u32) ?ByOrdinal {
|
|
if (mask & raw == 0) return null;
|
|
return @as(ByOrdinal, @bitCast(raw));
|
|
}
|
|
};
|
|
|
|
pub const ImportLookupEntry64 = struct {
|
|
pub const ByName = packed struct {
|
|
name_table_rva: u31,
|
|
unused: u32 = 0,
|
|
flag: u1 = 0,
|
|
};
|
|
|
|
pub const ByOrdinal = packed struct {
|
|
ordinal_number: u16,
|
|
unused: u47 = 0,
|
|
flag: u1 = 1,
|
|
};
|
|
|
|
const mask = 0x8000000000000000;
|
|
|
|
pub fn getImportByName(raw: u64) ?ByName {
|
|
if (mask & raw != 0) return null;
|
|
return @as(ByName, @bitCast(raw));
|
|
}
|
|
|
|
pub fn getImportByOrdinal(raw: u64) ?ByOrdinal {
|
|
if (mask & raw == 0) return null;
|
|
return @as(ByOrdinal, @bitCast(raw));
|
|
}
|
|
};
|
|
|
|
/// Every name ends with a NULL byte. IF the NULL byte does not fall on
|
|
/// 2byte boundary, the entry structure is padded to ensure 2byte alignment.
|
|
pub const ImportHintNameEntry = extern struct {
|
|
/// An index into the export name pointer table.
|
|
/// A match is attempted first with this value. If it fails, a binary search is performed on the DLL's export name pointer table.
|
|
hint: u16,
|
|
|
|
/// Pointer to NULL terminated ASCII name.
|
|
/// Variable length...
|
|
name: [1]u8,
|
|
};
|
|
|
|
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 setAlignment(self: *SectionHeader, new_alignment: u16) void {
|
|
assert(new_alignment > 0 and new_alignment <= 8192);
|
|
self.flags.ALIGN = @intCast(std.math.log2(new_alignment));
|
|
}
|
|
|
|
pub fn isCode(self: SectionHeader) bool {
|
|
return self.flags.CNT_CODE == 0b1;
|
|
}
|
|
|
|
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,
|
|
|
|
_reserved_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.readInt(u32, self.name[4..8], .little);
|
|
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,
|
|
/// Alpha AXP, 32-bit address space
|
|
ALPHA = 0x184,
|
|
/// Alpha 64, 64-bit address space
|
|
ALPHA64 = 0x284,
|
|
/// Matsushita AM33
|
|
AM33 = 0x1d3,
|
|
/// x64
|
|
X64 = 0x8664,
|
|
/// ARM little endian
|
|
ARM = 0x1c0,
|
|
/// ARM64 little endian
|
|
ARM64 = 0xaa64,
|
|
/// ARM64EC
|
|
ARM64EC = 0xa641,
|
|
/// ARM64X
|
|
ARM64X = 0xa64e,
|
|
/// ARM Thumb-2 little endian
|
|
ARMNT = 0x1c4,
|
|
/// CEE
|
|
CEE = 0xc0ee,
|
|
/// CEF
|
|
CEF = 0xcef,
|
|
/// Hybrid PE
|
|
CHPE_X86 = 0x3a64,
|
|
/// EFI byte code
|
|
EBC = 0xebc,
|
|
/// Intel 386 or later processors and compatible processors
|
|
I386 = 0x14c,
|
|
/// Intel Itanium processor family
|
|
IA64 = 0x200,
|
|
/// LoongArch32
|
|
LOONGARCH32 = 0x6232,
|
|
/// LoongArch64
|
|
LOONGARCH64 = 0x6264,
|
|
/// Mitsubishi M32R little endian
|
|
M32R = 0x9041,
|
|
/// MIPS16
|
|
MIPS16 = 0x266,
|
|
/// MIPS with FPU
|
|
MIPSFPU = 0x366,
|
|
/// MIPS16 with FPU
|
|
MIPSFPU16 = 0x466,
|
|
/// Power PC little endian
|
|
POWERPC = 0x1f0,
|
|
/// Power PC with floating point support
|
|
POWERPCFP = 0x1f1,
|
|
/// MIPS little endian
|
|
R3000 = 0x162,
|
|
/// MIPS little endian
|
|
R4000 = 0x166,
|
|
/// MIPS little endian
|
|
R10000 = 0x168,
|
|
/// RISC-V 32-bit address space
|
|
RISCV32 = 0x5032,
|
|
/// RISC-V 64-bit address space
|
|
RISCV64 = 0x5064,
|
|
/// RISC-V 128-bit address space
|
|
RISCV128 = 0x5128,
|
|
/// Hitachi SH3
|
|
SH3 = 0x1a2,
|
|
/// Hitachi SH3 DSP
|
|
SH3DSP = 0x1a3,
|
|
/// SH3E little-endian
|
|
SH3E = 0x1a4,
|
|
/// Hitachi SH4
|
|
SH4 = 0x1a6,
|
|
/// Hitachi SH5
|
|
SH5 = 0x1a8,
|
|
/// Thumb
|
|
Thumb = 0x1c2,
|
|
/// Infineon
|
|
TRICORE = 0x520,
|
|
/// MIPS little-endian WCE v2
|
|
WCEMIPSV2 = 0x169,
|
|
|
|
_,
|
|
|
|
pub fn fromTargetCpuArch(arch: std.Target.Cpu.Arch) MachineType {
|
|
return switch (arch) {
|
|
.arm => .ARM,
|
|
.powerpc => .POWERPC,
|
|
.riscv32 => .RISCV32,
|
|
.thumb => .Thumb,
|
|
.x86 => .I386,
|
|
.aarch64 => .ARM64,
|
|
.riscv64 => .RISCV64,
|
|
.x86_64 => .X64,
|
|
.loongarch32 => .LOONGARCH32,
|
|
.loongarch64 => .LOONGARCH64,
|
|
// there's cases we don't (yet) handle
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn toTargetCpuArch(machine_type: MachineType) ?std.Target.Cpu.Arch {
|
|
return switch (machine_type) {
|
|
.ARM => .arm,
|
|
.POWERPC => .powerpc,
|
|
.RISCV32 => .riscv32,
|
|
.Thumb => .thumb,
|
|
.I386 => .x86,
|
|
.ARM64 => .aarch64,
|
|
.RISCV64 => .riscv64,
|
|
.X64 => .x86_64,
|
|
.LOONGARCH32 => .loongarch32,
|
|
.LOONGARCH64 => .loongarch64,
|
|
// there's cases we don't (yet) handle
|
|
else => null,
|
|
};
|
|
}
|
|
};
|
|
|
|
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 {
|
|
data: []const u8,
|
|
// Set if `data` is backed by the image as loaded by the loader
|
|
is_loaded: bool,
|
|
is_image: bool,
|
|
coff_header_offset: usize,
|
|
|
|
guid: [16]u8 = undefined,
|
|
age: u32 = undefined,
|
|
|
|
// The lifetime of `data` must be longer than the lifetime of the returned Coff
|
|
pub fn init(data: []const u8, is_loaded: bool) !Coff {
|
|
const pe_pointer_offset = 0x3C;
|
|
const pe_magic = "PE\x00\x00";
|
|
|
|
var stream = std.io.fixedBufferStream(data);
|
|
const reader = stream.reader();
|
|
try stream.seekTo(pe_pointer_offset);
|
|
const coff_header_offset = try reader.readInt(u32, .little);
|
|
try stream.seekTo(coff_header_offset);
|
|
var buf: [4]u8 = undefined;
|
|
try reader.readNoEof(&buf);
|
|
const is_image = mem.eql(u8, pe_magic, &buf);
|
|
|
|
var coff = @This(){
|
|
.data = data,
|
|
.is_image = is_image,
|
|
.is_loaded = is_loaded,
|
|
.coff_header_offset = coff_header_offset,
|
|
};
|
|
|
|
// Do some basic validation upfront
|
|
if (is_image) {
|
|
coff.coff_header_offset = coff.coff_header_offset + 4;
|
|
const coff_header = coff.getCoffHeader();
|
|
if (coff_header.size_of_optional_header == 0) return error.MissingPEHeader;
|
|
}
|
|
|
|
// 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.
|
|
|
|
return coff;
|
|
}
|
|
|
|
pub fn getPdbPath(self: *Coff) !?[]const u8 {
|
|
assert(self.is_image);
|
|
|
|
const data_dirs = self.getDataDirectories();
|
|
if (@intFromEnum(DirectoryEntry.DEBUG) >= data_dirs.len) return null;
|
|
|
|
const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)];
|
|
var stream = std.io.fixedBufferStream(self.data);
|
|
const reader = stream.reader();
|
|
|
|
if (self.is_loaded) {
|
|
try stream.seekTo(debug_dir.virtual_address);
|
|
} else {
|
|
// Find what section the debug_dir is in, in order to convert the RVA to a file offset
|
|
for (self.getSectionHeaders()) |*sect| {
|
|
if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) {
|
|
try stream.seekTo(sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address));
|
|
break;
|
|
}
|
|
} else return error.InvalidDebugDirectory;
|
|
}
|
|
|
|
// 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;
|
|
while (i < debug_dir_entry_count) : (i += 1) {
|
|
const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
|
|
if (debug_dir_entry.type == .CODEVIEW) {
|
|
const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data;
|
|
try stream.seekTo(dir_offset);
|
|
break;
|
|
}
|
|
} else return null;
|
|
|
|
var cv_signature: [4]u8 = undefined; // CodeView signature
|
|
try reader.readNoEof(cv_signature[0..]);
|
|
// 'RSDS' indicates PDB70 format, used by lld.
|
|
if (!mem.eql(u8, &cv_signature, "RSDS"))
|
|
return error.InvalidPEMagic;
|
|
try reader.readNoEof(self.guid[0..]);
|
|
self.age = try reader.readInt(u32, .little);
|
|
|
|
// Finally read the null-terminated string.
|
|
const start = reader.context.pos;
|
|
const len = std.mem.indexOfScalar(u8, self.data[start..], 0) orelse return null;
|
|
return self.data[start .. start + len];
|
|
}
|
|
|
|
pub fn getCoffHeader(self: Coff) CoffHeader {
|
|
return @as(*align(1) const CoffHeader, @ptrCast(self.data[self.coff_header_offset..][0..@sizeOf(CoffHeader)])).*;
|
|
}
|
|
|
|
pub fn getOptionalHeader(self: Coff) OptionalHeader {
|
|
assert(self.is_image);
|
|
const offset = self.coff_header_offset + @sizeOf(CoffHeader);
|
|
return @as(*align(1) const OptionalHeader, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader)])).*;
|
|
}
|
|
|
|
pub fn getOptionalHeader32(self: Coff) OptionalHeaderPE32 {
|
|
assert(self.is_image);
|
|
const offset = self.coff_header_offset + @sizeOf(CoffHeader);
|
|
return @as(*align(1) const OptionalHeaderPE32, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeaderPE32)])).*;
|
|
}
|
|
|
|
pub fn getOptionalHeader64(self: Coff) OptionalHeaderPE64 {
|
|
assert(self.is_image);
|
|
const offset = self.coff_header_offset + @sizeOf(CoffHeader);
|
|
return @as(*align(1) const OptionalHeaderPE64, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeaderPE64)])).*;
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
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 @as([*]align(1) const ImageDataDirectory, @ptrCast(self.data[offset..]))[0..self.getNumberOfDataDirectories()];
|
|
}
|
|
|
|
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) error{InvalidStrtabSize}!?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.readInt(u32, self.data[offset..][0..4], .little);
|
|
if ((offset + size) > self.data.len) return error.InvalidStrtabSize;
|
|
|
|
return Strtab{ .buffer = self.data[offset..][0..size] };
|
|
}
|
|
|
|
pub fn strtabRequired(self: *const Coff) bool {
|
|
for (self.getSectionHeaders()) |*sect_hdr| if (sect_hdr.getName() == null) return true;
|
|
return false;
|
|
}
|
|
|
|
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 @as([*]align(1) const SectionHeader, @ptrCast(self.data.ptr + offset))[0..coff_header.number_of_sections];
|
|
}
|
|
|
|
pub fn getSectionHeadersAlloc(self: *const Coff, allocator: mem.Allocator) ![]SectionHeader {
|
|
const section_headers = self.getSectionHeaders();
|
|
const out_buff = try allocator.alloc(SectionHeader, section_headers.len);
|
|
for (out_buff, 0..) |*section_header, i| {
|
|
section_header.* = section_headers[i];
|
|
}
|
|
|
|
return out_buff;
|
|
}
|
|
|
|
pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) error{InvalidStrtabSize}![]const u8 {
|
|
const name = sect_hdr.getName() orelse blk: {
|
|
const strtab = (try 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| {
|
|
const section_name = self.getSectionName(sect) catch |e| switch (e) {
|
|
error.InvalidStrtabSize => continue, //ignore invalid(?) strtab entries - see also GitHub issue #15238
|
|
};
|
|
if (mem.eql(u8, section_name, name)) {
|
|
return sect;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 {
|
|
const offset = if (self.is_loaded) sec.virtual_address else sec.pointer_to_raw_data;
|
|
return self.data[offset..][0..sec.virtual_size];
|
|
}
|
|
|
|
pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {
|
|
const section_data = self.getSectionData(sec);
|
|
return allocator.dupe(u8, section_data);
|
|
}
|
|
};
|
|
|
|
pub const Symtab = struct {
|
|
buffer: []const u8,
|
|
|
|
pub fn len(self: Symtab) usize {
|
|
return @divExact(self.buffer.len, Symbol.sizeOf());
|
|
}
|
|
|
|
pub const Tag = enum {
|
|
symbol,
|
|
debug_info,
|
|
func_def,
|
|
weak_ext,
|
|
file_def,
|
|
sect_def,
|
|
};
|
|
|
|
pub const Record = union(Tag) {
|
|
symbol: Symbol,
|
|
debug_info: DebugInfoDefinition,
|
|
func_def: FunctionDefinition,
|
|
weak_ext: WeakExternalDefinition,
|
|
file_def: FileDefinition,
|
|
sect_def: SectionDefinition,
|
|
};
|
|
|
|
/// Lives as long as Symtab instance.
|
|
pub 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) },
|
|
};
|
|
}
|
|
|
|
fn asSymbol(raw: []const u8) Symbol {
|
|
return .{
|
|
.name = raw[0..8].*,
|
|
.value = mem.readInt(u32, raw[8..12], .little),
|
|
.section_number = @as(SectionNumber, @enumFromInt(mem.readInt(u16, raw[12..14], .little))),
|
|
.type = @as(SymType, @bitCast(mem.readInt(u16, raw[14..16], .little))),
|
|
.storage_class = @as(StorageClass, @enumFromInt(raw[16])),
|
|
.number_of_aux_symbols = raw[17],
|
|
};
|
|
}
|
|
|
|
fn asDebugInfo(raw: []const u8) DebugInfoDefinition {
|
|
return .{
|
|
.unused_1 = raw[0..4].*,
|
|
.linenumber = mem.readInt(u16, raw[4..6], .little),
|
|
.unused_2 = raw[6..12].*,
|
|
.pointer_to_next_function = mem.readInt(u32, raw[12..16], .little),
|
|
.unused_3 = raw[16..18].*,
|
|
};
|
|
}
|
|
|
|
fn asFuncDef(raw: []const u8) FunctionDefinition {
|
|
return .{
|
|
.tag_index = mem.readInt(u32, raw[0..4], .little),
|
|
.total_size = mem.readInt(u32, raw[4..8], .little),
|
|
.pointer_to_linenumber = mem.readInt(u32, raw[8..12], .little),
|
|
.pointer_to_next_function = mem.readInt(u32, raw[12..16], .little),
|
|
.unused = raw[16..18].*,
|
|
};
|
|
}
|
|
|
|
fn asWeakExtDef(raw: []const u8) WeakExternalDefinition {
|
|
return .{
|
|
.tag_index = mem.readInt(u32, raw[0..4], .little),
|
|
.flag = @as(WeakExternalFlag, @enumFromInt(mem.readInt(u32, raw[4..8], .little))),
|
|
.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.readInt(u32, raw[0..4], .little),
|
|
.number_of_relocations = mem.readInt(u16, raw[4..6], .little),
|
|
.number_of_linenumbers = mem.readInt(u16, raw[6..8], .little),
|
|
.checksum = mem.readInt(u32, raw[8..12], .little),
|
|
.number = mem.readInt(u16, raw[12..14], .little),
|
|
.selection = @as(ComdatSelection, @enumFromInt(raw[14])),
|
|
.unused = raw[15..18].*,
|
|
};
|
|
}
|
|
|
|
pub const Slice = struct {
|
|
buffer: []const u8,
|
|
num: usize,
|
|
count: usize = 0,
|
|
|
|
/// Lives as long as Symtab instance.
|
|
pub 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;
|
|
}
|
|
};
|
|
|
|
pub 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 };
|
|
}
|
|
};
|
|
|
|
pub const Strtab = struct {
|
|
buffer: []const u8,
|
|
|
|
pub fn get(self: Strtab, off: u32) []const u8 {
|
|
assert(off < self.buffer.len);
|
|
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.ptr + off)), 0);
|
|
}
|
|
};
|
|
|
|
pub const ImportHeader = extern struct {
|
|
sig1: MachineType,
|
|
sig2: u16,
|
|
version: u16,
|
|
machine: MachineType,
|
|
time_date_stamp: u32,
|
|
size_of_data: u32,
|
|
hint: u16,
|
|
types: packed struct {
|
|
type: ImportType,
|
|
name_type: ImportNameType,
|
|
reserved: u11,
|
|
},
|
|
};
|
|
|
|
pub const ImportType = enum(u2) {
|
|
/// Executable code.
|
|
CODE = 0,
|
|
/// Data.
|
|
DATA = 1,
|
|
/// Specified as CONST in .def file.
|
|
CONST = 2,
|
|
_,
|
|
};
|
|
|
|
pub const ImportNameType = enum(u3) {
|
|
/// The import is by ordinal. This indicates that the value in the Ordinal/Hint
|
|
/// field of the import header is the import's ordinal. If this constant is not
|
|
/// specified, then the Ordinal/Hint field should always be interpreted as the import's hint.
|
|
ORDINAL = 0,
|
|
/// The import name is identical to the public symbol name.
|
|
NAME = 1,
|
|
/// The import name is the public symbol name, but skipping the leading ?, @, or optionally _.
|
|
NAME_NOPREFIX = 2,
|
|
/// The import name is the public symbol name, but skipping the leading ?, @, or optionally _,
|
|
/// and truncating at the first @.
|
|
NAME_UNDECORATE = 3,
|
|
/// https://github.com/llvm/llvm-project/pull/83211
|
|
NAME_EXPORTAS = 4,
|
|
_,
|
|
};
|
|
|
|
pub const Relocation = extern struct {
|
|
virtual_address: u32,
|
|
symbol_table_index: u32,
|
|
type: u16,
|
|
};
|
|
|
|
pub const ImageRelAmd64 = enum(u16) {
|
|
/// The relocation is ignored.
|
|
absolute = 0,
|
|
|
|
/// The 64-bit VA of the relocation target.
|
|
addr64 = 1,
|
|
|
|
/// The 32-bit VA of the relocation target.
|
|
addr32 = 2,
|
|
|
|
/// The 32-bit address without an image base.
|
|
addr32nb = 3,
|
|
|
|
/// The 32-bit relative address from the byte following the relocation.
|
|
rel32 = 4,
|
|
|
|
/// The 32-bit address relative to byte distance 1 from the relocation.
|
|
rel32_1 = 5,
|
|
|
|
/// The 32-bit address relative to byte distance 2 from the relocation.
|
|
rel32_2 = 6,
|
|
|
|
/// The 32-bit address relative to byte distance 3 from the relocation.
|
|
rel32_3 = 7,
|
|
|
|
/// The 32-bit address relative to byte distance 4 from the relocation.
|
|
rel32_4 = 8,
|
|
|
|
/// The 32-bit address relative to byte distance 5 from the relocation.
|
|
rel32_5 = 9,
|
|
|
|
/// The 16-bit section index of the section that contains the target.
|
|
/// This is used to support debugging information.
|
|
section = 10,
|
|
|
|
/// The 32-bit offset of the target from the beginning of its section.
|
|
/// This is used to support debugging information and static thread local storage.
|
|
secrel = 11,
|
|
|
|
/// A 7-bit unsigned offset from the base of the section that contains the target.
|
|
secrel7 = 12,
|
|
|
|
/// CLR tokens.
|
|
token = 13,
|
|
|
|
/// A 32-bit signed span-dependent value emitted into the object.
|
|
srel32 = 14,
|
|
|
|
/// A pair that must immediately follow every span-dependent value.
|
|
pair = 15,
|
|
|
|
/// A 32-bit signed span-dependent value that is applied at link time.
|
|
sspan32 = 16,
|
|
|
|
_,
|
|
};
|
|
|
|
pub const ImageRelArm64 = enum(u16) {
|
|
/// The relocation is ignored.
|
|
absolute = 0,
|
|
|
|
/// The 32-bit VA of the target.
|
|
addr32 = 1,
|
|
|
|
/// The 32-bit RVA of the target.
|
|
addr32nb = 2,
|
|
|
|
/// The 26-bit relative displacement to the target, for B and BL instructions.
|
|
branch26 = 3,
|
|
|
|
/// The page base of the target, for ADRP instruction.
|
|
pagebase_rel21 = 4,
|
|
|
|
/// The 21-bit relative displacement to the target, for instruction ADR.
|
|
rel21 = 5,
|
|
|
|
/// The 12-bit page offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
|
|
pageoffset_12a = 6,
|
|
|
|
/// The 12-bit page offset of the target, for instruction LDR (indexed, unsigned immediate).
|
|
pageoffset_12l = 7,
|
|
|
|
/// The 32-bit offset of the target from the beginning of its section.
|
|
/// This is used to support debugging information and static thread local storage.
|
|
secrel = 8,
|
|
|
|
/// Bit 0:11 of section offset of the target for instructions ADD/ADDS (immediate) with zero shift.
|
|
low12a = 9,
|
|
|
|
/// Bit 12:23 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
|
|
high12a = 10,
|
|
|
|
/// Bit 0:11 of section offset of the target, for instruction LDR (indexed, unsigned immediate).
|
|
low12l = 11,
|
|
|
|
/// CLR token.
|
|
token = 12,
|
|
|
|
/// The 16-bit section index of the section that contains the target.
|
|
/// This is used to support debugging information.
|
|
section = 13,
|
|
|
|
/// The 64-bit VA of the relocation target.
|
|
addr64 = 14,
|
|
|
|
/// The 19-bit offset to the relocation target, for conditional B instruction.
|
|
branch19 = 15,
|
|
|
|
/// The 14-bit offset to the relocation target, for instructions TBZ and TBNZ.
|
|
branch14 = 16,
|
|
|
|
/// The 32-bit relative address from the byte following the relocation.
|
|
rel32 = 17,
|
|
|
|
_,
|
|
};
|