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