2019-03-02 21:46:04 +00:00
const std = @import ( " std.zig " ) ;
2022-08-22 17:48:14 +00:00
const assert = std . debug . assert ;
2018-07-21 18:30:11 +00:00
const mem = std . mem ;
2022-08-22 17:48:14 +00:00
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
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 ,
} ;
2022-08-28 10:14:15 +00:00
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-28 10:14:15 +00:00
} ;
pub const ImageDataDirectory = extern struct {
virtual_address : u32 ,
size : u32 ,
} ;
2022-08-30 21:07:28 +00:00
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.
2022-11-15 04:44:31 +00:00
type : BaseRelocationType ,
2022-08-30 21:07:28 +00:00
} ;
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-30 21:07:28 +00:00
} ;
2022-08-22 17:48:14 +00:00
pub const DebugDirectoryEntry = extern struct {
2022-08-28 10:14:15 +00:00
characteristics : u32 ,
2022-08-22 17:48:14 +00:00
time_date_stamp : u32 ,
major_version : u16 ,
minor_version : u16 ,
2022-11-15 04:44:31 +00:00
type : DebugType ,
2022-08-22 17:48:14 +00:00
size_of_data : u32 ,
address_of_raw_data : u32 ,
pointer_to_raw_data : u32 ,
} ;
2022-08-28 10:14:15 +00:00
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
2022-08-31 17:30:51 +00:00
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 ;
2023-06-22 17:46:56 +00:00
return @as ( ByName , @bitCast ( raw ) ) ;
2022-08-31 17:30:51 +00:00
}
pub fn getImportByOrdinal ( raw : u32 ) ? ByOrdinal {
if ( mask & raw = = 0 ) return null ;
2023-06-22 17:46:56 +00:00
return @as ( ByOrdinal , @bitCast ( raw ) ) ;
2022-08-31 17:30:51 +00:00
}
} ;
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 ;
2023-06-22 17:46:56 +00:00
return @as ( ByName , @bitCast ( raw ) ) ;
2022-08-31 17:30:51 +00:00
}
pub fn getImportByOrdinal ( raw : u64 ) ? ByOrdinal {
if ( mask & raw = = 0 ) return null ;
2023-06-22 17:46:56 +00:00
return @as ( ByOrdinal , @bitCast ( raw ) ) ;
2022-08-31 17:30:51 +00:00
}
} ;
/// 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 ,
} ;
2022-08-22 17:48:14 +00:00
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 ;
}
2022-08-26 12:11:14 +00:00
pub fn setAlignment ( self : * SectionHeader , new_alignment : u16 ) void {
assert ( new_alignment > 0 and new_alignment < = 8192 ) ;
2024-07-07 10:16:14 +00:00
self . flags . ALIGN = @intCast ( std . math . log2 ( new_alignment ) ) ;
2022-08-26 12:11:14 +00:00
}
pub fn isCode ( self : SectionHeader ) bool {
return self . flags . CNT_CODE = = 0b1 ;
}
2022-08-22 17:48:14 +00:00
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 ,
2024-07-09 21:25:42 +00:00
_reserved_2 : u1 = 0 ,
2022-08-22 17:48:14 +00:00
/// 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 ,
2022-11-15 04:44:31 +00:00
type : SymType ,
2022-08-22 17:48:14 +00:00
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 ;
2023-10-31 20:02:38 +00:00
const offset = std . mem . readInt ( u32 , self . name [ 4 . . 8 ] , . little ) ;
2022-08-22 17:48:14 +00:00
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
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 ,
2024-03-22 13:04:17 +00:00
_ ,
2022-08-22 17:48:14 +00:00
} ;
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 ,
} ;
2018-07-21 18:30:11 +00:00
2020-09-03 15:24:42 +00:00
pub const MachineType = enum ( u16 ) {
2024-08-09 19:19:24 +00:00
UNKNOWN = 0x0 ,
2024-07-24 03:49:31 +00:00
/// Alpha AXP, 32-bit address space
ALPHA = 0x184 ,
/// Alpha 64, 64-bit address space
ALPHA64 = 0x284 ,
2020-09-03 15:24:42 +00:00
/// Matsushita AM33
AM33 = 0x1d3 ,
/// x64
X64 = 0x8664 ,
/// ARM little endian
ARM = 0x1c0 ,
/// ARM64 little endian
ARM64 = 0xaa64 ,
2024-03-22 14:05:32 +00:00
/// ARM64EC
ARM64EC = 0xa641 ,
2024-07-24 03:49:31 +00:00
/// ARM64X
ARM64X = 0xa64e ,
2020-09-03 15:24:42 +00:00
/// ARM Thumb-2 little endian
ARMNT = 0x1c4 ,
2024-07-24 03:49:31 +00:00
/// CEE
CEE = 0xc0ee ,
/// CEF
CEF = 0xcef ,
/// Hybrid PE
CHPE_X86 = 0x3a64 ,
2020-09-03 15:24:42 +00:00
/// EFI byte code
EBC = 0xebc ,
/// Intel 386 or later processors and compatible processors
I386 = 0x14c ,
/// Intel Itanium processor family
IA64 = 0x200 ,
2024-07-12 07:47:32 +00:00
/// LoongArch32
LOONGARCH32 = 0x6232 ,
/// LoongArch64
LOONGARCH64 = 0x6264 ,
2020-09-03 15:24:42 +00:00
/// 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
2024-07-24 03:49:31 +00:00
R3000 = 0x162 ,
/// MIPS little endian
2020-09-03 15:24:42 +00:00
R4000 = 0x166 ,
2024-07-24 03:49:31 +00:00
/// MIPS little endian
R10000 = 0x168 ,
2020-09-03 15:24:42 +00:00
/// 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 ,
2024-07-24 03:49:31 +00:00
/// SH3E little-endian
SH3E = 0x1a4 ,
2020-09-03 15:24:42 +00:00
/// Hitachi SH4
SH4 = 0x1a6 ,
/// Hitachi SH5
SH5 = 0x1a8 ,
/// Thumb
2024-08-09 19:19:24 +00:00
THUMB = 0x1c2 ,
2024-07-24 03:49:31 +00:00
/// Infineon
TRICORE = 0x520 ,
2020-09-03 15:24:42 +00:00
/// MIPS little-endian WCE v2
WCEMIPSV2 = 0x169 ,
2022-03-10 22:17:07 +00:00
2024-03-22 13:04:17 +00:00
_ ,
2020-09-03 15:24:42 +00:00
} ;
2018-11-13 13:08:37 +00:00
pub const CoffError = error {
2018-07-21 18:30:11 +00:00
InvalidPEMagic ,
InvalidPEHeader ,
InvalidMachine ,
2022-08-22 17:48:14 +00:00
MissingPEHeader ,
2018-07-21 18:30:11 +00:00
MissingCoffSection ,
2021-06-18 18:36:28 +00:00
MissingStringTable ,
2018-07-21 18:30:11 +00:00
} ;
2019-07-28 17:03:36 +00:00
// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
2018-11-13 13:08:37 +00:00
pub const Coff = struct {
2022-09-25 01:50:15 +00:00
data : [ ] const u8 ,
2023-08-15 06:22:22 +00:00
// Set if `data` is backed by the image as loaded by the loader
is_loaded : bool ,
2022-09-25 01:50:15 +00:00
is_image : bool ,
coff_header_offset : usize ,
2018-07-21 18:30:11 +00:00
2022-08-22 17:48:14 +00:00
guid : [ 16 ] u8 = undefined ,
age : u32 = undefined ,
2019-06-29 18:56:23 +00:00
2022-09-25 01:50:15 +00:00
// The lifetime of `data` must be longer than the lifetime of the returned Coff
2023-08-15 06:22:22 +00:00
pub fn init ( data : [ ] const u8 , is_loaded : bool ) ! Coff {
2022-08-22 17:48:14 +00:00
const pe_pointer_offset = 0x3C ;
const pe_magic = " PE \x00 \x00 " ;
2018-07-21 18:30:11 +00:00
2022-09-25 01:50:15 +00:00
var stream = std . io . fixedBufferStream ( data ) ;
2022-08-22 17:48:14 +00:00
const reader = stream . reader ( ) ;
try stream . seekTo ( pe_pointer_offset ) ;
2023-11-10 05:27:17 +00:00
const coff_header_offset = try reader . readInt ( u32 , . little ) ;
2022-08-22 17:48:14 +00:00
try stream . seekTo ( coff_header_offset ) ;
var buf : [ 4 ] u8 = undefined ;
try reader . readNoEof ( & buf ) ;
2022-09-25 01:50:15 +00:00
const is_image = mem . eql ( u8 , pe_magic , & buf ) ;
var coff = @This ( ) {
. data = data ,
. is_image = is_image ,
2023-08-15 06:22:22 +00:00
. is_loaded = is_loaded ,
2022-09-25 01:50:15 +00:00
. coff_header_offset = coff_header_offset ,
} ;
2018-07-21 18:30:11 +00:00
2022-08-22 17:48:14 +00:00
// Do some basic validation upfront
2022-09-25 01:50:15 +00:00
if ( is_image ) {
coff . coff_header_offset = coff . coff_header_offset + 4 ;
const coff_header = coff . getCoffHeader ( ) ;
2022-08-26 12:13:57 +00:00
if ( coff_header . size_of_optional_header = = 0 ) return error . MissingPEHeader ;
2021-06-18 18:36:28 +00:00
}
2022-08-22 17:48:14 +00:00
// 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.
2022-09-25 01:50:15 +00:00
return coff ;
2018-07-21 18:30:11 +00:00
}
2024-01-27 07:11:03 +00:00
pub fn getPdbPath ( self : * Coff ) ! ? [ ] const u8 {
2022-08-22 17:48:14 +00:00
assert ( self . is_image ) ;
2019-07-28 17:03:36 +00:00
2022-08-22 17:48:14 +00:00
const data_dirs = self . getDataDirectories ( ) ;
2023-08-15 06:44:34 +00:00
if ( @intFromEnum ( DirectoryEntry . DEBUG ) > = data_dirs . len ) return null ;
2018-07-21 18:30:11 +00:00
2023-08-15 06:44:34 +00:00
const debug_dir = data_dirs [ @intFromEnum ( DirectoryEntry . DEBUG ) ] ;
2022-08-22 17:48:14 +00:00
var stream = std . io . fixedBufferStream ( self . data ) ;
const reader = stream . reader ( ) ;
2023-08-15 06:22:22 +00:00
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 ;
}
2019-07-28 17:03:36 +00:00
// 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 ;
2023-08-15 06:44:34 +00:00
while ( i < debug_dir_entry_count ) : ( i + = 1 ) {
2022-08-22 17:48:14 +00:00
const debug_dir_entry = try reader . readStruct ( DebugDirectoryEntry ) ;
2022-08-28 10:14:15 +00:00
if ( debug_dir_entry . type = = . CODEVIEW ) {
2023-08-15 06:22:22 +00:00
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 ) ;
2023-08-15 06:44:34 +00:00
break ;
2019-07-28 17:03:36 +00:00
}
2023-08-15 06:44:34 +00:00
} else return null ;
2018-07-21 18:30:11 +00:00
var cv_signature : [ 4 ] u8 = undefined ; // CodeView signature
2022-08-22 17:48:14 +00:00
try reader . readNoEof ( cv_signature [ 0 . . ] ) ;
2018-07-21 18:30:11 +00:00
// 'RSDS' indicates PDB70 format, used by lld.
2019-11-30 02:55:27 +00:00
if ( ! mem . eql ( u8 , & cv_signature , " RSDS " ) )
2018-07-21 18:30:11 +00:00
return error . InvalidPEMagic ;
2022-08-22 17:48:14 +00:00
try reader . readNoEof ( self . guid [ 0 . . ] ) ;
2023-10-31 20:02:38 +00:00
self . age = try reader . readInt ( u32 , . little ) ;
2018-07-21 18:30:11 +00:00
// Finally read the null-terminated string.
2024-01-27 07:11:03 +00:00
const start = reader . context . pos ;
const len = std . mem . indexOfScalar ( u8 , self . data [ start . . ] , 0 ) orelse return null ;
return self . data [ start . . start + len ] ;
2018-07-21 18:30:11 +00:00
}
2022-08-22 17:48:14 +00:00
pub fn getCoffHeader ( self : Coff ) CoffHeader {
2023-06-22 17:46:56 +00:00
return @as ( * align ( 1 ) const CoffHeader , @ptrCast ( self . data [ self . coff_header_offset . . ] [ 0 . . @sizeOf ( CoffHeader ) ] ) ) . * ;
2022-08-22 17:48:14 +00:00
}
2018-07-21 18:30:11 +00:00
2022-08-22 17:48:14 +00:00
pub fn getOptionalHeader ( self : Coff ) OptionalHeader {
assert ( self . is_image ) ;
const offset = self . coff_header_offset + @sizeOf ( CoffHeader ) ;
2023-06-22 17:46:56 +00:00
return @as ( * align ( 1 ) const OptionalHeader , @ptrCast ( self . data [ offset . . ] [ 0 . . @sizeOf ( OptionalHeader ) ] ) ) . * ;
2022-08-22 17:48:14 +00:00
}
2018-07-21 18:30:11 +00:00
2022-08-22 17:48:14 +00:00
pub fn getOptionalHeader32 ( self : Coff ) OptionalHeaderPE32 {
assert ( self . is_image ) ;
const offset = self . coff_header_offset + @sizeOf ( CoffHeader ) ;
2023-06-22 17:46:56 +00:00
return @as ( * align ( 1 ) const OptionalHeaderPE32 , @ptrCast ( self . data [ offset . . ] [ 0 . . @sizeOf ( OptionalHeaderPE32 ) ] ) ) . * ;
2022-08-22 17:48:14 +00:00
}
2018-07-21 18:30:11 +00:00
2022-08-22 17:48:14 +00:00
pub fn getOptionalHeader64 ( self : Coff ) OptionalHeaderPE64 {
assert ( self . is_image ) ;
const offset = self . coff_header_offset + @sizeOf ( CoffHeader ) ;
2023-06-22 17:46:56 +00:00
return @as ( * align ( 1 ) const OptionalHeaderPE64 , @ptrCast ( self . data [ offset . . ] [ 0 . . @sizeOf ( OptionalHeaderPE64 ) ] ) ) . * ;
2022-08-22 17:48:14 +00:00
}
2018-07-21 18:30:11 +00:00
2022-08-22 17:48:14 +00:00
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
} ;
}
2021-06-18 18:36:28 +00:00
2022-08-22 17:48:14 +00:00
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
} ;
}
2021-06-18 18:36:28 +00:00
2022-08-22 17:48:14 +00:00
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 ;
2023-06-22 17:46:56 +00:00
return @as ( [ * ] align ( 1 ) const ImageDataDirectory , @ptrCast ( self . data [ offset . . ] ) ) [ 0 . . self . getNumberOfDataDirectories ( ) ] ;
2022-08-22 17:48:14 +00:00
}
2021-06-18 18:36:28 +00:00
2022-08-22 17:48:14 +00:00
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 ] } ;
}
2023-04-10 20:41:34 +00:00
pub fn getStrtab ( self : * const Coff ) error { InvalidStrtabSize } ! ? Strtab {
2022-08-22 17:48:14 +00:00
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 ;
2023-10-31 20:02:38 +00:00
const size = mem . readInt ( u32 , self . data [ offset . . ] [ 0 . . 4 ] , . little ) ;
2023-04-10 20:41:34 +00:00
if ( ( offset + size ) > self . data . len ) return error . InvalidStrtabSize ;
2022-08-24 08:05:24 +00:00
return Strtab { . buffer = self . data [ offset . . ] [ 0 . . size ] } ;
2022-08-22 17:48:14 +00:00
}
2023-06-29 00:37:45 +00:00
pub fn strtabRequired ( self : * const Coff ) bool {
for ( self . getSectionHeaders ( ) ) | * sect_hdr | if ( sect_hdr . getName ( ) = = null ) return true ;
return false ;
}
2022-08-22 17:48:14 +00:00
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 ;
2023-06-22 17:46:56 +00:00
return @as ( [ * ] align ( 1 ) const SectionHeader , @ptrCast ( self . data . ptr + offset ) ) [ 0 . . coff_header . number_of_sections ] ;
2018-07-21 18:30:11 +00:00
}
2022-09-25 01:50:15 +00:00
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 ) ;
2023-02-18 16:02:57 +00:00
for ( out_buff , 0 . . ) | * section_header , i | {
2022-09-25 01:50:15 +00:00
section_header . * = section_headers [ i ] ;
}
return out_buff ;
}
2023-04-10 20:41:34 +00:00
pub fn getSectionName ( self : * const Coff , sect_hdr : * align ( 1 ) const SectionHeader ) error { InvalidStrtabSize } ! [ ] const u8 {
2022-08-22 17:48:14 +00:00
const name = sect_hdr . getName ( ) orelse blk : {
2023-04-10 20:41:34 +00:00
const strtab = ( try self . getStrtab ( ) ) . ? ;
2022-08-22 17:48:14 +00:00
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 | {
2023-04-10 20:41:34 +00:00
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 ) ) {
2022-08-22 17:48:14 +00:00
return sect ;
2018-07-21 18:30:11 +00:00
}
}
return null ;
}
2021-06-18 18:40:40 +00:00
2023-07-16 06:00:17 +00:00
pub fn getSectionData ( self : * const Coff , sec : * align ( 1 ) const SectionHeader ) [ ] const u8 {
2023-08-15 06:22:22 +00:00
const offset = if ( self . is_loaded ) sec . virtual_address else sec . pointer_to_raw_data ;
return self . data [ offset . . ] [ 0 . . sec . virtual_size ] ;
2022-09-25 01:50:15 +00:00
}
2023-07-16 06:00:17 +00:00
pub fn getSectionDataAlloc ( self : * const Coff , sec : * align ( 1 ) const SectionHeader , allocator : mem . Allocator ) ! [ ] u8 {
const section_data = self . getSectionData ( sec ) ;
2022-09-25 01:50:15 +00:00
return allocator . dupe ( u8 , section_data ) ;
2021-06-18 18:40:40 +00:00
}
2022-08-30 13:51:46 +00:00
} ;
2018-07-21 18:30:11 +00:00
2022-08-30 13:51:46 +00:00
pub const Symtab = struct {
buffer : [ ] const u8 ,
2018-07-21 18:30:11 +00:00
2022-08-30 13:51:46 +00:00
pub fn len ( self : Symtab ) usize {
return @divExact ( self . buffer . len , Symbol . sizeOf ( ) ) ;
}
2019-07-28 17:03:36 +00:00
2022-08-30 13:51:46 +00:00
pub const Tag = enum {
symbol ,
debug_info ,
2023-09-01 17:48:24 +00:00
func_def ,
2022-08-30 13:51:46 +00:00
weak_ext ,
file_def ,
sect_def ,
} ;
2018-07-21 18:30:11 +00:00
2022-08-30 13:51:46 +00:00
pub const Record = union ( Tag ) {
symbol : Symbol ,
debug_info : DebugInfoDefinition ,
func_def : FunctionDefinition ,
weak_ext : WeakExternalDefinition ,
file_def : FileDefinition ,
sect_def : SectionDefinition ,
} ;
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
/// 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 ) } ,
} ;
}
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
fn asSymbol ( raw : [ ] const u8 ) Symbol {
return . {
. name = raw [ 0 . . 8 ] . * ,
2023-10-31 20:02:38 +00:00
. 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 ) ) ) ,
2023-06-22 17:46:56 +00:00
. storage_class = @as ( StorageClass , @enumFromInt ( raw [ 16 ] ) ) ,
2022-08-30 13:51:46 +00:00
. number_of_aux_symbols = raw [ 17 ] ,
} ;
}
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
fn asDebugInfo ( raw : [ ] const u8 ) DebugInfoDefinition {
return . {
. unused_1 = raw [ 0 . . 4 ] . * ,
2023-10-31 20:02:38 +00:00
. linenumber = mem . readInt ( u16 , raw [ 4 . . 6 ] , . little ) ,
2022-08-30 13:51:46 +00:00
. unused_2 = raw [ 6 . . 12 ] . * ,
2023-10-31 20:02:38 +00:00
. pointer_to_next_function = mem . readInt ( u32 , raw [ 12 . . 16 ] , . little ) ,
2022-08-30 13:51:46 +00:00
. unused_3 = raw [ 16 . . 18 ] . * ,
} ;
}
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
fn asFuncDef ( raw : [ ] const u8 ) FunctionDefinition {
return . {
2023-10-31 20:02:38 +00:00
. 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 ) ,
2022-08-30 13:51:46 +00:00
. unused = raw [ 16 . . 18 ] . * ,
} ;
}
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
fn asWeakExtDef ( raw : [ ] const u8 ) WeakExternalDefinition {
return . {
2023-10-31 20:02:38 +00:00
. tag_index = mem . readInt ( u32 , raw [ 0 . . 4 ] , . little ) ,
. flag = @as ( WeakExternalFlag , @enumFromInt ( mem . readInt ( u32 , raw [ 4 . . 8 ] , . little ) ) ) ,
2022-08-30 13:51:46 +00:00
. unused = raw [ 8 . . 18 ] . * ,
} ;
}
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
fn asFileDef ( raw : [ ] const u8 ) FileDefinition {
return . {
. file_name = raw [ 0 . . 18 ] . * ,
2022-08-22 17:48:14 +00:00
} ;
2022-08-30 13:51:46 +00:00
}
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
fn asSectDef ( raw : [ ] const u8 ) SectionDefinition {
return . {
2023-10-31 20:02:38 +00:00
. 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 ) ,
2023-06-22 17:46:56 +00:00
. selection = @as ( ComdatSelection , @enumFromInt ( raw [ 14 ] ) ) ,
2022-08-30 13:51:46 +00:00
. unused = raw [ 15 . . 18 ] . * ,
} ;
}
2018-07-21 18:30:11 +00:00
2022-08-30 13:51:46 +00:00
pub const Slice = struct {
2022-08-22 17:48:14 +00:00
buffer : [ ] const u8 ,
2022-08-30 13:51:46 +00:00
num : usize ,
count : usize = 0 ,
2022-08-22 17:48:14 +00:00
2022-08-30 13:51:46 +00:00
/// 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 ;
2022-08-22 17:48:14 +00:00
}
} ;
2022-08-30 13:51:46 +00:00
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 ) ;
2023-06-22 17:46:56 +00:00
return mem . sliceTo ( @as ( [ * : 0 ] const u8 , @ptrCast ( self . buffer . ptr + off ) ) , 0 ) ;
2022-08-30 13:51:46 +00:00
}
2018-08-29 23:00:24 +00:00
} ;
2024-03-22 14:12:11 +00:00
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 ,
2024-03-28 17:36:58 +00:00
/// https://github.com/llvm/llvm-project/pull/83211
NAME_EXPORTAS = 4 ,
2024-03-22 14:12:11 +00:00
_ ,
} ;
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 ,
_ ,
} ;