| ... | @@ -1,14 +1,731 @@ | ... | @@ -1,14 +1,731 @@ |
| 1 | const std = @import("std.zig"); | 1 | const std = @import("std.zig"); |
| | 2 | const assert = std.debug.assert; |
| 2 | const io = std.io; | 3 | const io = std.io; |
| 3 | const mem = std.mem; | 4 | const mem = std.mem; |
| 4 | const os = std.os; | 5 | const os = std.os; |
| 5 | const File = std.fs.File; | 6 | const fs = std.fs; |
| 6 | | 7 | |
| 7 | // CoffHeader.machine values | 8 | pub const CoffHeaderFlags = packed struct { |
| 8 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx | 9 | /// Image only, Windows CE, and Microsoft Windows NT and later. |
| 9 | const IMAGE_FILE_MACHINE_I386 = 0x014c; | 10 | /// This indicates that the file does not contain base relocations |
| 10 | const IMAGE_FILE_MACHINE_IA64 = 0x0200; | 11 | /// and must therefore be loaded at its preferred base address. |
| 11 | const IMAGE_FILE_MACHINE_AMD64 = 0x8664; | 12 | /// If the base address is not available, the loader reports an error. |
| | 13 | /// The default behavior of the linker is to strip base relocations |
| | 14 | /// from executable (EXE) files. |
| | 15 | RELOCS_STRIPPED: u1 = 0, |
| | 16 | |
| | 17 | /// Image only. This indicates that the image file is valid and can be run. |
| | 18 | /// If this flag is not set, it indicates a linker error. |
| | 19 | EXECUTABLE_IMAGE: u1 = 0, |
| | 20 | |
| | 21 | /// COFF line numbers have been removed. This flag is deprecated and should be zero. |
| | 22 | LINE_NUMS_STRIPPED: u1 = 0, |
| | 23 | |
| | 24 | /// COFF symbol table entries for local symbols have been removed. |
| | 25 | /// This flag is deprecated and should be zero. |
| | 26 | LOCAL_SYMS_STRIPPED: u1 = 0, |
| | 27 | |
| | 28 | /// Obsolete. Aggressively trim working set. |
| | 29 | /// This flag is deprecated for Windows 2000 and later and must be zero. |
| | 30 | AGGRESSIVE_WS_TRIM: u1 = 0, |
| | 31 | |
| | 32 | /// Application can handle > 2-GB addresses. |
| | 33 | LARGE_ADDRESS_AWARE: u1 = 0, |
| | 34 | |
| | 35 | /// This flag is reserved for future use. |
| | 36 | RESERVED: u1 = 0, |
| | 37 | |
| | 38 | /// Little endian: the least significant bit (LSB) precedes the |
| | 39 | /// most significant bit (MSB) in memory. This flag is deprecated and should be zero. |
| | 40 | BYTES_REVERSED_LO: u1 = 0, |
| | 41 | |
| | 42 | /// Machine is based on a 32-bit-word architecture. |
| | 43 | @"32BIT_MACHINE": u1 = 0, |
| | 44 | |
| | 45 | /// Debugging information is removed from the image file. |
| | 46 | DEBUG_STRIPPED: u1 = 0, |
| | 47 | |
| | 48 | /// If the image is on removable media, fully load it and copy it to the swap file. |
| | 49 | REMOVABLE_RUN_FROM_SWAP: u1 = 0, |
| | 50 | |
| | 51 | /// If the image is on network media, fully load it and copy it to the swap file. |
| | 52 | NET_RUN_FROM_SWAP: u1 = 0, |
| | 53 | |
| | 54 | /// The image file is a system file, not a user program. |
| | 55 | SYSTEM: u1 = 0, |
| | 56 | |
| | 57 | /// The image file is a dynamic-link library (DLL). |
| | 58 | /// Such files are considered executable files for almost all purposes, |
| | 59 | /// although they cannot be directly run. |
| | 60 | DLL: u1 = 0, |
| | 61 | |
| | 62 | /// The file should be run only on a uniprocessor machine. |
| | 63 | UP_SYSTEM_ONLY: u1 = 0, |
| | 64 | |
| | 65 | /// Big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero. |
| | 66 | BYTES_REVERSED_HI: u1 = 0, |
| | 67 | }; |
| | 68 | |
| | 69 | pub const CoffHeader = extern struct { |
| | 70 | /// The number that identifies the type of target machine. |
| | 71 | machine: MachineType, |
| | 72 | |
| | 73 | /// The number of sections. This indicates the size of the section table, which immediately follows the headers. |
| | 74 | number_of_sections: u16, |
| | 75 | |
| | 76 | /// The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value), |
| | 77 | /// which indicates when the file was created. |
| | 78 | time_date_stamp: u32, |
| | 79 | |
| | 80 | /// The file offset of the COFF symbol table, or zero if no COFF symbol table is present. |
| | 81 | /// This value should be zero for an image because COFF debugging information is deprecated. |
| | 82 | pointer_to_symbol_table: u32, |
| | 83 | |
| | 84 | /// The number of entries in the symbol table. |
| | 85 | /// This data can be used to locate the string table, which immediately follows the symbol table. |
| | 86 | /// This value should be zero for an image because COFF debugging information is deprecated. |
| | 87 | number_of_symbols: u32, |
| | 88 | |
| | 89 | /// The size of the optional header, which is required for executable files but not for object files. |
| | 90 | /// This value should be zero for an object file. For a description of the header format, see Optional Header (Image Only). |
| | 91 | size_of_optional_header: u16, |
| | 92 | |
| | 93 | /// The flags that indicate the attributes of the file. |
| | 94 | flags: CoffHeaderFlags, |
| | 95 | }; |
| | 96 | |
| | 97 | // OptionalHeader.magic values |
| | 98 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx |
| | 99 | pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; |
| | 100 | pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; |
| | 101 | |
| | 102 | pub const DllFlags = packed struct { |
| | 103 | _reserved_0: u5 = 0, |
| | 104 | |
| | 105 | /// Image can handle a high entropy 64-bit virtual address space. |
| | 106 | HIGH_ENTROPY_VA: u1 = 0, |
| | 107 | |
| | 108 | /// DLL can be relocated at load time. |
| | 109 | DYNAMIC_BASE: u1 = 0, |
| | 110 | |
| | 111 | /// Code Integrity checks are enforced. |
| | 112 | FORCE_INTEGRITY: u1 = 0, |
| | 113 | |
| | 114 | /// Image is NX compatible. |
| | 115 | NX_COMPAT: u1 = 0, |
| | 116 | |
| | 117 | /// Isolation aware, but do not isolate the image. |
| | 118 | NO_ISOLATION: u1 = 0, |
| | 119 | |
| | 120 | /// Does not use structured exception (SE) handling. No SE handler may be called in this image. |
| | 121 | NO_SEH: u1 = 0, |
| | 122 | |
| | 123 | /// Do not bind the image. |
| | 124 | NO_BIND: u1 = 0, |
| | 125 | |
| | 126 | /// Image must execute in an AppContainer. |
| | 127 | APPCONTAINER: u1 = 0, |
| | 128 | |
| | 129 | /// A WDM driver. |
| | 130 | WDM_DRIVER: u1 = 0, |
| | 131 | |
| | 132 | /// Image supports Control Flow Guard. |
| | 133 | GUARD_CF: u1 = 0, |
| | 134 | |
| | 135 | /// Terminal Server aware. |
| | 136 | TERMINAL_SERVER_AWARE: u1 = 0, |
| | 137 | }; |
| | 138 | |
| | 139 | pub const Subsystem = enum(u16) { |
| | 140 | /// An unknown subsystem |
| | 141 | UNKNOWN = 0, |
| | 142 | |
| | 143 | /// Device drivers and native Windows processes |
| | 144 | NATIVE = 1, |
| | 145 | |
| | 146 | /// The Windows graphical user interface (GUI) subsystem |
| | 147 | WINDOWS_GUI = 2, |
| | 148 | |
| | 149 | /// The Windows character subsystem |
| | 150 | WINDOWS_CUI = 3, |
| | 151 | |
| | 152 | /// The OS/2 character subsystem |
| | 153 | OS2_CUI = 5, |
| | 154 | |
| | 155 | /// The Posix character subsystem |
| | 156 | POSIX_CUI = 7, |
| | 157 | |
| | 158 | /// Native Win9x driver |
| | 159 | NATIVE_WINDOWS = 8, |
| | 160 | |
| | 161 | /// Windows CE |
| | 162 | WINDOWS_CE_GUI = 9, |
| | 163 | |
| | 164 | /// An Extensible Firmware Interface (EFI) application |
| | 165 | EFI_APPLICATION = 10, |
| | 166 | |
| | 167 | /// An EFI driver with boot services |
| | 168 | EFI_BOOT_SERVICE_DRIVER = 11, |
| | 169 | |
| | 170 | /// An EFI driver with run-time services |
| | 171 | EFI_RUNTIME_DRIVER = 12, |
| | 172 | |
| | 173 | /// An EFI ROM image |
| | 174 | EFI_ROM = 13, |
| | 175 | |
| | 176 | /// XBOX |
| | 177 | XBOX = 14, |
| | 178 | |
| | 179 | /// Windows boot application |
| | 180 | WINDOWS_BOOT_APPLICATION = 16, |
| | 181 | }; |
| | 182 | |
| | 183 | pub const OptionalHeader = extern struct { |
| | 184 | magic: u16, |
| | 185 | major_linker_version: u8, |
| | 186 | minor_linker_version: u8, |
| | 187 | size_of_code: u32, |
| | 188 | size_of_initialized_data: u32, |
| | 189 | size_of_uninitialized_data: u32, |
| | 190 | address_of_entry_point: u32, |
| | 191 | base_of_code: u32, |
| | 192 | }; |
| | 193 | |
| | 194 | pub const OptionalHeaderPE32 = extern struct { |
| | 195 | magic: u16, |
| | 196 | major_linker_version: u8, |
| | 197 | minor_linker_version: u8, |
| | 198 | size_of_code: u32, |
| | 199 | size_of_initialized_data: u32, |
| | 200 | size_of_uninitialized_data: u32, |
| | 201 | address_of_entry_point: u32, |
| | 202 | base_of_code: u32, |
| | 203 | base_of_data: u32, |
| | 204 | image_base: u32, |
| | 205 | section_alignment: u32, |
| | 206 | file_alignment: u32, |
| | 207 | major_operating_system_version: u16, |
| | 208 | minor_operating_system_version: u16, |
| | 209 | major_image_version: u16, |
| | 210 | minor_image_version: u16, |
| | 211 | major_subsystem_version: u16, |
| | 212 | minor_subsystem_version: u16, |
| | 213 | win32_version_value: u32, |
| | 214 | size_of_image: u32, |
| | 215 | size_of_headers: u32, |
| | 216 | checksum: u32, |
| | 217 | subsystem: Subsystem, |
| | 218 | dll_flags: DllFlags, |
| | 219 | size_of_stack_reserve: u32, |
| | 220 | size_of_stack_commit: u32, |
| | 221 | size_of_heap_reserve: u32, |
| | 222 | size_of_heap_commit: u32, |
| | 223 | loader_flags: u32, |
| | 224 | number_of_rva_and_sizes: u32, |
| | 225 | }; |
| | 226 | |
| | 227 | pub const OptionalHeaderPE64 = extern struct { |
| | 228 | magic: u16, |
| | 229 | major_linker_version: u8, |
| | 230 | minor_linker_version: u8, |
| | 231 | size_of_code: u32, |
| | 232 | size_of_initialized_data: u32, |
| | 233 | size_of_uninitialized_data: u32, |
| | 234 | address_of_entry_point: u32, |
| | 235 | base_of_code: u32, |
| | 236 | image_base: u64, |
| | 237 | section_alignment: u32, |
| | 238 | file_alignment: u32, |
| | 239 | major_operating_system_version: u16, |
| | 240 | minor_operating_system_version: u16, |
| | 241 | major_image_version: u16, |
| | 242 | minor_image_version: u16, |
| | 243 | major_subsystem_version: u16, |
| | 244 | minor_subsystem_version: u16, |
| | 245 | win32_version_value: u32, |
| | 246 | size_of_image: u32, |
| | 247 | size_of_headers: u32, |
| | 248 | checksum: u32, |
| | 249 | subsystem: Subsystem, |
| | 250 | dll_flags: DllFlags, |
| | 251 | size_of_stack_reserve: u64, |
| | 252 | size_of_stack_commit: u64, |
| | 253 | size_of_heap_reserve: u64, |
| | 254 | size_of_heap_commit: u64, |
| | 255 | loader_flags: u32, |
| | 256 | number_of_rva_and_sizes: u32, |
| | 257 | }; |
| | 258 | |
| | 259 | pub const DebugDirectoryEntry = extern struct { |
| | 260 | characteristiccs: u32, |
| | 261 | time_date_stamp: u32, |
| | 262 | major_version: u16, |
| | 263 | minor_version: u16, |
| | 264 | @"type": u32, |
| | 265 | size_of_data: u32, |
| | 266 | address_of_raw_data: u32, |
| | 267 | pointer_to_raw_data: u32, |
| | 268 | }; |
| | 269 | |
| | 270 | pub const ImageDataDirectory = extern struct { |
| | 271 | virtual_address: u32, |
| | 272 | size: u32, |
| | 273 | }; |
| | 274 | |
| | 275 | pub const SectionHeader = extern struct { |
| | 276 | name: [8]u8, |
| | 277 | virtual_size: u32, |
| | 278 | virtual_address: u32, |
| | 279 | size_of_raw_data: u32, |
| | 280 | pointer_to_raw_data: u32, |
| | 281 | pointer_to_relocations: u32, |
| | 282 | pointer_to_linenumbers: u32, |
| | 283 | number_of_relocations: u16, |
| | 284 | number_of_linenumbers: u16, |
| | 285 | flags: SectionHeaderFlags, |
| | 286 | |
| | 287 | pub fn getName(self: *align(1) const SectionHeader) ?[]const u8 { |
| | 288 | if (self.name[0] == '/') return null; |
| | 289 | const len = std.mem.indexOfScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len; |
| | 290 | return self.name[0..len]; |
| | 291 | } |
| | 292 | |
| | 293 | pub fn getNameOffset(self: SectionHeader) ?u32 { |
| | 294 | if (self.name[0] != '/') return null; |
| | 295 | const len = std.mem.indexOfScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len; |
| | 296 | const offset = std.fmt.parseInt(u32, self.name[1..len], 10) catch unreachable; |
| | 297 | return offset; |
| | 298 | } |
| | 299 | |
| | 300 | /// Applicable only to section headers in COFF objects. |
| | 301 | pub fn getAlignment(self: SectionHeader) ?u16 { |
| | 302 | if (self.flags.ALIGN == 0) return null; |
| | 303 | return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable; |
| | 304 | } |
| | 305 | |
| | 306 | pub fn isComdat(self: SectionHeader) bool { |
| | 307 | return self.flags.LNK_COMDAT == 0b1; |
| | 308 | } |
| | 309 | }; |
| | 310 | |
| | 311 | pub const SectionHeaderFlags = packed struct { |
| | 312 | _reserved_0: u3 = 0, |
| | 313 | |
| | 314 | /// The section should not be padded to the next boundary. |
| | 315 | /// This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. |
| | 316 | /// This is valid only for object files. |
| | 317 | TYPE_NO_PAD: u1 = 0, |
| | 318 | |
| | 319 | _reserved_1: u1 = 0, |
| | 320 | |
| | 321 | /// The section contains executable code. |
| | 322 | CNT_CODE: u1 = 0, |
| | 323 | |
| | 324 | /// The section contains initialized data. |
| | 325 | CNT_INITIALIZED_DATA: u1 = 0, |
| | 326 | |
| | 327 | /// The section contains uninitialized data. |
| | 328 | CNT_UNINITIALIZED_DATA: u1 = 0, |
| | 329 | |
| | 330 | /// Reserved for future use. |
| | 331 | LNK_OTHER: u1 = 0, |
| | 332 | |
| | 333 | /// The section contains comments or other information. |
| | 334 | /// The .drectve section has this type. |
| | 335 | /// This is valid for object files only. |
| | 336 | LNK_INFO: u1 = 0, |
| | 337 | |
| | 338 | _reserverd_2: u1 = 0, |
| | 339 | |
| | 340 | /// The section will not become part of the image. |
| | 341 | /// This is valid only for object files. |
| | 342 | LNK_REMOVE: u1 = 0, |
| | 343 | |
| | 344 | /// The section contains COMDAT data. |
| | 345 | /// For more information, see COMDAT Sections (Object Only). |
| | 346 | /// This is valid only for object files. |
| | 347 | LNK_COMDAT: u1 = 0, |
| | 348 | |
| | 349 | _reserved_3: u2 = 0, |
| | 350 | |
| | 351 | /// The section contains data referenced through the global pointer (GP). |
| | 352 | GPREL: u1 = 0, |
| | 353 | |
| | 354 | /// Reserved for future use. |
| | 355 | MEM_PURGEABLE: u1 = 0, |
| | 356 | |
| | 357 | /// Reserved for future use. |
| | 358 | MEM_16BIT: u1 = 0, |
| | 359 | |
| | 360 | /// Reserved for future use. |
| | 361 | MEM_LOCKED: u1 = 0, |
| | 362 | |
| | 363 | /// Reserved for future use. |
| | 364 | MEM_PRELOAD: u1 = 0, |
| | 365 | |
| | 366 | /// Takes on multiple values according to flags: |
| | 367 | /// pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x100000; |
| | 368 | /// pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x200000; |
| | 369 | /// pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x300000; |
| | 370 | /// pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x400000; |
| | 371 | /// pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x500000; |
| | 372 | /// pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x600000; |
| | 373 | /// pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x700000; |
| | 374 | /// pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x800000; |
| | 375 | /// pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x900000; |
| | 376 | /// pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0xA00000; |
| | 377 | /// pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0xB00000; |
| | 378 | /// pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0xC00000; |
| | 379 | /// pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0xD00000; |
| | 380 | /// pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0xE00000; |
| | 381 | ALIGN: u4 = 0, |
| | 382 | |
| | 383 | /// The section contains extended relocations. |
| | 384 | LNK_NRELOC_OVFL: u1 = 0, |
| | 385 | |
| | 386 | /// The section can be discarded as needed. |
| | 387 | MEM_DISCARDABLE: u1 = 0, |
| | 388 | |
| | 389 | /// The section cannot be cached. |
| | 390 | MEM_NOT_CACHED: u1 = 0, |
| | 391 | |
| | 392 | /// The section is not pageable. |
| | 393 | MEM_NOT_PAGED: u1 = 0, |
| | 394 | |
| | 395 | /// The section can be shared in memory. |
| | 396 | MEM_SHARED: u1 = 0, |
| | 397 | |
| | 398 | /// The section can be executed as code. |
| | 399 | MEM_EXECUTE: u1 = 0, |
| | 400 | |
| | 401 | /// The section can be read. |
| | 402 | MEM_READ: u1 = 0, |
| | 403 | |
| | 404 | /// The section can be written to. |
| | 405 | MEM_WRITE: u1 = 0, |
| | 406 | }; |
| | 407 | |
| | 408 | pub const Symbol = struct { |
| | 409 | name: [8]u8, |
| | 410 | value: u32, |
| | 411 | section_number: SectionNumber, |
| | 412 | @"type": SymType, |
| | 413 | storage_class: StorageClass, |
| | 414 | number_of_aux_symbols: u8, |
| | 415 | |
| | 416 | pub fn sizeOf() usize { |
| | 417 | return 18; |
| | 418 | } |
| | 419 | |
| | 420 | pub fn getName(self: *const Symbol) ?[]const u8 { |
| | 421 | if (std.mem.eql(u8, self.name[0..4], "\x00\x00\x00\x00")) return null; |
| | 422 | const len = std.mem.indexOfScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len; |
| | 423 | return self.name[0..len]; |
| | 424 | } |
| | 425 | |
| | 426 | pub fn getNameOffset(self: Symbol) ?u32 { |
| | 427 | if (!std.mem.eql(u8, self.name[0..4], "\x00\x00\x00\x00")) return null; |
| | 428 | const offset = std.mem.readIntLittle(u32, self.name[4..8]); |
| | 429 | return offset; |
| | 430 | } |
| | 431 | }; |
| | 432 | |
| | 433 | pub const SectionNumber = enum(u16) { |
| | 434 | /// The symbol record is not yet assigned a section. |
| | 435 | /// A value of zero indicates that a reference to an external symbol is defined elsewhere. |
| | 436 | /// A value of non-zero is a common symbol with a size that is specified by the value. |
| | 437 | UNDEFINED = 0, |
| | 438 | |
| | 439 | /// The symbol has an absolute (non-relocatable) value and is not an address. |
| | 440 | ABSOLUTE = 0xffff, |
| | 441 | |
| | 442 | /// The symbol provides general type or debugging information but does not correspond to a section. |
| | 443 | /// Microsoft tools use this setting along with .file records (storage class FILE). |
| | 444 | DEBUG = 0xfffe, |
| | 445 | _, |
| | 446 | }; |
| | 447 | |
| | 448 | pub const SymType = packed struct { |
| | 449 | complex_type: ComplexType, |
| | 450 | base_type: BaseType, |
| | 451 | }; |
| | 452 | |
| | 453 | pub const BaseType = enum(u8) { |
| | 454 | /// No type information or unknown base type. Microsoft tools use this setting |
| | 455 | NULL = 0, |
| | 456 | |
| | 457 | /// No valid type; used with void pointers and functions |
| | 458 | VOID = 1, |
| | 459 | |
| | 460 | /// A character (signed byte) |
| | 461 | CHAR = 2, |
| | 462 | |
| | 463 | /// A 2-byte signed integer |
| | 464 | SHORT = 3, |
| | 465 | |
| | 466 | /// A natural integer type (normally 4 bytes in Windows) |
| | 467 | INT = 4, |
| | 468 | |
| | 469 | /// A 4-byte signed integer |
| | 470 | LONG = 5, |
| | 471 | |
| | 472 | /// A 4-byte floating-point number |
| | 473 | FLOAT = 6, |
| | 474 | |
| | 475 | /// An 8-byte floating-point number |
| | 476 | DOUBLE = 7, |
| | 477 | |
| | 478 | /// A structure |
| | 479 | STRUCT = 8, |
| | 480 | |
| | 481 | /// A union |
| | 482 | UNION = 9, |
| | 483 | |
| | 484 | /// An enumerated type |
| | 485 | ENUM = 10, |
| | 486 | |
| | 487 | /// A member of enumeration (a specified value) |
| | 488 | MOE = 11, |
| | 489 | |
| | 490 | /// A byte; unsigned 1-byte integer |
| | 491 | BYTE = 12, |
| | 492 | |
| | 493 | /// A word; unsigned 2-byte integer |
| | 494 | WORD = 13, |
| | 495 | |
| | 496 | /// An unsigned integer of natural size (normally, 4 bytes) |
| | 497 | UINT = 14, |
| | 498 | |
| | 499 | /// An unsigned 4-byte integer |
| | 500 | DWORD = 15, |
| | 501 | }; |
| | 502 | |
| | 503 | pub const ComplexType = enum(u8) { |
| | 504 | /// No derived type; the symbol is a simple scalar variable. |
| | 505 | NULL = 0, |
| | 506 | |
| | 507 | /// The symbol is a pointer to base type. |
| | 508 | POINTER = 16, |
| | 509 | |
| | 510 | /// The symbol is a function that returns a base type. |
| | 511 | FUNCTION = 32, |
| | 512 | |
| | 513 | /// The symbol is an array of base type. |
| | 514 | ARRAY = 48, |
| | 515 | }; |
| | 516 | |
| | 517 | pub const StorageClass = enum(u8) { |
| | 518 | /// A special symbol that represents the end of function, for debugging purposes. |
| | 519 | END_OF_FUNCTION = 0xff, |
| | 520 | |
| | 521 | /// No assigned storage class. |
| | 522 | NULL = 0, |
| | 523 | |
| | 524 | /// The automatic (stack) variable. The Value field specifies the stack frame offset. |
| | 525 | AUTOMATIC = 1, |
| | 526 | |
| | 527 | /// A value that Microsoft tools use for external symbols. |
| | 528 | /// The Value field indicates the size if the section number is IMAGE_SYM_UNDEFINED (0). |
| | 529 | /// If the section number is not zero, then the Value field specifies the offset within the section. |
| | 530 | EXTERNAL = 2, |
| | 531 | |
| | 532 | /// The offset of the symbol within the section. |
| | 533 | /// If the Value field is zero, then the symbol represents a section name. |
| | 534 | STATIC = 3, |
| | 535 | |
| | 536 | /// A register variable. |
| | 537 | /// The Value field specifies the register number. |
| | 538 | REGISTER = 4, |
| | 539 | |
| | 540 | /// A symbol that is defined externally. |
| | 541 | EXTERNAL_DEF = 5, |
| | 542 | |
| | 543 | /// A code label that is defined within the module. |
| | 544 | /// The Value field specifies the offset of the symbol within the section. |
| | 545 | LABEL = 6, |
| | 546 | |
| | 547 | /// A reference to a code label that is not defined. |
| | 548 | UNDEFINED_LABEL = 7, |
| | 549 | |
| | 550 | /// The structure member. The Value field specifies the n th member. |
| | 551 | MEMBER_OF_STRUCT = 8, |
| | 552 | |
| | 553 | /// A formal argument (parameter) of a function. The Value field specifies the n th argument. |
| | 554 | ARGUMENT = 9, |
| | 555 | |
| | 556 | /// The structure tag-name entry. |
| | 557 | STRUCT_TAG = 10, |
| | 558 | |
| | 559 | /// A union member. The Value field specifies the n th member. |
| | 560 | MEMBER_OF_UNION = 11, |
| | 561 | |
| | 562 | /// The Union tag-name entry. |
| | 563 | UNION_TAG = 12, |
| | 564 | |
| | 565 | /// A Typedef entry. |
| | 566 | TYPE_DEFINITION = 13, |
| | 567 | |
| | 568 | /// A static data declaration. |
| | 569 | UNDEFINED_STATIC = 14, |
| | 570 | |
| | 571 | /// An enumerated type tagname entry. |
| | 572 | ENUM_TAG = 15, |
| | 573 | |
| | 574 | /// A member of an enumeration. The Value field specifies the n th member. |
| | 575 | MEMBER_OF_ENUM = 16, |
| | 576 | |
| | 577 | /// A register parameter. |
| | 578 | REGISTER_PARAM = 17, |
| | 579 | |
| | 580 | /// A bit-field reference. The Value field specifies the n th bit in the bit field. |
| | 581 | BIT_FIELD = 18, |
| | 582 | |
| | 583 | /// A .bb (beginning of block) or .eb (end of block) record. |
| | 584 | /// The Value field is the relocatable address of the code location. |
| | 585 | BLOCK = 100, |
| | 586 | |
| | 587 | /// 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 ). |
| | 588 | /// For .lf records, the Value field gives the number of source lines in the function. |
| | 589 | /// For .ef records, the Value field gives the size of the function code. |
| | 590 | FUNCTION = 101, |
| | 591 | |
| | 592 | /// An end-of-structure entry. |
| | 593 | END_OF_STRUCT = 102, |
| | 594 | |
| | 595 | /// A value that Microsoft tools, as well as traditional COFF format, use for the source-file symbol record. |
| | 596 | /// The symbol is followed by auxiliary records that name the file. |
| | 597 | FILE = 103, |
| | 598 | |
| | 599 | /// A definition of a section (Microsoft tools use STATIC storage class instead). |
| | 600 | SECTION = 104, |
| | 601 | |
| | 602 | /// A weak external. For more information, see Auxiliary Format 3: Weak Externals. |
| | 603 | WEAK_EXTERNAL = 105, |
| | 604 | |
| | 605 | /// A CLR token symbol. The name is an ASCII string that consists of the hexadecimal value of the token. |
| | 606 | /// For more information, see CLR Token Definition (Object Only). |
| | 607 | CLR_TOKEN = 107, |
| | 608 | }; |
| | 609 | |
| | 610 | pub const FunctionDefinition = struct { |
| | 611 | /// The symbol-table index of the corresponding .bf (begin function) symbol record. |
| | 612 | tag_index: u32, |
| | 613 | |
| | 614 | /// The size of the executable code for the function itself. |
| | 615 | /// If the function is in its own section, the SizeOfRawData in the section header is greater or equal to this field, |
| | 616 | /// depending on alignment considerations. |
| | 617 | total_size: u32, |
| | 618 | |
| | 619 | /// The file offset of the first COFF line-number entry for the function, or zero if none exists. |
| | 620 | pointer_to_linenumber: u32, |
| | 621 | |
| | 622 | /// The symbol-table index of the record for the next function. |
| | 623 | /// If the function is the last in the symbol table, this field is set to zero. |
| | 624 | pointer_to_next_function: u32, |
| | 625 | |
| | 626 | unused: [2]u8, |
| | 627 | }; |
| | 628 | |
| | 629 | pub const SectionDefinition = struct { |
| | 630 | /// The size of section data; the same as SizeOfRawData in the section header. |
| | 631 | length: u32, |
| | 632 | |
| | 633 | /// The number of relocation entries for the section. |
| | 634 | number_of_relocations: u16, |
| | 635 | |
| | 636 | /// The number of line-number entries for the section. |
| | 637 | number_of_linenumbers: u16, |
| | 638 | |
| | 639 | /// The checksum for communal data. It is applicable if the IMAGE_SCN_LNK_COMDAT flag is set in the section header. |
| | 640 | checksum: u32, |
| | 641 | |
| | 642 | /// One-based index into the section table for the associated section. This is used when the COMDAT selection setting is 5. |
| | 643 | number: u16, |
| | 644 | |
| | 645 | /// The COMDAT selection number. This is applicable if the section is a COMDAT section. |
| | 646 | selection: ComdatSelection, |
| | 647 | |
| | 648 | unused: [3]u8, |
| | 649 | }; |
| | 650 | |
| | 651 | pub const FileDefinition = struct { |
| | 652 | /// An ANSI string that gives the name of the source file. |
| | 653 | /// This is padded with nulls if it is less than the maximum length. |
| | 654 | file_name: [18]u8, |
| | 655 | |
| | 656 | pub fn getFileName(self: *const FileDefinition) []const u8 { |
| | 657 | const len = std.mem.indexOfScalar(u8, &self.file_name, @as(u8, 0)) orelse self.file_name.len; |
| | 658 | return self.file_name[0..len]; |
| | 659 | } |
| | 660 | }; |
| | 661 | |
| | 662 | pub const WeakExternalDefinition = struct { |
| | 663 | /// The symbol-table index of sym2, the symbol to be linked if sym1 is not found. |
| | 664 | tag_index: u32, |
| | 665 | |
| | 666 | /// A value of IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY indicates that no library search for sym1 should be performed. |
| | 667 | /// A value of IMAGE_WEAK_EXTERN_SEARCH_LIBRARY indicates that a library search for sym1 should be performed. |
| | 668 | /// A value of IMAGE_WEAK_EXTERN_SEARCH_ALIAS indicates that sym1 is an alias for sym2. |
| | 669 | flag: WeakExternalFlag, |
| | 670 | |
| | 671 | unused: [10]u8, |
| | 672 | }; |
| | 673 | |
| | 674 | // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/km/ntimage.h |
| | 675 | pub const WeakExternalFlag = enum(u32) { |
| | 676 | SEARCH_NOLIBRARY = 1, |
| | 677 | SEARCH_LIBRARY = 2, |
| | 678 | SEARCH_ALIAS = 3, |
| | 679 | ANTI_DEPENDENCY = 4, |
| | 680 | }; |
| | 681 | |
| | 682 | pub const ComdatSelection = enum(u8) { |
| | 683 | /// Not a COMDAT section. |
| | 684 | NONE = 0, |
| | 685 | |
| | 686 | /// If this symbol is already defined, the linker issues a "multiply defined symbol" error. |
| | 687 | NODUPLICATES = 1, |
| | 688 | |
| | 689 | /// Any section that defines the same COMDAT symbol can be linked; the rest are removed. |
| | 690 | ANY = 2, |
| | 691 | |
| | 692 | /// The linker chooses an arbitrary section among the definitions for this symbol. |
| | 693 | /// If all definitions are not the same size, a "multiply defined symbol" error is issued. |
| | 694 | SAME_SIZE = 3, |
| | 695 | |
| | 696 | /// The linker chooses an arbitrary section among the definitions for this symbol. |
| | 697 | /// If all definitions do not match exactly, a "multiply defined symbol" error is issued. |
| | 698 | EXACT_MATCH = 4, |
| | 699 | |
| | 700 | /// The section is linked if a certain other COMDAT section is linked. |
| | 701 | /// This other section is indicated by the Number field of the auxiliary symbol record for the section definition. |
| | 702 | /// This setting is useful for definitions that have components in multiple sections |
| | 703 | /// (for example, code in one and data in another), but where all must be linked or discarded as a set. |
| | 704 | /// The other section this section is associated with must be a COMDAT section, which can be another |
| | 705 | /// associative COMDAT section. An associative COMDAT section's section association chain can't form a loop. |
| | 706 | /// The section association chain must eventually come to a COMDAT section that doesn't have IMAGE_COMDAT_SELECT_ASSOCIATIVE set. |
| | 707 | ASSOCIATIVE = 5, |
| | 708 | |
| | 709 | /// The linker chooses the largest definition from among all of the definitions for this symbol. |
| | 710 | /// If multiple definitions have this size, the choice between them is arbitrary. |
| | 711 | LARGEST = 6, |
| | 712 | }; |
| | 713 | |
| | 714 | pub const DebugInfoDefinition = struct { |
| | 715 | unused_1: [4]u8, |
| | 716 | |
| | 717 | /// The actual ordinal line number (1, 2, 3, and so on) within the source file, corresponding to the .bf or .ef record. |
| | 718 | linenumber: u16, |
| | 719 | |
| | 720 | unused_2: [6]u8, |
| | 721 | |
| | 722 | /// The symbol-table index of the next .bf symbol record. |
| | 723 | /// If the function is the last in the symbol table, this field is set to zero. |
| | 724 | /// It is not used for .ef records. |
| | 725 | pointer_to_next_function: u32, |
| | 726 | |
| | 727 | unused_3: [2]u8, |
| | 728 | }; |
| 12 | | 729 | |
| 13 | pub const MachineType = enum(u16) { | 730 | pub const MachineType = enum(u16) { |
| 14 | Unknown = 0x0, | 731 | Unknown = 0x0, |
| ... | @@ -77,25 +794,6 @@ pub const MachineType = enum(u16) { | ... | @@ -77,25 +794,6 @@ pub const MachineType = enum(u16) { |
| 77 | } | 794 | } |
| 78 | }; | 795 | }; |
| 79 | | 796 | |
| 80 | // OptionalHeader.magic values | | |
| 81 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx | | |
| 82 | const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; | | |
| 83 | const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; | | |
| 84 | | | |
| 85 | // Image Characteristics | | |
| 86 | pub const IMAGE_FILE_RELOCS_STRIPPED = 0x1; | | |
| 87 | pub const IMAGE_FILE_DEBUG_STRIPPED = 0x200; | | |
| 88 | pub const IMAGE_FILE_EXECUTABLE_IMAGE = 0x2; | | |
| 89 | pub const IMAGE_FILE_32BIT_MACHINE = 0x100; | | |
| 90 | pub const IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x20; | | |
| 91 | | | |
| 92 | // Section flags | | |
| 93 | pub const IMAGE_SCN_CNT_INITIALIZED_DATA = 0x40; | | |
| 94 | pub const IMAGE_SCN_MEM_READ = 0x40000000; | | |
| 95 | pub const IMAGE_SCN_CNT_CODE = 0x20; | | |
| 96 | pub const IMAGE_SCN_MEM_EXECUTE = 0x20000000; | | |
| 97 | pub const IMAGE_SCN_MEM_WRITE = 0x80000000; | | |
| 98 | | | |
| 99 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | 797 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; |
| 100 | const IMAGE_DEBUG_TYPE_CODEVIEW = 2; | 798 | const IMAGE_DEBUG_TYPE_CODEVIEW = 2; |
| 101 | const DEBUG_DIRECTORY = 6; | 799 | const DEBUG_DIRECTORY = 6; |
| ... | @@ -104,166 +802,90 @@ pub const CoffError = error{ | ... | @@ -104,166 +802,90 @@ pub const CoffError = error{ |
| 104 | InvalidPEMagic, | 802 | InvalidPEMagic, |
| 105 | InvalidPEHeader, | 803 | InvalidPEHeader, |
| 106 | InvalidMachine, | 804 | InvalidMachine, |
| | 805 | MissingPEHeader, |
| 107 | MissingCoffSection, | 806 | MissingCoffSection, |
| 108 | MissingStringTable, | 807 | MissingStringTable, |
| 109 | }; | 808 | }; |
| 110 | | 809 | |
| 111 | // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format | 810 | // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format |
| 112 | pub const Coff = struct { | 811 | pub const Coff = struct { |
| 113 | in_file: File, | | |
| 114 | allocator: mem.Allocator, | 812 | allocator: mem.Allocator, |
| | 813 | data: []const u8 = undefined, |
| | 814 | is_image: bool = false, |
| | 815 | coff_header_offset: usize = 0, |
| 115 | | 816 | |
| 116 | coff_header: CoffHeader, | 817 | guid: [16]u8 = undefined, |
| 117 | pe_header: OptionalHeader, | 818 | age: u32 = undefined, |
| 118 | sections: std.ArrayListUnmanaged(Section) = .{}, | | |
| 119 | | | |
| 120 | guid: [16]u8, | | |
| 121 | age: u32, | | |
| 122 | | | |
| 123 | pub fn init(allocator: mem.Allocator, in_file: File) Coff { | | |
| 124 | return Coff{ | | |
| 125 | .in_file = in_file, | | |
| 126 | .allocator = allocator, | | |
| 127 | .coff_header = undefined, | | |
| 128 | .pe_header = undefined, | | |
| 129 | .guid = undefined, | | |
| 130 | .age = undefined, | | |
| 131 | }; | | |
| 132 | } | | |
| 133 | | 819 | |
| 134 | pub fn deinit(self: *Coff) void { | 820 | pub fn deinit(self: *Coff) void { |
| 135 | self.sections.deinit(self.allocator); | 821 | self.allocator.free(self.data); |
| 136 | } | 822 | } |
| 137 | | 823 | |
| 138 | pub fn loadHeader(self: *Coff) !void { | 824 | /// Takes ownership of `data`. |
| 139 | const pe_pointer_offset = 0x3C; | 825 | pub fn parse(self: *Coff, data: []const u8) !void { |
| 140 | | 826 | self.data = data; |
| 141 | const in = self.in_file.reader(); | | |
| 142 | | | |
| 143 | var magic: [2]u8 = undefined; | | |
| 144 | try in.readNoEof(magic[0..]); | | |
| 145 | if (!mem.eql(u8, &magic, "MZ")) | | |
| 146 | return error.InvalidPEMagic; | | |
| 147 | | | |
| 148 | // Seek to PE File Header (coff header) | | |
| 149 | try self.in_file.seekTo(pe_pointer_offset); | | |
| 150 | const pe_magic_offset = try in.readIntLittle(u32); | | |
| 151 | try self.in_file.seekTo(pe_magic_offset); | | |
| 152 | | | |
| 153 | var pe_header_magic: [4]u8 = undefined; | | |
| 154 | try in.readNoEof(pe_header_magic[0..]); | | |
| 155 | if (!mem.eql(u8, &pe_header_magic, &[_]u8{ 'P', 'E', 0, 0 })) | | |
| 156 | return error.InvalidPEHeader; | | |
| 157 | | | |
| 158 | self.coff_header = CoffHeader{ | | |
| 159 | .machine = try in.readIntLittle(u16), | | |
| 160 | .number_of_sections = try in.readIntLittle(u16), | | |
| 161 | .timedate_stamp = try in.readIntLittle(u32), | | |
| 162 | .pointer_to_symbol_table = try in.readIntLittle(u32), | | |
| 163 | .number_of_symbols = try in.readIntLittle(u32), | | |
| 164 | .size_of_optional_header = try in.readIntLittle(u16), | | |
| 165 | .characteristics = try in.readIntLittle(u16), | | |
| 166 | }; | | |
| 167 | | 827 | |
| 168 | switch (self.coff_header.machine) { | 828 | const pe_pointer_offset = 0x3C; |
| 169 | IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 => {}, | 829 | const pe_magic = "PE\x00\x00"; |
| 170 | else => return error.InvalidMachine, | | |
| 171 | } | | |
| 172 | | 830 | |
| 173 | try self.loadOptionalHeader(); | 831 | var stream = std.io.fixedBufferStream(self.data); |
| 174 | } | 832 | const reader = stream.reader(); |
| | 833 | try stream.seekTo(pe_pointer_offset); |
| | 834 | const coff_header_offset = try reader.readByte(); |
| | 835 | try stream.seekTo(coff_header_offset); |
| | 836 | var buf: [4]u8 = undefined; |
| | 837 | try reader.readNoEof(&buf); |
| | 838 | self.is_image = mem.eql(u8, pe_magic, &buf); |
| 175 | | 839 | |
| 176 | fn readStringFromTable(self: *Coff, offset: usize, buf: []u8) ![]const u8 { | 840 | // Do some basic validation upfront |
| 177 | if (self.coff_header.pointer_to_symbol_table == 0) { | 841 | if (self.is_image) { |
| 178 | // No symbol table therefore no string table | 842 | self.coff_header_offset = coff_header_offset + 4; |
| 179 | return error.MissingStringTable; | 843 | const coff_header = self.getCoffHeader(); |
| 180 | } | 844 | if (coff_header.size_of_optional_header == 0) { |
| 181 | // The string table is at the end of the symbol table and symbols are 18 bytes long | 845 | std.log.err("Required PE header missing for image file", .{}); |
| 182 | const string_table_offset = self.coff_header.pointer_to_symbol_table + (self.coff_header.number_of_symbols * 18) + offset; | 846 | return error.MissingPEHeader; |
| 183 | const in = self.in_file.reader(); | 847 | } |
| 184 | const old_pos = try self.in_file.getPos(); | | |
| 185 | | | |
| 186 | try self.in_file.seekTo(string_table_offset); | | |
| 187 | defer { | | |
| 188 | self.in_file.seekTo(old_pos) catch unreachable; | | |
| 189 | } | 848 | } |
| 190 | | 849 | |
| 191 | const str = try in.readUntilDelimiterOrEof(buf, 0); | 850 | // JK: we used to check for architecture here and throw an error if not x86 or derivative. |
| 192 | return str orelse ""; | 851 | // However I am willing to take a leap of faith and let aarch64 have a shot also. |
| 193 | } | | |
| 194 | | | |
| 195 | fn loadOptionalHeader(self: *Coff) !void { | | |
| 196 | const in = self.in_file.reader(); | | |
| 197 | const opt_header_pos = try self.in_file.getPos(); | | |
| 198 | | | |
| 199 | self.pe_header.magic = try in.readIntLittle(u16); | | |
| 200 | try self.in_file.seekTo(opt_header_pos + 16); | | |
| 201 | self.pe_header.entry_addr = try in.readIntLittle(u32); | | |
| 202 | try self.in_file.seekTo(opt_header_pos + 20); | | |
| 203 | self.pe_header.code_base = try in.readIntLittle(u32); | | |
| 204 | | | |
| 205 | // The header structure is different for 32 or 64 bit | | |
| 206 | var num_rva_pos: u64 = undefined; | | |
| 207 | if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | | |
| 208 | num_rva_pos = opt_header_pos + 92; | | |
| 209 | | | |
| 210 | try self.in_file.seekTo(opt_header_pos + 28); | | |
| 211 | const image_base32 = try in.readIntLittle(u32); | | |
| 212 | self.pe_header.image_base = image_base32; | | |
| 213 | } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | | |
| 214 | num_rva_pos = opt_header_pos + 108; | | |
| 215 | | | |
| 216 | try self.in_file.seekTo(opt_header_pos + 24); | | |
| 217 | self.pe_header.image_base = try in.readIntLittle(u64); | | |
| 218 | } else return error.InvalidPEMagic; | | |
| 219 | | | |
| 220 | try self.in_file.seekTo(num_rva_pos); | | |
| 221 | | | |
| 222 | const number_of_rva_and_sizes = try in.readIntLittle(u32); | | |
| 223 | if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) | | |
| 224 | return error.InvalidPEHeader; | | |
| 225 | | | |
| 226 | for (self.pe_header.data_directory) |*data_dir| { | | |
| 227 | data_dir.* = OptionalHeader.DataDirectory{ | | |
| 228 | .virtual_address = try in.readIntLittle(u32), | | |
| 229 | .size = try in.readIntLittle(u32), | | |
| 230 | }; | | |
| 231 | } | | |
| 232 | } | 852 | } |
| 233 | | 853 | |
| 234 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { | 854 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { |
| 235 | try self.loadSections(); | 855 | assert(self.is_image); |
| 236 | | 856 | |
| 237 | const header = blk: { | 857 | const header = blk: { |
| 238 | if (self.getSection(".buildid")) |section| { | 858 | if (self.getSectionByName(".buildid")) |hdr| { |
| 239 | break :blk section.header; | 859 | break :blk hdr; |
| 240 | } else if (self.getSection(".rdata")) |section| { | 860 | } else if (self.getSectionByName(".rdata")) |hdr| { |
| 241 | break :blk section.header; | 861 | break :blk hdr; |
| 242 | } else { | 862 | } else { |
| 243 | return error.MissingCoffSection; | 863 | return error.MissingCoffSection; |
| 244 | } | 864 | } |
| 245 | }; | 865 | }; |
| 246 | | 866 | |
| 247 | const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; | 867 | const data_dirs = self.getDataDirectories(); |
| | 868 | const debug_dir = data_dirs[DEBUG_DIRECTORY]; |
| 248 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; | 869 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; |
| 249 | | 870 | |
| 250 | const in = self.in_file.reader(); | 871 | var stream = std.io.fixedBufferStream(self.data); |
| 251 | try self.in_file.seekTo(file_offset); | 872 | const reader = stream.reader(); |
| | 873 | try stream.seekTo(file_offset); |
| 252 | | 874 | |
| 253 | // Find the correct DebugDirectoryEntry, and where its data is stored. | 875 | // Find the correct DebugDirectoryEntry, and where its data is stored. |
| 254 | // It can be in any section. | 876 | // It can be in any section. |
| 255 | const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); | 877 | const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); |
| 256 | var i: u32 = 0; | 878 | var i: u32 = 0; |
| 257 | blk: while (i < debug_dir_entry_count) : (i += 1) { | 879 | blk: while (i < debug_dir_entry_count) : (i += 1) { |
| 258 | const debug_dir_entry = try in.readStruct(DebugDirectoryEntry); | 880 | const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry); |
| 259 | if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) { | 881 | if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) { |
| 260 | for (self.sections.items) |*section| { | 882 | for (self.getSectionHeaders()) |*section| { |
| 261 | const section_start = section.header.virtual_address; | 883 | const section_start = section.virtual_address; |
| 262 | const section_size = section.header.misc.virtual_size; | 884 | const section_size = section.virtual_size; |
| 263 | const rva = debug_dir_entry.address_of_raw_data; | 885 | const rva = debug_dir_entry.address_of_raw_data; |
| 264 | const offset = rva - section_start; | 886 | const offset = rva - section_start; |
| 265 | if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) { | 887 | if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) { |
| 266 | try self.in_file.seekTo(section.header.pointer_to_raw_data + offset); | 888 | try stream.seekTo(section.pointer_to_raw_data + offset); |
| 267 | break :blk; | 889 | break :blk; |
| 268 | } | 890 | } |
| 269 | } | 891 | } |
| ... | @@ -271,19 +893,19 @@ pub const Coff = struct { | ... | @@ -271,19 +893,19 @@ pub const Coff = struct { |
| 271 | } | 893 | } |
| 272 | | 894 | |
| 273 | var cv_signature: [4]u8 = undefined; // CodeView signature | 895 | var cv_signature: [4]u8 = undefined; // CodeView signature |
| 274 | try in.readNoEof(cv_signature[0..]); | 896 | try reader.readNoEof(cv_signature[0..]); |
| 275 | // 'RSDS' indicates PDB70 format, used by lld. | 897 | // 'RSDS' indicates PDB70 format, used by lld. |
| 276 | if (!mem.eql(u8, &cv_signature, "RSDS")) | 898 | if (!mem.eql(u8, &cv_signature, "RSDS")) |
| 277 | return error.InvalidPEMagic; | 899 | return error.InvalidPEMagic; |
| 278 | try in.readNoEof(self.guid[0..]); | 900 | try reader.readNoEof(self.guid[0..]); |
| 279 | self.age = try in.readIntLittle(u32); | 901 | self.age = try reader.readIntLittle(u32); |
| 280 | | 902 | |
| 281 | // Finally read the null-terminated string. | 903 | // Finally read the null-terminated string. |
| 282 | var byte = try in.readByte(); | 904 | var byte = try reader.readByte(); |
| 283 | i = 0; | 905 | i = 0; |
| 284 | while (byte != 0 and i < buffer.len) : (i += 1) { | 906 | while (byte != 0 and i < buffer.len) : (i += 1) { |
| 285 | buffer[i] = byte; | 907 | buffer[i] = byte; |
| 286 | byte = try in.readByte(); | 908 | byte = try reader.readByte(); |
| 287 | } | 909 | } |
| 288 | | 910 | |
| 289 | if (byte != 0 and i == buffer.len) | 911 | if (byte != 0 and i == buffer.len) |
| ... | @@ -292,126 +914,232 @@ pub const Coff = struct { | ... | @@ -292,126 +914,232 @@ pub const Coff = struct { |
| 292 | return @as(usize, i); | 914 | return @as(usize, i); |
| 293 | } | 915 | } |
| 294 | | 916 | |
| 295 | pub fn loadSections(self: *Coff) !void { | 917 | pub fn getCoffHeader(self: Coff) CoffHeader { |
| 296 | if (self.sections.items.len == self.coff_header.number_of_sections) | 918 | return @ptrCast(*align(1) CoffHeader, self.data[self.coff_header_offset..][0..@sizeOf(CoffHeader)]).*; |
| 297 | return; | 919 | } |
| 298 | | 920 | |
| 299 | try self.sections.ensureTotalCapacityPrecise(self.allocator, self.coff_header.number_of_sections); | 921 | pub fn getOptionalHeader(self: Coff) OptionalHeader { |
| | 922 | assert(self.is_image); |
| | 923 | const offset = self.coff_header_offset + @sizeOf(CoffHeader); |
| | 924 | return @ptrCast(*align(1) OptionalHeader, self.data[offset..][0..@sizeOf(OptionalHeader)]).*; |
| | 925 | } |
| 300 | | 926 | |
| 301 | const in = self.in_file.reader(); | 927 | pub fn getOptionalHeader32(self: Coff) OptionalHeaderPE32 { |
| | 928 | assert(self.is_image); |
| | 929 | const offset = self.coff_header_offset + @sizeOf(CoffHeader); |
| | 930 | return @ptrCast(*align(1) OptionalHeaderPE32, self.data[offset..][0..@sizeOf(OptionalHeaderPE32)]).*; |
| | 931 | } |
| 302 | | 932 | |
| 303 | var name: [32]u8 = undefined; | 933 | pub fn getOptionalHeader64(self: Coff) OptionalHeaderPE64 { |
| | 934 | assert(self.is_image); |
| | 935 | const offset = self.coff_header_offset + @sizeOf(CoffHeader); |
| | 936 | return @ptrCast(*align(1) OptionalHeaderPE64, self.data[offset..][0..@sizeOf(OptionalHeaderPE64)]).*; |
| | 937 | } |
| 304 | | 938 | |
| 305 | var i: u16 = 0; | 939 | pub fn getImageBase(self: Coff) u64 { |
| 306 | while (i < self.coff_header.number_of_sections) : (i += 1) { | 940 | const hdr = self.getOptionalHeader(); |
| 307 | try in.readNoEof(name[0..8]); | 941 | return switch (hdr.magic) { |
| | 942 | IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().image_base, |
| | 943 | IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().image_base, |
| | 944 | else => unreachable, // We assume we have validated the header already |
| | 945 | }; |
| | 946 | } |
| 308 | | 947 | |
| 309 | if (name[0] == '/') { | 948 | pub fn getNumberOfDataDirectories(self: Coff) u32 { |
| 310 | // This is a long name and stored in the string table | 949 | const hdr = self.getOptionalHeader(); |
| 311 | const offset_len = mem.indexOfScalar(u8, name[1..], 0) orelse 7; | 950 | return switch (hdr.magic) { |
| | 951 | IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().number_of_rva_and_sizes, |
| | 952 | IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().number_of_rva_and_sizes, |
| | 953 | else => unreachable, // We assume we have validated the header already |
| | 954 | }; |
| | 955 | } |
| 312 | | 956 | |
| 313 | const str_offset = try std.fmt.parseInt(u32, name[1 .. offset_len + 1], 10); | 957 | pub fn getDataDirectories(self: *const Coff) []align(1) const ImageDataDirectory { |
| 314 | const str = try self.readStringFromTable(str_offset, &name); | 958 | const hdr = self.getOptionalHeader(); |
| 315 | std.mem.set(u8, name[str.len..], 0); | 959 | const size: usize = switch (hdr.magic) { |
| 316 | } else { | 960 | IMAGE_NT_OPTIONAL_HDR32_MAGIC => @sizeOf(OptionalHeaderPE32), |
| 317 | std.mem.set(u8, name[8..], 0); | 961 | IMAGE_NT_OPTIONAL_HDR64_MAGIC => @sizeOf(OptionalHeaderPE64), |
| 318 | } | 962 | else => unreachable, // We assume we have validated the header already |
| | 963 | }; |
| | 964 | const offset = self.coff_header_offset + @sizeOf(CoffHeader) + size; |
| | 965 | return @ptrCast([*]align(1) ImageDataDirectory, self.data[offset..])[0..self.getNumberOfDataDirectories()]; |
| | 966 | } |
| 319 | | 967 | |
| 320 | self.sections.appendAssumeCapacity(Section{ | 968 | pub fn getSymtab(self: *const Coff) ?Symtab { |
| 321 | .header = SectionHeader{ | 969 | const coff_header = self.getCoffHeader(); |
| 322 | .name = name, | 970 | if (coff_header.pointer_to_symbol_table == 0) return null; |
| 323 | .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) }, | 971 | |
| 324 | .virtual_address = try in.readIntLittle(u32), | 972 | const offset = coff_header.pointer_to_symbol_table; |
| 325 | .size_of_raw_data = try in.readIntLittle(u32), | 973 | const size = coff_header.number_of_symbols * Symbol.sizeOf(); |
| 326 | .pointer_to_raw_data = try in.readIntLittle(u32), | 974 | return .{ .buffer = self.data[offset..][0..size] }; |
| 327 | .pointer_to_relocations = try in.readIntLittle(u32), | 975 | } |
| 328 | .pointer_to_line_numbers = try in.readIntLittle(u32), | 976 | |
| 329 | .number_of_relocations = try in.readIntLittle(u16), | 977 | pub fn getStrtab(self: *const Coff) ?Strtab { |
| 330 | .number_of_line_numbers = try in.readIntLittle(u16), | 978 | const coff_header = self.getCoffHeader(); |
| 331 | .characteristics = try in.readIntLittle(u32), | 979 | if (coff_header.pointer_to_symbol_table == 0) return null; |
| 332 | }, | 980 | |
| 333 | }); | 981 | const offset = coff_header.pointer_to_symbol_table + Symbol.sizeOf() * coff_header.number_of_symbols; |
| 334 | } | 982 | const size = mem.readIntLittle(u32, self.data[offset..][0..4]); |
| | 983 | return .{ .buffer = self.data[offset..][0..size] }; |
| | 984 | } |
| | 985 | |
| | 986 | pub fn getSectionHeaders(self: *const Coff) []align(1) const SectionHeader { |
| | 987 | const coff_header = self.getCoffHeader(); |
| | 988 | const offset = self.coff_header_offset + @sizeOf(CoffHeader) + coff_header.size_of_optional_header; |
| | 989 | return @ptrCast([*]align(1) SectionHeader, self.data.ptr + offset)[0..coff_header.number_of_sections]; |
| 335 | } | 990 | } |
| 336 | | 991 | |
| 337 | pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section { | 992 | pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) []const u8 { |
| 338 | for (self.sections.items) |*sec| { | 993 | const name = sect_hdr.getName() orelse blk: { |
| 339 | if (mem.eql(u8, sec.header.name[0..name.len], name)) { | 994 | const strtab = self.getStrtab().?; |
| 340 | return sec; | 995 | const name_offset = sect_hdr.getNameOffset().?; |
| | 996 | break :blk strtab.get(name_offset); |
| | 997 | }; |
| | 998 | return name; |
| | 999 | } |
| | 1000 | |
| | 1001 | pub fn getSectionByName(self: *const Coff, comptime name: []const u8) ?*align(1) const SectionHeader { |
| | 1002 | for (self.getSectionHeaders()) |*sect| { |
| | 1003 | if (mem.eql(u8, self.getSectionName(sect), name)) { |
| | 1004 | return sect; |
| 341 | } | 1005 | } |
| 342 | } | 1006 | } |
| 343 | return null; | 1007 | return null; |
| 344 | } | 1008 | } |
| 345 | | 1009 | |
| 346 | // Return an owned slice full of the section data | 1010 | // Return an owned slice full of the section data |
| 347 | pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 { | 1011 | pub fn getSectionDataAlloc(self: *const Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 { |
| 348 | const sec = for (self.sections.items) |*sec| { | 1012 | const sec = self.getSectionByName(name) orelse return error.MissingCoffSection; |
| 349 | if (mem.eql(u8, sec.header.name[0..name.len], name)) { | 1013 | const out_buff = try allocator.alloc(u8, sec.virtual_size); |
| 350 | break sec; | 1014 | mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]); |
| 351 | } | | |
| 352 | } else { | | |
| 353 | return error.MissingCoffSection; | | |
| 354 | }; | | |
| 355 | const in = self.in_file.reader(); | | |
| 356 | try self.in_file.seekTo(sec.header.pointer_to_raw_data); | | |
| 357 | const out_buff = try allocator.alloc(u8, sec.header.misc.virtual_size); | | |
| 358 | try in.readNoEof(out_buff); | | |
| 359 | return out_buff; | 1015 | return out_buff; |
| 360 | } | 1016 | } |
| 361 | }; | | |
| 362 | | 1017 | |
| 363 | const CoffHeader = struct { | 1018 | pub const Symtab = struct { |
| 364 | machine: u16, | 1019 | buffer: []const u8, |
| 365 | number_of_sections: u16, | | |
| 366 | timedate_stamp: u32, | | |
| 367 | pointer_to_symbol_table: u32, | | |
| 368 | number_of_symbols: u32, | | |
| 369 | size_of_optional_header: u16, | | |
| 370 | characteristics: u16, | | |
| 371 | }; | | |
| 372 | | 1020 | |
| 373 | const OptionalHeader = struct { | 1021 | fn len(self: Symtab) usize { |
| 374 | const DataDirectory = struct { | 1022 | return @divExact(self.buffer.len, Symbol.sizeOf()); |
| 375 | virtual_address: u32, | 1023 | } |
| 376 | size: u32, | | |
| 377 | }; | | |
| 378 | | 1024 | |
| 379 | magic: u16, | 1025 | const Tag = enum { |
| 380 | data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, | 1026 | symbol, |
| 381 | entry_addr: u32, | 1027 | func_def, |
| 382 | code_base: u32, | 1028 | debug_info, |
| 383 | image_base: u64, | 1029 | weak_ext, |
| 384 | }; | 1030 | file_def, |
| | 1031 | sect_def, |
| | 1032 | }; |
| 385 | | 1033 | |
| 386 | const DebugDirectoryEntry = extern struct { | 1034 | const Record = union(Tag) { |
| 387 | characteristiccs: u32, | 1035 | symbol: Symbol, |
| 388 | time_date_stamp: u32, | 1036 | debug_info: DebugInfoDefinition, |
| 389 | major_version: u16, | 1037 | func_def: FunctionDefinition, |
| 390 | minor_version: u16, | 1038 | weak_ext: WeakExternalDefinition, |
| 391 | @"type": u32, | 1039 | file_def: FileDefinition, |
| 392 | size_of_data: u32, | 1040 | sect_def: SectionDefinition, |
| 393 | address_of_raw_data: u32, | 1041 | }; |
| 394 | pointer_to_raw_data: u32, | | |
| 395 | }; | | |
| 396 | | 1042 | |
| 397 | pub const Section = struct { | 1043 | /// Lives as long as Symtab instance. |
| 398 | header: SectionHeader, | 1044 | fn at(self: Symtab, index: usize, tag: Tag) Record { |
| 399 | }; | 1045 | const offset = index * Symbol.sizeOf(); |
| | 1046 | const raw = self.buffer[offset..][0..Symbol.sizeOf()]; |
| | 1047 | return switch (tag) { |
| | 1048 | .symbol => .{ .symbol = asSymbol(raw) }, |
| | 1049 | .debug_info => .{ .debug_info = asDebugInfo(raw) }, |
| | 1050 | .func_def => .{ .func_def = asFuncDef(raw) }, |
| | 1051 | .weak_ext => .{ .weak_ext = asWeakExtDef(raw) }, |
| | 1052 | .file_def => .{ .file_def = asFileDef(raw) }, |
| | 1053 | .sect_def => .{ .sect_def = asSectDef(raw) }, |
| | 1054 | }; |
| | 1055 | } |
| 400 | | 1056 | |
| 401 | const SectionHeader = struct { | 1057 | fn asSymbol(raw: []const u8) Symbol { |
| 402 | const Misc = union { | 1058 | return .{ |
| 403 | physical_address: u32, | 1059 | .name = raw[0..8].*, |
| 404 | virtual_size: u32, | 1060 | .value = mem.readIntLittle(u32, raw[8..12]), |
| | 1061 | .section_number = @intToEnum(SectionNumber, mem.readIntLittle(u16, raw[12..14])), |
| | 1062 | .@"type" = @bitCast(SymType, mem.readIntLittle(u16, raw[14..16])), |
| | 1063 | .storage_class = @intToEnum(StorageClass, raw[16]), |
| | 1064 | .number_of_aux_symbols = raw[17], |
| | 1065 | }; |
| | 1066 | } |
| | 1067 | |
| | 1068 | fn asDebugInfo(raw: []const u8) DebugInfoDefinition { |
| | 1069 | return .{ |
| | 1070 | .unused_1 = raw[0..4].*, |
| | 1071 | .linenumber = mem.readIntLittle(u16, raw[4..6]), |
| | 1072 | .unused_2 = raw[6..12].*, |
| | 1073 | .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]), |
| | 1074 | .unused_3 = raw[16..18].*, |
| | 1075 | }; |
| | 1076 | } |
| | 1077 | |
| | 1078 | fn asFuncDef(raw: []const u8) FunctionDefinition { |
| | 1079 | return .{ |
| | 1080 | .tag_index = mem.readIntLittle(u32, raw[0..4]), |
| | 1081 | .total_size = mem.readIntLittle(u32, raw[4..8]), |
| | 1082 | .pointer_to_linenumber = mem.readIntLittle(u32, raw[8..12]), |
| | 1083 | .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]), |
| | 1084 | .unused = raw[16..18].*, |
| | 1085 | }; |
| | 1086 | } |
| | 1087 | |
| | 1088 | fn asWeakExtDef(raw: []const u8) WeakExternalDefinition { |
| | 1089 | return .{ |
| | 1090 | .tag_index = mem.readIntLittle(u32, raw[0..4]), |
| | 1091 | .flag = @intToEnum(WeakExternalFlag, mem.readIntLittle(u32, raw[4..8])), |
| | 1092 | .unused = raw[8..18].*, |
| | 1093 | }; |
| | 1094 | } |
| | 1095 | |
| | 1096 | fn asFileDef(raw: []const u8) FileDefinition { |
| | 1097 | return .{ |
| | 1098 | .file_name = raw[0..18].*, |
| | 1099 | }; |
| | 1100 | } |
| | 1101 | |
| | 1102 | fn asSectDef(raw: []const u8) SectionDefinition { |
| | 1103 | return .{ |
| | 1104 | .length = mem.readIntLittle(u32, raw[0..4]), |
| | 1105 | .number_of_relocations = mem.readIntLittle(u16, raw[4..6]), |
| | 1106 | .number_of_linenumbers = mem.readIntLittle(u16, raw[6..8]), |
| | 1107 | .checksum = mem.readIntLittle(u32, raw[8..12]), |
| | 1108 | .number = mem.readIntLittle(u16, raw[12..14]), |
| | 1109 | .selection = @intToEnum(ComdatSelection, raw[14]), |
| | 1110 | .unused = raw[15..18].*, |
| | 1111 | }; |
| | 1112 | } |
| | 1113 | |
| | 1114 | const Slice = struct { |
| | 1115 | buffer: []const u8, |
| | 1116 | num: usize, |
| | 1117 | count: usize = 0, |
| | 1118 | |
| | 1119 | /// Lives as long as Symtab instance. |
| | 1120 | fn next(self: *Slice) ?Symbol { |
| | 1121 | if (self.count >= self.num) return null; |
| | 1122 | const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]); |
| | 1123 | self.count += 1; |
| | 1124 | self.buffer = self.buffer[Symbol.sizeOf()..]; |
| | 1125 | return sym; |
| | 1126 | } |
| | 1127 | }; |
| | 1128 | |
| | 1129 | fn slice(self: Symtab, start: usize, end: ?usize) Slice { |
| | 1130 | const offset = start * Symbol.sizeOf(); |
| | 1131 | const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len; |
| | 1132 | const num = @divExact(llen - offset, Symbol.sizeOf()); |
| | 1133 | return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num }; |
| | 1134 | } |
| 405 | }; | 1135 | }; |
| 406 | | 1136 | |
| 407 | name: [32]u8, | 1137 | pub const Strtab = struct { |
| 408 | misc: Misc, | 1138 | buffer: []const u8, |
| 409 | virtual_address: u32, | 1139 | |
| 410 | size_of_raw_data: u32, | 1140 | fn get(self: Strtab, off: u32) []const u8 { |
| 411 | pointer_to_raw_data: u32, | 1141 | assert(off < self.buffer.len); |
| 412 | pointer_to_relocations: u32, | 1142 | return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0); |
| 413 | pointer_to_line_numbers: u32, | 1143 | } |
| 414 | number_of_relocations: u16, | 1144 | }; |
| 415 | number_of_line_numbers: u16, | | |
| 416 | characteristics: u32, | | |
| 417 | }; | 1145 | }; |