zig/lib/std/coff.zig
2025-10-10 22:47:47 -07:00

1922 lines
76 KiB
Zig

const std = @import("std.zig");
const assert = std.debug.assert;
const mem = std.mem;
pub const Header = extern struct {
/// The number that identifies the type of target machine.
machine: IMAGE.FILE.MACHINE,
/// 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: Header.Flags,
pub const Flags = packed struct(u16) {
/// 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: bool = false,
/// 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: bool = false,
/// COFF line numbers have been removed. This flag is deprecated and should be zero.
LINE_NUMS_STRIPPED: bool = false,
/// COFF symbol table entries for local symbols have been removed.
/// This flag is deprecated and should be zero.
LOCAL_SYMS_STRIPPED: bool = false,
/// Obsolete. Aggressively trim working set.
/// This flag is deprecated for Windows 2000 and later and must be zero.
AGGRESSIVE_WS_TRIM: bool = false,
/// Application can handle > 2-GB addresses.
LARGE_ADDRESS_AWARE: bool = false,
/// This flag is reserved for future use.
RESERVED: bool = false,
/// 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: bool = false,
/// Machine is based on a 32-bit-word architecture.
@"32BIT_MACHINE": bool = false,
/// Debugging information is removed from the image file.
DEBUG_STRIPPED: bool = false,
/// If the image is on removable media, fully load it and copy it to the swap file.
REMOVABLE_RUN_FROM_SWAP: bool = false,
/// If the image is on network media, fully load it and copy it to the swap file.
NET_RUN_FROM_SWAP: bool = false,
/// The image file is a system file, not a user program.
SYSTEM: bool = false,
/// 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: bool = false,
/// The file should be run only on a uniprocessor machine.
UP_SYSTEM_ONLY: bool = false,
/// Big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero.
BYTES_REVERSED_HI: bool = false,
};
};
// 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 = @intFromEnum(OptionalHeader.Magic.PE32);
pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC = @intFromEnum(OptionalHeader.Magic.@"PE32+");
pub const DllFlags = packed struct(u16) {
_reserved_0: u5 = 0,
/// Image can handle a high entropy 64-bit virtual address space.
HIGH_ENTROPY_VA: bool = false,
/// DLL can be relocated at load time.
DYNAMIC_BASE: bool = false,
/// Code Integrity checks are enforced.
FORCE_INTEGRITY: bool = false,
/// Image is NX compatible.
NX_COMPAT: bool = false,
/// Isolation aware, but do not isolate the image.
NO_ISOLATION: bool = false,
/// Does not use structured exception (SE) handling. No SE handler may be called in this image.
NO_SEH: bool = false,
/// Do not bind the image.
NO_BIND: bool = false,
/// Image must execute in an AppContainer.
APPCONTAINER: bool = false,
/// A WDM driver.
WDM_DRIVER: bool = false,
/// Image supports Control Flow Guard.
GUARD_CF: bool = false,
/// Terminal Server aware.
TERMINAL_SERVER_AWARE: bool = false,
};
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: OptionalHeader.Magic,
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 Magic = enum(u16) {
PE32 = 0x10b,
@"PE32+" = 0x20b,
_,
};
pub const PE32 = extern struct {
standard: OptionalHeader,
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 @"PE32+" = extern struct {
standard: OptionalHeader,
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 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(u16) {
/// 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(u32) {
name_table_rva: u31,
flag: u1 = 0,
};
pub const ByOrdinal = packed struct(u32) {
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(u64) {
name_table_rva: u31,
unused: u32 = 0,
flag: u1 = 0,
};
pub const ByOrdinal = packed struct(u64) {
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: SectionHeader.Flags,
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 {
return self.flags.ALIGN.toByteUnits();
}
pub fn setAlignment(self: *SectionHeader, new_alignment: u16) void {
self.flags.ALIGN = .fromByteUnits(new_alignment);
}
pub fn isCode(self: SectionHeader) bool {
return self.flags.CNT_CODE;
}
pub fn isComdat(self: SectionHeader) bool {
return self.flags.LNK_COMDAT;
}
pub const Flags = packed struct(u32) {
SCALE_INDEX: bool = false,
unused1: u2 = 0,
/// The section should not be padded to the next boundary.
/// This flag is obsolete and is replaced by `.ALIGN = .@"1BYTES"`.
/// This is valid only for object files.
TYPE_NO_PAD: bool = false,
unused4: u1 = 0,
/// The section contains executable code.
CNT_CODE: bool = false,
/// The section contains initialized data.
CNT_INITIALIZED_DATA: bool = false,
/// The section contains uninitialized data.
CNT_UNINITIALIZED_DATA: bool = false,
/// Reserved for future use.
LNK_OTHER: bool = false,
/// The section contains comments or other information.
/// The .drectve section has this type.
/// This is valid for object files only.
LNK_INFO: bool = false,
unused10: u1 = 0,
/// The section will not become part of the image.
/// This is valid only for object files.
LNK_REMOVE: bool = false,
/// The section contains COMDAT data.
/// For more information, see COMDAT Sections (Object Only).
/// This is valid only for object files.
LNK_COMDAT: bool = false,
unused13: u2 = 0,
union14: packed union {
mask: u1,
/// The section contains data referenced through the global pointer (GP).
GPREL: bool,
MEM_FARDATA: bool,
} = .{ .mask = 0 },
unused15: u1 = 0,
union16: packed union {
mask: u1,
MEM_PURGEABLE: bool,
MEM_16BIT: bool,
} = .{ .mask = 0 },
/// Reserved for future use.
MEM_LOCKED: bool = false,
/// Reserved for future use.
MEM_PRELOAD: bool = false,
ALIGN: SectionHeader.Flags.Align = .NONE,
/// The section contains extended relocations.
LNK_NRELOC_OVFL: bool = false,
/// The section can be discarded as needed.
MEM_DISCARDABLE: bool = false,
/// The section cannot be cached.
MEM_NOT_CACHED: bool = false,
/// The section is not pageable.
MEM_NOT_PAGED: bool = false,
/// The section can be shared in memory.
MEM_SHARED: bool = false,
/// The section can be executed as code.
MEM_EXECUTE: bool = false,
/// The section can be read.
MEM_READ: bool = false,
/// The section can be written to.
MEM_WRITE: bool = false,
pub const Align = enum(u4) {
NONE = 0,
@"1BYTES" = 1,
@"2BYTES" = 2,
@"4BYTES" = 3,
@"8BYTES" = 4,
@"16BYTES" = 5,
@"32BYTES" = 6,
@"64BYTES" = 7,
@"128BYTES" = 8,
@"256BYTES" = 9,
@"512BYTES" = 10,
@"1024BYTES" = 11,
@"2048BYTES" = 12,
@"4096BYTES" = 13,
@"8192BYTES" = 14,
_,
pub fn toByteUnits(a: Align) ?u16 {
if (a == .NONE) return null;
return @as(u16, 1) << (@intFromEnum(a) - 1);
}
pub fn fromByteUnits(n: u16) Align {
std.debug.assert(std.math.isPowerOfTwo(n));
return @enumFromInt(@ctz(n) + 1);
}
};
};
};
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(u16) {
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,
pub fn sizeOf() usize {
return 18;
}
};
// 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 Error = 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) error{ EndOfStream, MissingPEHeader }!Coff {
const pe_pointer_offset = 0x3C;
const pe_magic = "PE\x00\x00";
if (data.len < pe_pointer_offset + 4) return error.EndOfStream;
const header_offset = mem.readInt(u32, data[pe_pointer_offset..][0..4], .little);
if (data.len < header_offset + 4) return error.EndOfStream;
const is_image = mem.eql(u8, data[header_offset..][0..4], pe_magic);
const coff: Coff = .{
.data = data,
.is_image = is_image,
.is_loaded = is_loaded,
.coff_header_offset = o: {
if (is_image) break :o header_offset + 4;
break :o header_offset;
},
};
// Do some basic validation upfront
if (is_image) {
const coff_header = coff.getHeader();
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(IMAGE.DIRECTORY_ENTRY.DEBUG) >= data_dirs.len) return null;
const debug_dir = data_dirs[@intFromEnum(IMAGE.DIRECTORY_ENTRY.DEBUG)];
var reader: std.Io.Reader = .fixed(self.data);
if (self.is_loaded) {
reader.seek = 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) {
reader.seek = 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.takeStruct(DebugDirectoryEntry, .little);
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;
reader.seek = dir_offset;
break;
}
} else return null;
const code_view_signature = try reader.takeArray(4);
// 'RSDS' indicates PDB70 format, used by lld.
if (!mem.eql(u8, code_view_signature, "RSDS"))
return error.InvalidPEMagic;
try reader.readSliceAll(self.guid[0..]);
self.age = try reader.takeInt(u32, .little);
// Finally read the null-terminated string.
const start = reader.seek;
const len = std.mem.indexOfScalar(u8, self.data[start..], 0) orelse return null;
return self.data[start .. start + len];
}
pub fn getHeader(self: Coff) Header {
return @as(*align(1) const Header, @ptrCast(self.data[self.coff_header_offset..][0..@sizeOf(Header)])).*;
}
pub fn getOptionalHeader(self: Coff) OptionalHeader {
assert(self.is_image);
const offset = self.coff_header_offset + @sizeOf(Header);
return @as(*align(1) const OptionalHeader, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader)])).*;
}
pub fn getOptionalHeader32(self: Coff) OptionalHeader.PE32 {
assert(self.is_image);
const offset = self.coff_header_offset + @sizeOf(Header);
return @as(*align(1) const OptionalHeader.PE32, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader.PE32)])).*;
}
pub fn getOptionalHeader64(self: Coff) OptionalHeader.@"PE32+" {
assert(self.is_image);
const offset = self.coff_header_offset + @sizeOf(Header);
return @as(*align(1) const OptionalHeader.@"PE32+", @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader.@"PE32+")])).*;
}
pub fn getImageBase(self: Coff) u64 {
const hdr = self.getOptionalHeader();
return switch (@intFromEnum(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 (@intFromEnum(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 (@intFromEnum(hdr.magic)) {
IMAGE_NT_OPTIONAL_HDR32_MAGIC => @sizeOf(OptionalHeader.PE32),
IMAGE_NT_OPTIONAL_HDR64_MAGIC => @sizeOf(OptionalHeader.@"PE32+"),
else => unreachable, // We assume we have validated the header already
};
const offset = self.coff_header_offset + @sizeOf(Header) + 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.getHeader();
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.getHeader();
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.getHeader();
const offset = self.coff_header_offset + @sizeOf(Header) + 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 {
/// Must be IMAGE_FILE_MACHINE_UNKNOWN
sig1: IMAGE.FILE.MACHINE = .UNKNOWN,
/// Must be 0xFFFF
sig2: u16 = 0xFFFF,
version: u16,
machine: IMAGE.FILE.MACHINE,
time_date_stamp: u32,
size_of_data: u32,
hint: u16,
types: packed struct(u16) {
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 IMAGE = struct {
pub const DIRECTORY_ENTRY = enum(u32) {
/// 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,
/// must be zero
RESERVED = 15,
_,
pub const len = @typeInfo(IMAGE.DIRECTORY_ENTRY).@"enum".fields.len;
};
pub const FILE = struct {
/// Machine Types
/// The Machine field has one of the following values, which specify the CPU type.
/// An image file can be run only on the specified machine or on a system that emulates the specified machine.
pub const MACHINE = enum(u16) {
/// The content of this field is assumed to be applicable to any machine type
UNKNOWN = 0x0,
/// Alpha AXP, 32-bit address space
ALPHA = 0x184,
/// Alpha 64, 64-bit address space
ALPHA64 = 0x284,
/// Matsushita AM33
AM33 = 0x1d3,
/// x64
AMD64 = 0x8664,
/// ARM little endian
ARM = 0x1c0,
/// ARM64 little endian
ARM64 = 0xaa64,
/// ABI that enables interoperability between native ARM64 and emulated x64 code.
ARM64EC = 0xA641,
/// Binary format that allows both native ARM64 and ARM64EC code to coexist in the same file.
ARM64X = 0xA64E,
/// ARM Thumb-2 little endian
ARMNT = 0x1c4,
/// EFI byte code
EBC = 0xebc,
/// Intel 386 or later processors and compatible processors
I386 = 0x14c,
/// Intel Itanium processor family
IA64 = 0x200,
/// LoongArch 32-bit processor family
LOONGARCH32 = 0x6232,
/// LoongArch 64-bit processor family
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 I compatible 32-bit big endian
R3000BE = 0x160,
/// MIPS I compatible 32-bit little endian
R3000 = 0x162,
/// MIPS III compatible 64-bit little endian
R4000 = 0x166,
/// MIPS IV compatible 64-bit 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,
/// Hitachi SH4
SH4 = 0x1a6,
/// Hitachi SH5
SH5 = 0x1a8,
/// Thumb
THUMB = 0x1c2,
/// MIPS little-endian WCE v2
WCEMIPSV2 = 0x169,
_,
/// AXP 64 (Same as Alpha 64)
pub const AXP64: IMAGE.FILE.MACHINE = .ALPHA64;
};
};
pub const REL = struct {
/// x64 Processors
/// The following relocation type indicators are defined for x64 and compatible processors.
pub const AMD64 = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The 64-bit VA of the relocation target.
ADDR64 = 0x0001,
/// The 32-bit VA of the relocation target.
ADDR32 = 0x0002,
/// The 32-bit address without an image base (RVA).
ADDR32NB = 0x0003,
/// The 32-bit relative address from the byte following the relocation.
REL32 = 0x0004,
/// The 32-bit address relative to byte distance 1 from the relocation.
REL32_1 = 0x0005,
/// The 32-bit address relative to byte distance 2 from the relocation.
REL32_2 = 0x0006,
/// The 32-bit address relative to byte distance 3 from the relocation.
REL32_3 = 0x0007,
/// The 32-bit address relative to byte distance 4 from the relocation.
REL32_4 = 0x0008,
/// The 32-bit address relative to byte distance 5 from the relocation.
REL32_5 = 0x0009,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
SECTION = 0x000A,
/// 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 = 0x000B,
/// A 7-bit unsigned offset from the base of the section that contains the target.
SECREL7 = 0x000C,
/// CLR tokens.
TOKEN = 0x000D,
/// A 32-bit signed span-dependent value emitted into the object.
SREL32 = 0x000E,
/// A pair that must immediately follow every span-dependent value.
PAIR = 0x000F,
/// A 32-bit signed span-dependent value that is applied at link time.
SSPAN32 = 0x0010,
_,
};
/// ARM Processors
/// The following relocation type indicators are defined for ARM processors.
pub const ARM = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The 32-bit VA of the target.
ADDR32 = 0x0001,
/// The 32-bit RVA of the target.
ADDR32NB = 0x0002,
/// The 24-bit relative displacement to the target.
BRANCH24 = 0x0003,
/// The reference to a subroutine call.
/// The reference consists of two 16-bit instructions with 11-bit offsets.
BRANCH11 = 0x0004,
/// The 32-bit relative address from the byte following the relocation.
REL32 = 0x000A,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
SECTION = 0x000E,
/// 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 = 0x000F,
/// The 32-bit VA of the target.
/// This relocation is applied using a MOVW instruction for the low 16 bits followed by a MOVT for the high 16 bits.
MOV32 = 0x0010,
/// The 32-bit VA of the target.
/// This relocation is applied using a MOVW instruction for the low 16 bits followed by a MOVT for the high 16 bits.
THUMB_MOV32 = 0x0011,
/// The instruction is fixed up with the 21-bit relative displacement to the 2-byte aligned target.
/// The least significant bit of the displacement is always zero and is not stored.
/// This relocation corresponds to a Thumb-2 32-bit conditional B instruction.
THUMB_BRANCH20 = 0x0012,
Unused = 0x0013,
/// The instruction is fixed up with the 25-bit relative displacement to the 2-byte aligned target.
/// The least significant bit of the displacement is zero and is not stored.This relocation corresponds to a Thumb-2 B instruction.
THUMB_BRANCH24 = 0x0014,
/// The instruction is fixed up with the 25-bit relative displacement to the 4-byte aligned target.
/// The low 2 bits of the displacement are zero and are not stored.
/// This relocation corresponds to a Thumb-2 BLX instruction.
THUMB_BLX23 = 0x0015,
/// The relocation is valid only when it immediately follows a ARM_REFHI or THUMB_REFHI.
/// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
PAIR = 0x0016,
_,
};
/// ARM64 Processors
/// The following relocation type indicators are defined for ARM64 processors.
pub const ARM64 = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The 32-bit VA of the target.
ADDR32 = 0x0001,
/// The 32-bit RVA of the target.
ADDR32NB = 0x0002,
/// The 26-bit relative displacement to the target, for B and BL instructions.
BRANCH26 = 0x0003,
/// The page base of the target, for ADRP instruction.
PAGEBASE_REL21 = 0x0004,
/// The 12-bit relative displacement to the target, for instruction ADR
REL21 = 0x0005,
/// The 12-bit page offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
PAGEOFFSET_12A = 0x0006,
/// The 12-bit page offset of the target, for instruction LDR (indexed, unsigned immediate).
PAGEOFFSET_12L = 0x0007,
/// 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 = 0x0008,
/// Bit 0:11 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
SECREL_LOW12A = 0x0009,
/// Bit 12:23 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
SECREL_HIGH12A = 0x000A,
/// Bit 0:11 of section offset of the target, for instruction LDR (indexed, unsigned immediate).
SECREL_LOW12L = 0x000B,
/// CLR token.
TOKEN = 0x000C,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
SECTION = 0x000D,
/// The 64-bit VA of the relocation target.
ADDR64 = 0x000E,
/// The 19-bit offset to the relocation target, for conditional B instruction.
BRANCH19 = 0x000F,
/// The 14-bit offset to the relocation target, for instructions TBZ and TBNZ.
BRANCH14 = 0x0010,
/// The 32-bit relative address from the byte following the relocation.
REL32 = 0x0011,
_,
};
/// Hitachi SuperH Processors
/// The following relocation type indicators are defined for SH3 and SH4 processors.
/// SH5-specific relocations are noted as SHM (SH Media).
pub const SH = enum(u16) {
/// The relocation is ignored.
@"3_ABSOLUTE" = 0x0000,
/// A reference to the 16-bit location that contains the VA of the target symbol.
@"3_DIRECT16" = 0x0001,
/// The 32-bit VA of the target symbol.
@"3_DIRECT32" = 0x0002,
/// A reference to the 8-bit location that contains the VA of the target symbol.
@"3_DIRECT8" = 0x0003,
/// A reference to the 8-bit instruction that contains the effective 16-bit VA of the target symbol.
@"3_DIRECT8_WORD" = 0x0004,
/// A reference to the 8-bit instruction that contains the effective 32-bit VA of the target symbol.
@"3_DIRECT8_LONG" = 0x0005,
/// A reference to the 8-bit location whose low 4 bits contain the VA of the target symbol.
@"3_DIRECT4" = 0x0006,
/// A reference to the 8-bit instruction whose low 4 bits contain the effective 16-bit VA of the target symbol.
@"3_DIRECT4_WORD" = 0x0007,
/// A reference to the 8-bit instruction whose low 4 bits contain the effective 32-bit VA of the target symbol.
@"3_DIRECT4_LONG" = 0x0008,
/// A reference to the 8-bit instruction that contains the effective 16-bit relative offset of the target symbol.
@"3_PCREL8_WORD" = 0x0009,
/// A reference to the 8-bit instruction that contains the effective 32-bit relative offset of the target symbol.
@"3_PCREL8_LONG" = 0x000A,
/// A reference to the 16-bit instruction whose low 12 bits contain the effective 16-bit relative offset of the target symbol.
@"3_PCREL12_WORD" = 0x000B,
/// A reference to a 32-bit location that is the VA of the section that contains the target symbol.
@"3_STARTOF_SECTION" = 0x000C,
/// A reference to the 32-bit location that is the size of the section that contains the target symbol.
@"3_SIZEOF_SECTION" = 0x000D,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
@"3_SECTION" = 0x000E,
/// 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.
@"3_SECREL" = 0x000F,
/// The 32-bit RVA of the target symbol.
@"3_DIRECT32_NB" = 0x0010,
/// GP relative.
@"3_GPREL4_LONG" = 0x0011,
/// CLR token.
@"3_TOKEN" = 0x0012,
/// The offset from the current instruction in longwords.
/// If the NOMODE bit is not set, insert the inverse of the low bit at bit 32 to select PTA or PTB.
M_PCRELPT = 0x0013,
/// The low 16 bits of the 32-bit address.
M_REFLO = 0x0014,
/// The high 16 bits of the 32-bit address.
M_REFHALF = 0x0015,
/// The low 16 bits of the relative address.
M_RELLO = 0x0016,
/// The high 16 bits of the relative address.
M_RELHALF = 0x0017,
/// The relocation is valid only when it immediately follows a REFHALF, RELHALF, or RELLO relocation.
/// The SymbolTableIndex field of the relocation contains a displacement and not an index into the symbol table.
M_PAIR = 0x0018,
/// The relocation ignores section mode.
M_NOMODE = 0x8000,
_,
};
/// IBM PowerPC Processors
/// The following relocation type indicators are defined for PowerPC processors.
pub const PPC = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The 64-bit VA of the target.
ADDR64 = 0x0001,
/// The 32-bit VA of the target.
ADDR32 = 0x0002,
/// The low 24 bits of the VA of the target.
/// This is valid only when the target symbol is absolute and can be sign-extended to its original value.
ADDR24 = 0x0003,
/// The low 16 bits of the target's VA.
ADDR16 = 0x0004,
/// The low 14 bits of the target's VA.
/// This is valid only when the target symbol is absolute and can be sign-extended to its original value.
ADDR14 = 0x0005,
/// A 24-bit PC-relative offset to the symbol's location.
REL24 = 0x0006,
/// A 14-bit PC-relative offset to the symbol's location.
REL14 = 0x0007,
/// The 32-bit RVA of the target.
ADDR32NB = 0x000A,
/// 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 = 0x000B,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
SECTION = 0x000C,
/// The 16-bit offset of the target from the beginning of its section.
/// This is used to support debugging information and static thread local storage.
SECREL16 = 0x000F,
/// The high 16 bits of the target's 32-bit VA.
/// This is used for the first instruction in a two-instruction sequence that loads a full address.
/// This relocation must be immediately followed by a PAIR relocation whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that was taken from the location that is being relocated.
REFHI = 0x0010,
/// The low 16 bits of the target's VA.
REFLO = 0x0011,
/// A relocation that is valid only when it immediately follows a REFHI or SECRELHI relocation.
/// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
PAIR = 0x0012,
/// The low 16 bits of the 32-bit offset of the target from the beginning of its section.
SECRELLO = 0x0013,
/// The 16-bit signed displacement of the target relative to the GP register.
GPREL = 0x0015,
/// The CLR token.
TOKEN = 0x0016,
_,
};
/// Intel 386 Processors
/// The following relocation type indicators are defined for Intel 386 and compatible processors.
pub const I386 = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// Not supported.
DIR16 = 0x0001,
/// Not supported.
REL16 = 0x0002,
/// The target's 32-bit VA.
DIR32 = 0x0006,
/// The target's 32-bit RVA.
DIR32NB = 0x0007,
/// Not supported.
SEG12 = 0x0009,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
SECTION = 0x000A,
/// 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 = 0x000B,
/// The CLR token.
TOKEN = 0x000C,
/// A 7-bit offset from the base of the section that contains the target.
SECREL7 = 0x000D,
/// The 32-bit relative displacement to the target.
/// This supports the x86 relative branch and call instructions.
REL32 = 0x0014,
_,
};
/// Intel Itanium Processor Family (IPF)
/// The following relocation type indicators are defined for the Intel Itanium processor family and compatible processors.
/// Note that relocations on instructions use the bundle's offset and slot number for the relocation offset.
pub const IA64 = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The instruction relocation can be followed by an ADDEND relocation whose value is added to the target address before it is inserted into the specified slot in the IMM14 bundle.
/// The relocation target must be absolute or the image must be fixed.
IMM14 = 0x0001,
/// The instruction relocation can be followed by an ADDEND relocation whose value is added to the target address before it is inserted into the specified slot in the IMM22 bundle.
/// The relocation target must be absolute or the image must be fixed.
IMM22 = 0x0002,
/// The slot number of this relocation must be one (1).
/// The relocation can be followed by an ADDEND relocation whose value is added to the target address before it is stored in all three slots of the IMM64 bundle.
IMM64 = 0x0003,
/// The target's 32-bit VA.
/// This is supported only for /LARGEADDRESSAWARE:NO images.
DIR32 = 0x0004,
/// The target's 64-bit VA.
DIR64 = 0x0005,
/// The instruction is fixed up with the 25-bit relative displacement to the 16-bit aligned target.
/// The low 4 bits of the displacement are zero and are not stored.
PCREL21B = 0x0006,
/// The instruction is fixed up with the 25-bit relative displacement to the 16-bit aligned target.
/// The low 4 bits of the displacement, which are zero, are not stored.
PCREL21M = 0x0007,
/// The LSBs of this relocation's offset must contain the slot number whereas the rest is the bundle address.
/// The bundle is fixed up with the 25-bit relative displacement to the 16-bit aligned target.
/// The low 4 bits of the displacement are zero and are not stored.
PCREL21F = 0x0008,
/// The instruction relocation can be followed by an ADDEND relocation whose value is added to the target address and then a 22-bit GP-relative offset that is calculated and applied to the GPREL22 bundle.
GPREL22 = 0x0009,
/// The instruction is fixed up with the 22-bit GP-relative offset to the target symbol's literal table entry.
/// The linker creates this literal table entry based on this relocation and the ADDEND relocation that might follow.
LTOFF22 = 0x000A,
/// The 16-bit section index of the section contains the target.
/// This is used to support debugging information.
SECTION = 0x000B,
/// The instruction is fixed up with the 22-bit offset of the target from the beginning of its section.
/// This relocation can be followed immediately by an ADDEND relocation, whose Value field contains the 32-bit unsigned offset of the target from the beginning of the section.
SECREL22 = 0x000C,
/// The slot number for this relocation must be one (1).
/// The instruction is fixed up with the 64-bit offset of the target from the beginning of its section.
/// This relocation can be followed immediately by an ADDEND relocation whose Value field contains the 32-bit unsigned offset of the target from the beginning of the section.
SECREL64I = 0x000D,
/// The address of data to be fixed up with the 32-bit offset of the target from the beginning of its section.
SECREL32 = 0x000E,
/// The target's 32-bit RVA.
DIR32NB = 0x0010,
/// This is applied to a signed 14-bit immediate that contains the difference between two relocatable targets.
/// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
SREL14 = 0x0011,
/// This is applied to a signed 22-bit immediate that contains the difference between two relocatable targets.
/// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
SREL22 = 0x0012,
/// This is applied to a signed 32-bit immediate that contains the difference between two relocatable values.
/// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
SREL32 = 0x0013,
/// This is applied to an unsigned 32-bit immediate that contains the difference between two relocatable values.
/// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
UREL32 = 0x0014,
/// A 60-bit PC-relative fixup that always stays as a BRL instruction of an MLX bundle.
PCREL60X = 0x0015,
/// A 60-bit PC-relative fixup.
/// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MBB bundle with NOP.B in slot 1 and a 25-bit BR instruction (with the 4 lowest bits all zero and dropped) in slot 2.
PCREL60B = 0x0016,
/// A 60-bit PC-relative fixup.
/// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MFB bundle with NOP.F in slot 1 and a 25-bit (4 lowest bits all zero and dropped) BR instruction in slot 2.
PCREL60F = 0x0017,
/// A 60-bit PC-relative fixup.
/// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MIB bundle with NOP.I in slot 1 and a 25-bit (4 lowest bits all zero and dropped) BR instruction in slot 2.
PCREL60I = 0x0018,
/// A 60-bit PC-relative fixup.
/// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MMB bundle with NOP.M in slot 1 and a 25-bit (4 lowest bits all zero and dropped) BR instruction in slot 2.
PCREL60M = 0x0019,
/// A 64-bit GP-relative fixup.
IMMGPREL64 = 0x001a,
/// A CLR token.
TOKEN = 0x001b,
/// A 32-bit GP-relative fixup.
GPREL32 = 0x001c,
/// The relocation is valid only when it immediately follows one of the following relocations: IMM14, IMM22, IMM64, GPREL22, LTOFF22, LTOFF64, SECREL22, SECREL64I, or SECREL32.
/// Its value contains the addend to apply to instructions within a bundle, not for data.
ADDEND = 0x001F,
_,
};
/// MIPS Processors
/// The following relocation type indicators are defined for MIPS processors.
pub const MIPS = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The high 16 bits of the target's 32-bit VA.
REFHALF = 0x0001,
/// The target's 32-bit VA.
REFWORD = 0x0002,
/// The low 26 bits of the target's VA.
/// This supports the MIPS J and JAL instructions.
JMPADDR = 0x0003,
/// The high 16 bits of the target's 32-bit VA.
/// This is used for the first instruction in a two-instruction sequence that loads a full address.
/// This relocation must be immediately followed by a PAIR relocation whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that are taken from the location that is being relocated.
REFHI = 0x0004,
/// The low 16 bits of the target's VA.
REFLO = 0x0005,
/// A 16-bit signed displacement of the target relative to the GP register.
GPREL = 0x0006,
/// The same as IMAGE_REL_MIPS_GPREL.
LITERAL = 0x0007,
/// The 16-bit section index of the section contains the target.
/// This is used to support debugging information.
SECTION = 0x000A,
/// 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 = 0x000B,
/// The low 16 bits of the 32-bit offset of the target from the beginning of its section.
SECRELLO = 0x000C,
/// The high 16 bits of the 32-bit offset of the target from the beginning of its section.
/// An IMAGE_REL_MIPS_PAIR relocation must immediately follow this one.
/// The SymbolTableIndex of the PAIR relocation contains a signed 16-bit displacement that is added to the upper 16 bits that are taken from the location that is being relocated.
SECRELHI = 0x000D,
/// The low 26 bits of the target's VA.
/// This supports the MIPS16 JAL instruction.
JMPADDR16 = 0x0010,
/// The target's 32-bit RVA.
REFWORDNB = 0x0022,
/// The relocation is valid only when it immediately follows a REFHI or SECRELHI relocation.
/// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
PAIR = 0x0025,
_,
};
/// Mitsubishi M32R
/// The following relocation type indicators are defined for the Mitsubishi M32R processors.
pub const M32R = enum(u16) {
/// The relocation is ignored.
ABSOLUTE = 0x0000,
/// The target's 32-bit VA.
ADDR32 = 0x0001,
/// The target's 32-bit RVA.
ADDR32NB = 0x0002,
/// The target's 24-bit VA.
ADDR24 = 0x0003,
/// The target's 16-bit offset from the GP register.
GPREL16 = 0x0004,
/// The target's 24-bit offset from the program counter (PC), shifted left by 2 bits and sign-extended
PCREL24 = 0x0005,
/// The target's 16-bit offset from the PC, shifted left by 2 bits and sign-extended
PCREL16 = 0x0006,
/// The target's 8-bit offset from the PC, shifted left by 2 bits and sign-extended
PCREL8 = 0x0007,
/// The 16 MSBs of the target VA.
REFHALF = 0x0008,
/// The 16 MSBs of the target VA, adjusted for LSB sign extension.
/// This is used for the first instruction in a two-instruction sequence that loads a full 32-bit address.
/// This relocation must be immediately followed by a PAIR relocation whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that are taken from the location that is being relocated.
REFHI = 0x0009,
/// The 16 LSBs of the target VA.
REFLO = 0x000A,
/// The relocation must follow the REFHI relocation.
/// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
PAIR = 0x000B,
/// The 16-bit section index of the section that contains the target.
/// This is used to support debugging information.
SECTION = 0x000C,
/// 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 = 0x000D,
/// The CLR token.
TOKEN = 0x000E,
_,
};
};
};