diff --git a/lib/std/coff.zig b/lib/std/coff.zig index 4371444b96..a017ae8422 100644 --- a/lib/std/coff.zig +++ b/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 { + /// Takes ownership of `data`. + pub fn parse(self: *Coff, data: []const u8) !void { + self.data = data; + const pe_pointer_offset = 0x3C; + const pe_magic = "PE\x00\x00"; - const in = self.in_file.reader(); + 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); - 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), - }; - - switch (self.coff_header.machine) { - IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 => {}, - else => return error.InvalidMachine, + // 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; + } } - try self.loadOptionalHeader(); - } - - 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; - } - - 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; - - try self.sections.ensureTotalCapacityPrecise(self.allocator, self.coff_header.number_of_sections); - - const in = self.in_file.reader(); - - var name: [32]u8 = undefined; - - var i: u16 = 0; - while (i < self.coff_header.number_of_sections) : (i += 1) { - try in.readNoEof(name[0..8]); - - 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; - - 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); - } - - 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 getCoffHeader(self: Coff) CoffHeader { + return @ptrCast(*align(1) CoffHeader, self.data[self.coff_header_offset..][0..@sizeOf(CoffHeader)]).*; } - 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 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)]).*; + } + + 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)]).*; + } + + 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)]).*; + } + + pub fn getImageBase(self: Coff) u64 { + const hdr = self.getOptionalHeader(); + return switch (hdr.magic) { + IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().image_base, + IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().image_base, + else => unreachable, // We assume we have validated the header already + }; + } + + pub fn getNumberOfDataDirectories(self: Coff) u32 { + const hdr = self.getOptionalHeader(); + return switch (hdr.magic) { + IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().number_of_rva_and_sizes, + IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().number_of_rva_and_sizes, + else => unreachable, // We assume we have validated the header already + }; + } + + pub fn getDataDirectories(self: *const Coff) []align(1) const ImageDataDirectory { + const hdr = self.getOptionalHeader(); + const size: usize = switch (hdr.magic) { + IMAGE_NT_OPTIONAL_HDR32_MAGIC => @sizeOf(OptionalHeaderPE32), + IMAGE_NT_OPTIONAL_HDR64_MAGIC => @sizeOf(OptionalHeaderPE64), + else => unreachable, // We assume we have validated the header already + }; + const offset = self.coff_header_offset + @sizeOf(CoffHeader) + size; + return @ptrCast([*]align(1) ImageDataDirectory, self.data[offset..])[0..self.getNumberOfDataDirectories()]; + } + + pub fn getSymtab(self: *const Coff) ?Symtab { + const coff_header = self.getCoffHeader(); + if (coff_header.pointer_to_symbol_table == 0) return null; + + const offset = coff_header.pointer_to_symbol_table; + const size = coff_header.number_of_symbols * Symbol.sizeOf(); + return .{ .buffer = self.data[offset..][0..size] }; + } + + pub fn getStrtab(self: *const Coff) ?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 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()); + } + + const Tag = enum { + symbol, + func_def, + debug_info, + weak_ext, + file_def, + sect_def, + }; + + 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. + 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.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 }; + } }; - magic: u16, - data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, - entry_addr: u32, - code_base: u32, - image_base: u64, -}; + pub const Strtab = struct { + buffer: []const u8, -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 Section = struct { - header: SectionHeader, -}; - -const SectionHeader = struct { - const Misc = union { - physical_address: u32, - virtual_size: u32, + 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); + } }; - - 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, }; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 5e217890b7..917354edf7 100644 --- a/lib/std/debug.zig +++ b/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{ diff --git a/src/link/Coff.zig b/src/link/Coff.zig index a01b9cf7c3..6536fbd1ac 100644 --- a/src/link/Coff.zig +++ b/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);