authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-22 19:48:14+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-23 08:55:04+02:00
logc84e5ee87852eafff0cbf986bf02c5221cbcec35
treea978b80bbcc7273ad3a0e3d4de843a8a3ec947e9
parent9043e665a549b87fa73ade7af06682368092d8a9

coff: improve default COFF/PE object parser

We now do not allocate memory for headers and other metadata unless requested by the caller. Instead, we read-in the entire contents of the image into memory and operate on pointers and casts wherever possible. I have a left a TODO to hook up Windows' memory-mapped API here in-place of standard `readToEndAlloc` which should be more memory proof on memory constrained hosts. This commit also supplements our `std.coff` with a lot missing basic extern structs required to make our COFF linker.

3 files changed, 1016 insertions(+), 277 deletions(-)

lib/std/coff.zig+976-248
......@@ -1,14 +1,731 @@
11const std = @import("std.zig");
2const assert = std.debug.assert;
23const io = std.io;
34const mem = std.mem;
45const os = std.os;
5const File = std.fs.File;
6const fs = std.fs;
67
7// CoffHeader.machine values
8// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
9const IMAGE_FILE_MACHINE_I386 = 0x014c;
10const IMAGE_FILE_MACHINE_IA64 = 0x0200;
11const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
8pub 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
69pub 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
99pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
100pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
101
102pub 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
139pub 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
183pub 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
194pub 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
227pub 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
259pub 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
270pub const ImageDataDirectory = extern struct {
271 virtual_address: u32,
272 size: u32,
273};
274
275pub 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
311pub 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
408pub 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
433pub 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
448pub const SymType = packed struct {
449 complex_type: ComplexType,
450 base_type: BaseType,
451};
452
453pub 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
503pub 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
517pub 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
610pub 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
629pub 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
651pub 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
662pub 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
675pub const WeakExternalFlag = enum(u32) {
676 SEARCH_NOLIBRARY = 1,
677 SEARCH_LIBRARY = 2,
678 SEARCH_ALIAS = 3,
679 ANTI_DEPENDENCY = 4,
680};
681
682pub 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
714pub 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};
12729
13730pub const MachineType = enum(u16) {
14731 Unknown = 0x0,
......@@ -77,25 +794,6 @@ pub const MachineType = enum(u16) {
77794 }
78795};
79796
80// OptionalHeader.magic values
81// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
82const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
83const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
84
85// Image Characteristics
86pub const IMAGE_FILE_RELOCS_STRIPPED = 0x1;
87pub const IMAGE_FILE_DEBUG_STRIPPED = 0x200;
88pub const IMAGE_FILE_EXECUTABLE_IMAGE = 0x2;
89pub const IMAGE_FILE_32BIT_MACHINE = 0x100;
90pub const IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x20;
91
92// Section flags
93pub const IMAGE_SCN_CNT_INITIALIZED_DATA = 0x40;
94pub const IMAGE_SCN_MEM_READ = 0x40000000;
95pub const IMAGE_SCN_CNT_CODE = 0x20;
96pub const IMAGE_SCN_MEM_EXECUTE = 0x20000000;
97pub const IMAGE_SCN_MEM_WRITE = 0x80000000;
98
99797const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
100798const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
101799const DEBUG_DIRECTORY = 6;
......@@ -104,166 +802,90 @@ pub const CoffError = error{
104802 InvalidPEMagic,
105803 InvalidPEHeader,
106804 InvalidMachine,
805 MissingPEHeader,
107806 MissingCoffSection,
108807 MissingStringTable,
109808};
110809
111810// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
112811pub const Coff = struct {
113 in_file: File,
114812 allocator: mem.Allocator,
813 data: []const u8 = undefined,
814 is_image: bool = false,
815 coff_header_offset: usize = 0,
115816
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,
133819
134820 pub fn deinit(self: *Coff) void {
135 self.sections.deinit(self.allocator);
821 self.allocator.free(self.data);
136822 }
137823
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;
167827
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";
172830
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);
175839
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 }
189848 }
190849
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.
232852 }
233853
234854 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
235 try self.loadSections();
855 assert(self.is_image);
236856
237857 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;
242862 } else {
243863 return error.MissingCoffSection;
244864 }
245865 };
246866
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];
248869 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
249870
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);
252874
253875 // Find the correct DebugDirectoryEntry, and where its data is stored.
254876 // It can be in any section.
255877 const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
256878 var i: u32 = 0;
257879 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);
259881 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;
263885 const rva = debug_dir_entry.address_of_raw_data;
264886 const offset = rva - section_start;
265887 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);
267889 break :blk;
268890 }
269891 }
......@@ -271,19 +893,19 @@ pub const Coff = struct {
271893 }
272894
273895 var cv_signature: [4]u8 = undefined; // CodeView signature
274 try in.readNoEof(cv_signature[0..]);
896 try reader.readNoEof(cv_signature[0..]);
275897 // 'RSDS' indicates PDB70 format, used by lld.
276898 if (!mem.eql(u8, &cv_signature, "RSDS"))
277899 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);
280902
281903 // Finally read the null-terminated string.
282 var byte = try in.readByte();
904 var byte = try reader.readByte();
283905 i = 0;
284906 while (byte != 0 and i < buffer.len) : (i += 1) {
285907 buffer[i] = byte;
286 byte = try in.readByte();
908 byte = try reader.readByte();
287909 }
288910
289911 if (byte != 0 and i == buffer.len)
......@@ -292,126 +914,232 @@ pub const Coff = struct {
292914 return @as(usize, i);
293915 }
294916
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 }
298920
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 }
300926
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 }
302932
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 }
304938
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 }
308947
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 }
312956
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 }
319967
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];
335990 }
336991
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;
3411005 }
3421006 }
3431007 return null;
3441008 }
3451009
3461010 // 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]);
3591015 return out_buff;
3601016 }
361};
3621017
363const 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,
3721020
373const 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 }
3781024
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 };
3851033
386const 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 };
3961042
397pub 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 }
4001056
401const 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 }
4051135 };
4061136
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 };
4171145};
lib/std/debug.zig+22-18
......@@ -816,11 +816,11 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) anyerror!DebugInfo {
816816/// TODO it's weird to take ownership even on error, rework this code.
817817fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo {
818818 nosuspend {
819 errdefer coff_file.close();
819 defer coff_file.close();
820820
821821 const coff_obj = try allocator.create(coff.Coff);
822822 errdefer allocator.destroy(coff_obj);
823 coff_obj.* = coff.Coff.init(allocator, coff_file);
823 coff_obj.* = .{ .allocator = allocator };
824824
825825 var di = ModuleDebugInfo{
826826 .base_address = undefined,
......@@ -828,18 +828,21 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo
828828 .debug_data = undefined,
829829 };
830830
831 try di.coff.loadHeader();
832 try di.coff.loadSections();
833 if (di.coff.getSection(".debug_info")) |sec| {
831 // TODO convert to Windows' memory-mapped file API
832 const file_len = math.cast(usize, try coff_file.getEndPos()) orelse math.maxInt(usize);
833 const data = try coff_file.readToEndAlloc(allocator, file_len);
834 try di.coff.parse(data);
835
836 if (di.coff.getSectionByName(".debug_info")) |sec| {
834837 // This coff file has embedded DWARF debug info
835838 _ = sec;
836839 // TODO: free the section data slices
837 const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null;
838 const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null;
839 const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null;
840 const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null;
841 const debug_line_str_data = di.coff.getSectionData(".debug_line_str", allocator) catch null;
842 const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null;
840 const debug_info_data = di.coff.getSectionDataAlloc(".debug_info", allocator) catch null;
841 const debug_abbrev_data = di.coff.getSectionDataAlloc(".debug_abbrev", allocator) catch null;
842 const debug_str_data = di.coff.getSectionDataAlloc(".debug_str", allocator) catch null;
843 const debug_line_data = di.coff.getSectionDataAlloc(".debug_line", allocator) catch null;
844 const debug_line_str_data = di.coff.getSectionDataAlloc(".debug_line_str", allocator) catch null;
845 const debug_ranges_data = di.coff.getSectionDataAlloc(".debug_ranges", allocator) catch null;
843846
844847 var dwarf = DW.DwarfInfo{
845848 .endian = native_endian,
......@@ -1628,7 +1631,7 @@ pub const ModuleDebugInfo = switch (native_os) {
16281631
16291632 switch (self.debug_data) {
16301633 .dwarf => |*dwarf| {
1631 const dwarf_address = relocated_address + self.coff.pe_header.image_base;
1634 const dwarf_address = relocated_address + self.coff.getImageBase();
16321635 return getSymbolFromDwarf(allocator, dwarf_address, dwarf);
16331636 },
16341637 .pdb => {
......@@ -1636,13 +1639,14 @@ pub const ModuleDebugInfo = switch (native_os) {
16361639 },
16371640 }
16381641
1639 var coff_section: *coff.Section = undefined;
1642 var coff_section: *align(1) const coff.SectionHeader = undefined;
16401643 const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
1641 if (sect_contrib.Section > self.coff.sections.items.len) continue;
1644 const sections = self.coff.getSectionHeaders();
1645 if (sect_contrib.Section > sections.len) continue;
16421646 // Remember that SectionContribEntry.Section is 1-based.
1643 coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
1647 coff_section = &sections[sect_contrib.Section - 1];
16441648
1645 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
1649 const vaddr_start = coff_section.virtual_address + sect_contrib.Offset;
16461650 const vaddr_end = vaddr_start + sect_contrib.Size;
16471651 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
16481652 break sect_contrib.ModuleIndex;
......@@ -1658,11 +1662,11 @@ pub const ModuleDebugInfo = switch (native_os) {
16581662
16591663 const symbol_name = self.debug_data.pdb.getSymbolName(
16601664 module,
1661 relocated_address - coff_section.header.virtual_address,
1665 relocated_address - coff_section.virtual_address,
16621666 ) orelse "???";
16631667 const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
16641668 module,
1665 relocated_address - coff_section.header.virtual_address,
1669 relocated_address - coff_section.virtual_address,
16661670 );
16671671
16681672 return SymbolInfo{
src/link/Coff.zig+18-11
......@@ -204,15 +204,18 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
204204 index += 2;
205205
206206 // Characteristics
207 var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
207 var characteristics: std.coff.CoffHeaderFlags = .{
208 .DEBUG_STRIPPED = 1, // TODO remove debug info stripped flag when necessary
209 .RELOCS_STRIPPED = 1,
210 };
208211 if (options.output_mode == .Exe) {
209 characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
212 characteristics.EXECUTABLE_IMAGE = 1;
210213 }
211214 switch (self.ptr_width) {
212 .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
213 .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
215 .p32 => characteristics.@"32BIT_MACHINE" = 1,
216 .p64 => characteristics.LARGE_ADDRESS_AWARE = 1,
214217 }
215 mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
218 mem.writeIntLittle(u16, hdr_data[index..][0..2], @bitCast(u16, characteristics));
216219 index += 2;
217220
218221 assert(index == 20);
......@@ -352,7 +355,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
352355 mem.set(u8, hdr_data[index..][0..12], 0);
353356 index += 12;
354357 // Section flags
355 mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
358 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
359 .CNT_INITIALIZED_DATA = 1,
360 .MEM_READ = 1,
361 }));
356362 index += 4;
357363 // Then, the .text section
358364 hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
......@@ -378,11 +384,12 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
378384 mem.set(u8, hdr_data[index..][0..12], 0);
379385 index += 12;
380386 // Section flags
381 mem.writeIntLittle(
382 u32,
383 hdr_data[index..][0..4],
384 std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
385 );
387 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
388 .CNT_CODE = 1,
389 .MEM_EXECUTE = 1,
390 .MEM_READ = 1,
391 .MEM_WRITE = 1,
392 }));
386393 index += 4;
387394
388395 assert(index == optional_header_size + section_table_size);