1const std = @import("std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4
5pub const archive_signature = "!<arch>\n";
6pub const archive_end_of_header = "`\n";
7
8pub const pe_signature = "PE\x00\x00";
9pub const pe_pointer_offset = 0x3C;
10
11pub const Header = extern struct {
12 /// The number that identifies the type of target machine.
13 machine: IMAGE.FILE.MACHINE,
14
15 /// The number of sections. This indicates the size of the section table, which immediately follows the headers.
16 number_of_sections: u16,
17
18 /// The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value),
19 /// which indicates when the file was created.
20 time_date_stamp: u32,
21
22 /// The file offset of the COFF symbol table, or zero if no COFF symbol table is present.
23 /// This value should be zero for an image because COFF debugging information is deprecated.
24 pointer_to_symbol_table: u32,
25
26 /// The number of entries in the symbol table.
27 /// This data can be used to locate the string table, which immediately follows the symbol table.
28 /// This value should be zero for an image because COFF debugging information is deprecated.
29 number_of_symbols: u32,
30
31 /// The size of the optional header, which is required for executable files but not for object files.
32 /// This value should be zero for an object file. For a description of the header format, see Optional Header (Image Only).
33 size_of_optional_header: u16,
34
35 /// The flags that indicate the attributes of the file.
36 flags: Header.Flags,
37
38 pub const Flags = packed struct(u16) {
39 /// Image only, Windows CE, and Microsoft Windows NT and later.
40 /// This indicates that the file does not contain base relocations
41 /// and must therefore be loaded at its preferred base address.
42 /// If the base address is not available, the loader reports an error.
43 /// The default behavior of the linker is to strip base relocations
44 /// from executable (EXE) files.
45 RELOCS_STRIPPED: bool = false,
46
47 /// Image only. This indicates that the image file is valid and can be run.
48 /// If this flag is not set, it indicates a linker error.
49 EXECUTABLE_IMAGE: bool = false,
50
51 /// COFF line numbers have been removed. This flag is deprecated and should be zero.
52 LINE_NUMS_STRIPPED: bool = false,
53
54 /// COFF symbol table entries for local symbols have been removed.
55 /// This flag is deprecated and should be zero.
56 LOCAL_SYMS_STRIPPED: bool = false,
57
58 /// Obsolete. Aggressively trim working set.
59 /// This flag is deprecated for Windows 2000 and later and must be zero.
60 AGGRESSIVE_WS_TRIM: bool = false,
61
62 /// Application can handle > 2-GB addresses.
63 LARGE_ADDRESS_AWARE: bool = false,
64
65 /// This flag is reserved for future use.
66 RESERVED: bool = false,
67
68 /// Little endian: the least significant bit (LSB) precedes the
69 /// most significant bit (MSB) in memory. This flag is deprecated and should be zero.
70 BYTES_REVERSED_LO: bool = false,
71
72 /// Machine is based on a 32-bit-word architecture.
73 @"32BIT_MACHINE": bool = false,
74
75 /// Debugging information is removed from the image file.
76 DEBUG_STRIPPED: bool = false,
77
78 /// If the image is on removable media, fully load it and copy it to the swap file.
79 REMOVABLE_RUN_FROM_SWAP: bool = false,
80
81 /// If the image is on network media, fully load it and copy it to the swap file.
82 NET_RUN_FROM_SWAP: bool = false,
83
84 /// The image file is a system file, not a user program.
85 SYSTEM: bool = false,
86
87 /// The image file is a dynamic-link library (DLL).
88 /// Such files are considered executable files for almost all purposes,
89 /// although they cannot be directly run.
90 DLL: bool = false,
91
92 /// The file should be run only on a uniprocessor machine.
93 UP_SYSTEM_ONLY: bool = false,
94
95 /// Big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero.
96 BYTES_REVERSED_HI: bool = false,
97 };
98};
99
100// OptionalHeader.magic values
101// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
102pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC = @backingInt(OptionalHeader.Magic.PE32);
103pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC = @backingInt(OptionalHeader.Magic.@"PE32+");
104
105pub const DllFlags = packed struct(u16) {
106 _reserved_0: u5 = 0,
107
108 /// Image can handle a high entropy 64-bit virtual address space.
109 HIGH_ENTROPY_VA: bool = false,
110
111 /// DLL can be relocated at load time.
112 DYNAMIC_BASE: bool = false,
113
114 /// Code Integrity checks are enforced.
115 FORCE_INTEGRITY: bool = false,
116
117 /// Image is NX compatible.
118 NX_COMPAT: bool = false,
119
120 /// Isolation aware, but do not isolate the image.
121 NO_ISOLATION: bool = false,
122
123 /// Does not use structured exception (SE) handling. No SE handler may be called in this image.
124 NO_SEH: bool = false,
125
126 /// Do not bind the image.
127 NO_BIND: bool = false,
128
129 /// Image must execute in an AppContainer.
130 APPCONTAINER: bool = false,
131
132 /// A WDM driver.
133 WDM_DRIVER: bool = false,
134
135 /// Image supports Control Flow Guard.
136 GUARD_CF: bool = false,
137
138 /// Terminal Server aware.
139 TERMINAL_SERVER_AWARE: bool = false,
140};
141
142pub const Subsystem = enum(u16) {
143 /// An unknown subsystem
144 UNKNOWN = 0,
145
146 /// Device drivers and native Windows processes
147 NATIVE = 1,
148
149 /// The Windows graphical user interface (GUI) subsystem
150 WINDOWS_GUI = 2,
151
152 /// The Windows character subsystem
153 WINDOWS_CUI = 3,
154
155 /// The OS/2 character subsystem
156 OS2_CUI = 5,
157
158 /// The Posix character subsystem
159 POSIX_CUI = 7,
160
161 /// Native Win9x driver
162 NATIVE_WINDOWS = 8,
163
164 /// Windows CE
165 WINDOWS_CE_GUI = 9,
166
167 /// An Extensible Firmware Interface (EFI) application
168 EFI_APPLICATION = 10,
169
170 /// An EFI driver with boot services
171 EFI_BOOT_SERVICE_DRIVER = 11,
172
173 /// An EFI driver with run-time services
174 EFI_RUNTIME_DRIVER = 12,
175
176 /// An EFI ROM image
177 EFI_ROM = 13,
178
179 /// XBOX
180 XBOX = 14,
181
182 /// Windows boot application
183 WINDOWS_BOOT_APPLICATION = 16,
184
185 _,
186};
187
188pub const OptionalHeader = extern struct {
189 magic: OptionalHeader.Magic,
190 major_linker_version: u8,
191 minor_linker_version: u8,
192 size_of_code: u32,
193 size_of_initialized_data: u32,
194 size_of_uninitialized_data: u32,
195 address_of_entry_point: u32,
196 base_of_code: u32,
197
198 pub const Magic = enum(u16) {
199 PE32 = 0x10b,
200 @"PE32+" = 0x20b,
201 _,
202 };
203
204 pub const PE32 = extern struct {
205 standard: OptionalHeader,
206 base_of_data: u32,
207 image_base: u32,
208 section_alignment: u32,
209 file_alignment: u32,
210 major_operating_system_version: u16,
211 minor_operating_system_version: u16,
212 major_image_version: u16,
213 minor_image_version: u16,
214 major_subsystem_version: u16,
215 minor_subsystem_version: u16,
216 win32_version_value: u32,
217 size_of_image: u32,
218 size_of_headers: u32,
219 checksum: u32,
220 subsystem: Subsystem,
221 dll_flags: DllFlags,
222 size_of_stack_reserve: u32,
223 size_of_stack_commit: u32,
224 size_of_heap_reserve: u32,
225 size_of_heap_commit: u32,
226 loader_flags: u32,
227 number_of_rva_and_sizes: u32,
228 };
229
230 pub const @"PE32+" = extern struct {
231 standard: OptionalHeader,
232 image_base: u64,
233 section_alignment: u32,
234 file_alignment: u32,
235 major_operating_system_version: u16,
236 minor_operating_system_version: u16,
237 major_image_version: u16,
238 minor_image_version: u16,
239 major_subsystem_version: u16,
240 minor_subsystem_version: u16,
241 win32_version_value: u32,
242 size_of_image: u32,
243 size_of_headers: u32,
244 checksum: u32,
245 subsystem: Subsystem,
246 dll_flags: DllFlags,
247 size_of_stack_reserve: u64,
248 size_of_stack_commit: u64,
249 size_of_heap_reserve: u64,
250 size_of_heap_commit: u64,
251 loader_flags: u32,
252 number_of_rva_and_sizes: u32,
253 };
254};
255
256pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
257
258pub const ImageDataDirectory = extern struct {
259 virtual_address: u32,
260 size: u32,
261};
262
263pub const BaseRelocationDirectoryEntry = extern struct {
264 /// The image base plus the page RVA is added to each offset to create the VA where the base relocation must be applied.
265 page_rva: u32,
266
267 /// The total number of bytes in the base relocation block, including the Page RVA and Block Size fields and the Type/Offset fields that follow.
268 block_size: u32,
269};
270
271pub const BaseRelocation = packed struct(u16) {
272 /// Stored in the remaining 12 bits of the WORD, an offset from the starting address that was specified in the Page RVA field for the block.
273 /// This offset specifies where the base relocation is to be applied.
274 offset: u12,
275
276 /// Stored in the high 4 bits of the WORD, a value that indicates the type of base relocation to be applied.
277 type: BaseRelocationType,
278};
279
280pub const BaseRelocationType = enum(u4) {
281 /// The base relocation is skipped. This type can be used to pad a block.
282 ABSOLUTE = 0,
283
284 /// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word.
285 HIGH = 1,
286
287 /// The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word.
288 LOW = 2,
289
290 /// The base relocation applies all 32 bits of the difference to the 32-bit field at offset.
291 HIGHLOW = 3,
292
293 /// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset.
294 /// The 16-bit field represents the high value of a 32-bit word.
295 /// The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation.
296 /// This means that this base relocation occupies two slots.
297 HIGHADJ = 4,
298
299 /// When the machine type is MIPS, the base relocation applies to a MIPS jump instruction.
300 MIPS_JMPADDR = 5,
301
302 /// This relocation is meaningful only when the machine type is ARM or Thumb.
303 /// The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair.
304 // ARM_MOV32 = 5,
305
306 /// This relocation is only meaningful when the machine type is RISC-V.
307 /// The base relocation applies to the high 20 bits of a 32-bit absolute address.
308 // RISCV_HIGH20 = 5,
309
310 /// Reserved, must be zero.
311 RESERVED = 6,
312
313 /// This relocation is meaningful only when the machine type is Thumb.
314 /// The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair.
315 THUMB_MOV32 = 7,
316
317 /// This relocation is only meaningful when the machine type is RISC-V.
318 /// The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format.
319 // RISCV_LOW12I = 7,
320
321 /// This relocation is only meaningful when the machine type is RISC-V.
322 /// The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format.
323 RISCV_LOW12S = 8,
324
325 /// This relocation is only meaningful when the machine type is LoongArch 32-bit.
326 /// The base relocation applies to a 32-bit absolute address formed in two consecutive instructions.
327 // LOONGARCH32_MARK_LA = 8,
328
329 /// This relocation is only meaningful when the machine type is LoongArch 64-bit.
330 /// The base relocation applies to a 64-bit absolute address formed in four consecutive instructions.
331 // LOONGARCH64_MARK_LA = 8,
332
333 /// The relocation is only meaningful when the machine type is MIPS.
334 /// The base relocation applies to a MIPS16 jump instruction.
335 MIPS_JMPADDR16 = 9,
336
337 /// The base relocation applies the difference to the 64-bit field at offset.
338 DIR64 = 10,
339
340 _,
341};
342
343pub const DebugDirectoryEntry = extern struct {
344 characteristics: u32,
345 time_date_stamp: u32,
346 major_version: u16,
347 minor_version: u16,
348 type: DebugType,
349 size_of_data: u32,
350 address_of_raw_data: u32,
351 pointer_to_raw_data: u32,
352};
353
354pub const DebugType = enum(u32) {
355 UNKNOWN = 0,
356 COFF = 1,
357 CODEVIEW = 2,
358 FPO = 3,
359 MISC = 4,
360 EXCEPTION = 5,
361 FIXUP = 6,
362 OMAP_TO_SRC = 7,
363 OMAP_FROM_SRC = 8,
364 BORLAND = 9,
365 RESERVED10 = 10,
366 VC_FEATURE = 12,
367 POGO = 13,
368 ILTCG = 14,
369 MPX = 15,
370 REPRO = 16,
371 EX_DLLCHARACTERISTICS = 20,
372
373 _,
374};
375
376pub fn TlsDirectoryEntry(comptime magic: std.coff.OptionalHeader.Magic) type {
377 return switch (magic) {
378 _ => comptime unreachable,
379 .PE32 => extern struct {
380 raw_data_start_va: u32,
381 raw_data_end_va: u32,
382 tls_index_va: u32,
383 callbacks_va: u32,
384 size_of_zero_fill: u32,
385 characteristics: packed struct(u32) {
386 _reserved_0: u19,
387 alignment: SectionHeader.Flags.Align,
388 _reserved_1: u9,
389 },
390 },
391 .@"PE32+" => extern struct {
392 raw_data_start_va: u64,
393 raw_data_end_va: u64,
394 tls_index_va: u64,
395 callbacks_va: u64,
396 size_of_zero_fill: u32,
397 characteristics: packed struct(u32) {
398 _reserved_0: u19,
399 alignment: SectionHeader.Flags.Align,
400 _reserved_1: u9,
401 },
402 },
403 };
404}
405
406pub const ImportDirectoryEntry = extern struct {
407 /// The RVA of the import lookup table.
408 /// This table contains a name or ordinal for each import.
409 /// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.)
410 import_lookup_table_rva: u32,
411
412 /// The stamp that is set to zero until the image is bound.
413 /// After the image is bound, this field is set to the time/data stamp of the DLL.
414 time_date_stamp: u32,
415
416 /// The index of the first forwarder reference.
417 forwarder_chain: u32,
418
419 /// The address of an ASCII string that contains the name of the DLL.
420 /// This address is relative to the image base.
421 name_rva: u32,
422
423 /// The RVA of the import address table.
424 /// The contents of this table are identical to the contents of the import lookup table until the image is bound.
425 import_address_table_rva: u32,
426};
427
428pub fn ImportLookupTableEntry(comptime magic: std.coff.OptionalHeader.Magic) type {
429 const Payload = packed union(u31) {
430 ordinal: packed struct(u31) {
431 ordinal: u16,
432 _: u15 = 0,
433 },
434 hint_name_rva: u31,
435 };
436
437 return switch (magic) {
438 _ => comptime unreachable,
439 .PE32 => packed struct(u32) {
440 payload: Payload,
441 is_ordinal: bool,
442 },
443 .@"PE32+" => packed struct(u64) {
444 payload: Payload,
445 _: u32 = 0,
446 is_ordinal: bool,
447 },
448 };
449}
450
451/// Every name ends with a NULL byte. IF the NULL byte does not fall on
452/// 2byte boundary, the entry structure is padded to ensure 2byte alignment.
453pub const ImportHintNameEntry = extern struct {
454 /// An index into the export name pointer table.
455 /// A match is attempted first with this value. If it fails, a binary search is performed on the DLL's export name pointer table.
456 hint: u16,
457
458 /// Pointer to NULL terminated ASCII name.
459 /// Variable length...
460 name: [1]u8,
461};
462
463pub const ExportDirectoryTable = extern struct {
464 /// Reserved
465 flags: u32,
466
467 /// Creation time of this table
468 time_date_stamp: u32,
469
470 major_version: u16,
471 minor_version: u16,
472
473 /// The address of an ASCII string that contains the name of the DLL.
474 /// This address is relative to the image base.
475 name_rva: u32,
476
477 /// The ordinal of the first export in this image
478 ordinal_base: u32,
479
480 /// Number of entries in the export address table
481 number_of_entries: u32,
482
483 /// Number of entries in the name pointer table and ordinal table
484 number_of_names: u32,
485
486 export_address_table_rva: u32,
487 name_pointer_table_rva: u32,
488 ordinal_table_rva: u32,
489};
490
491pub const ExportAddressTableEntry = extern struct {
492 /// If this address is within the export section, then this is the address of the export
493 /// Otherwise, this is the address of a string that specfies a symbol in another DLL:
494 /// <dll name>.<export name>
495 /// <dll name>.#<export ordinal>
496 export_or_forwarder_rva: u32,
497};
498
499pub const ExportNamePointerTableEntry = extern struct {
500 name_rva: u32,
501};
502
503pub const ExportOrdinalTableEntry = extern struct {
504 unbiased_ordinal: u16,
505};
506
507pub const SectionHeader = extern struct {
508 name: [8]u8,
509 virtual_size: u32,
510 virtual_address: u32,
511 size_of_raw_data: u32,
512 pointer_to_raw_data: u32,
513 pointer_to_relocations: u32,
514 pointer_to_linenumbers: u32,
515 number_of_relocations: u16,
516 number_of_linenumbers: u16,
517 flags: SectionHeader.Flags,
518
519 pub fn getName(self: *align(1) const SectionHeader) ?[]const u8 {
520 if (self.name[0] == '/') return null;
521 const len = std.mem.findScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len;
522 return self.name[0..len];
523 }
524
525 pub fn getNameOffset(self: SectionHeader) ?u32 {
526 if (self.name[0] != '/') return null;
527 const len = std.mem.findScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len;
528 const offset = std.fmt.parseInt(u32, self.name[1..len], 10) catch unreachable;
529 return offset;
530 }
531
532 /// Applicable only to section headers in COFF objects.
533 pub fn getAlignment(self: SectionHeader) ?u16 {
534 return self.flags.ALIGN.toByteUnits();
535 }
536
537 pub fn setAlignment(self: *SectionHeader, new_alignment: u16) void {
538 self.flags.ALIGN = .fromByteUnits(new_alignment);
539 }
540
541 pub fn isCode(self: SectionHeader) bool {
542 return self.flags.CNT_CODE;
543 }
544
545 pub fn isComdat(self: SectionHeader) bool {
546 return self.flags.LNK_COMDAT;
547 }
548
549 pub const Flags = packed struct(u32) {
550 SCALE_INDEX: bool = false,
551
552 unused1: u2 = 0,
553
554 /// The section should not be padded to the next boundary.
555 /// This flag is obsolete and is replaced by `.ALIGN = .@"1BYTES"`.
556 /// This is valid only for object files.
557 TYPE_NO_PAD: bool = false,
558
559 unused4: u1 = 0,
560
561 /// The section contains executable code.
562 CNT_CODE: bool = false,
563
564 /// The section contains initialized data.
565 CNT_INITIALIZED_DATA: bool = false,
566
567 /// The section contains uninitialized data.
568 CNT_UNINITIALIZED_DATA: bool = false,
569
570 /// Reserved for future use.
571 LNK_OTHER: bool = false,
572
573 /// The section contains comments or other information.
574 /// The .drectve section has this type.
575 /// This is valid for object files only.
576 LNK_INFO: bool = false,
577
578 unused10: u1 = 0,
579
580 /// The section will not become part of the image.
581 /// This is valid only for object files.
582 LNK_REMOVE: bool = false,
583
584 /// The section contains COMDAT data.
585 /// For more information, see COMDAT Sections (Object Only).
586 /// This is valid only for object files.
587 LNK_COMDAT: bool = false,
588
589 unused13: u2 = 0,
590
591 union14: packed union {
592 mask: u1,
593 /// The section contains data referenced through the global pointer (GP).
594 GPREL: bool,
595 MEM_FARDATA: bool,
596 } = .{ .mask = 0 },
597
598 unused15: u1 = 0,
599
600 union16: packed union {
601 mask: u1,
602 MEM_PURGEABLE: bool,
603 MEM_16BIT: bool,
604 } = .{ .mask = 0 },
605
606 /// Reserved for future use.
607 MEM_LOCKED: bool = false,
608
609 /// Reserved for future use.
610 MEM_PRELOAD: bool = false,
611
612 ALIGN: SectionHeader.Flags.Align = .NONE,
613
614 /// The section contains extended relocations.
615 LNK_NRELOC_OVFL: bool = false,
616
617 /// The section can be discarded as needed.
618 MEM_DISCARDABLE: bool = false,
619
620 /// The section cannot be cached.
621 MEM_NOT_CACHED: bool = false,
622
623 /// The section is not pageable.
624 MEM_NOT_PAGED: bool = false,
625
626 /// The section can be shared in memory.
627 MEM_SHARED: bool = false,
628
629 /// The section can be executed as code.
630 MEM_EXECUTE: bool = false,
631
632 /// The section can be read.
633 MEM_READ: bool = false,
634
635 /// The section can be written to.
636 MEM_WRITE: bool = false,
637
638 pub const Align = enum(u4) {
639 NONE = 0,
640 @"1BYTES" = 1,
641 @"2BYTES" = 2,
642 @"4BYTES" = 3,
643 @"8BYTES" = 4,
644 @"16BYTES" = 5,
645 @"32BYTES" = 6,
646 @"64BYTES" = 7,
647 @"128BYTES" = 8,
648 @"256BYTES" = 9,
649 @"512BYTES" = 10,
650 @"1024BYTES" = 11,
651 @"2048BYTES" = 12,
652 @"4096BYTES" = 13,
653 @"8192BYTES" = 14,
654 _,
655
656 pub fn toByteUnits(a: Align) ?u16 {
657 if (a == .NONE) return null;
658 return @as(u16, 1) << (@backingInt(a) - 1);
659 }
660
661 pub fn fromByteUnits(n: u16) Align {
662 std.debug.assert(std.math.isPowerOfTwo(n));
663 return @fromBackingInt(@intCast(@ctz(n) + 1));
664 }
665
666 pub fn alignment(a: Align) ?std.mem.Alignment {
667 return .fromByteUnitsOptional(a.toByteUnits() orelse null);
668 }
669 };
670 };
671};
672
673pub const Symbol = extern struct {
674 name: [8]u8,
675 value: u32,
676 section_number: SectionNumber,
677 type: SymType,
678 storage_class: StorageClass,
679 number_of_aux_symbols: u8,
680
681 pub fn sizeOf() comptime_int {
682 return 18;
683 }
684
685 pub fn getName(self: *const Symbol) ?[]const u8 {
686 if (std.mem.eql(u8, self.name[0..4], "\x00\x00\x00\x00")) return null;
687 const len = std.mem.findScalar(u8, &self.name, @as(u8, 0)) orelse self.name.len;
688 return self.name[0..len];
689 }
690
691 pub fn getNameOffset(self: Symbol) ?u32 {
692 if (!std.mem.eql(u8, self.name[0..4], "\x00\x00\x00\x00")) return null;
693 const offset = std.mem.readInt(u32, self.name[4..8], .little);
694 return offset;
695 }
696};
697
698pub const SectionNumber = enum(i16) {
699 /// The symbol record is not yet assigned a section.
700 /// A value of zero indicates that a reference to an external symbol is defined elsewhere.
701 /// A value of non-zero is a common symbol with a size that is specified by the value.
702 UNDEFINED = 0,
703
704 /// The symbol has an absolute (non-relocatable) value and is not an address.
705 ABSOLUTE = -1,
706
707 /// The symbol provides general type or debugging information but does not correspond to a section.
708 /// Microsoft tools use this setting along with .file records (storage class FILE).
709 DEBUG = -2,
710 _,
711};
712
713pub const SymType = packed struct(u16) {
714 complex_type: ComplexType,
715 base_type: BaseType,
716};
717
718pub const BaseType = enum(u8) {
719 /// No type information or unknown base type. Microsoft tools use this setting
720 NULL = 0,
721
722 /// No valid type; used with void pointers and functions
723 VOID = 1,
724
725 /// A character (signed byte)
726 CHAR = 2,
727
728 /// A 2-byte signed integer
729 SHORT = 3,
730
731 /// A natural integer type (normally 4 bytes in Windows)
732 INT = 4,
733
734 /// A 4-byte signed integer
735 LONG = 5,
736
737 /// A 4-byte floating-point number
738 FLOAT = 6,
739
740 /// An 8-byte floating-point number
741 DOUBLE = 7,
742
743 /// A structure
744 STRUCT = 8,
745
746 /// A union
747 UNION = 9,
748
749 /// An enumerated type
750 ENUM = 10,
751
752 /// A member of enumeration (a specified value)
753 MOE = 11,
754
755 /// A byte; unsigned 1-byte integer
756 BYTE = 12,
757
758 /// A word; unsigned 2-byte integer
759 WORD = 13,
760
761 /// An unsigned integer of natural size (normally, 4 bytes)
762 UINT = 14,
763
764 /// An unsigned 4-byte integer
765 DWORD = 15,
766
767 _,
768};
769
770pub const ComplexType = enum(u8) {
771 /// No derived type; the symbol is a simple scalar variable.
772 NULL = 0,
773
774 /// The symbol is a pointer to base type.
775 POINTER = 16,
776
777 /// The symbol is a function that returns a base type.
778 FUNCTION = 32,
779
780 /// The symbol is an array of base type.
781 ARRAY = 48,
782
783 _,
784};
785
786pub const StorageClass = enum(u8) {
787 /// A special symbol that represents the end of function, for debugging purposes.
788 END_OF_FUNCTION = 0xff,
789
790 /// No assigned storage class.
791 NULL = 0,
792
793 /// The automatic (stack) variable. The Value field specifies the stack frame offset.
794 AUTOMATIC = 1,
795
796 /// A value that Microsoft tools use for external symbols.
797 /// The Value field indicates the size if the section number is IMAGE_SYM_UNDEFINED (0).
798 /// If the section number is not zero, then the Value field specifies the offset within the section.
799 EXTERNAL = 2,
800
801 /// The offset of the symbol within the section.
802 /// If the Value field is zero, then the symbol represents a section name.
803 STATIC = 3,
804
805 /// A register variable.
806 /// The Value field specifies the register number.
807 REGISTER = 4,
808
809 /// A symbol that is defined externally.
810 EXTERNAL_DEF = 5,
811
812 /// A code label that is defined within the module.
813 /// The Value field specifies the offset of the symbol within the section.
814 LABEL = 6,
815
816 /// A reference to a code label that is not defined.
817 UNDEFINED_LABEL = 7,
818
819 /// The structure member. The Value field specifies the n th member.
820 MEMBER_OF_STRUCT = 8,
821
822 /// A formal argument (parameter) of a function. The Value field specifies the n th argument.
823 ARGUMENT = 9,
824
825 /// The structure tag-name entry.
826 STRUCT_TAG = 10,
827
828 /// A union member. The Value field specifies the n th member.
829 MEMBER_OF_UNION = 11,
830
831 /// The Union tag-name entry.
832 UNION_TAG = 12,
833
834 /// A Typedef entry.
835 TYPE_DEFINITION = 13,
836
837 /// A static data declaration.
838 UNDEFINED_STATIC = 14,
839
840 /// An enumerated type tagname entry.
841 ENUM_TAG = 15,
842
843 /// A member of an enumeration. The Value field specifies the n th member.
844 MEMBER_OF_ENUM = 16,
845
846 /// A register parameter.
847 REGISTER_PARAM = 17,
848
849 /// A bit-field reference. The Value field specifies the n th bit in the bit field.
850 BIT_FIELD = 18,
851
852 /// A .bb (beginning of block) or .eb (end of block) record.
853 /// The Value field is the relocatable address of the code location.
854 BLOCK = 100,
855
856 /// 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 ).
857 /// For .lf records, the Value field gives the number of source lines in the function.
858 /// For .ef records, the Value field gives the size of the function code.
859 FUNCTION = 101,
860
861 /// An end-of-structure entry.
862 END_OF_STRUCT = 102,
863
864 /// A value that Microsoft tools, as well as traditional COFF format, use for the source-file symbol record.
865 /// The symbol is followed by auxiliary records that name the file.
866 FILE = 103,
867
868 /// A definition of a section (Microsoft tools use STATIC storage class instead).
869 SECTION = 104,
870
871 /// A weak external. For more information, see Auxiliary Format 3: Weak Externals.
872 WEAK_EXTERNAL = 105,
873
874 /// A CLR token symbol. The name is an ASCII string that consists of the hexadecimal value of the token.
875 /// For more information, see CLR Token Definition (Object Only).
876 CLR_TOKEN = 107,
877
878 _,
879};
880
881pub const FunctionDefinition = extern struct {
882 /// The symbol-table index of the corresponding .bf (begin function) symbol record.
883 tag_index: u32,
884
885 /// The size of the executable code for the function itself.
886 /// If the function is in its own section, the SizeOfRawData in the section header is greater or equal to this field,
887 /// depending on alignment considerations.
888 total_size: u32,
889
890 /// The file offset of the first COFF line-number entry for the function, or zero if none exists.
891 pointer_to_linenumber: u32,
892
893 /// The symbol-table index of the record for the next function.
894 /// If the function is the last in the symbol table, this field is set to zero.
895 pointer_to_next_function: u32,
896
897 unused: [2]u8,
898};
899
900pub const SectionDefinition = extern struct {
901 /// The size of section data; the same as SizeOfRawData in the section header.
902 length: u32,
903
904 /// The number of relocation entries for the section.
905 number_of_relocations: u16,
906
907 /// The number of line-number entries for the section.
908 number_of_linenumbers: u16,
909
910 /// The checksum for communal data. It is applicable if the IMAGE_SCN_LNK_COMDAT flag is set in the section header.
911 checksum: u32,
912
913 /// One-based index into the section table for the associated section. This is used when the COMDAT selection setting is 5.
914 number: u16,
915
916 /// The COMDAT selection number. This is applicable if the section is a COMDAT section.
917 selection: ComdatSelection,
918
919 unused: [3]u8,
920};
921
922pub const FileDefinition = extern struct {
923 /// An ANSI string that gives the name of the source file.
924 /// This is padded with nulls if it is less than the maximum length.
925 file_name: [18]u8,
926
927 pub fn getFileName(self: *const FileDefinition) []const u8 {
928 const len = std.mem.findScalar(u8, &self.file_name, @as(u8, 0)) orelse self.file_name.len;
929 return self.file_name[0..len];
930 }
931};
932
933pub const WeakExternalDefinition = extern struct {
934 /// The symbol-table index of sym2, the symbol to be linked if sym1 is not found.
935 tag_index: u32,
936
937 /// A value of IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY indicates that no library search for sym1 should be performed.
938 /// A value of IMAGE_WEAK_EXTERN_SEARCH_LIBRARY indicates that a library search for sym1 should be performed.
939 /// A value of IMAGE_WEAK_EXTERN_SEARCH_ALIAS indicates that sym1 is an alias for sym2.
940 flag: WeakExternalFlag,
941
942 unused: [10]u8,
943
944 pub fn sizeOf() comptime_int {
945 return 18;
946 }
947};
948
949// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/km/ntimage.h
950pub const WeakExternalFlag = enum(u32) {
951 SEARCH_NOLIBRARY = 1,
952 SEARCH_LIBRARY = 2,
953 SEARCH_ALIAS = 3,
954 ANTI_DEPENDENCY = 4,
955 _,
956};
957
958pub const ComdatSelection = enum(u8) {
959 /// Not a COMDAT section.
960 NONE = 0,
961
962 /// If this symbol is already defined, the linker issues a "multiply defined symbol" error.
963 NODUPLICATES = 1,
964
965 /// Any section that defines the same COMDAT symbol can be linked; the rest are removed.
966 ANY = 2,
967
968 /// The linker chooses an arbitrary section among the definitions for this symbol.
969 /// If all definitions are not the same size, a "multiply defined symbol" error is issued.
970 SAME_SIZE = 3,
971
972 /// The linker chooses an arbitrary section among the definitions for this symbol.
973 /// If all definitions do not match exactly, a "multiply defined symbol" error is issued.
974 EXACT_MATCH = 4,
975
976 /// The section is linked if a certain other COMDAT section is linked.
977 /// This other section is indicated by the Number field of the auxiliary symbol record for the section definition.
978 /// This setting is useful for definitions that have components in multiple sections
979 /// (for example, code in one and data in another), but where all must be linked or discarded as a set.
980 /// The other section this section is associated with must be a COMDAT section, which can be another
981 /// associative COMDAT section. An associative COMDAT section's section association chain can't form a loop.
982 /// The section association chain must eventually come to a COMDAT section that doesn't have IMAGE_COMDAT_SELECT_ASSOCIATIVE set.
983 ASSOCIATIVE = 5,
984
985 /// The linker chooses the largest definition from among all of the definitions for this symbol.
986 /// If multiple definitions have this size, the choice between them is arbitrary.
987 LARGEST = 6,
988
989 _,
990};
991
992pub const DebugInfoDefinition = extern struct {
993 unused_1: [4]u8,
994
995 /// The actual ordinal line number (1, 2, 3, and so on) within the source file, corresponding to the .bf or .ef record.
996 linenumber: u16,
997
998 unused_2: [6]u8,
999
1000 /// The symbol-table index of the next .bf symbol record.
1001 /// If the function is the last in the symbol table, this field is set to zero.
1002 /// It is not used for .ef records.
1003 pointer_to_next_function: u32,
1004
1005 unused_3: [2]u8,
1006};
1007
1008pub const Error = error{
1009 InvalidPEMagic,
1010 InvalidPEHeader,
1011 InvalidMachine,
1012 MissingPEHeader,
1013 MissingCoffSection,
1014 MissingStringTable,
1015};
1016
1017// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
1018pub const Coff = struct {
1019 data: []const u8,
1020 // Set if `data` is backed by the image as loaded by the loader
1021 is_loaded: bool,
1022 is_image: bool,
1023 coff_header_offset: usize,
1024
1025 guid: [16]u8 = undefined,
1026 age: u32 = undefined,
1027
1028 // The lifetime of `data` must be longer than the lifetime of the returned Coff
1029 pub fn init(data: []const u8, is_loaded: bool) error{ EndOfStream, MissingPEHeader }!Coff {
1030 if (data.len < pe_pointer_offset + 4) return error.EndOfStream;
1031 const header_offset = mem.readInt(u32, data[pe_pointer_offset..][0..4], .little);
1032 if (data.len < header_offset + 4) return error.EndOfStream;
1033 const is_image = mem.eql(u8, data[header_offset..][0..4], pe_signature);
1034
1035 const coff: Coff = .{
1036 .data = data,
1037 .is_image = is_image,
1038 .is_loaded = is_loaded,
1039 .coff_header_offset = o: {
1040 if (is_image) break :o header_offset + 4;
1041 break :o header_offset;
1042 },
1043 };
1044
1045 // Do some basic validation upfront
1046 if (is_image) {
1047 const coff_header = coff.getHeader();
1048 if (coff_header.size_of_optional_header == 0) return error.MissingPEHeader;
1049 }
1050
1051 // JK: we used to check for architecture here and throw an error if not x86 or derivative.
1052 // However I am willing to take a leap of faith and let aarch64 have a shot also.
1053
1054 return coff;
1055 }
1056
1057 pub fn getPdbPath(self: *Coff) !?[]const u8 {
1058 assert(self.is_image);
1059
1060 const data_dirs = self.getDataDirectories();
1061 if (@backingInt(IMAGE.DIRECTORY_ENTRY.DEBUG) >= data_dirs.len) return null;
1062
1063 const debug_dir = data_dirs[@backingInt(IMAGE.DIRECTORY_ENTRY.DEBUG)];
1064 var reader: std.Io.Reader = .fixed(self.data);
1065
1066 if (self.is_loaded) {
1067 reader.seek = debug_dir.virtual_address;
1068 } else {
1069 // Find what section the debug_dir is in, in order to convert the RVA to a file offset
1070 for (self.getSectionHeaders()) |*sect| {
1071 if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) {
1072 reader.seek = sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address);
1073 break;
1074 }
1075 } else return error.InvalidDebugDirectory;
1076 }
1077
1078 // Find the correct DebugDirectoryEntry, and where its data is stored.
1079 // It can be in any section.
1080 const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
1081 var i: u32 = 0;
1082 while (i < debug_dir_entry_count) : (i += 1) {
1083 const debug_dir_entry = try reader.takeStruct(DebugDirectoryEntry, .little);
1084 if (debug_dir_entry.type == .CODEVIEW) {
1085 const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data;
1086 reader.seek = dir_offset;
1087 break;
1088 }
1089 } else return null;
1090
1091 const code_view_signature = try reader.takeArray(4);
1092 // 'RSDS' indicates PDB70 format, used by lld.
1093 if (!mem.eql(u8, code_view_signature, "RSDS"))
1094 return error.InvalidPEMagic;
1095 try reader.readSliceAll(self.guid[0..]);
1096 self.age = try reader.takeInt(u32, .little);
1097
1098 // Finally read the null-terminated string.
1099 const start = reader.seek;
1100 const len = std.mem.findScalar(u8, self.data[start..], 0) orelse return null;
1101 return self.data[start .. start + len];
1102 }
1103
1104 pub fn getHeader(self: Coff) Header {
1105 return @as(*align(1) const Header, @ptrCast(self.data[self.coff_header_offset..][0..@sizeOf(Header)])).*;
1106 }
1107
1108 pub fn getOptionalHeader(self: Coff) OptionalHeader {
1109 assert(self.is_image);
1110 const offset = self.coff_header_offset + @sizeOf(Header);
1111 return @as(*align(1) const OptionalHeader, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader)])).*;
1112 }
1113
1114 pub fn getOptionalHeader32(self: Coff) OptionalHeader.PE32 {
1115 assert(self.is_image);
1116 const offset = self.coff_header_offset + @sizeOf(Header);
1117 return @as(*align(1) const OptionalHeader.PE32, @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader.PE32)])).*;
1118 }
1119
1120 pub fn getOptionalHeader64(self: Coff) OptionalHeader.@"PE32+" {
1121 assert(self.is_image);
1122 const offset = self.coff_header_offset + @sizeOf(Header);
1123 return @as(*align(1) const OptionalHeader.@"PE32+", @ptrCast(self.data[offset..][0..@sizeOf(OptionalHeader.@"PE32+")])).*;
1124 }
1125
1126 pub fn getImageBase(self: Coff) u64 {
1127 const hdr = self.getOptionalHeader();
1128 return switch (@backingInt(hdr.magic)) {
1129 IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().image_base,
1130 IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().image_base,
1131 else => unreachable, // We assume we have validated the header already
1132 };
1133 }
1134
1135 pub fn getNumberOfDataDirectories(self: Coff) u32 {
1136 const hdr = self.getOptionalHeader();
1137 return switch (@backingInt(hdr.magic)) {
1138 IMAGE_NT_OPTIONAL_HDR32_MAGIC => self.getOptionalHeader32().number_of_rva_and_sizes,
1139 IMAGE_NT_OPTIONAL_HDR64_MAGIC => self.getOptionalHeader64().number_of_rva_and_sizes,
1140 else => unreachable, // We assume we have validated the header already
1141 };
1142 }
1143
1144 pub fn getDataDirectories(self: *const Coff) []align(1) const ImageDataDirectory {
1145 const hdr = self.getOptionalHeader();
1146 const size: usize = switch (@backingInt(hdr.magic)) {
1147 IMAGE_NT_OPTIONAL_HDR32_MAGIC => @sizeOf(OptionalHeader.PE32),
1148 IMAGE_NT_OPTIONAL_HDR64_MAGIC => @sizeOf(OptionalHeader.@"PE32+"),
1149 else => unreachable, // We assume we have validated the header already
1150 };
1151 const offset = self.coff_header_offset + @sizeOf(Header) + size;
1152 return @as([*]align(1) const ImageDataDirectory, @ptrCast(self.data[offset..]))[0..self.getNumberOfDataDirectories()];
1153 }
1154
1155 pub fn getSymtab(self: *const Coff) ?Symtab {
1156 const coff_header = self.getHeader();
1157 if (coff_header.pointer_to_symbol_table == 0) return null;
1158
1159 const offset = coff_header.pointer_to_symbol_table;
1160 const size = coff_header.number_of_symbols * Symbol.sizeOf();
1161 return .{ .buffer = self.data[offset..][0..size] };
1162 }
1163
1164 pub fn getStrtab(self: *const Coff) error{InvalidStrtabSize}!?Strtab {
1165 const coff_header = self.getHeader();
1166 if (coff_header.pointer_to_symbol_table == 0) return null;
1167
1168 const offset = coff_header.pointer_to_symbol_table + Symbol.sizeOf() * coff_header.number_of_symbols;
1169 const size = mem.readInt(u32, self.data[offset..][0..4], .little);
1170 if ((offset + size) > self.data.len) return error.InvalidStrtabSize;
1171
1172 return Strtab{ .buffer = self.data[offset..][0..size] };
1173 }
1174
1175 pub fn strtabRequired(self: *const Coff) bool {
1176 for (self.getSectionHeaders()) |*sect_hdr| if (sect_hdr.getName() == null) return true;
1177 return false;
1178 }
1179
1180 pub fn getSectionHeaders(self: *const Coff) []align(1) const SectionHeader {
1181 const coff_header = self.getHeader();
1182 const offset = self.coff_header_offset + @sizeOf(Header) + coff_header.size_of_optional_header;
1183 return @as([*]align(1) const SectionHeader, @ptrCast(self.data.ptr + offset))[0..coff_header.number_of_sections];
1184 }
1185
1186 pub fn getSectionHeadersAlloc(self: *const Coff, allocator: mem.Allocator) ![]SectionHeader {
1187 const section_headers = self.getSectionHeaders();
1188 const out_buff = try allocator.alloc(SectionHeader, section_headers.len);
1189 for (out_buff, 0..) |*section_header, i| {
1190 section_header.* = section_headers[i];
1191 }
1192
1193 return out_buff;
1194 }
1195
1196 pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) error{InvalidStrtabSize}![]const u8 {
1197 const name = sect_hdr.getName() orelse blk: {
1198 const strtab = (try self.getStrtab()).?;
1199 const name_offset = sect_hdr.getNameOffset().?;
1200 break :blk strtab.get(name_offset);
1201 };
1202 return name;
1203 }
1204
1205 pub fn getSectionByName(self: *const Coff, comptime name: []const u8) ?*align(1) const SectionHeader {
1206 for (self.getSectionHeaders()) |*sect| {
1207 const section_name = self.getSectionName(sect) catch |e| switch (e) {
1208 error.InvalidStrtabSize => continue, //ignore invalid(?) strtab entries - see also GitHub issue #15238
1209 };
1210 if (mem.eql(u8, section_name, name)) {
1211 return sect;
1212 }
1213 }
1214 return null;
1215 }
1216
1217 pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 {
1218 const offset = if (self.is_loaded) sec.virtual_address else sec.pointer_to_raw_data;
1219 return self.data[offset..][0..sec.virtual_size];
1220 }
1221
1222 pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {
1223 const section_data = self.getSectionData(sec);
1224 return allocator.dupe(u8, section_data);
1225 }
1226};
1227
1228pub const Symtab = struct {
1229 buffer: []const u8,
1230
1231 pub fn len(self: Symtab) usize {
1232 return @divExact(self.buffer.len, Symbol.sizeOf());
1233 }
1234
1235 pub const Tag = enum {
1236 symbol,
1237 debug_info,
1238 func_def,
1239 weak_ext,
1240 file_def,
1241 sect_def,
1242 };
1243
1244 pub const Record = union(Tag) {
1245 symbol: Symbol,
1246 debug_info: DebugInfoDefinition,
1247 func_def: FunctionDefinition,
1248 weak_ext: WeakExternalDefinition,
1249 file_def: FileDefinition,
1250 sect_def: SectionDefinition,
1251 };
1252
1253 /// Lives as long as Symtab instance.
1254 pub fn at(self: Symtab, index: usize, tag: Tag) Record {
1255 const offset = index * Symbol.sizeOf();
1256 const raw = self.buffer[offset..][0..Symbol.sizeOf()];
1257 return switch (tag) {
1258 .symbol => .{ .symbol = asSymbol(raw) },
1259 .debug_info => .{ .debug_info = asDebugInfo(raw) },
1260 .func_def => .{ .func_def = asFuncDef(raw) },
1261 .weak_ext => .{ .weak_ext = asWeakExtDef(raw) },
1262 .file_def => .{ .file_def = asFileDef(raw) },
1263 .sect_def => .{ .sect_def = asSectDef(raw) },
1264 };
1265 }
1266
1267 fn asSymbol(raw: []const u8) Symbol {
1268 return .{
1269 .name = raw[0..8].*,
1270 .value = mem.readInt(u32, raw[8..12], .little),
1271 .section_number = @as(SectionNumber, @fromBackingInt(@intCast(mem.readInt(u16, raw[12..14], .little)))),
1272 .type = @as(SymType, @bitCast(mem.readInt(u16, raw[14..16], .little))),
1273 .storage_class = @as(StorageClass, @fromBackingInt(@intCast(raw[16]))),
1274 .number_of_aux_symbols = raw[17],
1275 };
1276 }
1277
1278 fn asDebugInfo(raw: []const u8) DebugInfoDefinition {
1279 return .{
1280 .unused_1 = raw[0..4].*,
1281 .linenumber = mem.readInt(u16, raw[4..6], .little),
1282 .unused_2 = raw[6..12].*,
1283 .pointer_to_next_function = mem.readInt(u32, raw[12..16], .little),
1284 .unused_3 = raw[16..18].*,
1285 };
1286 }
1287
1288 fn asFuncDef(raw: []const u8) FunctionDefinition {
1289 return .{
1290 .tag_index = mem.readInt(u32, raw[0..4], .little),
1291 .total_size = mem.readInt(u32, raw[4..8], .little),
1292 .pointer_to_linenumber = mem.readInt(u32, raw[8..12], .little),
1293 .pointer_to_next_function = mem.readInt(u32, raw[12..16], .little),
1294 .unused = raw[16..18].*,
1295 };
1296 }
1297
1298 fn asWeakExtDef(raw: []const u8) WeakExternalDefinition {
1299 return .{
1300 .tag_index = mem.readInt(u32, raw[0..4], .little),
1301 .flag = @as(WeakExternalFlag, @fromBackingInt(@intCast(mem.readInt(u32, raw[4..8], .little)))),
1302 .unused = raw[8..18].*,
1303 };
1304 }
1305
1306 fn asFileDef(raw: []const u8) FileDefinition {
1307 return .{
1308 .file_name = raw[0..18].*,
1309 };
1310 }
1311
1312 fn asSectDef(raw: []const u8) SectionDefinition {
1313 return .{
1314 .length = mem.readInt(u32, raw[0..4], .little),
1315 .number_of_relocations = mem.readInt(u16, raw[4..6], .little),
1316 .number_of_linenumbers = mem.readInt(u16, raw[6..8], .little),
1317 .checksum = mem.readInt(u32, raw[8..12], .little),
1318 .number = mem.readInt(u16, raw[12..14], .little),
1319 .selection = @as(ComdatSelection, @fromBackingInt(@intCast(raw[14]))),
1320 .unused = raw[15..18].*,
1321 };
1322 }
1323
1324 pub const Slice = struct {
1325 buffer: []const u8,
1326 num: usize,
1327 count: usize = 0,
1328
1329 /// Lives as long as Symtab instance.
1330 pub fn next(self: *Slice) ?Symbol {
1331 if (self.count >= self.num) return null;
1332 const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]);
1333 self.count += 1;
1334 self.buffer = self.buffer[Symbol.sizeOf()..];
1335 return sym;
1336 }
1337 };
1338
1339 pub fn slice(self: Symtab, start: usize, end: ?usize) Slice {
1340 const offset = start * Symbol.sizeOf();
1341 const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len;
1342 const num = @divExact(llen - offset, Symbol.sizeOf());
1343 return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num };
1344 }
1345};
1346
1347pub const Strtab = struct {
1348 buffer: []const u8,
1349
1350 pub fn get(self: Strtab, off: u32) []const u8 {
1351 assert(off < self.buffer.len);
1352 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.ptr + off)), 0);
1353 }
1354};
1355
1356pub const ImportHeader = extern struct {
1357 /// Must be IMAGE_FILE_MACHINE_UNKNOWN
1358 sig1: IMAGE.FILE.MACHINE = .UNKNOWN,
1359 /// Must be 0xFFFF
1360 sig2: u16 = 0xFFFF,
1361 version: u16,
1362 machine: IMAGE.FILE.MACHINE,
1363 time_date_stamp: u32,
1364 size_of_data: u32,
1365 hint: u16,
1366 types: packed struct(u16) {
1367 type: ImportType,
1368 name_type: ImportNameType,
1369 reserved: u11,
1370 },
1371};
1372
1373pub const ImportType = enum(u2) {
1374 /// Executable code.
1375 CODE = 0,
1376 /// Data.
1377 DATA = 1,
1378 /// Specified as CONST in .def file.
1379 CONST = 2,
1380 _,
1381};
1382
1383pub const ImportNameType = enum(u3) {
1384 /// The import is by ordinal. This indicates that the value in the Ordinal/Hint
1385 /// field of the import header is the import's ordinal. If this constant is not
1386 /// specified, then the Ordinal/Hint field should always be interpreted as the import's hint.
1387 ORDINAL = 0,
1388 /// The import name is identical to the public symbol name.
1389 NAME = 1,
1390 /// The import name is the public symbol name, but skipping the leading ?, @, or optionally _.
1391 NAME_NOPREFIX = 2,
1392 /// The import name is the public symbol name, but skipping the leading ?, @, or optionally _,
1393 /// and truncating at the first @.
1394 NAME_UNDECORATE = 3,
1395 /// https://github.com/llvm/llvm-project/pull/83211
1396 NAME_EXPORTAS = 4,
1397 _,
1398};
1399
1400pub const Relocation = extern struct {
1401 virtual_address: u32,
1402 symbol_table_index: u32,
1403 type: u16,
1404
1405 pub fn sizeOf() comptime_int {
1406 return 10;
1407 }
1408};
1409
1410pub const IMAGE = struct {
1411 pub const DIRECTORY_ENTRY = enum(u32) {
1412 /// Export Directory
1413 EXPORT = 0,
1414 /// Import Directory
1415 IMPORT = 1,
1416 /// Resource Directory
1417 RESOURCE = 2,
1418 /// Exception Directory
1419 EXCEPTION = 3,
1420 /// Security Directory
1421 SECURITY = 4,
1422 /// Base Relocation Table
1423 BASERELOC = 5,
1424 /// Debug Directory
1425 DEBUG = 6,
1426 /// Architecture Specific Data
1427 ARCHITECTURE = 7,
1428 /// RVA of GP
1429 GLOBALPTR = 8,
1430 /// TLS Directory
1431 TLS = 9,
1432 /// Load Configuration Directory
1433 LOAD_CONFIG = 10,
1434 /// Bound Import Directory in headers
1435 BOUND_IMPORT = 11,
1436 /// Import Address Table
1437 IAT = 12,
1438 /// Delay Load Import Descriptors
1439 DELAY_IMPORT = 13,
1440 /// COM Runtime descriptor
1441 COM_DESCRIPTOR = 14,
1442 /// must be zero
1443 RESERVED = 15,
1444 _,
1445
1446 pub const len = @typeInfo(IMAGE.DIRECTORY_ENTRY).@"enum".field_names.len;
1447 };
1448
1449 pub const FILE = struct {
1450 /// Machine Types
1451 /// The Machine field has one of the following values, which specify the CPU type.
1452 /// An image file can be run only on the specified machine or on a system that emulates the specified machine.
1453 pub const MACHINE = enum(u16) {
1454 /// The content of this field is assumed to be applicable to any machine type
1455 UNKNOWN = 0x0,
1456 /// Alpha AXP, 32-bit address space
1457 ALPHA = 0x184,
1458 /// Alpha 64, 64-bit address space
1459 ALPHA64 = 0x284,
1460 /// Matsushita AM33
1461 AM33 = 0x1d3,
1462 /// x64
1463 AMD64 = 0x8664,
1464 /// ARM little endian
1465 ARM = 0x1c0,
1466 /// ARM64 little endian
1467 ARM64 = 0xaa64,
1468 /// ABI that enables interoperability between native ARM64 and emulated x64 code.
1469 ARM64EC = 0xA641,
1470 /// Binary format that allows both native ARM64 and ARM64EC code to coexist in the same file.
1471 ARM64X = 0xA64E,
1472 /// ARM Thumb-2 little endian
1473 ARMNT = 0x1c4,
1474 /// EFI byte code
1475 EBC = 0xebc,
1476 /// Intel 386 or later processors and compatible processors
1477 I386 = 0x14c,
1478 /// Intel Itanium processor family
1479 IA64 = 0x200,
1480 /// LoongArch 32-bit processor family
1481 LOONGARCH32 = 0x6232,
1482 /// LoongArch 64-bit processor family
1483 LOONGARCH64 = 0x6264,
1484 /// Mitsubishi M32R little endian
1485 M32R = 0x9041,
1486 /// MIPS16
1487 MIPS16 = 0x266,
1488 /// MIPS with FPU
1489 MIPSFPU = 0x366,
1490 /// MIPS16 with FPU
1491 MIPSFPU16 = 0x466,
1492 /// Power PC little endian
1493 POWERPC = 0x1f0,
1494 /// Power PC with floating point support
1495 POWERPCFP = 0x1f1,
1496 /// MIPS I compatible 32-bit big endian
1497 R3000BE = 0x160,
1498 /// MIPS I compatible 32-bit little endian
1499 R3000 = 0x162,
1500 /// MIPS III compatible 64-bit little endian
1501 R4000 = 0x166,
1502 /// MIPS IV compatible 64-bit little endian
1503 R10000 = 0x168,
1504 /// RISC-V 32-bit address space
1505 RISCV32 = 0x5032,
1506 /// RISC-V 64-bit address space
1507 RISCV64 = 0x5064,
1508 /// RISC-V 128-bit address space
1509 RISCV128 = 0x5128,
1510 /// Hitachi SH3
1511 SH3 = 0x1a2,
1512 /// Hitachi SH3 DSP
1513 SH3DSP = 0x1a3,
1514 /// Hitachi SH4
1515 SH4 = 0x1a6,
1516 /// Hitachi SH5
1517 SH5 = 0x1a8,
1518 /// Thumb
1519 THUMB = 0x1c2,
1520 /// MIPS little-endian WCE v2
1521 WCEMIPSV2 = 0x169,
1522 _,
1523 /// AXP 64 (Same as Alpha 64)
1524 pub const AXP64: IMAGE.FILE.MACHINE = .ALPHA64;
1525
1526 pub fn RelocationType(comptime machine: IMAGE.FILE.MACHINE) type {
1527 return switch (machine) {
1528 .AMD64,
1529 => REL.AMD64,
1530 .ARM,
1531 .ARMNT,
1532 => REL.ARM,
1533 .ARM64,
1534 .ARM64EC,
1535 .ARM64X,
1536 => REL.ARM64,
1537 .I386 => REL.I386,
1538 .IA64 => REL.IA64,
1539 .M32R => REL.M32R,
1540 .MIPS16,
1541 .MIPSFPU,
1542 .MIPSFPU16,
1543 => REL.MIPS,
1544 .POWERPC,
1545 .POWERPCFP,
1546 => REL.PPC,
1547 .SH3,
1548 .SH3DSP,
1549 .SH4,
1550 .SH5,
1551 => REL.SH,
1552 else => void,
1553 };
1554 }
1555 };
1556 };
1557
1558 pub const REL = struct {
1559 /// x64 Processors
1560 /// The following relocation type indicators are defined for x64 and compatible processors.
1561 pub const AMD64 = enum(u16) {
1562 /// The relocation is ignored.
1563 ABSOLUTE = 0x0000,
1564 /// The 64-bit VA of the relocation target.
1565 ADDR64 = 0x0001,
1566 /// The 32-bit VA of the relocation target.
1567 ADDR32 = 0x0002,
1568 /// The 32-bit address without an image base (RVA).
1569 ADDR32NB = 0x0003,
1570 /// The 32-bit relative address from the byte following the relocation.
1571 REL32 = 0x0004,
1572 /// The 32-bit address relative to byte distance 1 from the relocation.
1573 REL32_1 = 0x0005,
1574 /// The 32-bit address relative to byte distance 2 from the relocation.
1575 REL32_2 = 0x0006,
1576 /// The 32-bit address relative to byte distance 3 from the relocation.
1577 REL32_3 = 0x0007,
1578 /// The 32-bit address relative to byte distance 4 from the relocation.
1579 REL32_4 = 0x0008,
1580 /// The 32-bit address relative to byte distance 5 from the relocation.
1581 REL32_5 = 0x0009,
1582 /// The 16-bit section index of the section that contains the target.
1583 /// This is used to support debugging information.
1584 SECTION = 0x000A,
1585 /// The 32-bit offset of the target from the beginning of its section.
1586 /// This is used to support debugging information and static thread local storage.
1587 SECREL = 0x000B,
1588 /// A 7-bit unsigned offset from the base of the section that contains the target.
1589 SECREL7 = 0x000C,
1590 /// CLR tokens.
1591 TOKEN = 0x000D,
1592 /// A 32-bit signed span-dependent value emitted into the object.
1593 SREL32 = 0x000E,
1594 /// A pair that must immediately follow every span-dependent value.
1595 PAIR = 0x000F,
1596 /// A 32-bit signed span-dependent value that is applied at link time.
1597 SSPAN32 = 0x0010,
1598 _,
1599 };
1600
1601 /// ARM Processors
1602 /// The following relocation type indicators are defined for ARM processors.
1603 pub const ARM = enum(u16) {
1604 /// The relocation is ignored.
1605 ABSOLUTE = 0x0000,
1606 /// The 32-bit VA of the target.
1607 ADDR32 = 0x0001,
1608 /// The 32-bit RVA of the target.
1609 ADDR32NB = 0x0002,
1610 /// The 24-bit relative displacement to the target.
1611 BRANCH24 = 0x0003,
1612 /// The reference to a subroutine call.
1613 /// The reference consists of two 16-bit instructions with 11-bit offsets.
1614 BRANCH11 = 0x0004,
1615 /// The 32-bit relative address from the byte following the relocation.
1616 REL32 = 0x000A,
1617 /// The 16-bit section index of the section that contains the target.
1618 /// This is used to support debugging information.
1619 SECTION = 0x000E,
1620 /// The 32-bit offset of the target from the beginning of its section.
1621 /// This is used to support debugging information and static thread local storage.
1622 SECREL = 0x000F,
1623 /// The 32-bit VA of the target.
1624 /// This relocation is applied using a MOVW instruction for the low 16 bits followed by a MOVT for the high 16 bits.
1625 MOV32 = 0x0010,
1626 /// The 32-bit VA of the target.
1627 /// This relocation is applied using a MOVW instruction for the low 16 bits followed by a MOVT for the high 16 bits.
1628 THUMB_MOV32 = 0x0011,
1629 /// The instruction is fixed up with the 21-bit relative displacement to the 2-byte aligned target.
1630 /// The least significant bit of the displacement is always zero and is not stored.
1631 /// This relocation corresponds to a Thumb-2 32-bit conditional B instruction.
1632 THUMB_BRANCH20 = 0x0012,
1633 Unused = 0x0013,
1634 /// The instruction is fixed up with the 25-bit relative displacement to the 2-byte aligned target.
1635 /// The least significant bit of the displacement is zero and is not stored.This relocation corresponds to a Thumb-2 B instruction.
1636 THUMB_BRANCH24 = 0x0014,
1637 /// The instruction is fixed up with the 25-bit relative displacement to the 4-byte aligned target.
1638 /// The low 2 bits of the displacement are zero and are not stored.
1639 /// This relocation corresponds to a Thumb-2 BLX instruction.
1640 THUMB_BLX23 = 0x0015,
1641 /// The relocation is valid only when it immediately follows a ARM_REFHI or THUMB_REFHI.
1642 /// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
1643 PAIR = 0x0016,
1644 _,
1645 };
1646
1647 /// ARM64 Processors
1648 /// The following relocation type indicators are defined for ARM64 processors.
1649 pub const ARM64 = enum(u16) {
1650 /// The relocation is ignored.
1651 ABSOLUTE = 0x0000,
1652 /// The 32-bit VA of the target.
1653 ADDR32 = 0x0001,
1654 /// The 32-bit RVA of the target.
1655 ADDR32NB = 0x0002,
1656 /// The 26-bit relative displacement to the target, for B and BL instructions.
1657 BRANCH26 = 0x0003,
1658 /// The page base of the target, for ADRP instruction.
1659 PAGEBASE_REL21 = 0x0004,
1660 /// The 12-bit relative displacement to the target, for instruction ADR
1661 REL21 = 0x0005,
1662 /// The 12-bit page offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
1663 PAGEOFFSET_12A = 0x0006,
1664 /// The 12-bit page offset of the target, for instruction LDR (indexed, unsigned immediate).
1665 PAGEOFFSET_12L = 0x0007,
1666 /// The 32-bit offset of the target from the beginning of its section.
1667 /// This is used to support debugging information and static thread local storage.
1668 SECREL = 0x0008,
1669 /// Bit 0:11 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
1670 SECREL_LOW12A = 0x0009,
1671 /// Bit 12:23 of section offset of the target, for instructions ADD/ADDS (immediate) with zero shift.
1672 SECREL_HIGH12A = 0x000A,
1673 /// Bit 0:11 of section offset of the target, for instruction LDR (indexed, unsigned immediate).
1674 SECREL_LOW12L = 0x000B,
1675 /// CLR token.
1676 TOKEN = 0x000C,
1677 /// The 16-bit section index of the section that contains the target.
1678 /// This is used to support debugging information.
1679 SECTION = 0x000D,
1680 /// The 64-bit VA of the relocation target.
1681 ADDR64 = 0x000E,
1682 /// The 19-bit offset to the relocation target, for conditional B instruction.
1683 BRANCH19 = 0x000F,
1684 /// The 14-bit offset to the relocation target, for instructions TBZ and TBNZ.
1685 BRANCH14 = 0x0010,
1686 /// The 32-bit relative address from the byte following the relocation.
1687 REL32 = 0x0011,
1688 _,
1689 };
1690
1691 /// Hitachi SuperH Processors
1692 /// The following relocation type indicators are defined for SH3 and SH4 processors.
1693 /// SH5-specific relocations are noted as SHM (SH Media).
1694 pub const SH = enum(u16) {
1695 /// The relocation is ignored.
1696 @"3_ABSOLUTE" = 0x0000,
1697 /// A reference to the 16-bit location that contains the VA of the target symbol.
1698 @"3_DIRECT16" = 0x0001,
1699 /// The 32-bit VA of the target symbol.
1700 @"3_DIRECT32" = 0x0002,
1701 /// A reference to the 8-bit location that contains the VA of the target symbol.
1702 @"3_DIRECT8" = 0x0003,
1703 /// A reference to the 8-bit instruction that contains the effective 16-bit VA of the target symbol.
1704 @"3_DIRECT8_WORD" = 0x0004,
1705 /// A reference to the 8-bit instruction that contains the effective 32-bit VA of the target symbol.
1706 @"3_DIRECT8_LONG" = 0x0005,
1707 /// A reference to the 8-bit location whose low 4 bits contain the VA of the target symbol.
1708 @"3_DIRECT4" = 0x0006,
1709 /// A reference to the 8-bit instruction whose low 4 bits contain the effective 16-bit VA of the target symbol.
1710 @"3_DIRECT4_WORD" = 0x0007,
1711 /// A reference to the 8-bit instruction whose low 4 bits contain the effective 32-bit VA of the target symbol.
1712 @"3_DIRECT4_LONG" = 0x0008,
1713 /// A reference to the 8-bit instruction that contains the effective 16-bit relative offset of the target symbol.
1714 @"3_PCREL8_WORD" = 0x0009,
1715 /// A reference to the 8-bit instruction that contains the effective 32-bit relative offset of the target symbol.
1716 @"3_PCREL8_LONG" = 0x000A,
1717 /// A reference to the 16-bit instruction whose low 12 bits contain the effective 16-bit relative offset of the target symbol.
1718 @"3_PCREL12_WORD" = 0x000B,
1719 /// A reference to a 32-bit location that is the VA of the section that contains the target symbol.
1720 @"3_STARTOF_SECTION" = 0x000C,
1721 /// A reference to the 32-bit location that is the size of the section that contains the target symbol.
1722 @"3_SIZEOF_SECTION" = 0x000D,
1723 /// The 16-bit section index of the section that contains the target.
1724 /// This is used to support debugging information.
1725 @"3_SECTION" = 0x000E,
1726 /// The 32-bit offset of the target from the beginning of its section.
1727 /// This is used to support debugging information and static thread local storage.
1728 @"3_SECREL" = 0x000F,
1729 /// The 32-bit RVA of the target symbol.
1730 @"3_DIRECT32_NB" = 0x0010,
1731 /// GP relative.
1732 @"3_GPREL4_LONG" = 0x0011,
1733 /// CLR token.
1734 @"3_TOKEN" = 0x0012,
1735 /// The offset from the current instruction in longwords.
1736 /// If the NOMODE bit is not set, insert the inverse of the low bit at bit 32 to select PTA or PTB.
1737 M_PCRELPT = 0x0013,
1738 /// The low 16 bits of the 32-bit address.
1739 M_REFLO = 0x0014,
1740 /// The high 16 bits of the 32-bit address.
1741 M_REFHALF = 0x0015,
1742 /// The low 16 bits of the relative address.
1743 M_RELLO = 0x0016,
1744 /// The high 16 bits of the relative address.
1745 M_RELHALF = 0x0017,
1746 /// The relocation is valid only when it immediately follows a REFHALF, RELHALF, or RELLO relocation.
1747 /// The SymbolTableIndex field of the relocation contains a displacement and not an index into the symbol table.
1748 M_PAIR = 0x0018,
1749 /// The relocation ignores section mode.
1750 M_NOMODE = 0x8000,
1751 _,
1752 };
1753
1754 /// IBM PowerPC Processors
1755 /// The following relocation type indicators are defined for PowerPC processors.
1756 pub const PPC = enum(u16) {
1757 /// The relocation is ignored.
1758 ABSOLUTE = 0x0000,
1759 /// The 64-bit VA of the target.
1760 ADDR64 = 0x0001,
1761 /// The 32-bit VA of the target.
1762 ADDR32 = 0x0002,
1763 /// The low 24 bits of the VA of the target.
1764 /// This is valid only when the target symbol is absolute and can be sign-extended to its original value.
1765 ADDR24 = 0x0003,
1766 /// The low 16 bits of the target's VA.
1767 ADDR16 = 0x0004,
1768 /// The low 14 bits of the target's VA.
1769 /// This is valid only when the target symbol is absolute and can be sign-extended to its original value.
1770 ADDR14 = 0x0005,
1771 /// A 24-bit PC-relative offset to the symbol's location.
1772 REL24 = 0x0006,
1773 /// A 14-bit PC-relative offset to the symbol's location.
1774 REL14 = 0x0007,
1775 /// The 32-bit RVA of the target.
1776 ADDR32NB = 0x000A,
1777 /// The 32-bit offset of the target from the beginning of its section.
1778 /// This is used to support debugging information and static thread local storage.
1779 SECREL = 0x000B,
1780 /// The 16-bit section index of the section that contains the target.
1781 /// This is used to support debugging information.
1782 SECTION = 0x000C,
1783 /// The 16-bit offset of the target from the beginning of its section.
1784 /// This is used to support debugging information and static thread local storage.
1785 SECREL16 = 0x000F,
1786 /// The high 16 bits of the target's 32-bit VA.
1787 /// This is used for the first instruction in a two-instruction sequence that loads a full address.
1788 /// This relocation must be immediately followed by a PAIR relocation whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that was taken from the location that is being relocated.
1789 REFHI = 0x0010,
1790 /// The low 16 bits of the target's VA.
1791 REFLO = 0x0011,
1792 /// A relocation that is valid only when it immediately follows a REFHI or SECRELHI relocation.
1793 /// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
1794 PAIR = 0x0012,
1795 /// The low 16 bits of the 32-bit offset of the target from the beginning of its section.
1796 SECRELLO = 0x0013,
1797 /// The 16-bit signed displacement of the target relative to the GP register.
1798 GPREL = 0x0015,
1799 /// The CLR token.
1800 TOKEN = 0x0016,
1801 _,
1802 };
1803
1804 /// Intel 386 Processors
1805 /// The following relocation type indicators are defined for Intel 386 and compatible processors.
1806 pub const I386 = enum(u16) {
1807 /// The relocation is ignored.
1808 ABSOLUTE = 0x0000,
1809 /// Not supported.
1810 DIR16 = 0x0001,
1811 /// Not supported.
1812 REL16 = 0x0002,
1813 /// The target's 32-bit VA.
1814 DIR32 = 0x0006,
1815 /// The target's 32-bit RVA.
1816 DIR32NB = 0x0007,
1817 /// Not supported.
1818 SEG12 = 0x0009,
1819 /// The 16-bit section index of the section that contains the target.
1820 /// This is used to support debugging information.
1821 SECTION = 0x000A,
1822 /// The 32-bit offset of the target from the beginning of its section.
1823 /// This is used to support debugging information and static thread local storage.
1824 SECREL = 0x000B,
1825 /// The CLR token.
1826 TOKEN = 0x000C,
1827 /// A 7-bit offset from the base of the section that contains the target.
1828 SECREL7 = 0x000D,
1829 /// The 32-bit relative displacement to the target.
1830 /// This supports the x86 relative branch and call instructions.
1831 REL32 = 0x0014,
1832 _,
1833 };
1834
1835 /// Intel Itanium Processor Family (IPF)
1836 /// The following relocation type indicators are defined for the Intel Itanium processor family and compatible processors.
1837 /// Note that relocations on instructions use the bundle's offset and slot number for the relocation offset.
1838 pub const IA64 = enum(u16) {
1839 /// The relocation is ignored.
1840 ABSOLUTE = 0x0000,
1841 /// The instruction relocation can be followed by an ADDEND relocation whose value is added to the target address before it is inserted into the specified slot in the IMM14 bundle.
1842 /// The relocation target must be absolute or the image must be fixed.
1843 IMM14 = 0x0001,
1844 /// The instruction relocation can be followed by an ADDEND relocation whose value is added to the target address before it is inserted into the specified slot in the IMM22 bundle.
1845 /// The relocation target must be absolute or the image must be fixed.
1846 IMM22 = 0x0002,
1847 /// The slot number of this relocation must be one (1).
1848 /// The relocation can be followed by an ADDEND relocation whose value is added to the target address before it is stored in all three slots of the IMM64 bundle.
1849 IMM64 = 0x0003,
1850 /// The target's 32-bit VA.
1851 /// This is supported only for /LARGEADDRESSAWARE:NO images.
1852 DIR32 = 0x0004,
1853 /// The target's 64-bit VA.
1854 DIR64 = 0x0005,
1855 /// The instruction is fixed up with the 25-bit relative displacement to the 16-bit aligned target.
1856 /// The low 4 bits of the displacement are zero and are not stored.
1857 PCREL21B = 0x0006,
1858 /// The instruction is fixed up with the 25-bit relative displacement to the 16-bit aligned target.
1859 /// The low 4 bits of the displacement, which are zero, are not stored.
1860 PCREL21M = 0x0007,
1861 /// The LSBs of this relocation's offset must contain the slot number whereas the rest is the bundle address.
1862 /// The bundle is fixed up with the 25-bit relative displacement to the 16-bit aligned target.
1863 /// The low 4 bits of the displacement are zero and are not stored.
1864 PCREL21F = 0x0008,
1865 /// The instruction relocation can be followed by an ADDEND relocation whose value is added to the target address and then a 22-bit GP-relative offset that is calculated and applied to the GPREL22 bundle.
1866 GPREL22 = 0x0009,
1867 /// The instruction is fixed up with the 22-bit GP-relative offset to the target symbol's literal table entry.
1868 /// The linker creates this literal table entry based on this relocation and the ADDEND relocation that might follow.
1869 LTOFF22 = 0x000A,
1870 /// The 16-bit section index of the section contains the target.
1871 /// This is used to support debugging information.
1872 SECTION = 0x000B,
1873 /// The instruction is fixed up with the 22-bit offset of the target from the beginning of its section.
1874 /// This relocation can be followed immediately by an ADDEND relocation, whose Value field contains the 32-bit unsigned offset of the target from the beginning of the section.
1875 SECREL22 = 0x000C,
1876 /// The slot number for this relocation must be one (1).
1877 /// The instruction is fixed up with the 64-bit offset of the target from the beginning of its section.
1878 /// This relocation can be followed immediately by an ADDEND relocation whose Value field contains the 32-bit unsigned offset of the target from the beginning of the section.
1879 SECREL64I = 0x000D,
1880 /// The address of data to be fixed up with the 32-bit offset of the target from the beginning of its section.
1881 SECREL32 = 0x000E,
1882 /// The target's 32-bit RVA.
1883 DIR32NB = 0x0010,
1884 /// This is applied to a signed 14-bit immediate that contains the difference between two relocatable targets.
1885 /// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
1886 SREL14 = 0x0011,
1887 /// This is applied to a signed 22-bit immediate that contains the difference between two relocatable targets.
1888 /// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
1889 SREL22 = 0x0012,
1890 /// This is applied to a signed 32-bit immediate that contains the difference between two relocatable values.
1891 /// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
1892 SREL32 = 0x0013,
1893 /// This is applied to an unsigned 32-bit immediate that contains the difference between two relocatable values.
1894 /// This is a declarative field for the linker that indicates that the compiler has already emitted this value.
1895 UREL32 = 0x0014,
1896 /// A 60-bit PC-relative fixup that always stays as a BRL instruction of an MLX bundle.
1897 PCREL60X = 0x0015,
1898 /// A 60-bit PC-relative fixup.
1899 /// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MBB bundle with NOP.B in slot 1 and a 25-bit BR instruction (with the 4 lowest bits all zero and dropped) in slot 2.
1900 PCREL60B = 0x0016,
1901 /// A 60-bit PC-relative fixup.
1902 /// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MFB bundle with NOP.F in slot 1 and a 25-bit (4 lowest bits all zero and dropped) BR instruction in slot 2.
1903 PCREL60F = 0x0017,
1904 /// A 60-bit PC-relative fixup.
1905 /// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MIB bundle with NOP.I in slot 1 and a 25-bit (4 lowest bits all zero and dropped) BR instruction in slot 2.
1906 PCREL60I = 0x0018,
1907 /// A 60-bit PC-relative fixup.
1908 /// If the target displacement fits in a signed 25-bit field, convert the entire bundle to an MMB bundle with NOP.M in slot 1 and a 25-bit (4 lowest bits all zero and dropped) BR instruction in slot 2.
1909 PCREL60M = 0x0019,
1910 /// A 64-bit GP-relative fixup.
1911 IMMGPREL64 = 0x001a,
1912 /// A CLR token.
1913 TOKEN = 0x001b,
1914 /// A 32-bit GP-relative fixup.
1915 GPREL32 = 0x001c,
1916 /// The relocation is valid only when it immediately follows one of the following relocations: IMM14, IMM22, IMM64, GPREL22, LTOFF22, LTOFF64, SECREL22, SECREL64I, or SECREL32.
1917 /// Its value contains the addend to apply to instructions within a bundle, not for data.
1918 ADDEND = 0x001F,
1919 _,
1920 };
1921
1922 /// MIPS Processors
1923 /// The following relocation type indicators are defined for MIPS processors.
1924 pub const MIPS = enum(u16) {
1925 /// The relocation is ignored.
1926 ABSOLUTE = 0x0000,
1927 /// The high 16 bits of the target's 32-bit VA.
1928 REFHALF = 0x0001,
1929 /// The target's 32-bit VA.
1930 REFWORD = 0x0002,
1931 /// The low 26 bits of the target's VA.
1932 /// This supports the MIPS J and JAL instructions.
1933 JMPADDR = 0x0003,
1934 /// The high 16 bits of the target's 32-bit VA.
1935 /// This is used for the first instruction in a two-instruction sequence that loads a full address.
1936 /// This relocation must be immediately followed by a PAIR relocation whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that are taken from the location that is being relocated.
1937 REFHI = 0x0004,
1938 /// The low 16 bits of the target's VA.
1939 REFLO = 0x0005,
1940 /// A 16-bit signed displacement of the target relative to the GP register.
1941 GPREL = 0x0006,
1942 /// The same as IMAGE_REL_MIPS_GPREL.
1943 LITERAL = 0x0007,
1944 /// The 16-bit section index of the section contains the target.
1945 /// This is used to support debugging information.
1946 SECTION = 0x000A,
1947 /// The 32-bit offset of the target from the beginning of its section.
1948 /// This is used to support debugging information and static thread local storage.
1949 SECREL = 0x000B,
1950 /// The low 16 bits of the 32-bit offset of the target from the beginning of its section.
1951 SECRELLO = 0x000C,
1952 /// The high 16 bits of the 32-bit offset of the target from the beginning of its section.
1953 /// An IMAGE_REL_MIPS_PAIR relocation must immediately follow this one.
1954 /// The SymbolTableIndex of the PAIR relocation contains a signed 16-bit displacement that is added to the upper 16 bits that are taken from the location that is being relocated.
1955 SECRELHI = 0x000D,
1956 /// The low 26 bits of the target's VA.
1957 /// This supports the MIPS16 JAL instruction.
1958 JMPADDR16 = 0x0010,
1959 /// The target's 32-bit RVA.
1960 REFWORDNB = 0x0022,
1961 /// The relocation is valid only when it immediately follows a REFHI or SECRELHI relocation.
1962 /// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
1963 PAIR = 0x0025,
1964 _,
1965 };
1966
1967 /// Mitsubishi M32R
1968 /// The following relocation type indicators are defined for the Mitsubishi M32R processors.
1969 pub const M32R = enum(u16) {
1970 /// The relocation is ignored.
1971 ABSOLUTE = 0x0000,
1972 /// The target's 32-bit VA.
1973 ADDR32 = 0x0001,
1974 /// The target's 32-bit RVA.
1975 ADDR32NB = 0x0002,
1976 /// The target's 24-bit VA.
1977 ADDR24 = 0x0003,
1978 /// The target's 16-bit offset from the GP register.
1979 GPREL16 = 0x0004,
1980 /// The target's 24-bit offset from the program counter (PC), shifted left by 2 bits and sign-extended
1981 PCREL24 = 0x0005,
1982 /// The target's 16-bit offset from the PC, shifted left by 2 bits and sign-extended
1983 PCREL16 = 0x0006,
1984 /// The target's 8-bit offset from the PC, shifted left by 2 bits and sign-extended
1985 PCREL8 = 0x0007,
1986 /// The 16 MSBs of the target VA.
1987 REFHALF = 0x0008,
1988 /// The 16 MSBs of the target VA, adjusted for LSB sign extension.
1989 /// This is used for the first instruction in a two-instruction sequence that loads a full 32-bit address.
1990 /// This relocation must be immediately followed by a PAIR relocation whose SymbolTableIndex contains a signed 16-bit displacement that is added to the upper 16 bits that are taken from the location that is being relocated.
1991 REFHI = 0x0009,
1992 /// The 16 LSBs of the target VA.
1993 REFLO = 0x000A,
1994 /// The relocation must follow the REFHI relocation.
1995 /// Its SymbolTableIndex contains a displacement and not an index into the symbol table.
1996 PAIR = 0x000B,
1997 /// The 16-bit section index of the section that contains the target.
1998 /// This is used to support debugging information.
1999 SECTION = 0x000C,
2000 /// The 32-bit offset of the target from the beginning of its section.
2001 /// This is used to support debugging information and static thread local storage.
2002 SECREL = 0x000D,
2003 /// The CLR token.
2004 TOKEN = 0x000E,
2005 _,
2006 };
2007 };
2008};
2009
2010pub const ArchiveMemberHeader = extern struct {
2011 /// Left-justified '/' terminated member name
2012 name: [16]u8,
2013 /// Left-justified ASCII decimal: seconds since January 1st, 1970
2014 date: [12]u8,
2015 /// Left-justified ASCII decimal: user id
2016 user_id: [6]u8,
2017 /// Left-justified ASCII decimal: group id
2018 group_id: [6]u8,
2019 /// Left-justified ASCII octal: file mode
2020 file_mode: [8]u8,
2021 /// Left-justified ASCII decimal: size of the member following this header,
2022 /// not including the size of this header.
2023 size: [10]u8,
2024 /// The literal string '`\n'
2025 end_of_header: [2]u8,
2026
2027 /// Extracts the name of the member by either reading it directly from
2028 /// the header, or by finding it inside the longnames member, if provided.
2029 pub fn parseName(
2030 self: *const ArchiveMemberHeader,
2031 opt_longnames: ?[]const u8,
2032 ) ![]const u8 {
2033 const trim = std.mem.trimEnd(u8, &self.name, &.{' '});
2034
2035 if (trim.len == 0) return error.BadName;
2036 return if (trim[0] == '/') name: {
2037 if (trim.len == 1 or
2038 trim.len == 2 and trim[1] == '/')
2039 break :name trim;
2040
2041 const offset = std.fmt.parseUnsigned(u50, trim[1..], 10) catch
2042 return error.BadName;
2043
2044 if (opt_longnames) |longnames| {
2045 if (offset >= longnames.len) return error.BadName;
2046 break :name std.mem.sliceTo(longnames[@intCast(offset)..], 0);
2047 } else return error.NoLongNames;
2048 } else if (trim[trim.len - 1] == '/')
2049 trim[0 .. trim.len - 1]
2050 else
2051 return error.BadName;
2052 }
2053
2054 fn parseField(field: []const u8, T: type, base: u8) !T {
2055 if (std.mem.allEqual(u8, field, ' ')) return 0;
2056 if (field[0] == '-')
2057 return @bitCast(try std.fmt.parseInt(
2058 @Int(.signed, @typeInfo(T).int.bits),
2059 std.mem.trimEnd(u8, field, &.{' '}),
2060 base,
2061 ));
2062
2063 return std.fmt.parseUnsigned(T, std.mem.trimEnd(u8, field, &.{' '}), base);
2064 }
2065
2066 pub fn parseDate(self: *const ArchiveMemberHeader) !u40 {
2067 return parseField(&self.date, u40, 10);
2068 }
2069
2070 pub fn parseUserId(self: *const ArchiveMemberHeader) !u20 {
2071 return parseField(&self.user_id, u20, 10);
2072 }
2073
2074 pub fn parseGroupId(self: *const ArchiveMemberHeader) !u20 {
2075 return parseField(&self.group_id, u20, 10);
2076 }
2077
2078 pub fn parseFileMode(self: *const ArchiveMemberHeader) !u20 {
2079 return parseField(&self.group_id, u20, 8);
2080 }
2081
2082 pub fn parseSize(self: *const ArchiveMemberHeader) !u34 {
2083 return parseField(&self.size, u34, 10);
2084 }
2085
2086 pub const Kind = enum {
2087 first_linker,
2088 second_linker,
2089 longnames,
2090 coff,
2091 import,
2092 };
2093};
2094
2095pub const LineNumber = extern struct {
2096 type: extern union {
2097 symbol_table_index: u32,
2098 virtual_address: u32,
2099 },
2100 line_number: u16,
2101
2102 pub fn sizeOf() comptime_int {
2103 return 6;
2104 }
2105};