| ... | ... | @@ -1,14 +1,731 @@ |
| 1 | 1 | const std = @import("std.zig"); |
| 2 | const assert = std.debug.assert; |
| 2 | 3 | const io = std.io; |
| 3 | 4 | const mem = std.mem; |
| 4 | 5 | const os = std.os; |
| 5 | | const File = std.fs.File; |
| 6 | const fs = std.fs; |
| 6 | 7 | |
| 7 | | // CoffHeader.machine values |
| 8 | | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx |
| 9 | | const IMAGE_FILE_MACHINE_I386 = 0x014c; |
| 10 | | const IMAGE_FILE_MACHINE_IA64 = 0x0200; |
| 11 | | const IMAGE_FILE_MACHINE_AMD64 = 0x8664; |
| 8 | pub const CoffHeaderFlags = packed struct { |
| 9 | /// Image only, Windows CE, and Microsoft Windows NT and later. |
| 10 | /// This indicates that the file does not contain base relocations |
| 11 | /// and must therefore be loaded at its preferred base address. |
| 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 | 730 | pub const MachineType = enum(u16) { |
| 14 | 731 | Unknown = 0x0, |
| ... | ... | @@ -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 | 797 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; |
| 100 | 798 | const IMAGE_DEBUG_TYPE_CODEVIEW = 2; |
| 101 | 799 | const DEBUG_DIRECTORY = 6; |
| ... | ... | @@ -104,166 +802,90 @@ pub const CoffError = error{ |
| 104 | 802 | InvalidPEMagic, |
| 105 | 803 | InvalidPEHeader, |
| 106 | 804 | InvalidMachine, |
| 805 | MissingPEHeader, |
| 107 | 806 | MissingCoffSection, |
| 108 | 807 | MissingStringTable, |
| 109 | 808 | }; |
| 110 | 809 | |
| 111 | 810 | // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format |
| 112 | 811 | pub const Coff = struct { |
| 113 | | in_file: File, |
| 114 | 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, |
| 117 | | pe_header: OptionalHeader, |
| 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 | | } |
| 817 | guid: [16]u8 = undefined, |
| 818 | age: u32 = undefined, |
| 133 | 819 | |
| 134 | 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 { |
| 139 | | const pe_pointer_offset = 0x3C; |
| 140 | | |
| 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 | | }; |
| 824 | /// Takes ownership of `data`. |
| 825 | pub fn parse(self: *Coff, data: []const u8) !void { |
| 826 | self.data = data; |
| 167 | 827 | |
| 168 | | switch (self.coff_header.machine) { |
| 169 | | IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 => {}, |
| 170 | | else => return error.InvalidMachine, |
| 171 | | } |
| 828 | const pe_pointer_offset = 0x3C; |
| 829 | const pe_magic = "PE\x00\x00"; |
| 172 | 830 | |
| 173 | | try self.loadOptionalHeader(); |
| 174 | | } |
| 831 | var stream = std.io.fixedBufferStream(self.data); |
| 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 { |
| 177 | | if (self.coff_header.pointer_to_symbol_table == 0) { |
| 178 | | // No symbol table therefore no string table |
| 179 | | return error.MissingStringTable; |
| 180 | | } |
| 181 | | // The string table is at the end of the symbol table and symbols are 18 bytes long |
| 182 | | const string_table_offset = self.coff_header.pointer_to_symbol_table + (self.coff_header.number_of_symbols * 18) + offset; |
| 183 | | const in = self.in_file.reader(); |
| 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; |
| 840 | // Do some basic validation upfront |
| 841 | if (self.is_image) { |
| 842 | self.coff_header_offset = coff_header_offset + 4; |
| 843 | const coff_header = self.getCoffHeader(); |
| 844 | if (coff_header.size_of_optional_header == 0) { |
| 845 | std.log.err("Required PE header missing for image file", .{}); |
| 846 | return error.MissingPEHeader; |
| 847 | } |
| 189 | 848 | } |
| 190 | 849 | |
| 191 | | const str = try in.readUntilDelimiterOrEof(buf, 0); |
| 192 | | return str orelse ""; |
| 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 | | } |
| 850 | // JK: we used to check for architecture here and throw an error if not x86 or derivative. |
| 851 | // However I am willing to take a leap of faith and let aarch64 have a shot also. |
| 232 | 852 | } |
| 233 | 853 | |
| 234 | 854 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { |
| 235 | | try self.loadSections(); |
| 855 | assert(self.is_image); |
| 236 | 856 | |
| 237 | 857 | const header = blk: { |
| 238 | | if (self.getSection(".buildid")) |section| { |
| 239 | | break :blk section.header; |
| 240 | | } else if (self.getSection(".rdata")) |section| { |
| 241 | | break :blk section.header; |
| 858 | if (self.getSectionByName(".buildid")) |hdr| { |
| 859 | break :blk hdr; |
| 860 | } else if (self.getSectionByName(".rdata")) |hdr| { |
| 861 | break :blk hdr; |
| 242 | 862 | } else { |
| 243 | 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 | 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(); |
| 251 | | try self.in_file.seekTo(file_offset); |
| 871 | var stream = std.io.fixedBufferStream(self.data); |
| 872 | const reader = stream.reader(); |
| 873 | try stream.seekTo(file_offset); |
| 252 | 874 | |
| 253 | 875 | // Find the correct DebugDirectoryEntry, and where its data is stored. |
| 254 | 876 | // It can be in any section. |
| 255 | 877 | const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); |
| 256 | 878 | var i: u32 = 0; |
| 257 | 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 | 881 | if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) { |
| 260 | | for (self.sections.items) |*section| { |
| 261 | | const section_start = section.header.virtual_address; |
| 262 | | const section_size = section.header.misc.virtual_size; |
| 882 | for (self.getSectionHeaders()) |*section| { |
| 883 | const section_start = section.virtual_address; |
| 884 | const section_size = section.virtual_size; |
| 263 | 885 | const rva = debug_dir_entry.address_of_raw_data; |
| 264 | 886 | const offset = rva - section_start; |
| 265 | 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 | 889 | break :blk; |
| 268 | 890 | } |
| 269 | 891 | } |
| ... | ... | @@ -271,19 +893,19 @@ pub const Coff = struct { |
| 271 | 893 | } |
| 272 | 894 | |
| 273 | 895 | var cv_signature: [4]u8 = undefined; // CodeView signature |
| 274 | | try in.readNoEof(cv_signature[0..]); |
| 896 | try reader.readNoEof(cv_signature[0..]); |
| 275 | 897 | // 'RSDS' indicates PDB70 format, used by lld. |
| 276 | 898 | if (!mem.eql(u8, &cv_signature, "RSDS")) |
| 277 | 899 | return error.InvalidPEMagic; |
| 278 | | try in.readNoEof(self.guid[0..]); |
| 279 | | self.age = try in.readIntLittle(u32); |
| 900 | try reader.readNoEof(self.guid[0..]); |
| 901 | self.age = try reader.readIntLittle(u32); |
| 280 | 902 | |
| 281 | 903 | // Finally read the null-terminated string. |
| 282 | | var byte = try in.readByte(); |
| 904 | var byte = try reader.readByte(); |
| 283 | 905 | i = 0; |
| 284 | 906 | while (byte != 0 and i < buffer.len) : (i += 1) { |
| 285 | 907 | buffer[i] = byte; |
| 286 | | byte = try in.readByte(); |
| 908 | byte = try reader.readByte(); |
| 287 | 909 | } |
| 288 | 910 | |
| 289 | 911 | if (byte != 0 and i == buffer.len) |
| ... | ... | @@ -292,126 +914,232 @@ pub const Coff = struct { |
| 292 | 914 | return @as(usize, i); |
| 293 | 915 | } |
| 294 | 916 | |
| 295 | | pub fn loadSections(self: *Coff) !void { |
| 296 | | if (self.sections.items.len == self.coff_header.number_of_sections) |
| 297 | | return; |
| 917 | pub fn getCoffHeader(self: Coff) CoffHeader { |
| 918 | return @ptrCast(*align(1) CoffHeader, self.data[self.coff_header_offset..][0..@sizeOf(CoffHeader)]).*; |
| 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; |
| 306 | | while (i < self.coff_header.number_of_sections) : (i += 1) { |
| 307 | | try in.readNoEof(name[0..8]); |
| 939 | pub fn getImageBase(self: Coff) u64 { |
| 940 | const hdr = self.getOptionalHeader(); |
| 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] == '/') { |
| 310 | | // This is a long name and stored in the string table |
| 311 | | const offset_len = mem.indexOfScalar(u8, name[1..], 0) orelse 7; |
| 948 | pub fn getNumberOfDataDirectories(self: Coff) u32 { |
| 949 | const hdr = self.getOptionalHeader(); |
| 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); |
| 314 | | const str = try self.readStringFromTable(str_offset, &name); |
| 315 | | std.mem.set(u8, name[str.len..], 0); |
| 316 | | } else { |
| 317 | | std.mem.set(u8, name[8..], 0); |
| 318 | | } |
| 957 | pub fn getDataDirectories(self: *const Coff) []align(1) const ImageDataDirectory { |
| 958 | const hdr = self.getOptionalHeader(); |
| 959 | const size: usize = switch (hdr.magic) { |
| 960 | IMAGE_NT_OPTIONAL_HDR32_MAGIC => @sizeOf(OptionalHeaderPE32), |
| 961 | IMAGE_NT_OPTIONAL_HDR64_MAGIC => @sizeOf(OptionalHeaderPE64), |
| 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{ |
| 321 | | .header = SectionHeader{ |
| 322 | | .name = name, |
| 323 | | .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) }, |
| 324 | | .virtual_address = try in.readIntLittle(u32), |
| 325 | | .size_of_raw_data = try in.readIntLittle(u32), |
| 326 | | .pointer_to_raw_data = try in.readIntLittle(u32), |
| 327 | | .pointer_to_relocations = try in.readIntLittle(u32), |
| 328 | | .pointer_to_line_numbers = try in.readIntLittle(u32), |
| 329 | | .number_of_relocations = try in.readIntLittle(u16), |
| 330 | | .number_of_line_numbers = try in.readIntLittle(u16), |
| 331 | | .characteristics = try in.readIntLittle(u32), |
| 332 | | }, |
| 333 | | }); |
| 334 | | } |
| 968 | pub fn getSymtab(self: *const Coff) ?Symtab { |
| 969 | const coff_header = self.getCoffHeader(); |
| 970 | if (coff_header.pointer_to_symbol_table == 0) return null; |
| 971 | |
| 972 | const offset = coff_header.pointer_to_symbol_table; |
| 973 | const size = coff_header.number_of_symbols * Symbol.sizeOf(); |
| 974 | return .{ .buffer = self.data[offset..][0..size] }; |
| 975 | } |
| 976 | |
| 977 | pub fn getStrtab(self: *const Coff) ?Strtab { |
| 978 | const coff_header = self.getCoffHeader(); |
| 979 | if (coff_header.pointer_to_symbol_table == 0) return null; |
| 980 | |
| 981 | const offset = coff_header.pointer_to_symbol_table + Symbol.sizeOf() * coff_header.number_of_symbols; |
| 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 { |
| 338 | | for (self.sections.items) |*sec| { |
| 339 | | if (mem.eql(u8, sec.header.name[0..name.len], name)) { |
| 340 | | return sec; |
| 992 | pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) []const u8 { |
| 993 | const name = sect_hdr.getName() orelse blk: { |
| 994 | const strtab = self.getStrtab().?; |
| 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 | 1007 | return null; |
| 344 | 1008 | } |
| 345 | 1009 | |
| 346 | 1010 | // Return an owned slice full of the section data |
| 347 | | pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 { |
| 348 | | const sec = for (self.sections.items) |*sec| { |
| 349 | | if (mem.eql(u8, sec.header.name[0..name.len], name)) { |
| 350 | | break sec; |
| 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); |
| 1011 | pub fn getSectionDataAlloc(self: *const Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 { |
| 1012 | const sec = self.getSectionByName(name) orelse return error.MissingCoffSection; |
| 1013 | const out_buff = try allocator.alloc(u8, sec.virtual_size); |
| 1014 | mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]); |
| 359 | 1015 | return out_buff; |
| 360 | 1016 | } |
| 361 | | }; |
| 362 | 1017 | |
| 363 | | const CoffHeader = struct { |
| 364 | | machine: u16, |
| 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 | | }; |
| 1018 | pub const Symtab = struct { |
| 1019 | buffer: []const u8, |
| 372 | 1020 | |
| 373 | | const OptionalHeader = struct { |
| 374 | | const DataDirectory = struct { |
| 375 | | virtual_address: u32, |
| 376 | | size: u32, |
| 377 | | }; |
| 1021 | fn len(self: Symtab) usize { |
| 1022 | return @divExact(self.buffer.len, Symbol.sizeOf()); |
| 1023 | } |
| 378 | 1024 | |
| 379 | | magic: u16, |
| 380 | | data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, |
| 381 | | entry_addr: u32, |
| 382 | | code_base: u32, |
| 383 | | image_base: u64, |
| 384 | | }; |
| 1025 | const Tag = enum { |
| 1026 | symbol, |
| 1027 | func_def, |
| 1028 | debug_info, |
| 1029 | weak_ext, |
| 1030 | file_def, |
| 1031 | sect_def, |
| 1032 | }; |
| 385 | 1033 | |
| 386 | | const DebugDirectoryEntry = extern struct { |
| 387 | | characteristiccs: u32, |
| 388 | | time_date_stamp: u32, |
| 389 | | major_version: u16, |
| 390 | | minor_version: u16, |
| 391 | | @"type": u32, |
| 392 | | size_of_data: u32, |
| 393 | | address_of_raw_data: u32, |
| 394 | | pointer_to_raw_data: u32, |
| 395 | | }; |
| 1034 | const Record = union(Tag) { |
| 1035 | symbol: Symbol, |
| 1036 | debug_info: DebugInfoDefinition, |
| 1037 | func_def: FunctionDefinition, |
| 1038 | weak_ext: WeakExternalDefinition, |
| 1039 | file_def: FileDefinition, |
| 1040 | sect_def: SectionDefinition, |
| 1041 | }; |
| 396 | 1042 | |
| 397 | | pub const Section = struct { |
| 398 | | header: SectionHeader, |
| 399 | | }; |
| 1043 | /// Lives as long as Symtab instance. |
| 1044 | fn at(self: Symtab, index: usize, tag: Tag) Record { |
| 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 { |
| 402 | | const Misc = union { |
| 403 | | physical_address: u32, |
| 404 | | virtual_size: u32, |
| 1057 | fn asSymbol(raw: []const u8) Symbol { |
| 1058 | return .{ |
| 1059 | .name = raw[0..8].*, |
| 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, |
| 408 | | misc: Misc, |
| 409 | | virtual_address: u32, |
| 410 | | size_of_raw_data: u32, |
| 411 | | pointer_to_raw_data: u32, |
| 412 | | pointer_to_relocations: u32, |
| 413 | | pointer_to_line_numbers: u32, |
| 414 | | number_of_relocations: u16, |
| 415 | | number_of_line_numbers: u16, |
| 416 | | characteristics: u32, |
| 1137 | pub const Strtab = struct { |
| 1138 | buffer: []const u8, |
| 1139 | |
| 1140 | fn get(self: Strtab, off: u32) []const u8 { |
| 1141 | assert(off < self.buffer.len); |
| 1142 | return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0); |
| 1143 | } |
| 1144 | }; |
| 417 | 1145 | }; |