1const Coff = @This();
2
3const builtin = @import("builtin");
4const native_endian = builtin.cpu.arch.endian();
5
6const std = @import("std");
7const Io = std.Io;
8const assert = std.debug.assert;
9const log = std.log.scoped(.link);
10const Crc32 = std.hash.crc.@"CRC-32/JAMCRC";
11
12const codegen = @import("../codegen.zig");
13const Compilation = @import("../Compilation.zig");
14const InternPool = @import("../InternPool.zig");
15const link = @import("../link.zig");
16const MappedFile = @import("MappedFile.zig");
17const target_util = @import("../target.zig");
18const Type = @import("../Type.zig");
19const Value = @import("../Value.zig");
20const Zcu = @import("../Zcu.zig");
21const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;
22const implib = @import("../libs/mingw/implib.zig");
23const Path = std.Build.Cache.Path;
24const Alignment = MappedFile.Alignment;
25
26base: link.File,
27options: link.File.OpenOptions,
28mf: MappedFile,
29nodes: std.MultiArrayList(Node),
30members: std.ArrayList(Member),
31pending_members: std.array_hash_map.Auto(Member.Index, void),
32lib_string_table: std.ArrayList(String),
33lib_string_len: u32,
34long_names_table: LongNamesTable,
35import_table: ImportTable,
36export_table: ExportTable,
37symbol_table: SymbolTable,
38inputs: std.array_hash_map.Custom(std.Build.Cache.Path, void, std.Build.Cache.Path.TableAdapter, false),
39input_archives: std.ArrayList(InputArchive),
40input_archive_members: std.ArrayList(InputArchive.Member),
41input_archive_symbols: std.ArrayList(InputArchive.Member.Symbol),
42input_archive_symbol_indices: std.array_hash_map.Auto(String, InputArchive.SearchList),
43pending_input: ?InputArchive.Member.Index,
44pending_default_libs: std.ArrayList(struct {
45 path: []const u8,
46 ioi: InputObject.Index,
47}),
48alternate_names: std.array_hash_map.Auto(String, String),
49input_objects: std.ArrayList(InputObject),
50input_symbols: std.ArrayList(struct { si: Symbol.Index, name: String }),
51input_sections: std.ArrayList(Node.InputSection),
52input_section_pending_index: u32,
53inputs_complete: bool,
54exports_complete: bool,
55pending_special_symbol: SpecialSymbol,
56strings: std.HashMapUnmanaged(
57 u32,
58 void,
59 std.hash_map.StringIndexContext,
60 std.hash_map.default_max_load_percentage,
61),
62string_bytes: std.ArrayList(u8),
63section_table: std.array_hash_map.Auto(String, Section),
64pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),
65object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
66section_merges: std.array_hash_map.Auto(String, String),
67section_merge_pending_index: u32,
68symbols: std.ArrayList(Symbol),
69globals: std.array_hash_map.Auto(String, Global),
70global_pending_index: u32,
71navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),
72uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),
73lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
74 map: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),
75 pending_index: u32,
76}),
77pending_uavs: std.array_hash_map.Auto(Node.UavMapIndex, struct {
78 alignment: InternPool.Alignment,
79}),
80relocs: std.ArrayList(Reloc),
81first_free_reloc: Reloc.Index,
82last_free_reloc: Reloc.Index,
83const_prog_node: std.Progress.Node,
84synth_prog_node: std.Progress.Node,
85symbol_prog_node: std.Progress.Node,
86member_prog_node: std.Progress.Node,
87input_prog_node: std.Progress.Node,
88
89pub const default_file_alignment: u16 = 0x200;
90pub const default_size_of_stack_reserve: u32 = 0x1000000;
91pub const default_size_of_stack_commit: u32 = 0x1000;
92pub const default_size_of_heap_reserve: u32 = 0x100000;
93pub const default_size_of_heap_commit: u32 = 0x1000;
94
95pub const imp_prefix = "__imp_";
96
97const header_name_max_len = @typeInfo(@FieldType(std.coff.SectionHeader, "name")).array.len;
98
99const Error = link.Error || error{MappedFileIo};
100const LoadInputError = Error ||
101 Io.File.SeekError ||
102 Io.File.Reader.SizeError ||
103 Io.Reader.Error ||
104 MappedFile.Error;
105
106/// This is the start of a Portable Executable (PE) file.
107/// It starts with a MS-DOS header followed by a MS-DOS stub program.
108/// This data does not change so we include it as follows in all binaries.
109///
110/// In this context,
111/// A "paragraph" is 16 bytes.
112/// A "page" is 512 bytes.
113/// A "long" is 4 bytes.
114/// A "word" is 2 bytes.
115pub const msdos_stub: [120]u8 = .{
116 'M', 'Z', // Magic number. Stands for Mark Zbikowski (designer of the MS-DOS executable format).
117 0x78, 0x00, // Number of bytes in the last page. This matches the size of this entire MS-DOS stub.
118 0x01, 0x00, // Number of pages.
119 0x00, 0x00, // Number of entries in the relocation table.
120 0x04, 0x00, // The number of paragraphs taken up by the header. 4 * 16 = 64, which matches the header size (all bytes before the MS-DOS stub program).
121 0x00, 0x00, // The number of paragraphs required by the program.
122 0x00, 0x00, // The number of paragraphs requested by the program.
123 0x00, 0x00, // Initial value for SS (relocatable segment address).
124 0x00, 0x00, // Initial value for SP.
125 0x00, 0x00, // Checksum.
126 0x00, 0x00, // Initial value for IP.
127 0x00, 0x00, // Initial value for CS (relocatable segment address).
128 0x40, 0x00, // Absolute offset to relocation table. 64 matches the header size (all bytes before the MS-DOS stub program).
129 0x00, 0x00, // Overlay number. Zero means this is the main executable.
130}
131 // Reserved words.
132 ++ .{
133 0x00, 0x00,
134 0x00, 0x00,
135 0x00, 0x00,
136 0x00, 0x00,
137 }
138 // OEM-related fields.
139 ++ .{
140 0x00, 0x00, // OEM identifier.
141 0x00, 0x00, // OEM information.
142 }
143 // Reserved words.
144 ++ .{
145 0x00, 0x00,
146 0x00, 0x00,
147 0x00, 0x00,
148 0x00, 0x00,
149 0x00, 0x00,
150 0x00, 0x00,
151 0x00, 0x00,
152 0x00, 0x00,
153 0x00, 0x00,
154 0x00, 0x00,
155 }
156 // Address of the PE header (a long). This matches the size of this entire MS-DOS stub, so that's the address of what's after this MS-DOS stub.
157 ++ .{ 0x78, 0x00, 0x00, 0x00 }
158 // What follows is a 16-bit x86 MS-DOS program of 7 instructions that prints the bytes after these instructions and then exits.
159 ++ .{
160 // Set the value of the data segment to the same value as the code segment.
161 0x0e, // push cs
162 0x1f, // pop ds
163 // Set the DX register to the address of the message.
164 // If you count all bytes of these 7 instructions you get 14, so that's the address of what's after these instructions.
165 0xba, 14, 0x00, // mov dx, 14
166 // Set AH to the system call code for printing a message.
167 0xb4, 0x09, // mov ah, 0x09
168 // Perform the system call to print the message.
169 0xcd, 0x21, // int 0x21
170 // Set AH to 0x4c which is the system call code for exiting, and set AL to 0x01 which is the exit code.
171 0xb8, 0x01, 0x4c, // mov ax, 0x4c01
172 // Peform the system call to exit the program with exit code 1.
173 0xcd, 0x21, // int 0x21
174 }
175 // Message to print.
176 ++ "This program cannot be run in DOS mode.".*
177 // Message terminators.
178 ++ .{
179 '$', // We do not pass a length to the print system call; the string is terminated by this character.
180 0x00, 0x00, // Terminating zero bytes.
181 };
182
183pub const Node = union(enum) {
184 file,
185 header,
186 /// Images and archives only.
187 signature,
188 /// Archives only.
189 archive_member_header: Member.Index,
190 archive_member: Member.Index,
191
192 coff_header,
193
194 /// Image only
195 optional_header,
196 data_directories,
197
198 section_table,
199
200 /// Archives and objects only
201 symbol_table,
202 string_table,
203 relocation_table: Symbol.SectionNumber,
204 relocation_table_entry: Reloc.Index,
205
206 image_section: Symbol.Index,
207
208 /// Images only
209 import_directory_table,
210 import_lookup_table: ImportTable.Index,
211 import_address_table: ImportTable.Index,
212 import_hint_name_table: ImportTable.Index,
213
214 /// Images only
215 export_directory_table,
216 export_address_table,
217 export_name_pointer_table,
218 export_ordinal_table,
219 export_name_table,
220
221 pseudo_section: PseudoSectionMapIndex,
222 object_section: ObjectSectionMapIndex,
223 input_section: InputSection.Index,
224 import_thunk: GlobalMapIndex,
225 nav: NavMapIndex,
226 uav: UavMapIndex,
227 lazy_code: LazyMapRef.Index(.code),
228 lazy_const_data: LazyMapRef.Index(.const_data),
229 builtin: Symbol.Index,
230
231 /// Takes the place of a known node index when that node is not present in the output
232 placeholder,
233
234 pub const PseudoSectionMapIndex = enum(u32) {
235 _,
236
237 pub fn name(psmi: PseudoSectionMapIndex, coff: *const Coff) String {
238 return coff.pseudo_section_table.keys()[@backingInt(psmi)];
239 }
240
241 pub fn symbol(psmi: PseudoSectionMapIndex, coff: *const Coff) Symbol.Index {
242 return coff.pseudo_section_table.values()[@backingInt(psmi)];
243 }
244 };
245
246 pub const ObjectSectionMapIndex = enum(u32) {
247 _,
248
249 pub fn name(osmi: ObjectSectionMapIndex, coff: *const Coff) String {
250 return coff.object_section_table.keys()[@backingInt(osmi)];
251 }
252
253 pub fn symbol(osmi: ObjectSectionMapIndex, coff: *const Coff) Symbol.Index {
254 return coff.object_section_table.values()[@backingInt(osmi)];
255 }
256 };
257
258 pub const GlobalMapIndex = enum(u32) {
259 none,
260 _,
261
262 pub fn wrap(i: ?u32) GlobalMapIndex {
263 return @fromBackingInt(@intCast((i orelse return .none) + 1));
264 }
265
266 pub fn unwrap(gmi: GlobalMapIndex) ?u32 {
267 return switch (gmi) {
268 .none => null,
269 _ => @backingInt(gmi) - 1,
270 };
271 }
272
273 pub fn name(gmi: GlobalMapIndex, coff: *const Coff) String {
274 return coff.globals.keys()[gmi.unwrap().?];
275 }
276
277 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
278 return coff.globals.values()[gmi.unwrap().?].si;
279 }
280
281 pub fn libName(gmi: GlobalMapIndex, coff: *const Coff) String.Optional {
282 return coff.globals.values()[gmi.unwrap().?].lib_name;
283 }
284 };
285
286 pub const NavMapIndex = enum(u32) {
287 _,
288
289 pub fn navIndex(nmi: NavMapIndex, coff: *const Coff) InternPool.Nav.Index {
290 return coff.navs.keys()[@backingInt(nmi)];
291 }
292
293 pub fn symbol(nmi: NavMapIndex, coff: *const Coff) Symbol.Index {
294 return coff.navs.values()[@backingInt(nmi)];
295 }
296 };
297
298 pub const UavMapIndex = enum(u32) {
299 _,
300
301 pub fn uavValue(umi: UavMapIndex, coff: *const Coff) InternPool.Index {
302 return coff.uavs.keys()[@backingInt(umi)];
303 }
304
305 pub fn symbol(umi: UavMapIndex, coff: *const Coff) Symbol.Index {
306 return coff.uavs.values()[@backingInt(umi)];
307 }
308 };
309
310 const InputSection = struct {
311 ioi: InputObject.Index,
312 si: Symbol.Index,
313 comdat_si: Symbol.Index,
314 file_location: MappedFile.Node.FileLocation,
315 first_li: Node.InputSection.LocalIndex,
316 crc: u32,
317
318 pub const Index = enum(u32) {
319 _,
320
321 pub fn inputSection(isi: Index, coff: *const Coff) *InputSection {
322 return &coff.input_sections.items[@backingInt(isi)];
323 }
324
325 pub fn input(isi: Index, coff: *const Coff) InputObject.Index {
326 return coff.input_sections.items[@backingInt(isi)].ioi;
327 }
328
329 pub fn fileLocation(isi: Index, coff: *const Coff) MappedFile.Node.FileLocation {
330 return coff.input_sections.items[@backingInt(isi)].file_location;
331 }
332
333 pub fn symbol(isi: Index, coff: *const Coff) Symbol.Index {
334 return coff.input_sections.items[@backingInt(isi)].si;
335 }
336
337 pub fn firstSymbol(isi: Index, coff: *const Coff) LocalIndex {
338 return coff.input_sections.items[@backingInt(isi)].first_li;
339 }
340 };
341
342 const LocalIndex = enum(u32) {
343 _,
344
345 pub fn name(isli: LocalIndex, coff: *const Coff) String {
346 return coff.input_symbols.items[@backingInt(isli)].name;
347 }
348 };
349 };
350
351 pub const LazyMapRef = struct {
352 kind: link.File.LazySymbol.Kind,
353 index: u32,
354
355 pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
356 return enum(u32) {
357 _,
358
359 pub fn ref(lmi: @This()) LazyMapRef {
360 return .{ .kind = kind, .index = @backingInt(lmi) };
361 }
362
363 pub fn lazySymbol(lmi: @This(), coff: *const Coff) link.File.LazySymbol {
364 return lmi.ref().lazySymbol(coff);
365 }
366
367 pub fn symbol(lmi: @This(), coff: *const Coff) Symbol.Index {
368 return lmi.ref().symbol(coff);
369 }
370 };
371 }
372
373 pub fn lazySymbol(lmr: LazyMapRef, coff: *const Coff) link.File.LazySymbol {
374 return .{ .kind = lmr.kind, .ty = coff.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
375 }
376
377 pub fn symbol(lmr: LazyMapRef, coff: *const Coff) Symbol.Index {
378 return coff.lazy.getPtrConst(lmr.kind).map.values()[lmr.index];
379 }
380 };
381
382 pub const Tag = @typeInfo(Node).@"union".tag_type.?;
383
384 const known_count = @typeInfo(@TypeOf(known)).@"struct".field_names.len;
385 const known = known: {
386 const Known = enum {
387 file,
388 header,
389 signature,
390 first_linker_member_header,
391 first_linker_member,
392 second_linker_member_header,
393 second_linker_member,
394 longnames_member_header,
395 longnames_member,
396 zcu_member_header,
397 zcu_member,
398 coff_header,
399 optional_header,
400 data_directories,
401 section_table,
402 };
403 var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
404 const info = @typeInfo(Known).@"enum";
405 for (info.field_names, info.field_values) |field_name, field_value|
406 @field(mut_known, field_name) = @fromBackingInt(@intCast(field_value));
407 break :known mut_known;
408 };
409
410 comptime {
411 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
412 }
413};
414
415pub const InputArchive = struct {
416 path: std.Build.Cache.Path,
417
418 const Index = enum(u32) {
419 _,
420
421 pub fn path(iai: InputArchive.Index, coff: *Coff) std.Build.Cache.Path {
422 return coff.input_archives.items[@backingInt(iai)].path;
423 }
424 };
425
426 pub const Member = struct {
427 iai: InputArchive.Index,
428 name: String,
429 content: union(enum) {
430 // This range includes the member header
431 object: MappedFile.Node.FileLocation,
432 import: struct {
433 symbol_name: String,
434 lib_name: String,
435 // Either ordinal or hint, depending on value of name_type
436 import_ordinal_hint: u16,
437 type: std.coff.ImportType,
438 name_type: std.coff.ImportNameType,
439 },
440 },
441 flags: packed struct {
442 // Set if an attempt was made to load this member
443 is_loaded: bool,
444 },
445
446 const Index = enum(u32) {
447 _,
448
449 pub fn member(iami: InputArchive.Member.Index, coff: *Coff) *InputArchive.Member {
450 return &coff.input_archive_members.items[@backingInt(iami)];
451 }
452 };
453
454 pub const Symbol = struct {
455 iami: InputArchive.Member.Index,
456 // Set to its own index to indicate its the last in the list
457 next: InputArchive.Member.Symbol.Index,
458
459 const Index = enum(u32) {
460 _,
461 };
462 };
463 };
464
465 pub const SearchList = struct {
466 first: InputArchive.Member.Symbol.Index,
467 last: InputArchive.Member.Symbol.Index,
468 };
469};
470
471pub const InputObject = struct {
472 path: std.Build.Cache.Path,
473 member_name: ?[]const u8,
474 source_name: String.Optional,
475
476 pub const Index = enum(u32) {
477 _,
478
479 pub fn path(ioi: Index, coff: *const Coff) std.Build.Cache.Path {
480 return coff.input_objects.items[@backingInt(ioi)].path;
481 }
482
483 pub fn memberName(ioi: Index, coff: *const Coff) ?[]const u8 {
484 return coff.input_objects.items[@backingInt(ioi)].member_name;
485 }
486 };
487};
488
489pub const Member = struct {
490 kind: std.coff.ArchiveMemberHeader.Kind,
491 header_ni: MappedFile.Node.Index,
492 content_ni: MappedFile.Node.Index,
493 first_linker_indices: std.array_hash_map.Auto(struct {
494 mi: Member.Index,
495 name: String,
496 }, FirstLinkerIndex),
497
498 pub const Index = enum(u16) {
499 first,
500 second,
501 longnames,
502 _,
503
504 const known_count = @typeInfo(Index).@"enum".field_names.len;
505
506 pub fn get(member_index: Member.Index, coff: *Coff) *Member {
507 return &coff.members.items[@backingInt(member_index)];
508 }
509 };
510
511 pub const FirstLinkerIndex = enum(u32) {
512 _,
513 };
514
515 pub fn headerPtr(member: *Member, coff: *Coff) *std.coff.ArchiveMemberHeader {
516 return @ptrCast(@alignCast(member.header_ni.slice(&coff.mf)));
517 }
518
519 /// Sets `name` as the name field of this member's header, either directly (if it's short enough),
520 /// or by creating an entry in the longnames member and storing a reference to that entry.
521 pub fn initHeader(member: *Member, coff: *Coff, name: []const u8, timestamp: u32) !void {
522 const max_name_len = @typeInfo(@FieldType(std.coff.ArchiveMemberHeader, "name")).array.len;
523 const opt_name_offset = if (name.len >= max_name_len) offset: {
524 const gpa = coff.base.comp.gpa;
525 const entries_ctx = LongNamesTable.Adapter{ .coff = coff };
526 const gop = try coff.long_names_table.entries.getOrPutAdapted(
527 gpa,
528 name,
529 entries_ctx,
530 );
531
532 if (!gop.found_existing) {
533 errdefer _ = coff.export_table.entries.pop();
534
535 _, const old_size = Node.known.longnames_member.location(&coff.mf).resolve(&coff.mf);
536 const new_size = Alignment.@"4".forward(old_size + name.len + 1);
537 assert(new_size < comptime try std.math.powi(u64, 10, max_name_len - 1));
538
539 try Node.known.longnames_member.resizeLeaf(&coff.mf, gpa, new_size);
540 const name_table_slice = Node.known.longnames_member.slice(&coff.mf);
541 const name_slice = name_table_slice[@intCast(old_size)..][0 .. name.len + 1];
542 @memcpy(name_slice[0..name.len], name);
543 name_slice[name.len] = 0;
544
545 gop.value_ptr.* = .{
546 .offset = old_size,
547 .len = name.len,
548 };
549 }
550
551 break :offset gop.value_ptr.offset;
552 } else null;
553
554 const header = member.headerPtr(coff);
555 if (opt_name_offset) |name_offset| {
556 header.name[0] = '/';
557 storeHeaderDecimalStr(header.name[1..], name_offset);
558 } else {
559 @memcpy(header.name[0..name.len], name);
560 header.name[name.len] = '/';
561 const padding = max_name_len - name.len - 1;
562 @memset(header.name[max_name_len - padding ..], ' ');
563 }
564
565 storeHeaderDecimalStr(&header.date, timestamp);
566
567 // Matching the Microsoft behaviour of emitting blanks for these fields
568 header.user_id = @splat(' ');
569 header.group_id = @splat(' ');
570
571 // file_mode is actually octal, but we only ever write 0 to it
572 storeHeaderDecimalStr(&header.file_mode, 0);
573 if (!member.content_ni.hasResized(&coff.mf))
574 storeHeaderDecimalStr(
575 &header.size,
576 member.content_ni.location(&coff.mf).resolve(&coff.mf)[1],
577 );
578
579 @memcpy(&header.end_of_header, std.coff.archive_end_of_header);
580 }
581
582 pub fn storeHeaderDecimalStr(field_ptr: anytype, value: u64) void {
583 const array_info = @typeInfo(@typeInfo(@TypeOf(field_ptr)).pointer.child).array;
584 assert(array_info.child == u8);
585 assert(value < comptime try std.math.powi(u64, 10, array_info.len));
586 _ = std.fmt.printInt(field_ptr, value, 10, .lower, .{
587 .width = array_info.len,
588 .alignment = .left,
589 .fill = ' ',
590 });
591 }
592
593 pub fn loadHeaderDecimalStr(field_ptr: anytype, value: u64) void {
594 const array_info = @typeInfo(@typeInfo(@TypeOf(field_ptr)).pointer.child).array;
595 assert(array_info.child == u8);
596 assert(value < comptime try std.math.powi(u64, 10, array_info.len));
597 _ = std.fmt.printInt(field_ptr, value, 10, .lower, .{
598 .width = array_info.len,
599 .alignment = .left,
600 .fill = ' ',
601 });
602 }
603};
604
605pub const LongNamesTable = struct {
606 ni: MappedFile.Node.Index.Optional = .none,
607 entries: std.array_hash_map.Auto(void, Entry),
608
609 pub const Entry = struct {
610 offset: u64,
611 len: u64,
612 };
613
614 const Adapter = struct {
615 coff: *Coff,
616
617 pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
618 assert(adapter.coff.isArchive());
619 const longnames_slice = Node.known.longnames_member.slice(&adapter.coff.mf);
620 const rhs = adapter.coff.long_names_table.entries.values()[rhs_index];
621 return std.mem.eql(u8, longnames_slice[@intCast(rhs.offset)..][0..@intCast(rhs.len)], lhs_key);
622 }
623
624 pub fn hash(_: Adapter, key: []const u8) u32 {
625 assert(std.mem.findScalar(u8, key, 0) == null);
626 return std.array_hash_map.hashString(key);
627 }
628 };
629};
630
631pub const SymbolTable = struct {
632 ni: MappedFile.Node.Index,
633 strings_ni: MappedFile.Node.Index,
634 strings: std.array_hash_map.Auto(String, StringIndex),
635 symbols: std.array_hash_map.Auto(Symbol.Index, SymbolTable.Index),
636 pending_symbol_index: u32,
637
638 // Resizing the symbol table node has the result of accumulating padding
639 // between the last symbol in the symbol table node and the start of the
640 // string table node, due to the shifting method when resizing the parent in MappedFile.
641 // The spec requires the string table begin immediately after the last symbol,
642 // so we compact the symbol table node and move the string table back if needed.
643 pending_shrink: bool,
644
645 pub const StringIndex = enum(u32) {
646 _,
647 };
648
649 pub const SymbolName = union(enum) {
650 short: []const u8,
651 long: StringIndex,
652
653 pub fn store(name: SymbolName, coff: *const Coff, field: *[8]u8) void {
654 switch (name) {
655 .short => |s| {
656 @memcpy(field[0..s.len], s);
657 @memset(field[s.len..], 0);
658 },
659 .long => |l| {
660 @memset(field[0..4], 0);
661 std.mem.writePackedInt(u32, field[4..], 0, @backingInt(l), coff.targetEndian());
662 },
663 }
664 }
665 };
666
667 // Symbol.Index does not map 1:1 with SymbolTable.Index:
668 // - Not all symbols need a symbol table entry
669 // - A variable number of auxiliary entries may trail each symbol
670 pub const Index = enum(u32) {
671 none,
672 _,
673
674 pub fn wrap(i: u32) Index {
675 return @fromBackingInt(@intCast(i + 1));
676 }
677
678 pub fn unwrap(sti: Index) ?u32 {
679 return switch (sti) {
680 .none => null,
681 _ => @backingInt(sti) - 1,
682 };
683 }
684 };
685};
686
687pub const ExportTable = struct {
688 ni: MappedFile.Node.Index,
689 export_directory_table_ni: MappedFile.Node.Index,
690 export_address_table_si: Symbol.Index,
691 name_pointer_table_ni: MappedFile.Node.Index,
692 ordinal_table_ni: MappedFile.Node.Index,
693 name_table_ni: MappedFile.Node.Index,
694 entries: std.array_hash_map.Auto(void, Entry),
695 pending_sort: bool = false,
696
697 pub const Entry = struct {
698 si: Symbol.Index,
699 name_index: u32,
700 name_len: u32,
701 export_address_table_ri: Reloc.Index,
702 };
703
704 const Adapter = struct {
705 coff: *Coff,
706
707 pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
708 const coff = adapter.coff;
709 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
710 const rhs = coff.export_table.entries.values()[rhs_index];
711 return std.mem.eql(u8, name_table_slice[rhs.name_index..][0..rhs.name_len], lhs_key);
712 }
713
714 pub fn hash(_: Adapter, key: []const u8) u32 {
715 assert(std.mem.findScalar(u8, key, 0) == null);
716 return std.array_hash_map.hashString(key);
717 }
718 };
719
720 pub const Ordinal = enum(u16) {
721 _,
722
723 pub fn get(export_index: ExportTable.Ordinal, coff: *Coff) *Entry {
724 return &coff.export_table.entries.values()[@backingInt(export_index)];
725 }
726 };
727};
728
729pub const ImportTable = struct {
730 ni: MappedFile.Node.Index,
731 entries: std.array_hash_map.Auto(void, Entry),
732 iat_symbol_indices: std.array_hash_map.Auto(struct {
733 iti: ImportTable.Index,
734 name: String.Optional,
735 // If name == .none this is the ordinal, otherwise the hint
736 ordinal_hint: u16,
737 }, u32),
738
739 pub const Entry = struct {
740 import_lookup_table_ni: MappedFile.Node.Index,
741 import_address_table_si: Symbol.Index,
742 import_hint_name_table_ni: MappedFile.Node.Index,
743 // All .iat_ptr globals that reference this table.
744 // This is separate from `iat_symbol_indices` because multiple symbols
745 // can reference to the same iat entry, after name demangling.
746 import_address_table_symbols: std.ArrayList(Symbol.Index),
747 len: u32,
748 hint_name_len: u32,
749 };
750
751 const Adapter = struct {
752 coff: *Coff,
753
754 pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
755 const coff = adapter.coff;
756 const dll_name = coff.import_table.entries.values()[rhs_index]
757 .import_hint_name_table_ni.sliceConst(&coff.mf);
758 return std.mem.startsWith(u8, dll_name, lhs_key) and
759 std.mem.startsWith(u8, dll_name[lhs_key.len..], ".dll\x00");
760 }
761
762 pub fn hash(_: Adapter, key: []const u8) u32 {
763 assert(std.mem.findScalar(u8, key, 0) == null);
764 return std.array_hash_map.hashString(key);
765 }
766 };
767
768 pub const Index = enum(u32) {
769 _,
770
771 pub fn get(import_index: ImportTable.Index, coff: *Coff) *Entry {
772 return &coff.import_table.entries.values()[@backingInt(import_index)];
773 }
774 };
775};
776
777pub const String = enum(u32) {
778 @".data" = 0,
779 @".idata" = 6,
780 @".rdata" = 13,
781 @".text" = 20,
782 @".tls$" = 26,
783 @".edata" = 32,
784 @".ctors" = 39,
785 @".ctors$ZZZ" = 46,
786 @".dtors" = 57,
787 @".dtors$ZZZ" = 64,
788 @".bss" = 75,
789 @".fptable" = 80,
790 @".tls" = 89,
791 @".thunks" = 94,
792 _,
793
794 pub const Optional = enum(u32) {
795 @".data" = @backingInt(String.@".data"),
796 @".idata" = @backingInt(String.@".idata"),
797 @".rdata" = @backingInt(String.@".rdata"),
798 @".text" = @backingInt(String.@".text"),
799 @".tls$" = @backingInt(String.@".tls$"),
800 @".edata" = @backingInt(String.@".edata"),
801 @".ctors" = @backingInt(String.@".ctors"),
802 @".ctors$ZZZ" = @backingInt(String.@".ctors$ZZZ"),
803 @".dtors" = @backingInt(String.@".dtors"),
804 @".dtors$ZZZ" = @backingInt(String.@".dtors$ZZZ"),
805 @".bss" = @backingInt(String.@".bss"),
806 @".fptable" = @backingInt(String.@".fptable"),
807 @".tls" = @backingInt(String.@".tls"),
808 @".thunks" = @backingInt(String.@".thunks"),
809 none = std.math.maxInt(u32),
810 _,
811
812 pub fn unwrap(os: String.Optional) ?String {
813 return switch (os) {
814 else => |s| @fromBackingInt(@intCast(@backingInt(s))),
815 .none => null,
816 };
817 }
818
819 pub fn toSlice(os: String.Optional, coff: *Coff) ?[:0]const u8 {
820 return (os.unwrap() orelse return null).toSlice(coff);
821 }
822 };
823
824 pub fn toSlice(s: String, coff: *Coff) [:0]const u8 {
825 const slice = coff.string_bytes.items[@backingInt(s)..];
826 return slice[0..std.mem.findScalar(u8, slice, 0).? :0];
827 }
828
829 pub fn toOptional(s: String) String.Optional {
830 return @fromBackingInt(@intCast(@backingInt(s)));
831 }
832};
833
834pub const Section = struct {
835 si: Symbol.Index,
836 relocation_table_ni: MappedFile.Node.Index.Optional,
837
838 pub const RelocationIndex = enum(u16) {
839 none,
840 _,
841
842 pub fn wrap(i: ?u16) RelocationIndex {
843 return @fromBackingInt(@intCast((i orelse return .none) + 1));
844 }
845
846 pub fn unwrap(sri: RelocationIndex) ?u16 {
847 return switch (sri) {
848 .none => null,
849 _ => @backingInt(sri) - 1,
850 };
851 }
852
853 pub fn entry(
854 sri: RelocationIndex,
855 coff: *Coff,
856 sn: Symbol.SectionNumber,
857 ) ?*align(2) std.coff.Relocation {
858 if (sri == .none) return null;
859 const table_slice = sn.section(coff).relocation_table_ni.unwrap().?.slice(&coff.mf);
860 return @ptrCast(@alignCast(&table_slice[@as(u32, sri.unwrap().?) * std.coff.Relocation.sizeOf()]));
861 }
862 };
863};
864
865pub const Global = struct {
866 si: Symbol.Index,
867 lib_name: String.Optional,
868};
869
870pub const WeakExternalStrat = enum(u3) {
871 none,
872 no_library,
873 library,
874 alias,
875 anti_dependency,
876
877 pub fn fromFlag(flag: std.coff.WeakExternalFlag) WeakExternalStrat {
878 return switch (flag) {
879 .SEARCH_NOLIBRARY => .no_library,
880 .SEARCH_LIBRARY => .library,
881 .SEARCH_ALIAS => .alias,
882 .ANTI_DEPENDENCY => .anti_dependency,
883 _ => unreachable,
884 };
885 }
886};
887
888const SpecialSymbol = enum {
889 entry,
890 tls,
891 none,
892};
893
894pub const Symbol = struct {
895 ni: MappedFile.Node.Index.Optional,
896 rva: u32,
897 value: std.meta.BareUnion(Symbol.Value),
898 extra: std.meta.BareUnion(Symbol.Extra),
899 flags: packed struct(u16) {
900 value_tag: ValueTag,
901 extra_tag: ExtraTag,
902 type: Symbol.Type,
903 dll_storage_class: DllStorageClass,
904 weak_external_strat: WeakExternalStrat,
905 _: u5 = 0,
906 },
907 /// Relocations contained within this symbol
908 loc_relocs: Reloc.Index,
909 /// Relocations targeting this symbol
910 target_relocs: Reloc.Index,
911 section_number: SectionNumber,
912 gmi: Node.GlobalMapIndex,
913
914 pub const DllStorageClass = enum(u2) {
915 default,
916 dllimport,
917 dllexport,
918 };
919
920 pub const Type = enum(u2) {
921 unknown,
922 code,
923 data,
924 };
925
926 const ValueTag = enum(u2) {
927 none,
928 node_offset,
929 weak_alias_si,
930 weak_alias_name,
931 };
932
933 pub const Value = union(ValueTag) {
934 none,
935 /// The offset of the symbol within its node. Used with symbols that
936 /// don't create their own nodes: .input_section, .import_address_table
937 /// Images only.
938 node_offset: u32,
939 /// Images: the weak alias that should replace this symbol if it is not resolved.
940 /// Objects: he target of a weak external that hasn't been assigned an sti yet.
941 /// Globals only.
942 weak_alias_si: Symbol.Index,
943 /// For weak externals that have an alias that is also an undef
944 /// external, this is the name of the alias global that should
945 /// be generated and resolved if this symbol is not resolved.
946 /// Globals only, images only.
947 weak_alias_name: String,
948 };
949
950 const ExtraTag = enum(u2) {
951 size,
952 isli,
953 next_alias_si,
954 };
955
956 pub const Extra = union(ExtraTag) {
957 // The size of the symbol
958 size: u32,
959 /// Only valid when .ni == .input_section and .value_tag == .node_offset
960 isli: Node.InputSection.LocalIndex,
961 /// The next symbol in the list of aliases of this symbol.
962 next_alias_si: Symbol.Index,
963 };
964
965 pub fn setValue(sym: *Symbol, value: Symbol.Value) void {
966 sym.flags.value_tag = std.meta.activeTag(value);
967 sym.value = switch (sym.flags.value_tag) {
968 inline else => |t| @unionInit(
969 @FieldType(Symbol, "value"),
970 @tagName(t),
971 @field(value, @tagName(t)),
972 ),
973 };
974 }
975
976 pub fn setExtra(sym: *Symbol, extra: Symbol.Extra) void {
977 sym.flags.extra_tag = std.meta.activeTag(extra);
978 sym.extra = switch (sym.flags.extra_tag) {
979 inline else => |t| @unionInit(
980 @FieldType(Symbol, "extra"),
981 @tagName(t),
982 @field(extra, @tagName(t)),
983 ),
984 };
985 }
986
987 pub fn nodeOffset(sym: *const Symbol, coff: *Coff) u32 {
988 return switch (sym.flags.value_tag) {
989 .node_offset => offset: {
990 assert(switch (coff.getNode(sym.ni.unwrap().?)) {
991 // Separate nodes are not created for these entries per-symbol
992 .input_section, .import_address_table => true,
993 else => false,
994 });
995 break :offset sym.value.node_offset;
996 },
997 else => 0,
998 };
999 }
1000
1001 pub fn size(sym: *const Symbol) u32 {
1002 return if (sym.flags.extra_tag == .size) sym.extra.size else 0;
1003 }
1004
1005 pub const SectionNumber = enum(i16) {
1006 UNDEFINED = 0,
1007 ABSOLUTE = -1,
1008 DEBUG = -2,
1009 _,
1010
1011 fn toIndex(sn: SectionNumber) u15 {
1012 return @intCast(@backingInt(sn) - 1);
1013 }
1014
1015 fn hasIndex(sn: SectionNumber) bool {
1016 return @backingInt(sn) > 0;
1017 }
1018
1019 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
1020 return sn.section(coff).si;
1021 }
1022
1023 pub fn name(sn: SectionNumber, coff: *const Coff) String {
1024 return coff.section_table.keys()[sn.toIndex()];
1025 }
1026
1027 pub fn section(sn: SectionNumber, coff: *const Coff) *Section {
1028 return &coff.section_table.values()[sn.toIndex()];
1029 }
1030
1031 pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
1032 return &coff.sectionTableSlice()[sn.toIndex()];
1033 }
1034 };
1035
1036 pub const Index = enum(u32) {
1037 null,
1038 bss,
1039 data,
1040 rdata,
1041 text,
1042 _,
1043
1044 const known_count = @typeInfo(Index).@"enum".field_names.len;
1045
1046 pub fn get(si: Symbol.Index, coff: *Coff) *Symbol {
1047 return &coff.symbols.items[@backingInt(si)];
1048 }
1049
1050 pub fn unwrap(si: Symbol.Index) ?Symbol.Index {
1051 if (si == .null) return null;
1052 return si;
1053 }
1054
1055 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
1056 return si.get(coff).ni.unwrap().?;
1057 }
1058
1059 pub fn sti(si: Symbol.Index, coff: *Coff) SymbolTable.Index {
1060 assert(!coff.isImage());
1061 return coff.symbol_table.symbols.get(si) orelse .none;
1062 }
1063
1064 pub fn next(si: Symbol.Index) Symbol.Index {
1065 return @fromBackingInt(@intCast(@backingInt(si) + 1));
1066 }
1067
1068 pub fn knownString(si: Symbol.Index) String.Optional {
1069 return switch (si) {
1070 .null, _ => .none,
1071 inline else => |tag| @field(String.Optional, "." ++ @tagName(tag)),
1072 };
1073 }
1074
1075 pub fn flushMoved(si: Symbol.Index, coff: *Coff) !void {
1076 const sym = si.get(coff);
1077 sym.rva = coff.computeNodeRva(sym.ni.unwrap().?) + sym.nodeOffset(coff);
1078 try si.applyLocationRelocs(coff);
1079 try si.applyTargetRelocs(coff, .none);
1080
1081 var alias_sym = sym;
1082 while (alias_sym.flags.extra_tag == .next_alias_si) {
1083 const alias_si = alias_sym.extra.next_alias_si;
1084 alias_sym = alias_si.get(coff);
1085 assert(alias_sym.ni == sym.ni);
1086 alias_sym.rva = sym.rva;
1087 try alias_si.applyTargetRelocs(coff, .none);
1088 }
1089 }
1090
1091 pub fn flushSymbolTableIndex(si: Symbol.Index, coff: *Coff) void {
1092 const sym = si.get(coff);
1093 const index = si.sti(coff).unwrap().?;
1094 var ri = sym.target_relocs;
1095 while (ri != .none) {
1096 const reloc = ri.get(coff);
1097 assert(reloc.target == si);
1098 if (reloc.sri.entry(coff, reloc.loc.get(coff).section_number)) |entry|
1099 coff.targetStore(&entry.symbol_table_index, index);
1100 ri = reloc.next;
1101 }
1102 }
1103
1104 pub fn applyLocationRelocs(si: Symbol.Index, coff: *Coff) !void {
1105 const sym = si.get(coff);
1106 switch (sym.loc_relocs) {
1107 .none => {},
1108 else => |loc_relocs| {
1109 for (coff.relocs.items[@backingInt(loc_relocs)..]) |*reloc| {
1110 if (reloc.loc != si) break;
1111 if (reloc.sri.entry(coff, sym.section_number)) |entry| coff.targetStore(
1112 &entry.virtual_address,
1113 @intCast(coff.computeSymbolSectionOffset(sym, .image) + reloc.offset),
1114 );
1115 try reloc.apply(coff);
1116 }
1117 },
1118 }
1119 }
1120
1121 pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff, end: Reloc.Index) !void {
1122 const sym = si.get(coff);
1123
1124 var ri = sym.target_relocs;
1125 while (ri != end) {
1126 const reloc = ri.get(coff);
1127 assert(reloc.target == si);
1128 try reloc.apply(coff);
1129 ri = reloc.next;
1130 }
1131 }
1132
1133 pub fn deleteLocationRelocs(si: Symbol.Index, coff: *Coff) void {
1134 const sym = si.get(coff);
1135 switch (sym.loc_relocs) {
1136 .none => {},
1137 else => |loc_relocs| {
1138 for (coff.relocs.items[@backingInt(loc_relocs)..]) |*reloc| {
1139 if (reloc.loc != si) break;
1140 reloc.delete(coff);
1141 }
1142 sym.loc_relocs = .none;
1143 },
1144 }
1145 }
1146 };
1147
1148 comptime {
1149 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
1150 }
1151};
1152
1153pub const Reloc = extern struct {
1154 offset: u64,
1155 addend: i64,
1156 type: Reloc.Type,
1157 sri: Section.RelocationIndex,
1158 prev: Reloc.Index,
1159 next: Reloc.Index,
1160 loc: Symbol.Index,
1161 target: Symbol.Index,
1162 flags: packed struct(u8) {
1163 /// Indicates the addend is not known and should be recovered from the location itself.
1164 /// COFF relocation tables don't encode the addend, only the location.
1165 recover_addend: bool,
1166 /// Set if this reloc is in the free list.
1167 /// When set, `prev` / `next` refer to other relocs in the free list.
1168 /// All other fields are undefined.
1169 free: bool,
1170 _: u6 = 0,
1171 },
1172
1173 pub const Type = extern union {
1174 AMD64: std.coff.IMAGE.REL.AMD64,
1175 ARM: std.coff.IMAGE.REL.ARM,
1176 ARM64: std.coff.IMAGE.REL.ARM64,
1177 SH: std.coff.IMAGE.REL.SH,
1178 PPC: std.coff.IMAGE.REL.PPC,
1179 I386: std.coff.IMAGE.REL.I386,
1180 IA64: std.coff.IMAGE.REL.IA64,
1181 MIPS: std.coff.IMAGE.REL.MIPS,
1182 M32R: std.coff.IMAGE.REL.M32R,
1183 u16: u16,
1184 };
1185
1186 pub const Index = enum(u32) {
1187 none = std.math.maxInt(u32),
1188 _,
1189
1190 pub fn wrap(i: ?u32) Reloc.Index {
1191 return @fromBackingInt(@intCast((i orelse return .none) + 1));
1192 }
1193
1194 pub fn get(ri: Reloc.Index, coff: *Coff) *Reloc {
1195 return &coff.relocs.items[@backingInt(ri)];
1196 }
1197 };
1198
1199 pub fn apply(reloc: *Reloc, coff: *Coff) !void {
1200 const loc_sym = reloc.loc.get(coff);
1201
1202 const loc_sym_ni = loc_sym.ni.unwrap() orelse return;
1203 if (loc_sym_ni.hasMoved(&coff.mf)) return;
1204
1205 const loc_slice = loc_sym_ni.slice(&coff.mf)[@intCast(reloc.offset)..];
1206 const target_endian = coff.targetEndian();
1207 const target_machine = coff.targetLoad(&coff.headerPtr().machine);
1208
1209 if (!coff.isImage()) {
1210 assert(!reloc.flags.recover_addend);
1211 switch (target_machine) {
1212 else => |machine| @panic(@tagName(machine)),
1213 .AMD64 => switch (reloc.type.AMD64) {
1214 else => |kind| @panic(@tagName(kind)),
1215 .ABSOLUTE => {},
1216 .ADDR64 => std.mem.writeInt(
1217 u64,
1218 loc_slice[0..8],
1219 @intCast(reloc.addend),
1220 target_endian,
1221 ),
1222 .ADDR32,
1223 .ADDR32NB,
1224 .SECREL,
1225 => std.mem.writeInt(
1226 u32,
1227 loc_slice[0..4],
1228 @intCast(reloc.addend),
1229 target_endian,
1230 ),
1231 .REL32,
1232 .REL32_1,
1233 .REL32_2,
1234 .REL32_3,
1235 .REL32_4,
1236 .REL32_5,
1237 => std.mem.writeInt(
1238 i32,
1239 loc_slice[0..4],
1240 @intCast(reloc.addend),
1241 target_endian,
1242 ),
1243 },
1244 .I386 => switch (reloc.type.I386) {
1245 else => |kind| @panic(@tagName(kind)),
1246 .ABSOLUTE => {},
1247 .DIR16,
1248 => std.mem.writeInt(
1249 u16,
1250 loc_slice[0..2],
1251 @intCast(reloc.addend),
1252 target_endian,
1253 ),
1254 .REL16,
1255 => std.mem.writeInt(
1256 i16,
1257 loc_slice[0..2],
1258 @intCast(reloc.addend),
1259 target_endian,
1260 ),
1261 .DIR32,
1262 .DIR32NB,
1263 .SECREL,
1264 => std.mem.writeInt(
1265 u32,
1266 loc_slice[0..4],
1267 @intCast(reloc.addend),
1268 target_endian,
1269 ),
1270 .REL32,
1271 => std.mem.writeInt(
1272 i32,
1273 loc_slice[0..4],
1274 @intCast(reloc.addend),
1275 target_endian,
1276 ),
1277 },
1278 }
1279
1280 return;
1281 } else if (reloc.flags.recover_addend) {
1282 reloc.flags.recover_addend = false;
1283 reloc.addend = switch (target_machine) {
1284 else => |machine| @panic(@tagName(machine)),
1285 .AMD64 => switch (reloc.type.AMD64) {
1286 else => |kind| @panic(@tagName(kind)),
1287 .ABSOLUTE => 0,
1288 .ADDR64 => @bitCast(std.mem.readInt(
1289 u64,
1290 loc_slice[0..8],
1291 target_endian,
1292 )),
1293 .ADDR32,
1294 .ADDR32NB,
1295 .SECREL,
1296 .REL32,
1297 .REL32_1,
1298 .REL32_2,
1299 .REL32_3,
1300 .REL32_4,
1301 .REL32_5,
1302 => std.mem.readInt(
1303 i32,
1304 loc_slice[0..4],
1305 target_endian,
1306 ),
1307 },
1308 .I386 => switch (reloc.type.I386) {
1309 else => |kind| @panic(@tagName(kind)),
1310 .ABSOLUTE => 0,
1311 .DIR16,
1312 .REL16,
1313 => std.mem.readInt(
1314 i16,
1315 loc_slice[0..2],
1316 target_endian,
1317 ),
1318 .DIR32,
1319 .DIR32NB,
1320 .SECREL,
1321 .REL32,
1322 => std.mem.readInt(
1323 i32,
1324 loc_slice[0..4],
1325 target_endian,
1326 ),
1327 },
1328 };
1329 }
1330
1331 const target_sym = reloc.target.get(coff);
1332 const is_abs = if (target_sym.ni.unwrap()) |ni| is_abs: {
1333 if (ni.hasMoved(&coff.mf)) return;
1334 break :is_abs false;
1335 } else is_abs: {
1336 if (target_sym.section_number != .ABSOLUTE) return;
1337 break :is_abs true;
1338 };
1339
1340 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
1341 if (is_abs) {
1342 switch (target_machine) {
1343 else => |machine| @panic(@tagName(machine)),
1344 .AMD64 => switch (reloc.type.AMD64) {
1345 // TODO: Could wait to report these later, in reportUndefs -> reportRelocErrs,
1346 // so that this function doesn't return an err
1347 else => |kind| return coff.base.comp.link_diags.fail(
1348 "absolute symbol '{s}' targeted by invalid relocation type: {t}",
1349 .{ target_sym.gmi.name(coff).toSlice(coff), kind },
1350 ),
1351 .ABSOLUTE => {},
1352 .ADDR64 => std.mem.writeInt(
1353 u64,
1354 loc_slice[0..8],
1355 target_rva,
1356 target_endian,
1357 ),
1358 .ADDR32 => std.mem.writeInt(
1359 u32,
1360 loc_slice[0..4],
1361 @intCast(target_rva),
1362 target_endian,
1363 ),
1364 },
1365 .I386 => switch (reloc.type.I386) {
1366 else => |kind| return coff.base.comp.link_diags.fail(
1367 "absolute symbol '{s}' targeted by invalid relocation type: {t}",
1368 .{ target_sym.gmi.name(coff).toSlice(coff), kind },
1369 ),
1370 .ABSOLUTE => {},
1371 .DIR16 => std.mem.writeInt(
1372 u16,
1373 loc_slice[0..2],
1374 @intCast(target_rva),
1375 target_endian,
1376 ),
1377 .DIR32 => std.mem.writeInt(
1378 u32,
1379 loc_slice[0..4],
1380 @intCast(target_rva),
1381 target_endian,
1382 ),
1383 },
1384 }
1385 } else {
1386 switch (target_machine) {
1387 else => |machine| @panic(@tagName(machine)),
1388 .AMD64 => switch (reloc.type.AMD64) {
1389 else => |kind| @panic(@tagName(kind)),
1390 .ABSOLUTE => {},
1391 .ADDR64 => std.mem.writeInt(
1392 u64,
1393 loc_slice[0..8],
1394 coff.optionalHeaderField(.image_base) + target_rva,
1395 target_endian,
1396 ),
1397 .ADDR32 => std.mem.writeInt(
1398 u32,
1399 loc_slice[0..4],
1400 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
1401 target_endian,
1402 ),
1403 .ADDR32NB => std.mem.writeInt(
1404 u32,
1405 loc_slice[0..4],
1406 @intCast(target_rva),
1407 target_endian,
1408 ),
1409 .REL32 => std.mem.writeInt(
1410 i32,
1411 loc_slice[0..4],
1412 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
1413 target_endian,
1414 ),
1415 .REL32_1 => std.mem.writeInt(
1416 i32,
1417 loc_slice[0..4],
1418 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 5)))),
1419 target_endian,
1420 ),
1421 .REL32_2 => std.mem.writeInt(
1422 i32,
1423 loc_slice[0..4],
1424 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 6)))),
1425 target_endian,
1426 ),
1427 .REL32_3 => std.mem.writeInt(
1428 i32,
1429 loc_slice[0..4],
1430 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 7)))),
1431 target_endian,
1432 ),
1433 .REL32_4 => std.mem.writeInt(
1434 i32,
1435 loc_slice[0..4],
1436 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 8)))),
1437 target_endian,
1438 ),
1439 .REL32_5 => std.mem.writeInt(
1440 i32,
1441 loc_slice[0..4],
1442 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))),
1443 target_endian,
1444 ),
1445 .SECREL => std.mem.writeInt(
1446 u32,
1447 loc_slice[0..4],
1448 @intCast(coff.computeSymbolSectionOffset(target_sym, .pseudo) + reloc.addend),
1449 target_endian,
1450 ),
1451 },
1452 .I386 => switch (reloc.type.I386) {
1453 else => |kind| @panic(@tagName(kind)),
1454 .ABSOLUTE => {},
1455 .DIR16 => std.mem.writeInt(
1456 u16,
1457 loc_slice[0..2],
1458 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
1459 target_endian,
1460 ),
1461 .REL16 => std.mem.writeInt(
1462 i16,
1463 loc_slice[0..2],
1464 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 2)))),
1465 target_endian,
1466 ),
1467 .DIR32 => std.mem.writeInt(
1468 u32,
1469 loc_slice[0..4],
1470 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
1471 target_endian,
1472 ),
1473 .DIR32NB => std.mem.writeInt(
1474 u32,
1475 loc_slice[0..4],
1476 @intCast(target_rva),
1477 target_endian,
1478 ),
1479 .REL32 => std.mem.writeInt(
1480 i32,
1481 loc_slice[0..4],
1482 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
1483 target_endian,
1484 ),
1485 .SECREL => std.mem.writeInt(
1486 u32,
1487 loc_slice[0..4],
1488 @intCast(coff.computeSymbolSectionOffset(target_sym, .pseudo) + reloc.addend),
1489 target_endian,
1490 ),
1491 },
1492 }
1493 }
1494 }
1495
1496 pub fn delete(reloc: *Reloc, coff: *Coff) void {
1497 if (reloc.sri != .none) {
1498 // TODO: Need to remove this from the COFF relocation table (maybe removeswap?)
1499 // TODO: If this was the last reloc causing something to be in the symbol table, we should remove
1500 // the symbol table entry (and unset sti). That will require flushSymbolTableIndex on the
1501 // swapped symbol if we exchange indices
1502 @panic("TODO implement symbol table reloc deletions");
1503 }
1504
1505 switch (reloc.prev) {
1506 .none => {
1507 const target = reloc.target.get(coff);
1508 assert(target.target_relocs.get(coff) == reloc);
1509 target.target_relocs = reloc.next;
1510 },
1511 else => |prev| prev.get(coff).next = reloc.next,
1512 }
1513 switch (reloc.next) {
1514 .none => {},
1515 else => |next| next.get(coff).prev = reloc.prev,
1516 }
1517
1518 reloc.* = undefined;
1519 reloc.flags = .{
1520 .recover_addend = false,
1521 .free = true,
1522 };
1523
1524 const ri: Reloc.Index = .wrap(@intCast(reloc - coff.relocs.items.ptr));
1525 if (coff.last_free_reloc == .none) {
1526 assert(coff.first_free_reloc == .none);
1527 coff.first_free_reloc = ri;
1528 coff.last_free_reloc = ri;
1529 } else {
1530 coff.last_free_reloc.get(coff).next = ri;
1531 reloc.prev = coff.last_free_reloc;
1532 reloc.next = .none;
1533 coff.last_free_reloc = ri;
1534 }
1535 }
1536
1537 comptime {
1538 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
1539 }
1540};
1541
1542pub fn open(
1543 arena: std.mem.Allocator,
1544 comp: *Compilation,
1545 path: std.Build.Cache.Path,
1546 options: link.File.OpenOptions,
1547) !*Coff {
1548 return create(arena, comp, path, options);
1549}
1550pub fn createEmpty(
1551 arena: std.mem.Allocator,
1552 comp: *Compilation,
1553 path: std.Build.Cache.Path,
1554 options: link.File.OpenOptions,
1555) !*Coff {
1556 return create(arena, comp, path, options);
1557}
1558fn create(
1559 arena: std.mem.Allocator,
1560 comp: *Compilation,
1561 path: std.Build.Cache.Path,
1562 options: link.File.OpenOptions,
1563) !*Coff {
1564 const target = &comp.root_mod.resolved_target.result;
1565 assert(target.ofmt == .coff);
1566 if (target.cpu.arch.endian() != comptime targetEndian(undefined))
1567 return error.UnsupportedCOFFArchitecture;
1568 const machine = target.toCoffMachine();
1569 const timestamp: u32 = 0;
1570 const major_subsystem_version = options.major_subsystem_version orelse 6;
1571 const minor_subsystem_version = options.minor_subsystem_version orelse 0;
1572 const magic: std.coff.OptionalHeader.Magic = switch (target.ptrBitWidth()) {
1573 0...32 => .PE32,
1574 33...64 => .@"PE32+",
1575 else => return error.UnsupportedCOFFArchitecture,
1576 };
1577 const section_align: Alignment = switch (machine) {
1578 .AMD64, .I386 => @fromBackingInt(@intCast(12)),
1579 .SH3, .SH3DSP, .SH4, .SH5 => @fromBackingInt(@intCast(12)),
1580 .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @fromBackingInt(@intCast(12)),
1581 .POWERPC, .POWERPCFP => @fromBackingInt(@intCast(12)),
1582 .ALPHA, .ALPHA64 => @fromBackingInt(@intCast(13)),
1583 .IA64 => @fromBackingInt(@intCast(13)),
1584 .ARM => @fromBackingInt(@intCast(12)),
1585 else => return error.UnsupportedCOFFArchitecture,
1586 };
1587
1588 const io = comp.io;
1589
1590 const coff = try arena.create(Coff);
1591 const file = try path.root_dir.handle.createFile(io, path.sub_path, .{
1592 .read = true,
1593 .permissions = link.File.determinePermissions(comp.config.output_mode, comp.config.link_mode),
1594 });
1595 errdefer file.close(io);
1596 coff.* = .{
1597 .base = .{
1598 .tag = .coff2,
1599
1600 .comp = comp,
1601 .emit = path,
1602
1603 .file = file,
1604 .gc_sections = false,
1605 .print_gc_sections = false,
1606 .build_id = .none,
1607 .allow_shlib_undefined = false,
1608 .stack_size = 0,
1609 },
1610 .options = options,
1611 .mf = try .init(file, comp.gpa, io),
1612 .nodes = .empty,
1613 .members = .empty,
1614 .pending_members = .empty,
1615 .lib_string_table = .empty,
1616 .lib_string_len = 0,
1617 .long_names_table = .{
1618 .entries = .empty,
1619 },
1620 .import_table = .{
1621 .ni = undefined,
1622 .entries = .empty,
1623 .iat_symbol_indices = .empty,
1624 },
1625 .export_table = .{
1626 .ni = undefined,
1627 .export_directory_table_ni = undefined,
1628 .export_address_table_si = .null,
1629 .name_pointer_table_ni = undefined,
1630 .ordinal_table_ni = undefined,
1631 .name_table_ni = undefined,
1632 .entries = .empty,
1633 },
1634 .symbol_table = .{
1635 .ni = undefined,
1636 .strings_ni = undefined,
1637 .strings = .empty,
1638 .symbols = .empty,
1639 .pending_symbol_index = 0,
1640 .pending_shrink = false,
1641 },
1642 .inputs = .empty,
1643 .input_archives = .empty,
1644 .input_archive_members = .empty,
1645 .input_archive_symbols = .empty,
1646 .input_archive_symbol_indices = .empty,
1647 .pending_input = null,
1648 .pending_default_libs = .empty,
1649 .alternate_names = .empty,
1650 .input_objects = .empty,
1651 .input_symbols = .empty,
1652 .input_sections = .empty,
1653 .input_section_pending_index = 0,
1654 .inputs_complete = false,
1655 .exports_complete = false,
1656 .pending_special_symbol = .entry,
1657 .strings = .empty,
1658 .string_bytes = .empty,
1659 .section_table = .empty,
1660 .pseudo_section_table = .empty,
1661 .object_section_table = .empty,
1662 .section_merges = .empty,
1663 .section_merge_pending_index = 0,
1664 .symbols = .empty,
1665 .globals = .empty,
1666 .global_pending_index = 0,
1667 .navs = .empty,
1668 .uavs = .empty,
1669 .lazy = .initFill(.{
1670 .map = .empty,
1671 .pending_index = 0,
1672 }),
1673 .pending_uavs = .empty,
1674 .relocs = .empty,
1675 .first_free_reloc = .none,
1676 .last_free_reloc = .none,
1677 .const_prog_node = .none,
1678 .synth_prog_node = .none,
1679 .symbol_prog_node = .none,
1680 .member_prog_node = .none,
1681 .input_prog_node = .none,
1682 };
1683 errdefer coff.deinit();
1684
1685 {
1686 const strings = std.enums.values(String);
1687 try coff.strings.ensureTotalCapacityContext(comp.gpa, @intCast(strings.len), .{
1688 .bytes = &coff.string_bytes,
1689 });
1690 for (strings) |string| assert(try coff.getOrPutString(@tagName(string)) == string);
1691 }
1692
1693 try coff.initHeaders(
1694 machine,
1695 timestamp,
1696 major_subsystem_version,
1697 minor_subsystem_version,
1698 magic,
1699 if (options.subsystem) |s| switch (s) {
1700 .console => .WINDOWS_CUI,
1701 .windows => .WINDOWS_GUI,
1702 else => return error.UnsupportedCOFFSubsystem,
1703 } else .WINDOWS_CUI,
1704 section_align,
1705 std.fs.path.basename(path.sub_path),
1706 );
1707 try coff.initBuiltins();
1708 return coff;
1709}
1710
1711pub fn deinit(coff: *Coff) void {
1712 const gpa = coff.base.comp.gpa;
1713 coff.mf.deinit(gpa);
1714 coff.nodes.deinit(gpa);
1715 coff.pending_members.deinit(gpa);
1716 coff.lib_string_table.deinit(gpa);
1717 coff.long_names_table.entries.deinit(gpa);
1718 coff.import_table.entries.deinit(gpa);
1719 coff.import_table.iat_symbol_indices.deinit(gpa);
1720 coff.export_table.entries.deinit(gpa);
1721 coff.symbol_table.strings.deinit(gpa);
1722 coff.symbol_table.symbols.deinit(gpa);
1723 coff.inputs.deinit(gpa);
1724 coff.input_archives.deinit(gpa);
1725 coff.input_archive_members.deinit(gpa);
1726 coff.input_archive_symbols.deinit(gpa);
1727 coff.input_archive_symbol_indices.deinit(gpa);
1728 for (coff.pending_default_libs.items) |l| gpa.free(l.path);
1729 coff.pending_default_libs.deinit(gpa);
1730 coff.alternate_names.deinit(gpa);
1731 coff.input_objects.deinit(gpa);
1732 coff.input_symbols.deinit(gpa);
1733 coff.input_sections.deinit(gpa);
1734 coff.strings.deinit(gpa);
1735 coff.string_bytes.deinit(gpa);
1736 coff.section_table.deinit(gpa);
1737 coff.pseudo_section_table.deinit(gpa);
1738 coff.object_section_table.deinit(gpa);
1739 coff.symbols.deinit(gpa);
1740 coff.globals.deinit(gpa);
1741 coff.navs.deinit(gpa);
1742 coff.uavs.deinit(gpa);
1743 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
1744 coff.pending_uavs.deinit(gpa);
1745 coff.relocs.deinit(gpa);
1746 coff.* = undefined;
1747}
1748
1749fn isImage(coff: *const Coff) bool {
1750 const comp = coff.base.comp;
1751 return switch (comp.config.output_mode) {
1752 .Exe => true,
1753 .Lib => switch (comp.config.link_mode) {
1754 .static => false,
1755 .dynamic => true,
1756 },
1757 .Obj => false,
1758 };
1759}
1760
1761fn isArchive(coff: *const Coff) bool {
1762 const comp = coff.base.comp;
1763 return switch (comp.config.output_mode) {
1764 .Exe => false,
1765 .Lib => switch (comp.config.link_mode) {
1766 .static => true,
1767 .dynamic => false,
1768 },
1769 .Obj => false,
1770 };
1771}
1772
1773fn isExe(coff: *const Coff) bool {
1774 return coff.base.comp.config.output_mode == .Exe;
1775}
1776
1777fn isObj(coff: *const Coff) bool {
1778 return coff.base.comp.config.output_mode == .Obj;
1779}
1780
1781fn hasCoffHeader(coff: *const Coff) bool {
1782 return coff.base.comp.zcu != null or !coff.isArchive();
1783}
1784
1785fn sectionParent(coff: *Coff) MappedFile.Node.Index {
1786 assert(coff.hasCoffHeader());
1787 return if (coff.isArchive()) Node.known.zcu_member else Node.known.file;
1788}
1789
1790fn initHeaders(
1791 coff: *Coff,
1792 machine: std.coff.IMAGE.FILE.MACHINE,
1793 timestamp: u32,
1794 major_subsystem_version: u16,
1795 minor_subsystem_version: u16,
1796 magic: std.coff.OptionalHeader.Magic,
1797 subsystem: std.coff.Subsystem,
1798 section_align: Alignment,
1799 file_name: []const u8,
1800) !void {
1801 const comp = coff.base.comp;
1802 const gpa = comp.gpa;
1803 const target_endian = coff.targetEndian();
1804 const file_align: Alignment = comptime .fromByteUnits(default_file_alignment);
1805 const is_image = coff.isImage();
1806 const is_archive = coff.isArchive();
1807 const target = &comp.root_mod.resolved_target.result;
1808 const optional_header_size: u16 = if (is_image) switch (magic) {
1809 _ => unreachable,
1810 inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
1811 } else 0;
1812 const data_directories_size: u16 = if (is_image)
1813 @sizeOf(std.coff.ImageDataDirectory) * std.coff.IMAGE.DIRECTORY_ENTRY.len
1814 else
1815 0;
1816
1817 var expected_nodes_len: usize = Node.known_count;
1818 if (coff.hasCoffHeader()) {
1819 // Sections
1820 expected_nodes_len += 4;
1821
1822 if (is_image) {
1823 // Pseudo-sections and import / export table
1824 expected_nodes_len += 9;
1825 if (comp.config.link_libc and target.abi == .msvc)
1826 expected_nodes_len += 1;
1827 } else
1828 // Symbol table
1829 expected_nodes_len += 2;
1830
1831 // TLS section
1832 if (comp.config.any_non_single_threaded) {
1833 if (!is_image) expected_nodes_len += 1;
1834 expected_nodes_len += 1;
1835 }
1836 }
1837 defer assert(coff.nodes.len == expected_nodes_len);
1838
1839 try coff.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
1840 coff.nodes.appendAssumeCapacity(.file);
1841
1842 const header_ni = Node.known.header;
1843 assert(header_ni == try Node.known.file.addOnlyHeaderChild(&coff.mf, gpa, .{
1844 .alignment = coff.mf.flags.block_size,
1845 }));
1846 coff.nodes.appendAssumeCapacity(.header);
1847
1848 const coff_parent_ni: MappedFile.Node.Index = if (is_archive) parent: {
1849 assert(try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .wrap(header_ni), .{
1850 .size = std.coff.archive_signature.len,
1851 .alignment = .@"4",
1852 }) == Node.known.signature);
1853 coff.nodes.appendAssumeCapacity(.signature);
1854 const signature_slice = Node.known.signature.slice(&coff.mf);
1855 @memcpy(signature_slice, std.coff.archive_signature);
1856
1857 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);
1858 try coff.members.ensureTotalCapacity(gpa, initial_member_count);
1859
1860 assert(Member.Index.first == try coff.addMemberAssumeCapacity(.first_linker, @sizeOf(u32)));
1861 coff.targetStore(coff.firstLinkerMemberNumSymbolsPtr(), 0);
1862
1863 assert(Member.Index.second == try coff.addMemberAssumeCapacity(.second_linker, 2 * @sizeOf(u32)));
1864 coff.targetStore(coff.secondLinkerMemberNumMembersPtr(), 0);
1865 coff.targetStore(coff.secondLinkerMemberNumSymbolsPtr(), 0);
1866
1867 assert(Member.Index.longnames == try coff.addMemberAssumeCapacity(.longnames, 0));
1868
1869 const first_linker_member = Member.Index.first.get(coff);
1870 const second_linker_member = Member.Index.second.get(coff);
1871 const longnames_member = Member.Index.longnames.get(coff);
1872
1873 try first_linker_member.initHeader(coff, "", timestamp);
1874 try second_linker_member.initHeader(coff, "", timestamp);
1875 try longnames_member.initHeader(coff, "/", timestamp);
1876
1877 if (comp.zcu) |zcu| {
1878 const zcu_mi = try coff.addMemberAssumeCapacity(.coff, @sizeOf(std.coff.Header));
1879 const zcu_member = zcu_mi.get(coff);
1880 try zcu_member.initHeader(coff, zcu.main_mod.fully_qualified_name, timestamp);
1881
1882 assert(try zcu_member.content_ni.addOnlyHeaderChild(&coff.mf, gpa, .{
1883 .size = @sizeOf(std.coff.Header),
1884 .alignment = .@"4",
1885 }) == Node.known.coff_header);
1886 coff.nodes.appendAssumeCapacity(.coff_header);
1887
1888 break :parent zcu_member.content_ni;
1889 }
1890
1891 // If we're not generating any code, no more known nodes are used
1892
1893 // These placeholder nodes are placed before the first member - if there are
1894 // no other members then the last linker member (longnames) needs to expand
1895 // to fill the padding at the end of the file.
1896 while (coff.nodes.len < Node.known_count) {
1897 _ = try Node.known.header.addHeaderChildAfter(&coff.mf, gpa, .none, .{});
1898 coff.nodes.appendAssumeCapacity(.placeholder);
1899 }
1900
1901 return;
1902 } else parent: {
1903 assert(try header_ni.addOnlyHeaderChild(&coff.mf, gpa, .{
1904 .size = if (is_image) msdos_stub.len + std.coff.pe_signature.len else 0,
1905 .alignment = .@"4",
1906 }) == Node.known.signature);
1907 coff.nodes.appendAssumeCapacity(.signature);
1908 if (is_image) {
1909 const signature_slice = Node.known.signature.slice(&coff.mf);
1910 @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
1911 @memcpy(signature_slice[signature_slice.len - std.coff.pe_signature.len ..], std.coff.pe_signature);
1912 }
1913
1914 // TODO: Not ideal to have this many placeholder nodes - use two distinct `Node.known` types?
1915 while (true) {
1916 const placeholder_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .none, .{});
1917 coff.nodes.appendAssumeCapacity(.placeholder);
1918 if (placeholder_ni == Node.known.zcu_member) break;
1919 }
1920
1921 assert(try header_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(Node.known.signature), .{
1922 .size = @sizeOf(std.coff.Header),
1923 .alignment = .@"4",
1924 }) == Node.known.coff_header);
1925 coff.nodes.appendAssumeCapacity(.coff_header);
1926
1927 break :parent header_ni;
1928 };
1929
1930 {
1931 const coff_header = coff.headerPtr();
1932 coff_header.* = .{
1933 .machine = machine,
1934 .number_of_sections = 0,
1935 .time_date_stamp = timestamp,
1936 .pointer_to_symbol_table = 0,
1937 .number_of_symbols = 0,
1938 .size_of_optional_header = optional_header_size + data_directories_size,
1939 .flags = .{
1940 .RELOCS_STRIPPED = is_image,
1941 .EXECUTABLE_IMAGE = is_image,
1942 .DEBUG_STRIPPED = true,
1943 .@"32BIT_MACHINE" = magic == .PE32,
1944 .LARGE_ADDRESS_AWARE = magic == .@"PE32+",
1945 .DLL = comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic,
1946 },
1947 };
1948 if (target_endian != native_endian) std.mem.byteSwapAllFields(std.coff.Header, coff_header);
1949 }
1950
1951 const optional_header_ni = Node.known.optional_header;
1952 assert(optional_header_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(Node.known.coff_header), .{
1953 .size = optional_header_size,
1954 .alignment = .@"4",
1955 }));
1956 coff.nodes.appendAssumeCapacity(.optional_header);
1957 if (is_image) {
1958 coff.targetStore(&coff.optionalHeaderStandardPtr().magic, magic);
1959 switch (coff.optionalHeaderPtr()) {
1960 .PE32 => |optional_header| {
1961 optional_header.* = .{
1962 .standard = .{
1963 .magic = .PE32,
1964 .major_linker_version = 0,
1965 .minor_linker_version = 0,
1966 .size_of_code = 0,
1967 .size_of_initialized_data = 0,
1968 .size_of_uninitialized_data = 0,
1969 .address_of_entry_point = 0,
1970 .base_of_code = 0,
1971 },
1972 .base_of_data = 0,
1973 .image_base = switch (coff.base.comp.config.output_mode) {
1974 .Exe => 0x400000,
1975 .Lib => switch (coff.base.comp.config.link_mode) {
1976 .static => 0,
1977 .dynamic => 0x10000000,
1978 },
1979 .Obj => 0,
1980 },
1981 .section_alignment = @intCast(section_align.toByteUnits()),
1982 .file_alignment = @intCast(file_align.toByteUnits()),
1983 .major_operating_system_version = 6,
1984 .minor_operating_system_version = 0,
1985 .major_image_version = 0,
1986 .minor_image_version = 0,
1987 .major_subsystem_version = major_subsystem_version,
1988 .minor_subsystem_version = minor_subsystem_version,
1989 .win32_version_value = 0,
1990 .size_of_image = 0,
1991 .size_of_headers = 0,
1992 .checksum = 0,
1993 .subsystem = subsystem,
1994 .dll_flags = .{
1995 .HIGH_ENTROPY_VA = true,
1996 .DYNAMIC_BASE = true,
1997 .TERMINAL_SERVER_AWARE = true,
1998 .NX_COMPAT = true,
1999 },
2000 .size_of_stack_reserve = default_size_of_stack_reserve,
2001 .size_of_stack_commit = default_size_of_stack_commit,
2002 .size_of_heap_reserve = default_size_of_heap_reserve,
2003 .size_of_heap_commit = default_size_of_heap_commit,
2004 .loader_flags = 0,
2005 .number_of_rva_and_sizes = std.coff.IMAGE.DIRECTORY_ENTRY.len,
2006 };
2007 if (target_endian != native_endian)
2008 std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header);
2009 },
2010 .@"PE32+" => |optional_header| {
2011 optional_header.* = .{
2012 .standard = .{
2013 .magic = .@"PE32+",
2014 .major_linker_version = 0,
2015 .minor_linker_version = 0,
2016 .size_of_code = 0,
2017 .size_of_initialized_data = 0,
2018 .size_of_uninitialized_data = 0,
2019 .address_of_entry_point = 0,
2020 .base_of_code = 0,
2021 },
2022 .image_base = switch (coff.base.comp.config.output_mode) {
2023 .Exe => 0x140000000,
2024 .Lib => switch (coff.base.comp.config.link_mode) {
2025 .static => 0,
2026 .dynamic => 0x180000000,
2027 },
2028 .Obj => 0,
2029 },
2030 .section_alignment = @intCast(section_align.toByteUnits()),
2031 .file_alignment = @intCast(file_align.toByteUnits()),
2032 .major_operating_system_version = 6,
2033 .minor_operating_system_version = 0,
2034 .major_image_version = 0,
2035 .minor_image_version = 0,
2036 .major_subsystem_version = major_subsystem_version,
2037 .minor_subsystem_version = minor_subsystem_version,
2038 .win32_version_value = 0,
2039 .size_of_image = 0,
2040 .size_of_headers = 0,
2041 .checksum = 0,
2042 .subsystem = subsystem,
2043 .dll_flags = .{
2044 .HIGH_ENTROPY_VA = true,
2045 .DYNAMIC_BASE = true,
2046 .TERMINAL_SERVER_AWARE = true,
2047 .NX_COMPAT = true,
2048 },
2049 .size_of_stack_reserve = default_size_of_stack_reserve,
2050 .size_of_stack_commit = default_size_of_stack_commit,
2051 .size_of_heap_reserve = default_size_of_heap_reserve,
2052 .size_of_heap_commit = default_size_of_heap_commit,
2053 .loader_flags = 0,
2054 .number_of_rva_and_sizes = std.coff.IMAGE.DIRECTORY_ENTRY.len,
2055 };
2056 if (target_endian != native_endian)
2057 std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", optional_header);
2058 },
2059 }
2060 }
2061
2062 const data_directories_ni = Node.known.data_directories;
2063 assert(data_directories_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(optional_header_ni), .{
2064 .size = data_directories_size,
2065 .alignment = .@"4",
2066 }));
2067 coff.nodes.appendAssumeCapacity(.data_directories);
2068 if (is_image) {
2069 const data_directories = coff.dataDirectorySlice();
2070 @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
2071 if (target_endian != native_endian) std.mem.byteSwapAllFields(
2072 [std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory,
2073 data_directories,
2074 );
2075 }
2076
2077 const section_table_ni = Node.known.section_table;
2078 assert(section_table_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(data_directories_ni), .{
2079 .alignment = .@"4",
2080 }));
2081 coff.nodes.appendAssumeCapacity(.section_table);
2082
2083 assert(coff.nodes.len == Node.known_count);
2084
2085 if (!is_image) {
2086 // TODO: These two nodes could be inside one movable node?
2087 coff.symbol_table.ni = try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(section_table_ni), .{
2088 .alignment = .@"2",
2089 .moved = true,
2090 });
2091 coff.nodes.appendAssumeCapacity(.symbol_table);
2092
2093 coff.symbol_table.strings_ni = try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(coff.symbol_table.ni), .{
2094 .size = @sizeOf(u32),
2095 .resized = true,
2096 });
2097 coff.nodes.appendAssumeCapacity(.string_table);
2098 coff.targetStore(coff.symbolTableStringLenPtr(), @sizeOf(u32));
2099 }
2100
2101 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);
2102 assert(coff.addSymbolAssumeCapacity() == .null);
2103
2104 // TODO: How do we tell MappedFile not to allocate physical space for .bss?
2105 // TODO: Could have a node flag 'virtual' that can never have slice* or fileLocation called on it
2106 // TODO: Instead of it's own section, place .bss as a pseudo-section at the end of .text in the extra space
2107 assert(try coff.addSection(.@".bss", .{
2108 .CNT_UNINITIALIZED_DATA = true,
2109 .MEM_READ = true,
2110 .MEM_WRITE = true,
2111 }) == .bss);
2112 assert(try coff.addSection(.@".data", .{
2113 .CNT_INITIALIZED_DATA = true,
2114 .MEM_READ = true,
2115 .MEM_WRITE = true,
2116 }) == .data);
2117 assert(try coff.addSection(.@".rdata", .{
2118 .CNT_INITIALIZED_DATA = true,
2119 .MEM_READ = true,
2120 }) == .rdata);
2121 assert(try coff.addSection(.@".text", .{
2122 .CNT_CODE = true,
2123 .MEM_EXECUTE = true,
2124 .MEM_READ = true,
2125 }) == .text);
2126
2127 if (is_image) {
2128 if (comp.config.link_libc and target.abi == .msvc) {
2129 // This section contains a function pointer table used by control flow guard:
2130 // https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
2131 // The page containing it is set to PAGE_READONLY during startup, so this can't
2132 // be merged into .data this protection would overlap writable memory.
2133 _ = try coff.addSection(.@".fptable", .{
2134 .CNT_INITIALIZED_DATA = true,
2135 .MEM_READ = true,
2136 .MEM_WRITE = true,
2137 });
2138 }
2139
2140 // TODO: Lazily initialize this instead, avoid the extra logic for this in flushMoved / flushResized
2141 const import_table_parent_ni = (try coff.objectSectionMapIndex(
2142 .@".idata",
2143 coff.mf.flags.block_size,
2144 .{ .read = true, .initialized = true },
2145 )).symbol(coff).node(coff);
2146 coff.import_table.ni = try import_table_parent_ni.addFloatingChild(&coff.mf, gpa, .{
2147 .alignment = .@"4",
2148 });
2149 coff.nodes.appendAssumeCapacity(.import_directory_table);
2150
2151 coff.export_table.ni = (try coff.pseudoSectionMapIndex(
2152 .@".edata",
2153 .of(std.coff.ExportDirectoryTable),
2154 .{ .read = true, .initialized = true },
2155 )).symbol(coff).node(coff);
2156
2157 coff.export_table.export_directory_table_ni = try coff.export_table.ni.addHeaderChildAfter(&coff.mf, gpa, coff.export_table.ni.last(&coff.mf), .{
2158 .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1,
2159 .moved = true,
2160 });
2161 coff.nodes.appendAssumeCapacity(.export_directory_table);
2162
2163 const name_index = @sizeOf(std.coff.ExportDirectoryTable);
2164 const table_slice = coff.export_table.export_directory_table_ni.slice(&coff.mf);
2165 @memcpy(table_slice[name_index..][0..file_name.len], file_name[0..file_name.len]);
2166 @memset(table_slice[name_index + file_name.len ..], 0);
2167
2168 const export_address_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2169 .alignment = .of(std.coff.ExportAddressTableEntry),
2170 .moved = true,
2171 });
2172 coff.nodes.appendAssumeCapacity(.export_address_table);
2173
2174 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2175 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();
2176
2177 const export_address_table_sym = coff.export_table.export_address_table_si.get(coff);
2178 export_address_table_sym.ni = .wrap(export_address_table_ni);
2179 assert(export_address_table_sym.loc_relocs == .none);
2180 export_address_table_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
2181 export_address_table_sym.section_number =
2182 coff.getNode(coff.export_table.ni).pseudo_section.symbol(coff).get(coff).section_number;
2183
2184 coff.export_table.name_pointer_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2185 .alignment = .of(std.coff.ExportNamePointerTableEntry),
2186 .moved = true,
2187 });
2188 coff.nodes.appendAssumeCapacity(.export_name_pointer_table);
2189
2190 coff.export_table.ordinal_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2191 .alignment = .of(std.coff.ExportOrdinalTableEntry),
2192 .moved = true,
2193 });
2194 coff.nodes.appendAssumeCapacity(.export_ordinal_table);
2195
2196 coff.export_table.name_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2197 .alignment = .of(u8),
2198 .moved = true,
2199 });
2200 coff.nodes.appendAssumeCapacity(.export_name_table);
2201
2202 const export_directory_table = coff.exportDirectoryTable();
2203 export_directory_table.* = .{
2204 .flags = 0,
2205 .time_date_stamp = timestamp,
2206 .major_version = 0,
2207 .minor_version = 0,
2208 .name_rva = 0,
2209 .ordinal_base = 1,
2210 .number_of_entries = 0,
2211 .number_of_names = 0,
2212 .export_address_table_rva = 0,
2213 .name_pointer_table_rva = 0,
2214 .ordinal_table_rva = 0,
2215 };
2216 if (target_endian != native_endian)
2217 std.mem.byteSwapAllFields(std.coff.ExportDirectoryTable, export_directory_table);
2218 }
2219
2220 if (comp.config.any_non_single_threaded) {
2221 if (!is_image)
2222 _ = try coff.addSection(.@".tls$", .{
2223 .CNT_INITIALIZED_DATA = true,
2224 .MEM_READ = true,
2225 .MEM_WRITE = true,
2226 });
2227
2228 // While tls variables allocated at runtime are writable, the template itself is not.
2229 // In images, the template is in a .tls pseudo section in .rdata.
2230 // In objects / archives, this section is part of the above .tls$ section. The suffix
2231 // is maintained so merging can occur with other input tls symbols when linked later.
2232 _ = try coff.pseudoSectionMapIndex(
2233 if (is_image) .@".tls" else .@".tls$",
2234 coff.mf.flags.block_size,
2235 .{ .read = true, .write = !is_image, .initialized = true },
2236 );
2237 }
2238}
2239
2240pub fn initBuiltins(coff: *Coff) !void {
2241 const comp = coff.base.comp;
2242 const gpa = comp.gpa;
2243 const target = &comp.root_mod.resolved_target.result;
2244 if (coff.isImage()) {
2245 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
2246 const sym = si.get(coff);
2247 sym.ni = .wrap(Node.known.header);
2248 }
2249
2250 defer coff.flushSectionMerges() catch unreachable;
2251 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {
2252 try coff.symbols.ensureUnusedCapacity(gpa, 8);
2253 try coff.globals.ensureUnusedCapacity(gpa, 2);
2254 try coff.nodes.ensureUnusedCapacity(gpa, 8);
2255 try coff.section_merges.ensureUnusedCapacity(gpa, 2);
2256
2257 const lists: []const struct { global: []const u8, start: String, end: String } = &.{
2258 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },
2259 .{ .global = "__DTOR_LIST__", .start = .@".dtors", .end = .@".dtors$ZZZ" },
2260 };
2261
2262 // We need to explicitly merge these into .rdata as in objects they can be marked
2263 // as MEM_WRITE, and would have mismatced section flags.
2264 try coff.section_merges.put(gpa, .@".ctors", .@".rdata");
2265 try coff.section_merges.put(gpa, .@".dtors", .@".rdata");
2266
2267 for (lists) |list| {
2268 const addr_info = coff.targetAddrInfo();
2269
2270 // Any .(c|d)tor$(.*) input sections will merge in between these sections
2271 const start_osmi = try coff.objectSectionMapIndex(
2272 list.start,
2273 addr_info.alignment,
2274 .{ .read = true, .initialized = true },
2275 );
2276 const end_osmi = try coff.objectSectionMapIndex(
2277 list.end,
2278 addr_info.alignment,
2279 .{ .read = true, .initialized = true },
2280 );
2281
2282 // Additional nodes are used here, instead of just adding the sentinel
2283 // directly to the section data, since once input sections are added
2284 // as children, they would overwrite that data.
2285 const start_sym = start_osmi.symbol(coff).get(coff);
2286 const list_len_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });
2287 const list_len_sym = list_len_si.get(coff);
2288 list_len_sym.setExtra(.{ .size = addr_info.size });
2289 list_len_sym.ni = .wrap(try start_sym.ni.unwrap().?.addHeaderChildAfter(&coff.mf, gpa, .none, .{
2290 .size = addr_info.size,
2291 }));
2292 coff.nodes.appendAssumeCapacity(.{ .builtin = list_len_si });
2293 list_len_sym.section_number = start_sym.section_number;
2294
2295 const start_slice = list_len_sym.ni.unwrap().?.slice(&coff.mf);
2296 switch (addr_info.magic) {
2297 _ => unreachable,
2298 inline .PE32, .@"PE32+" => |t| {
2299 const addr: *TargetAddr(t) = @ptrCast(@alignCast(start_slice));
2300 // For __CTOR_LIST__ -1 indicates that the list is null terminated.
2301 // For __DTOR_LIST__, this value is ignored, the list is always null terminated
2302 coff.targetStore(addr, std.math.maxInt(TargetAddr(t)));
2303 },
2304 }
2305
2306 const end_sym = end_osmi.symbol(coff).get(coff);
2307 const list_end_si = coff.addSymbolAssumeCapacity();
2308 const list_end_sym = list_end_si.get(coff);
2309 list_end_sym.setExtra(.{ .size = addr_info.size });
2310 list_end_sym.ni = .wrap(try end_sym.ni.unwrap().?.addHeaderChildAfter(&coff.mf, gpa, .none, .{
2311 .size = addr_info.size,
2312 }));
2313 coff.nodes.appendAssumeCapacity(.{ .builtin = list_end_si });
2314 list_end_sym.section_number = start_sym.section_number;
2315
2316 @memset(list_end_sym.ni.unwrap().?.slice(&coff.mf), 0);
2317
2318 try list_len_si.flushMoved(coff);
2319 try list_end_si.flushMoved(coff);
2320 }
2321 }
2322}
2323
2324pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
2325 prog_node.increaseEstimatedTotalItems(3);
2326 coff.const_prog_node = prog_node.start("Constants", coff.pending_uavs.count());
2327 coff.synth_prog_node = prog_node.start("Synthetics", count: {
2328 var count =
2329 coff.globals.count() - coff.global_pending_index +
2330 coff.section_merges.count() - coff.section_merge_pending_index;
2331
2332 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
2333 break :count count;
2334 });
2335 if (!isImage(coff)) {
2336 prog_node.increaseEstimatedTotalItems(2);
2337 coff.symbol_prog_node = prog_node.start(
2338 "Symbols",
2339 coff.symbol_table.symbols.count() - coff.symbol_table.pending_symbol_index,
2340 );
2341 coff.member_prog_node = prog_node.start("Members", coff.pending_members.count());
2342 }
2343 coff.input_prog_node = prog_node.start(
2344 "Inputs",
2345 coff.input_sections.items.len - coff.input_section_pending_index,
2346 );
2347 coff.mf.update_prog_node = prog_node.start("Relocations", coff.mf.updates.items.len);
2348}
2349
2350pub fn endProgress(coff: *Coff) void {
2351 coff.mf.update_prog_node.end();
2352 coff.mf.update_prog_node = .none;
2353 coff.input_prog_node.end();
2354 coff.input_prog_node = .none;
2355 if (!coff.isImage()) {
2356 coff.member_prog_node.end();
2357 coff.member_prog_node = .none;
2358 coff.symbol_prog_node.end();
2359 coff.symbol_prog_node = .none;
2360 }
2361 coff.synth_prog_node.end();
2362 coff.synth_prog_node = .none;
2363 coff.const_prog_node.end();
2364 coff.const_prog_node = .none;
2365}
2366
2367fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
2368 return coff.nodes.get(@backingInt(ni));
2369}
2370fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
2371 const parent_rva = parent_rva: {
2372 const parent_si = switch (coff.getNode(ni.parent(&coff.mf).unwrap().?)) {
2373 .file,
2374 .header,
2375 .signature,
2376 .archive_member_header,
2377 .archive_member,
2378 .coff_header,
2379 .optional_header,
2380 .data_directories,
2381 .section_table,
2382 .export_name_table,
2383 .placeholder,
2384 .symbol_table,
2385 .string_table,
2386 .relocation_table,
2387 .relocation_table_entry,
2388 .input_section,
2389 .builtin,
2390 => unreachable,
2391 .image_section => |si| si,
2392 .import_directory_table => break :parent_rva coff.targetLoad(
2393 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
2394 ),
2395 .import_lookup_table => |import_index| break :parent_rva coff.targetLoad(
2396 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
2397 ),
2398 .import_address_table => |import_index| break :parent_rva coff.targetLoad(
2399 &coff.importDirectoryEntryPtr(import_index).import_address_table_rva,
2400 ),
2401 .import_hint_name_table => |import_index| break :parent_rva coff.targetLoad(
2402 &coff.importDirectoryEntryPtr(import_index).name_rva,
2403 ),
2404 .export_directory_table => break :parent_rva coff.targetLoad(
2405 &coff.dataDirectoryPtr(.EXPORT).virtual_address,
2406 ),
2407 .export_address_table => break :parent_rva coff.targetLoad(
2408 &coff.exportDirectoryTable().export_address_table_rva,
2409 ),
2410 .export_name_pointer_table => break :parent_rva coff.targetLoad(
2411 &coff.exportDirectoryTable().name_pointer_table_rva,
2412 ),
2413 .export_ordinal_table => break :parent_rva coff.targetLoad(
2414 &coff.exportDirectoryTable().ordinal_table_rva,
2415 ),
2416 inline .pseudo_section,
2417 .object_section,
2418 .import_thunk,
2419 .nav,
2420 .uav,
2421 .lazy_code,
2422 .lazy_const_data,
2423 => |mi| mi.symbol(coff),
2424 };
2425 break :parent_rva parent_si.get(coff).rva;
2426 };
2427 const offset, _ = ni.location(&coff.mf).resolve(&coff.mf);
2428 return @intCast(parent_rva + offset);
2429}
2430
2431fn computeSymbolSectionOffset(
2432 coff: *Coff,
2433 sym: *const Symbol,
2434 relative_to: enum { image, pseudo },
2435) u32 {
2436 var section_offset: u32 = sym.nodeOffset(coff);
2437 var parent_ni = sym.ni.unwrap().?;
2438 while (true) {
2439 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
2440 section_offset += @intCast(offset);
2441 parent_ni = parent_ni.parent(&coff.mf).unwrap().?;
2442 switch (coff.getNode(parent_ni)) {
2443 else => unreachable,
2444 .image_section => break,
2445 .pseudo_section => if (relative_to == .pseudo) break,
2446 .object_section,
2447 => {},
2448 }
2449 }
2450
2451 return section_offset;
2452}
2453
2454pub inline fn targetEndian(_: *const Coff) std.lang.Endian {
2455 return .little;
2456}
2457
2458fn targetAddrInfo(coff: *Coff) struct {
2459 size: u8,
2460 alignment: Alignment,
2461 magic: std.coff.OptionalHeader.Magic,
2462} {
2463 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
2464 switch (magic) {
2465 _ => unreachable,
2466 .PE32 => return .{ .size = 4, .alignment = .@"4", .magic = magic },
2467 .@"PE32+" => return .{ .size = 8, .alignment = .@"8", .magic = magic },
2468 }
2469}
2470
2471fn TargetAddr(comptime magic: std.coff.OptionalHeader.Magic) type {
2472 return switch (magic) {
2473 _ => comptime unreachable,
2474 .PE32 => u32,
2475 .@"PE32+" => u64,
2476 };
2477}
2478
2479fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
2480 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
2481 return switch (@typeInfo(Child)) {
2482 else => @compileError(@typeName(Child)),
2483 .int => std.mem.toNative(Child, ptr.*, coff.targetEndian()),
2484 .@"enum" => |@"enum"| @fromBackingInt(@intCast(coff.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr))))),
2485 .@"struct" => |@"struct"| @bitCast(
2486 coff.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
2487 ),
2488 };
2489}
2490fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
2491 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
2492 return switch (@typeInfo(Child)) {
2493 else => @compileError(@typeName(Child)),
2494 .int => ptr.* = std.mem.nativeTo(Child, val, coff.targetEndian()),
2495 .@"enum" => |@"enum"| coff.targetStore(
2496 @as(*@"enum".tag_type, @ptrCast(ptr)),
2497 @backingInt(val),
2498 ),
2499 .@"struct" => |@"struct"| coff.targetStore(
2500 @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
2501 @bitCast(val),
2502 ),
2503 };
2504}
2505
2506pub fn headerPtr(coff: *Coff) *std.coff.Header {
2507 assert(coff.hasCoffHeader());
2508 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
2509}
2510
2511pub fn firstLinkerMemberNumSymbolsPtr(coff: *Coff) *u32 {
2512 assert(coff.isArchive());
2513 return @ptrCast(@alignCast(Node.known.first_linker_member.slice(&coff.mf)));
2514}
2515
2516pub fn firstLinkerMemberOffsetsSlice(coff: *Coff) []u32 {
2517 const len = std.mem.toNative(u32, coff.firstLinkerMemberNumSymbolsPtr().*, .big);
2518 return @ptrCast(@alignCast(Node.known.first_linker_member.slice(&coff.mf)[@sizeOf(u32)..][0 .. len * @sizeOf(u32)]));
2519}
2520
2521pub fn secondLinkerMemberNumMembersPtr(coff: *Coff) *align(2) u32 {
2522 assert(coff.isArchive());
2523 return @ptrCast(@alignCast(Node.known.second_linker_member.slice(&coff.mf)));
2524}
2525
2526pub fn secondLinkerMemberOffsetsSlice(coff: *Coff) []align(2) u32 {
2527 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
2528 return @ptrCast(@alignCast(
2529 Node.known.second_linker_member.slice(&coff.mf)[@sizeOf(u32)..][0 .. num_members * @sizeOf(u32)],
2530 ));
2531}
2532
2533pub fn secondLinkerMemberNumSymbolsPtr(coff: *Coff) *align(2) u32 {
2534 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
2535 return @ptrCast(@alignCast(
2536 Node.known.second_linker_member.slice(&coff.mf)[(1 + num_members) * @sizeOf(u32) ..],
2537 ));
2538}
2539
2540pub fn secondLinkerMemberIndicesSlice(coff: *Coff) []u16 {
2541 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
2542 const num_symbols = coff.targetLoad(coff.secondLinkerMemberNumSymbolsPtr());
2543 return @ptrCast(@alignCast(
2544 Node.known.second_linker_member.slice(&coff.mf)[(2 + num_members) * @sizeOf(u32) ..][0 .. num_symbols * @sizeOf(u16)],
2545 ));
2546}
2547
2548pub fn secondLinkerMemberStringsSlice(coff: *Coff) []u8 {
2549 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
2550 const num_symbols = coff.targetLoad(coff.secondLinkerMemberNumSymbolsPtr());
2551 return @ptrCast(@alignCast(
2552 Node.known.second_linker_member.slice(&coff.mf)[(2 + num_members) * @sizeOf(u32) + num_symbols * @sizeOf(u16) ..],
2553 ));
2554}
2555
2556pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
2557 return @ptrCast(@alignCast(
2558 Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
2559 ));
2560}
2561
2562pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
2563 PE32: *std.coff.OptionalHeader.PE32,
2564 @"PE32+": *std.coff.OptionalHeader.@"PE32+",
2565};
2566pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
2567 assert(coff.isImage());
2568 const slice = Node.known.optional_header.slice(&coff.mf);
2569 return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
2570 _ => unreachable,
2571 inline else => |magic| @unionInit(
2572 OptionalHeaderPtr,
2573 @tagName(magic),
2574 @ptrCast(@alignCast(slice)),
2575 ),
2576 };
2577}
2578pub fn optionalHeaderField(
2579 coff: *Coff,
2580 comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
2581) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
2582 assert(coff.isImage());
2583 return switch (coff.optionalHeaderPtr()) {
2584 inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
2585 };
2586}
2587
2588pub fn dataDirectorySlice(
2589 coff: *Coff,
2590) *[std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory {
2591 assert(coff.isImage());
2592 return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
2593}
2594pub fn dataDirectoryPtr(
2595 coff: *Coff,
2596 entry: std.coff.IMAGE.DIRECTORY_ENTRY,
2597) *std.coff.ImageDataDirectory {
2598 return &coff.dataDirectorySlice()[@backingInt(entry)];
2599}
2600
2601pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
2602 return @ptrCast(@alignCast(
2603 Node.known.section_table.slice(&coff.mf)[0 .. coff.section_table.count() * @sizeOf(std.coff.SectionHeader)],
2604 ));
2605}
2606
2607pub fn symbolTableEntryStoragePtr(coff: *Coff, index: u32) *[std.coff.Symbol.sizeOf()]u8 {
2608 assert(!coff.isImage());
2609 const offset = index * std.coff.Symbol.sizeOf();
2610 return @ptrCast(@alignCast(coff.symbol_table.ni.slice(&coff.mf)[offset..][0..std.coff.Symbol.sizeOf()]));
2611}
2612
2613pub fn symbolTableEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.coff.Symbol {
2614 if (sti.unwrap()) |index|
2615 return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, index)))
2616 else
2617 return null;
2618}
2619
2620pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.coff.SectionDefinition {
2621 if (symbolTableEntryPtr(coff, sti)) |entry| {
2622 assert(entry.storage_class == .STATIC and entry.number_of_aux_symbols == 1);
2623 return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1)));
2624 } else {
2625 return null;
2626 }
2627}
2628
2629pub fn symbolTableWeakExternalAuxEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.coff.WeakExternalDefinition {
2630 if (symbolTableEntryPtr(coff, sti)) |entry| {
2631 assert(entry.storage_class == .WEAK_EXTERNAL and entry.number_of_aux_symbols == 1);
2632 return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1)));
2633 } else {
2634 return null;
2635 }
2636}
2637
2638pub fn symbolTableStringLenPtr(coff: *Coff) *align(1) u32 {
2639 return @ptrCast(@alignCast(coff.symbol_table.strings_ni.slice(&coff.mf)[0..@sizeOf(u32)]));
2640}
2641
2642pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {
2643 assert(coff.isImage());
2644 return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf)));
2645}
2646pub fn importDirectoryEntryPtr(
2647 coff: *Coff,
2648 import_index: ImportTable.Index,
2649) *std.coff.ImportDirectoryEntry {
2650 return &coff.importDirectoryTableSlice()[@backingInt(import_index)];
2651}
2652
2653pub fn exportDirectoryTable(coff: *Coff) *std.coff.ExportDirectoryTable {
2654 return @ptrCast(@alignCast(coff.export_table.export_directory_table_ni.slice(&coff.mf)));
2655}
2656
2657pub fn exportNamePointerTableSlice(coff: *Coff) []std.coff.ExportNamePointerTableEntry {
2658 const debug = coff.export_table.name_pointer_table_ni.slice(&coff.mf);
2659 _ = debug;
2660
2661 return @ptrCast(@alignCast(coff.export_table.name_pointer_table_ni.slice(&coff.mf)));
2662}
2663
2664pub fn exportOrdinalTableSlice(coff: *Coff) []std.coff.ExportOrdinalTableEntry {
2665 return @ptrCast(@alignCast(coff.export_table.ordinal_table_ni.slice(&coff.mf)));
2666}
2667
2668fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
2669 defer coff.symbols.addOneAssumeCapacity().* = .{
2670 .ni = .none,
2671 .rva = 0,
2672 .value = .{ .none = {} },
2673 .extra = .{ .size = 0 },
2674 .flags = .{
2675 .value_tag = .none,
2676 .extra_tag = .size,
2677 .type = .unknown,
2678 .dll_storage_class = .default,
2679 .weak_external_strat = .none,
2680 },
2681 .loc_relocs = .none,
2682 .target_relocs = .none,
2683 .section_number = .UNDEFINED,
2684 .gmi = .none,
2685 };
2686 return @fromBackingInt(@intCast(coff.symbols.items.len));
2687}
2688
2689fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
2690 const si = coff.addSymbolAssumeCapacity();
2691 return si;
2692}
2693
2694fn getOrPutString(coff: *Coff, string: []const u8) !String {
2695 try coff.ensureUnusedStringCapacity(string.len);
2696 return coff.getOrPutStringAssumeCapacity(string);
2697}
2698fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
2699 return (try coff.getOrPutString(string orelse return .none)).toOptional();
2700}
2701fn getString(coff: *Coff, string: []const u8) String.Optional {
2702 if (coff.strings.getKeyAdapted(
2703 string,
2704 std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
2705 )) |key|
2706 return @as(String, @fromBackingInt(@intCast(key))).toOptional()
2707 else
2708 return .none;
2709}
2710
2711/// If the name does not fit in the symbol header, adds it to the symbol table string table.
2712/// If the caller knows this name already has a String associated with it, they can avoid
2713/// a redundant call to `getOrPutString` by specifying `opt_string`.
2714/// The lifetime of the return value matches that of `name`.
2715fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName {
2716 assert(!coff.isImage());
2717 const gpa = coff.base.comp.gpa;
2718
2719 return if (name.len > header_name_max_len) name: {
2720 const string = opt_string orelse try coff.getOrPutString(name);
2721 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
2722 if (!string_gop.found_existing) {
2723 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
2724 string_gop.value_ptr.* = @fromBackingInt(@intCast(string_index));
2725
2726 try coff.symbol_table.strings_ni.resizeLeaf(&coff.mf, gpa, string_index + name.len + 1);
2727 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
2728 @memcpy(slice[@intCast(string_index)..][0..name.len], name);
2729 slice[@intCast(string_index + name.len)] = 0;
2730 }
2731
2732 break :name .{ .long = string_gop.value_ptr.* };
2733 } else .{ .short = name };
2734}
2735
2736/// `len` does not include null terminators
2737fn ensureUnusedStringCapacity(coff: *Coff, len: usize) !void {
2738 const gpa = coff.base.comp.gpa;
2739 try coff.strings.ensureUnusedCapacityContext(gpa, 1, .{ .bytes = &coff.string_bytes });
2740 try coff.string_bytes.ensureUnusedCapacity(gpa, len + 1);
2741}
2742
2743/// `total_len` includes null terminators
2744fn ensureManyUnusedStringCapacity(coff: *Coff, num_strings: u32, total_len: usize) !void {
2745 const gpa = coff.base.comp.gpa;
2746 try coff.strings.ensureUnusedCapacityContext(gpa, num_strings, .{ .bytes = &coff.string_bytes });
2747 try coff.string_bytes.ensureUnusedCapacity(gpa, total_len + num_strings);
2748}
2749
2750fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
2751 const gop = coff.strings.getOrPutAssumeCapacityAdapted(
2752 string,
2753 std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
2754 );
2755 if (!gop.found_existing) {
2756 gop.key_ptr.* = @intCast(coff.string_bytes.items.len);
2757 gop.value_ptr.* = {};
2758 coff.string_bytes.appendSliceAssumeCapacity(string);
2759 coff.string_bytes.appendAssumeCapacity(0);
2760 }
2761 return @fromBackingInt(@intCast(gop.key_ptr.*));
2762}
2763
2764const GlobalOptions = struct {
2765 name: []const u8,
2766 lib_name: ?[]const u8 = null,
2767 type: Symbol.Type = .unknown,
2768 dll_storage_class: Symbol.DllStorageClass = .default,
2769};
2770
2771fn getOrPutGlobalSymbol(
2772 coff: *Coff,
2773 opts: GlobalOptions,
2774) !std.array_hash_map.Auto(String, Global).GetOrPutResult {
2775 const comp = coff.base.comp;
2776 const gpa = comp.gpa;
2777 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2778
2779 const lib_name: String.Optional = if (opts.lib_name) |lib_name| lib_name: {
2780 const is_libc = std.zig.target.isLibCLibName(&comp.root_mod.resolved_target.result, lib_name);
2781 if (is_libc) {
2782 // This is guaranteed by Sema.handleExternLibName
2783 if (!comp.config.link_libc) unreachable;
2784
2785 // TODO: The user has requested this symbol come from libc, but this logic allows
2786 // it to come from anywhere. We need to know what inputs are libc inputs,
2787 // and set a flag to only search them for this symbol.
2788 break :lib_name .none;
2789 }
2790
2791 break :lib_name (try coff.getOrPutString(lib_name)).toOptional();
2792 } else .none;
2793
2794 const sym_gop = try coff.globals.getOrPut(gpa, try coff.getOrPutString(opts.name));
2795 if (!sym_gop.found_existing) {
2796 const si = coff.addSymbolAssumeCapacity();
2797 const sym = si.get(coff);
2798 sym.gmi = .wrap(@intCast(sym_gop.index));
2799 sym.flags.type = opts.type;
2800 sym.flags.dll_storage_class = opts.dll_storage_class;
2801 sym_gop.value_ptr.* = .{
2802 .si = si,
2803 .lib_name = lib_name,
2804 };
2805 coff.synth_prog_node.increaseEstimatedTotalItems(1);
2806
2807 log.debug("globalSymbol({s}, {?s}) = {d}", .{ opts.name, opts.lib_name, si });
2808 }
2809
2810 return sym_gop;
2811}
2812
2813fn getDefinedGlobal(coff: *Coff, name: []const u8) Symbol.Index {
2814 if (coff.globals.get(
2815 coff.getString(name).unwrap() orelse return .null,
2816 )) |global| if (global.si.get(coff).ni != .none) return global.si;
2817 return .null;
2818}
2819
2820pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {
2821 const gop = try coff.getOrPutGlobalSymbol(opts);
2822 return gop.value_ptr.si;
2823}
2824
2825pub fn pendingSymbolTableEntry(coff: *Coff, si: Symbol.Index) !void {
2826 assert(!coff.isImage());
2827 const sym = si.get(coff);
2828
2829 assert(sym.ni != .none or sym.gmi != .none);
2830 const gpa = coff.base.comp.gpa;
2831 const gop = try coff.symbol_table.symbols.getOrPut(gpa, si);
2832 if (!gop.found_existing) {
2833 coff.symbol_prog_node.increaseEstimatedTotalItems(1);
2834 gop.value_ptr.* = .none;
2835 }
2836}
2837
2838fn navSection(
2839 coff: *Coff,
2840 zcu: *Zcu,
2841 nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child,
2842) !Symbol.Index {
2843 const ip = &zcu.intern_pool;
2844 const default: String, const attributes: ObjectSectionAttributes =
2845 if (nav_resolved.@"threadlocal" and coff.base.comp.config.any_non_single_threaded) .{
2846 .@".tls$", .{ .read = true, .write = true, .initialized = true },
2847 } else if (ip.isFunctionType(nav_resolved.type)) .{
2848 .@".text", .{ .read = true, .execute = true },
2849 } else if (nav_resolved.@"const") .{
2850 .@".rdata", .{ .read = true, .initialized = true },
2851 } else .{
2852 .@".data", .{ .read = true, .write = true, .initialized = true },
2853 };
2854
2855 return (try coff.objectSectionMapIndex(
2856 (try coff.getOrPutOptionalString(nav_resolved.@"linksection".toSlice(ip))).unwrap() orelse default,
2857 switch (nav_resolved.@"linksection") {
2858 .none => coff.mf.flags.block_size,
2859 else => switch (nav_resolved.@"align") {
2860 .none => .fromIp(Type.fromInterned(ip.typeOf(nav_resolved.value)).abiAlignment(zcu)),
2861 else => |a| .fromIp(a),
2862 },
2863 },
2864 attributes,
2865 )).symbol(coff);
2866}
2867fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
2868 const gpa = zcu.gpa;
2869 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2870 const sym_gop = try coff.navs.getOrPut(gpa, nav_index);
2871 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
2872 return @fromBackingInt(@intCast(sym_gop.index));
2873}
2874pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
2875 const ip = &zcu.intern_pool;
2876 const nav = ip.getNav(nav_index);
2877 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(.{
2878 .name = @"extern".name.toSlice(ip),
2879 .lib_name = @"extern".lib_name.toSlice(ip),
2880 // TODO: Threadlocal as well?
2881 .type = if (ip.isFunctionType(nav.resolved.?.type)) .code else .data,
2882 .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default,
2883 });
2884 const nmi = try coff.navMapIndex(zcu, nav_index);
2885 return nmi.symbol(coff);
2886}
2887
2888fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
2889 const gpa = coff.base.comp.gpa;
2890 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2891 const sym_gop = try coff.uavs.getOrPut(gpa, uav_val);
2892 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
2893 return @fromBackingInt(@intCast(sym_gop.index));
2894}
2895pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index {
2896 const umi = try coff.uavMapIndex(uav_val);
2897 return umi.symbol(coff);
2898}
2899
2900pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
2901 const gpa = coff.base.comp.gpa;
2902 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2903 const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
2904 if (!sym_gop.found_existing) {
2905 sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity();
2906 coff.synth_prog_node.increaseEstimatedTotalItems(1);
2907 }
2908 return sym_gop.value_ptr.*;
2909}
2910
2911pub fn getNavVAddr(
2912 coff: *Coff,
2913 pt: Zcu.PerThread,
2914 nav: InternPool.Nav.Index,
2915 reloc_info: link.File.RelocInfo,
2916) link.Error!u64 {
2917 return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
2918}
2919
2920pub fn getUavVAddr(
2921 coff: *Coff,
2922 uav: InternPool.Index,
2923 reloc_info: link.File.RelocInfo,
2924) link.Error!u64 {
2925 return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
2926}
2927
2928pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) link.Error!u64 {
2929 try coff.addReloc(
2930 @fromBackingInt(@intCast(@backingInt(reloc_info.parent.atom_index))),
2931 reloc_info.offset,
2932 target_si,
2933 .{ .known = reloc_info.addend },
2934 switch (coff.targetLoad(&coff.headerPtr().machine)) {
2935 else => unreachable,
2936 .AMD64 => .{ .AMD64 = .ADDR64 },
2937 .I386 => .{ .I386 = .DIR32 },
2938 },
2939 );
2940
2941 var vaddr: u64 = target_si.get(coff).rva;
2942 if (coff.isImage()) vaddr += coff.optionalHeaderField(.image_base);
2943 return vaddr;
2944}
2945
2946/// Caller guarantees there is capacity for one member and two nodes
2947fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind, size: u64) !Member.Index {
2948 const comp = coff.base.comp;
2949 const gpa = comp.gpa;
2950
2951 const header_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, Node.known.file.last(&coff.mf), .{
2952 .size = @sizeOf(std.coff.ArchiveMemberHeader),
2953 .alignment = .@"2",
2954 .moved = true,
2955 });
2956
2957 // The actual alignment required by the spec is 2, but to allow aligned access to
2958 // the various COFF data structures in-place during linking we overalign
2959 const content_align: Alignment = switch (kind) {
2960 .first_linker, .second_linker, .longnames, .coff => .@"4",
2961 else => .@"2",
2962 };
2963 const content_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .wrap(header_ni), .{
2964 .alignment = content_align,
2965 .size = content_align.forward(size),
2966 .resized = size > 0,
2967 });
2968
2969 const mi: Member.Index = @fromBackingInt(@intCast(coff.members.items.len));
2970 coff.members.appendAssumeCapacity(.{
2971 .kind = kind,
2972 .header_ni = header_ni,
2973 .content_ni = content_ni,
2974 .first_linker_indices = .empty,
2975 });
2976
2977 coff.nodes.appendAssumeCapacity(.{ .archive_member_header = mi });
2978 coff.nodes.appendAssumeCapacity(.{ .archive_member = mi });
2979
2980 switch (kind) {
2981 .first_linker, .second_linker, .longnames => {},
2982 else => {
2983 const new_num_members = coff.members.items.len - Member.Index.known_count;
2984 coff.targetStore(
2985 coff.secondLinkerMemberNumMembersPtr(),
2986 @intCast(new_num_members),
2987 );
2988
2989 const old_size = Node.known.second_linker_member.location(&coff.mf).resolve(&coff.mf)[1];
2990 const old_header_size = new_num_members * @sizeOf(u32);
2991 const trailing_size: usize = @intCast(old_size - old_header_size);
2992 try Node.known.second_linker_member.resizeLeaf(&coff.mf, gpa, old_size + @sizeOf(u32));
2993
2994 const slice = Node.known.second_linker_member.slice(&coff.mf);
2995 @memmove(
2996 slice[old_header_size + @sizeOf(u32) ..][0..trailing_size],
2997 slice[old_header_size..][0..trailing_size],
2998 );
2999
3000 // Offset will be written by flushMoved on header_ni
3001 },
3002 }
3003
3004 switch (kind) {
3005 .first_linker,
3006 .longnames,
3007 .import,
3008 => {},
3009 .second_linker,
3010 .coff,
3011 => {
3012 try coff.pending_members.ensureTotalCapacity(
3013 gpa,
3014 coff.pending_members.capacity() + 1,
3015 );
3016 coff.member_prog_node.increaseEstimatedTotalItems(1);
3017 },
3018 }
3019
3020 return mi;
3021}
3022
3023fn appendMemberSymbolString(
3024 coff: *Coff,
3025 strings_ni: MappedFile.Node.Index,
3026 new_size: u64,
3027 name: []const u8,
3028 offset: u64,
3029) !void {
3030 try strings_ni.resizeLeaf(&coff.mf, coff.base.comp.gpa, new_size);
3031 const name_slice = strings_ni.slice(&coff.mf)[offset..][0 .. name.len + 1];
3032 @memcpy(name_slice[0..name.len], name);
3033 name_slice[name.len] = 0;
3034}
3035
3036fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {
3037 const gpa = coff.base.comp.gpa;
3038 const member = mi.get(coff);
3039 assert(member.kind == .coff);
3040
3041 const gop = try member.first_linker_indices.getOrPut(gpa, .{ .mi = mi, .name = name });
3042 if (gop.found_existing) return;
3043
3044 const mfli: Member.FirstLinkerIndex = blk: {
3045 const num_symbols_ptr = coff.firstLinkerMemberNumSymbolsPtr();
3046 const num_symbols = std.mem.toNative(u32, num_symbols_ptr.*, .big);
3047 num_symbols_ptr.* = std.mem.nativeTo(u32, num_symbols + 1, .big);
3048 break :blk @fromBackingInt(@intCast(num_symbols));
3049 };
3050
3051 gop.value_ptr.* = mfli;
3052
3053 // Linker member fields are not modeled as nodes because MappedFile
3054 // can't guarantee that they will be tightly packed after resizing
3055
3056 const name_slice = name.toSlice(coff);
3057 const new_string_table_size: u32 = @intCast(coff.lib_string_len + name_slice.len + 1);
3058 defer coff.lib_string_len = new_string_table_size;
3059
3060 {
3061 const old_header_size: usize = @intCast(@sizeOf(u32) + @backingInt(mfli) * @sizeOf(u32));
3062 const new_header_size: usize = @intCast(old_header_size + @sizeOf(u32));
3063 try Node.known.first_linker_member.resizeLeaf(&coff.mf, gpa, Alignment.@"4".forward(new_header_size + new_string_table_size));
3064
3065 const slice = Node.known.first_linker_member.slice(&coff.mf);
3066 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
3067 @memcpy(slice[new_header_size + coff.lib_string_len ..][0..name_slice.len], name_slice[0..name_slice.len]);
3068 slice[new_header_size + coff.lib_string_len + name_slice.len] = 0;
3069
3070 // New offset entry is written in flushMember
3071 }
3072
3073 {
3074 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
3075 const old_header_size = 2 * @sizeOf(u32) + num_members * @sizeOf(u32) + @backingInt(mfli) * @sizeOf(u16);
3076 const new_header_size = old_header_size + @sizeOf(u16);
3077 try Node.known.second_linker_member.resizeLeaf(&coff.mf, gpa, Alignment.@"4".forward(new_header_size + new_string_table_size));
3078
3079 const old_needs_sort = coff.pending_members.get(Member.Index.second) != null;
3080 const needs_sort = old_needs_sort or (if (coff.lib_string_table.items.len > 0)
3081 std.mem.lessThan(
3082 u8,
3083 name_slice,
3084 coff.lib_string_table.items[coff.lib_string_table.items.len - 1].toSlice(coff),
3085 )
3086 else
3087 false);
3088
3089 try coff.lib_string_table.append(gpa, name);
3090
3091 const slice = Node.known.second_linker_member.slice(&coff.mf);
3092 coff.targetStore(coff.secondLinkerMemberNumSymbolsPtr(), @backingInt(mfli) + 1);
3093 if (!needs_sort) {
3094 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
3095 @memcpy(slice[new_header_size + coff.lib_string_len ..][0..name_slice.len], name_slice[0..name_slice.len]);
3096 slice[new_header_size + coff.lib_string_len + name_slice.len] = 0;
3097 } else if (!old_needs_sort) {
3098 // The entire string table is rebuilt in flushMember after sorting
3099 coff.pending_members.putAssumeCapacity(Member.Index.second, {});
3100 }
3101
3102 // Indices in this table are 1-based
3103 const index_ptr: *u16 = @ptrCast(@alignCast(slice[old_header_size..]));
3104 coff.targetStore(index_ptr, @intCast(@backingInt(mi) - Member.Index.known_count + 1));
3105 }
3106
3107 coff.pending_members.putAssumeCapacity(mi, {});
3108 coff.member_prog_node.increaseEstimatedTotalItems(1);
3109}
3110
3111fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {
3112 assert(!coff.isImage());
3113 const gpa = coff.base.comp.gpa;
3114
3115 const si = coff.symbol_table.symbols.keys()[index];
3116 const sti = &coff.symbol_table.symbols.values()[index];
3117
3118 const sym = si.get(coff);
3119 assert(sym.ni != .none or sym.gmi != .none);
3120
3121 const entry = coff.symbolTableEntryPtr(sti.*) orelse entry: {
3122 var buf: [15]u8 = undefined;
3123 const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
3124 if (sym.gmi != .none) blk: {
3125 const name = sym.gmi.name(coff);
3126 break :blk .{
3127 try coff.getOrPutSymbolName(name.toSlice(coff), name),
3128 @intFromBool(sym.flags.weak_external_strat != .none),
3129 if (Symbol.Index.text.get(coff).section_number == sym.section_number)
3130 .FUNCTION
3131 else
3132 .NULL,
3133 };
3134 } else blk: switch (coff.getNode(sym.ni.unwrap().?)) {
3135 .image_section => .{
3136 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),
3137 1,
3138 .NULL,
3139 },
3140 .nav => |nmi| {
3141 const zcu = coff.base.comp.zcu.?;
3142 const ip = &zcu.intern_pool;
3143 const nav = ip.getNav(nmi.navIndex(coff));
3144 break :blk .{
3145 try coff.getOrPutSymbolName(nav.fqn.toSlice(ip), null),
3146 0,
3147 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,
3148 };
3149 },
3150 .uav => |umi| {
3151 var w = Io.Writer.fixed(&buf);
3152 w.print("__anon_{x}", .{umi.uavValue(coff)}) catch unreachable;
3153 break :blk .{
3154 try coff.getOrPutSymbolName(w.buffered(), null),
3155 0,
3156 .NULL,
3157 };
3158 },
3159 inline .lazy_code, .lazy_const_data => |mi, tag| {
3160 const lazy_sym = mi.lazySymbol(coff);
3161 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
3162 @tagName(lazy_sym.kind),
3163 Type.fromInterned(lazy_sym.ty).fmt(pt),
3164 });
3165 defer gpa.free(name);
3166
3167 const string = try coff.getOrPutString(name);
3168 break :blk .{
3169 try coff.getOrPutSymbolName(string.toSlice(coff), string),
3170 0,
3171 if (tag == .lazy_code) .FUNCTION else .NULL,
3172 };
3173 },
3174 else => {
3175 log.err("TODO implement symbol table init for {s} ({d})", .{ @tagName(coff.getNode(sym.ni.unwrap().?)), si });
3176 unreachable;
3177 },
3178 };
3179
3180 const old_num_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
3181 const new_num_symbols = old_num_symbols + 1 + num_aux_symbols;
3182 coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);
3183
3184 try coff.symbol_table.ni.resizeLeaf(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());
3185
3186 sti.* = .wrap(old_num_symbols);
3187 si.flushSymbolTableIndex(coff);
3188
3189 const entry = coff.symbolTableEntryPtr(sti.*).?;
3190 symbol_name.store(coff, &entry.name);
3191
3192 entry.section_number = @fromBackingInt(@intCast(@backingInt(sym.section_number)));
3193 entry.type = .{
3194 .complex_type = complex_type,
3195 .base_type = .NULL,
3196 };
3197
3198 entry.storage_class = if (sym.gmi != .none)
3199 .EXTERNAL
3200 else if (sym.flags.extra_tag == .next_alias_si) storage: {
3201 var alias_sym = sym;
3202 const weak_external = while (alias_sym.flags.extra_tag == .next_alias_si) {
3203 const alias_si = alias_sym.extra.next_alias_si;
3204 alias_sym = alias_si.get(coff);
3205 assert(alias_sym.ni == sym.ni);
3206 if (alias_sym.flags.weak_external_strat != .none)
3207 break true;
3208 } else false;
3209 break :storage if (weak_external) .EXTERNAL else .STATIC;
3210 } else .STATIC;
3211
3212 entry.number_of_aux_symbols = num_aux_symbols;
3213 if (coff.targetEndian() != native_endian)
3214 std.mem.byteSwapAllFieldsAligned(std.coff.Symbol, .@"2", entry);
3215
3216 if (num_aux_symbols > 0) aux_init: {
3217 if (sym.gmi != .none) {
3218 entry.section_number = .UNDEFINED;
3219 entry.storage_class = .WEAK_EXTERNAL;
3220
3221 const tag_index = sym.value.weak_alias_si.sti(coff).unwrap().?;
3222 const aux_ptr = coff.symbolTableWeakExternalAuxEntryPtr(sti.*).?;
3223 aux_ptr.* = .{
3224 .tag_index = tag_index,
3225 .flag = switch (sym.flags.weak_external_strat) {
3226 .none => unreachable,
3227 .no_library => .SEARCH_NOLIBRARY,
3228 .library => .SEARCH_LIBRARY,
3229 .alias => .SEARCH_ALIAS,
3230 .anti_dependency => .ANTI_DEPENDENCY,
3231 },
3232 .unused = @splat(0),
3233 };
3234 if (coff.targetEndian() != native_endian)
3235 std.mem.byteSwapAllFieldsAligned(std.coff.WeakExternalDefinition, .@"2", aux_ptr);
3236
3237 break :aux_init;
3238 } else switch (coff.getNode(sym.ni.unwrap().?)) {
3239 .image_section => |sec_si| {
3240 assert(si == sec_si);
3241 const header = sym.section_number.header(coff);
3242 const aux_ptr = coff.symbolTableSectionAuxEntryPtr(sti.*).?;
3243 aux_ptr.* = .{
3244 .length = @intCast(sym.ni.unwrap().?.location(&coff.mf).resolve(&coff.mf)[1]),
3245 .number_of_relocations = header.number_of_relocations,
3246 .number_of_linenumbers = header.number_of_linenumbers,
3247 .checksum = 0,
3248 .number = 0,
3249 .selection = .NONE,
3250 .unused = @splat(0),
3251 };
3252 if (coff.targetEndian() != native_endian)
3253 std.mem.byteSwapAllFieldsAligned(std.coff.SectionDefinition, .@"2", aux_ptr);
3254
3255 break :aux_init;
3256 },
3257 else => {},
3258 }
3259
3260 unreachable;
3261 }
3262
3263 break :entry entry;
3264 };
3265
3266 coff.targetStore(&entry.value, switch (sym.section_number) {
3267 .UNDEFINED => if (entry.storage_class == .WEAK_EXTERNAL) 0 else sym.size(),
3268 .ABSOLUTE,
3269 .DEBUG,
3270 => unreachable,
3271 else => switch (coff.getNode(sym.ni.unwrap().?)) {
3272 .image_section => 0,
3273 else => coff.computeSymbolSectionOffset(sym, .image),
3274 },
3275 });
3276
3277 log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sti.* });
3278}
3279
3280fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
3281 const member = iami.member(coff);
3282 assert(!member.flags.is_loaded);
3283 defer member.flags.is_loaded = true;
3284 switch (member.content) {
3285 .import => unreachable,
3286 .object => |file_location| {
3287 if (file_location.size == 0) return;
3288 const comp = coff.base.comp;
3289 const io = comp.io;
3290 const path = member.iai.path(coff);
3291 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
3292 defer file.close(io);
3293 var buffer: [4096]u8 = undefined;
3294 var fr = file.reader(io, &buffer);
3295 const offset = file_location.offset + @sizeOf(std.coff.ArchiveMemberHeader);
3296 try fr.seekTo(offset);
3297 log.debug("flushInputMember({f}({s}))", .{ path, member.name.toSlice(coff) });
3298 try coff.loadObject(path, member.name.toSlice(coff), &fr, .{
3299 .offset = offset,
3300 .size = file_location.size,
3301 });
3302 },
3303 }
3304}
3305
3306fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
3307 const file_loc = isi.fileLocation(coff);
3308 if (file_loc.size == 0) return;
3309 const comp = coff.base.comp;
3310 const io = comp.io;
3311 const gpa = comp.gpa;
3312 const ioi = isi.input(coff);
3313 const path = ioi.path(coff);
3314 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
3315 defer file.close(io);
3316 var fr = file.reader(io, &.{});
3317 try fr.seekTo(file_loc.offset);
3318 var nw: MappedFile.Node.Writer = undefined;
3319 const si = isi.symbol(coff);
3320 si.node(coff).writer(&coff.mf, gpa, &nw);
3321 defer nw.deinit();
3322 log.debug("flushInputSection({f}{f}, {s}, {d}, n{d})", .{
3323 path,
3324 fmtMemberNameString(ioi.memberName(coff)),
3325 si.get(coff).section_number.name(coff).toSlice(coff),
3326 si,
3327 si.node(coff),
3328 });
3329 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
3330 return error.EndOfStream;
3331 try si.applyLocationRelocs(coff);
3332}
3333
3334fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
3335 assert(coff.hasCoffHeader());
3336
3337 const gpa = coff.base.comp.gpa;
3338 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3339 try coff.section_table.ensureUnusedCapacity(gpa, 1);
3340 try coff.symbols.ensureUnusedCapacity(gpa, 1);
3341 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
3342
3343 const coff_header = coff.headerPtr();
3344 const section_index = coff.targetLoad(&coff_header.number_of_sections);
3345 const section_table_len = section_index + 1;
3346 coff.targetStore(&coff_header.number_of_sections, section_table_len);
3347 try Node.known.section_table.resizeLeaf(
3348 &coff.mf,
3349 gpa,
3350 @sizeOf(std.coff.SectionHeader) * section_table_len,
3351 );
3352
3353 const ni = try coff.sectionParent().addFloatingChild(&coff.mf, gpa, .{
3354 .alignment = coff.mf.flags.block_size,
3355 .moved = true,
3356 .bubbles_moved = false,
3357 });
3358
3359 const si = coff.addSymbolAssumeCapacity();
3360 coff.section_table.putAssumeCapacity(name, .{
3361 .si = si,
3362 .relocation_table_ni = .none,
3363 });
3364 coff.nodes.appendAssumeCapacity(.{ .image_section = si });
3365 const section_table = coff.sectionTableSlice();
3366
3367 const virtual_size, const rva = if (coff.isImage()) block: {
3368 const virtual_size = coff.optionalHeaderField(.section_alignment);
3369 const rva: u32 = switch (section_index) {
3370 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
3371 else => coff.section_table.values()[section_index - 1].si.get(coff).rva +
3372 coff.targetLoad(&section_table[section_index - 1].virtual_size),
3373 };
3374
3375 break :block .{ virtual_size, rva };
3376 } else .{ 0, 0 };
3377
3378 {
3379 const sym = si.get(coff);
3380 sym.ni = .wrap(ni);
3381 sym.rva = rva;
3382 sym.section_number = @fromBackingInt(@intCast(section_table_len));
3383 }
3384 const section = &section_table[section_index];
3385 section.* = .{
3386 .name = undefined,
3387 .virtual_size = virtual_size,
3388 .virtual_address = rva,
3389 .size_of_raw_data = 0,
3390 .pointer_to_raw_data = 0,
3391 .pointer_to_relocations = 0,
3392 .pointer_to_linenumbers = 0,
3393 .number_of_relocations = 0,
3394 .number_of_linenumbers = 0,
3395 .flags = flags,
3396 };
3397 if (coff.targetEndian() != native_endian)
3398 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
3399
3400 const name_slice = name.toSlice(coff);
3401 if (coff.isImage()) {
3402 @memcpy(section.name[0..name_slice.len], name_slice);
3403 @memset(section.name[name_slice.len..], 0);
3404 switch (coff.optionalHeaderPtr()) {
3405 inline else => |optional_header| coff.targetStore(
3406 &optional_header.size_of_image,
3407 @intCast(rva + virtual_size),
3408 ),
3409 }
3410 } else {
3411 (try coff.getOrPutSymbolName(name_slice, name)).store(coff, &section.name);
3412 try coff.pendingSymbolTableEntry(si);
3413 }
3414
3415 return si;
3416}
3417
3418const ObjectSectionAttributes = packed struct {
3419 read: bool = false,
3420 write: bool = false,
3421 execute: bool = false,
3422 shared: bool = false,
3423 nopage: bool = false,
3424 nocache: bool = false,
3425 discard: bool = false,
3426 remove: bool = false,
3427 initialized: bool = false,
3428 uninitialized: bool = false,
3429
3430 pub fn fromFlags(flags: std.coff.SectionHeader.Flags) ObjectSectionAttributes {
3431 return .{
3432 .read = flags.MEM_READ,
3433 .write = flags.MEM_WRITE,
3434 .execute = flags.MEM_EXECUTE,
3435 .shared = flags.MEM_SHARED,
3436 .nopage = flags.MEM_NOT_PAGED,
3437 .nocache = flags.MEM_NOT_CACHED,
3438 .discard = flags.MEM_DISCARDABLE,
3439 .remove = flags.LNK_REMOVE,
3440 .initialized = flags.CNT_INITIALIZED_DATA,
3441 .uninitialized = flags.CNT_UNINITIALIZED_DATA,
3442 };
3443 }
3444
3445 pub fn asFlags(attr: ObjectSectionAttributes) std.coff.SectionHeader.Flags {
3446 return .{
3447 .MEM_READ = attr.read,
3448 .MEM_WRITE = attr.write,
3449 .MEM_EXECUTE = attr.execute,
3450 .MEM_SHARED = attr.shared,
3451 .MEM_NOT_PAGED = attr.nopage,
3452 .MEM_NOT_CACHED = attr.nocache,
3453 .MEM_DISCARDABLE = attr.discard,
3454 .LNK_REMOVE = attr.remove,
3455 .CNT_INITIALIZED_DATA = attr.uninitialized,
3456 .CNT_UNINITIALIZED_DATA = attr.uninitialized,
3457 };
3458 }
3459};
3460
3461fn pseudoSectionMapIndex(
3462 coff: *Coff,
3463 name: String,
3464 alignment: Alignment,
3465 attributes: ObjectSectionAttributes,
3466) !Node.PseudoSectionMapIndex {
3467 const gpa = coff.base.comp.gpa;
3468 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);
3469 const psmi: Node.PseudoSectionMapIndex = @fromBackingInt(@intCast(pseudo_section_gop.index));
3470 const parent_sn = if (!pseudo_section_gop.found_existing) sn: {
3471 const effective_name = coff.section_merges.get(name) orelse name;
3472 const parent = if (coff.section_table.get(effective_name)) |existing_sec|
3473 existing_sec.si
3474 else if (coff.isImage()) parent: {
3475 const parent: Symbol.Index = if (attributes.uninitialized)
3476 .bss
3477 else if (attributes.execute)
3478 .text
3479 else if (attributes.write)
3480 .data
3481 else
3482 .rdata;
3483
3484 break :parent parent;
3485 } else try coff.addSection(effective_name, attributes.asFlags());
3486
3487 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3488 try coff.symbols.ensureUnusedCapacity(gpa, 1);
3489 const ni = try parent.node(coff).addFloatingChild(&coff.mf, gpa, .{ .alignment = alignment });
3490 const si = coff.addSymbolAssumeCapacity();
3491 pseudo_section_gop.value_ptr.* = si;
3492 const sym = si.get(coff);
3493 sym.ni = .wrap(ni);
3494 sym.rva = coff.computeNodeRva(ni);
3495 sym.section_number = parent.get(coff).section_number;
3496 assert(sym.loc_relocs == .none);
3497 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
3498 coff.nodes.appendAssumeCapacity(.{ .pseudo_section = psmi });
3499 break :sn sym.section_number;
3500 } else pseudo_section_gop.value_ptr.get(coff).section_number;
3501
3502 try coff.verifyParentSectionAttributes(
3503 parent_sn,
3504 name,
3505 .pseudo,
3506 .fromFlags(parent_sn.header(coff).flags),
3507 attributes,
3508 );
3509
3510 return psmi;
3511}
3512
3513fn objectSectionParentName(coff: *Coff, name: []const u8) []const u8 {
3514 // In images we want to sort object sections into the final root section name.
3515 // Otherwise, we want to keep the full name so that this sort can occur correctly when
3516 // the object is finally linked into an image.
3517 return if (coff.isImage())
3518 name[0 .. std.mem.findScalar(u8, name, '$') orelse name.len]
3519 else
3520 name;
3521}
3522
3523fn objectSectionMapIndex(
3524 coff: *Coff,
3525 name: String,
3526 alignment: Alignment,
3527 attributes: ObjectSectionAttributes,
3528) !Node.ObjectSectionMapIndex {
3529 const gpa = coff.base.comp.gpa;
3530 const name_slice = name.toSlice(coff);
3531 // TODO: Should this be a section merge instead?
3532 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name_slice, ".tls")) attr: {
3533 // In images, the .tls section is a read-only template
3534 var attr = attributes;
3535 attr.write = false;
3536 break :attr attr;
3537 } else attributes;
3538
3539 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
3540 const osmi: Node.ObjectSectionMapIndex = @fromBackingInt(@intCast(object_section_gop.index));
3541 const sym = if (!object_section_gop.found_existing) sym: {
3542 try coff.ensureUnusedStringCapacity(name_slice.len);
3543 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
3544 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
3545 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3546 try coff.symbols.ensureUnusedCapacity(gpa, 1);
3547 const parent_ni = parent.node(coff);
3548 var prev_oni: MappedFile.Node.Index.Optional = .none;
3549 {
3550 var child_oni = parent_ni.first(&coff.mf);
3551 while (child_oni.unwrap()) |child_ni| : (child_oni = child_ni.next(&coff.mf)) {
3552 switch (std.mem.order(
3553 u8,
3554 name_slice,
3555 coff.getNode(child_ni).object_section.name(coff).toSlice(coff),
3556 )) {
3557 .lt => break,
3558 .eq => unreachable,
3559 .gt => prev_oni = .wrap(child_ni),
3560 }
3561 }
3562 }
3563 const ni = try parent_ni.addHeaderChildAfter(&coff.mf, gpa, prev_oni, .{
3564 .alignment = alignment,
3565 });
3566 const si = coff.addSymbolAssumeCapacity();
3567 object_section_gop.value_ptr.* = si;
3568 const sym = si.get(coff);
3569 sym.ni = .wrap(ni);
3570 sym.rva = coff.computeNodeRva(ni);
3571 sym.section_number = parent.get(coff).section_number;
3572 assert(sym.loc_relocs == .none);
3573 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
3574 coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
3575 break :sym sym;
3576 } else object_section_gop.value_ptr.get(coff);
3577
3578 const parent_ni = sym.ni.unwrap().?.parent(&coff.mf).unwrap().?;
3579 const parent_alignment = parent_ni.alignment(&coff.mf);
3580 if (alignment.compare(.gt, parent_alignment)) {
3581 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });
3582 try parent_ni.realign(&coff.mf, gpa, alignment);
3583 }
3584
3585 const old_alignment = sym.ni.unwrap().?.alignment(&coff.mf);
3586 if (alignment.compare(.gt, old_alignment)) {
3587 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });
3588 try sym.ni.unwrap().?.realign(&coff.mf, gpa, alignment);
3589 }
3590
3591 try coff.verifyParentSectionAttributes(
3592 sym.section_number,
3593 name,
3594 .object,
3595 .fromFlags(sym.section_number.header(coff).flags),
3596 effective_attributes,
3597 );
3598
3599 return osmi;
3600}
3601
3602fn verifyParentSectionAttributes(
3603 coff: *Coff,
3604 parent: Symbol.SectionNumber,
3605 child_name: String,
3606 child_kind: enum { pseudo, object },
3607 parent_attrs: ObjectSectionAttributes,
3608 child_attrs: ObjectSectionAttributes,
3609) !void {
3610 if (parent_attrs == child_attrs) return;
3611
3612 const was_merged = switch (child_kind) {
3613 .pseudo => coff.section_merges.contains(child_name),
3614 .object => if (coff.getString(
3615 coff.objectSectionParentName(child_name.toSlice(coff)),
3616 ).unwrap()) |pseudo_name|
3617 coff.section_merges.contains(pseudo_name)
3618 else
3619 false,
3620 };
3621
3622 // The section was intentionally merged by the user or builtin rule
3623 if (was_merged) return;
3624
3625 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;
3626 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));
3627 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
3628 try err.addMsg("{t} section '{s}' was placed in parent section '{s}' with mismatched flags", .{
3629 child_kind,
3630 child_name.toSlice(coff),
3631 parent.name(coff).toSlice(coff),
3632 });
3633
3634 inline for (@typeInfo(ObjectSectionAttributes).@"struct".field_names) |field| {
3635 if (@field(child_attrs, field) != @field(parent_attrs, field)) {
3636 err.addNote("flags.{s} was {d} in {s}, but {d} in {s}", .{
3637 field,
3638 @intFromBool(@field(child_attrs, field)),
3639 child_name.toSlice(coff),
3640 @intFromBool(@field(parent_attrs, field)),
3641 parent.name(coff).toSlice(coff),
3642 });
3643 }
3644 }
3645
3646 return error.AlreadyReported;
3647}
3648
3649const RelocAddend = union(enum) {
3650 known: i64,
3651 /// Relocs tables in input objects don't include the addend.
3652 /// The value needs to be recovered from the reloc location.
3653 pending: void,
3654};
3655
3656// TODO: There should be an API where the caller can indicate how many contiguous relocs they need
3657// and it should attempt to allocate these from from the free list if available. We can cache
3658// the run length of each segment on Reloc when `free` is set.
3659pub fn addReloc(
3660 coff: *Coff,
3661 loc_si: Symbol.Index,
3662 offset: u64,
3663 target_si: Symbol.Index,
3664 addend: RelocAddend,
3665 @"type": Reloc.Type,
3666) link.Error!void {
3667 const diags = &coff.base.comp.link_diags;
3668 try coff.ensureUnusedRelocCapacity(loc_si, 1);
3669 coff.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type") catch |err| switch (err) {
3670 error.MappedFileIo => return diags.fail(
3671 "failed to write output file: {t}",
3672 .{coff.mf.io_err.?},
3673 ),
3674 else => |e| return e,
3675 };
3676}
3677
3678fn ensureUnusedRelocCapacity(coff: *Coff, loc_si: Symbol.Index, len: usize) !void {
3679 const gpa = coff.base.comp.gpa;
3680 try coff.relocs.ensureUnusedCapacity(gpa, len);
3681 if (isImage(coff)) return;
3682 switch (loc_si.get(coff).section_number) {
3683 .UNDEFINED, .ABSOLUTE, .DEBUG => {},
3684 else => |loc_sn| {
3685 const section = loc_sn.section(coff);
3686 if (section.relocation_table_ni == .none)
3687 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3688 },
3689 }
3690}
3691
3692fn addRelocAssumeCapacity(
3693 coff: *Coff,
3694 loc_si: Symbol.Index,
3695 offset: u64,
3696 target_si: Symbol.Index,
3697 addend: RelocAddend,
3698 @"type": Reloc.Type,
3699) !void {
3700 const gpa = coff.base.comp.gpa;
3701 const target = target_si.get(coff);
3702
3703 const ri: Reloc.Index = @fromBackingInt(@intCast(coff.relocs.items.len));
3704 log.debug("addReloc({d}@{d}+0x{x} -> {d}@{d}+0x{x}{s}) = {d}", .{
3705 loc_si,
3706 loc_si.get(coff).section_number,
3707 offset,
3708 target_si,
3709 target_si.get(coff).section_number,
3710 if (addend == .pending) 0 else addend.known,
3711 if (addend == .pending) "p" else "k",
3712 ri,
3713 });
3714
3715 const sri: Section.RelocationIndex = if (isImage(coff))
3716 .none
3717 else switch (loc_si.get(coff).section_number) {
3718 .UNDEFINED,
3719 .ABSOLUTE,
3720 .DEBUG,
3721 => .none,
3722 else => |loc_sn| sri: {
3723 // The target may not have a node yet, or it could be an extern that will never
3724 // have a node. In that case, flushGlobal will create the symbol table entry.
3725 const existing_sti = target_si.sti(coff);
3726 const sti: SymbolTable.Index = if (existing_sti != .none)
3727 existing_sti
3728 else if (target.ni != .none) sti: {
3729 try coff.pendingSymbolTableEntry(target_si);
3730 break :sti .none;
3731 } else .none;
3732
3733 const sri: Section.RelocationIndex = blk: {
3734 const section = loc_sn.section(coff);
3735 const header = loc_sn.header(coff);
3736 const old_num_relocations = coff.targetLoad(&header.number_of_relocations);
3737 const new_num_relocations = old_num_relocations + 1;
3738 const new_size = @as(u32, new_num_relocations) * std.coff.Relocation.sizeOf();
3739
3740 coff.targetStore(&header.number_of_relocations, new_num_relocations);
3741 if (coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff).sti(coff))) |aux_ptr|
3742 coff.targetStore(&aux_ptr.number_of_relocations, new_num_relocations);
3743
3744 if (section.relocation_table_ni.unwrap()) |relocation_table_ni| {
3745 try relocation_table_ni.resizeLeaf(&coff.mf, gpa, new_size);
3746 } else {
3747 section.relocation_table_ni = .wrap(try coff.sectionParent().addFloatingChild(&coff.mf, gpa, .{
3748 .size = new_size,
3749 .alignment = .@"2",
3750 .moved = true,
3751 .resized = true,
3752 }));
3753 coff.nodes.appendAssumeCapacity(.{ .relocation_table = loc_sn });
3754 }
3755
3756 // TODO: These need to allocate from a free list, once deleting relocs from the table is supported
3757 break :blk .wrap(old_num_relocations);
3758 };
3759
3760 const entry = sri.entry(coff, loc_sn).?;
3761 if (sti.unwrap()) |index| coff.targetStore(&entry.symbol_table_index, index);
3762
3763 // applyLocationRelocs updates `virtual_address`
3764 // flushSymbolTableIndex updates `symbol_table_index`
3765 coff.targetStore(&entry.type, @"type".u16);
3766
3767 break :sri sri;
3768 },
3769 };
3770
3771 coff.relocs.addOneAssumeCapacity().* = .{
3772 .type = @"type",
3773 .prev = .none,
3774 .next = target.target_relocs,
3775 .loc = loc_si,
3776 .target = target_si,
3777 .sri = sri,
3778 .offset = offset,
3779 .addend = if (addend == .pending) 0 else addend.known,
3780 .flags = .{
3781 .recover_addend = addend == .pending,
3782 .free = false,
3783 },
3784 };
3785 switch (target.target_relocs) {
3786 .none => {},
3787 else => |target_ri| target_ri.get(coff).prev = ri,
3788 }
3789 target.target_relocs = ri;
3790}
3791
3792fn failLoadInput(
3793 coff: *Coff,
3794 err: LoadInputError,
3795 fr: *Io.File.Reader,
3796 path: std.Build.Cache.Path,
3797) link.Error {
3798 const diags = &coff.base.comp.link_diags;
3799 switch (err) {
3800 else => |e| return e,
3801 error.MappedFileIo => return diags.fail(
3802 "failed to write output file: {t}",
3803 .{coff.mf.io_err.?},
3804 ),
3805 error.EndOfStream => return diags.failParse(
3806 path,
3807 "unexpected eof",
3808 .{},
3809 ),
3810 error.AccessDenied,
3811 error.Unexpected,
3812 error.Unseekable,
3813 => |e| return diags.fail(
3814 "failed to read \"{f}\": {t}",
3815 .{ path.fmtEscapeString(), e },
3816 ),
3817 error.PermissionDenied,
3818 error.SystemResources,
3819 error.Streaming,
3820 => |e| return diags.fail(
3821 "failed to stat \"{f}\": {t}",
3822 .{ path.fmtEscapeString(), e },
3823 ),
3824 error.ReadFailed => switch (fr.err.?) {
3825 error.Canceled => |e| return e,
3826 else => |e| return diags.fail(
3827 "failed to read \"{f}\": {t}",
3828 .{ path.fmtEscapeString(), e },
3829 ),
3830 },
3831 }
3832}
3833
3834pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3835 const comp = coff.base.comp;
3836 const io = comp.io;
3837
3838 const path = input.path() orelse unreachable;
3839 const gop = try coff.inputs.getOrPut(comp.gpa, path);
3840 if (gop.found_existing) return;
3841 errdefer _ = coff.inputs.swapRemove(path);
3842
3843 var buf: [4096]u8 = undefined;
3844 switch (input) {
3845 .object => |object| {
3846 var fr = object.file.reader(io, &buf);
3847 coff.loadObject(object.path, null, &fr, .{
3848 .offset = fr.logicalPos(),
3849 .size = fr.getSize() catch |err|
3850 return coff.failLoadInput(err, &fr, object.path),
3851 }) catch |err| return coff.failLoadInput(err, &fr, object.path);
3852 },
3853 .archive => |archive| {
3854 var fr = archive.file.reader(io, &buf);
3855 coff.loadArchive(archive.path, &fr) catch |err|
3856 return coff.failLoadInput(err, &fr, archive.path);
3857 },
3858 .res => |res| {
3859 var fr = res.file.reader(io, &buf);
3860 coff.loadRes(res.path, &fr) catch |err|
3861 return coff.failLoadInput(err, &fr, res.path);
3862 },
3863 .dso => |dso| {
3864 var fr = dso.file.reader(io, &buf);
3865 coff.loadDll(dso.path, &fr) catch |err|
3866 return coff.failLoadInput(err, &fr, dso.path);
3867 },
3868 .dso_exact => unreachable,
3869 }
3870}
3871
3872fn fmtMemberNameString(memberName: ?[]const u8) std.fmt.Alt(?[]const u8, memberNameStringEscape) {
3873 return .{ .data = memberName };
3874}
3875
3876fn memberNameStringEscape(memberName: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
3877 try w.print("({f})", .{std.zig.fmtString(memberName orelse return)});
3878}
3879
3880fn inputSectionHeaderNameSlice(
3881 coff: *Coff,
3882 header: *const std.coff.SectionHeader,
3883 string_table: []const u8,
3884 path: std.Build.Cache.Path,
3885 section_i: usize,
3886) ![]const u8 {
3887 const diags = &coff.base.comp.link_diags;
3888 return if (header.name[0] == '/') name: {
3889 const offset_str = std.mem.sliceTo(header.name[1..], 0);
3890 const name_offset = std.fmt.parseUnsigned(u24, offset_str, 10) catch
3891 return diags.failParse(path, "ill-formed section name in section {d}: '{s}'", .{
3892 section_i,
3893 header.name[0 .. offset_str.len + 1],
3894 });
3895
3896 if (name_offset > string_table.len)
3897 return diags.failParse(path, "out-of-bounds section name offset in section {d}: {d}", .{ section_i, name_offset });
3898
3899 break :name std.mem.sliceTo(string_table[name_offset..], 0);
3900 } else std.mem.sliceTo(&header.name, 0);
3901}
3902
3903fn loadObject(
3904 coff: *Coff,
3905 path: std.Build.Cache.Path,
3906 member_name: ?[]const u8,
3907 fr: *Io.File.Reader,
3908 fl: MappedFile.Node.FileLocation,
3909) LoadInputError!void {
3910 const comp = coff.base.comp;
3911 const gpa = comp.gpa;
3912 const diags = &comp.link_diags;
3913 const r = &fr.interface;
3914 const target = &comp.root_mod.resolved_target.result;
3915 const target_endian = coff.targetEndian();
3916 const is_archive = coff.isArchive();
3917 assert(!coff.isObj());
3918 // We want to evaluate new merges as we see them in .drectve sections to avoid redundant work
3919 assert(coff.section_merge_pending_index == coff.section_merges.count());
3920
3921 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberNameString(member_name) });
3922
3923 const header = try r.peekStruct(std.coff.Header, .little);
3924 if (header.machine != target.toCoffMachine())
3925 return diags.failParse(path, "machine mismatch: expected {t}, found {t}", .{
3926 target.toCoffMachine(),
3927 header.machine,
3928 });
3929 if (header.number_of_sections == 0) return;
3930 if (@sizeOf(std.coff.Header) + @as(usize, header.number_of_sections) * @sizeOf(std.coff.SectionHeader) > fl.size)
3931 return diags.failParse(path, "invalid section table", .{});
3932 const unexpected_header_flags: []const std.meta.FieldEnum(std.coff.Header.Flags) = &.{
3933 .RELOCS_STRIPPED,
3934 .EXECUTABLE_IMAGE,
3935 .AGGRESSIVE_WS_TRIM,
3936 .RESERVED,
3937 .BYTES_REVERSED_LO,
3938 .DLL,
3939 .BYTES_REVERSED_HI,
3940 };
3941 inline for (unexpected_header_flags) |flag|
3942 if (@field(header.flags, @tagName(flag)))
3943 return diags.failParse(path, "unexpected flag set: {t}", .{flag});
3944
3945 if (header.size_of_optional_header != 0)
3946 return diags.failParse(path, "unexpected optional header", .{});
3947
3948 const symbol_table_len = header.number_of_symbols * std.coff.Symbol.sizeOf();
3949 const symbol_table_end = header.pointer_to_symbol_table + symbol_table_len;
3950 // String table length (which includes the length field) immediately trails the symbol table
3951 if (symbol_table_end + @sizeOf(u32) > fl.size)
3952 return diags.failParse(path, "bad symbol table location", .{});
3953
3954 try fr.seekTo(fl.offset + symbol_table_end);
3955 const string_table_len = try r.peekInt(u32, target_endian);
3956 if (string_table_len < @sizeOf(u32) or
3957 symbol_table_end + string_table_len > fl.size)
3958 return diags.failParse(path, "bad string table length: 0x{x}", .{string_table_len});
3959
3960 const ioi: InputObject.Index = @fromBackingInt(@intCast(coff.input_objects.items.len));
3961 try coff.input_objects.ensureUnusedCapacity(gpa, 1);
3962 const input = coff.input_objects.addOneAssumeCapacity();
3963 input.* = .{
3964 .path = path,
3965 .member_name = if (member_name) |m| try gpa.dupe(u8, m) else null,
3966 .source_name = .none,
3967 };
3968
3969 const string_table = string_table: {
3970 const string_table = try gpa.alloc(u8, string_table_len);
3971 errdefer gpa.free(string_table);
3972 try r.readSliceAll(string_table);
3973 break :string_table string_table;
3974 };
3975 defer gpa.free(string_table);
3976
3977 try coff.ensureManyUnusedStringCapacity(
3978 header.number_of_sections + header.number_of_symbols,
3979 header.number_of_sections * 9 +
3980 header.number_of_symbols * 9 +
3981 string_table_len - @sizeOf(u32),
3982 );
3983
3984 const PendingSymbolIndex = enum(u32) {
3985 none,
3986 _,
3987
3988 pub fn wrap(i: ?u32) @This() {
3989 return @fromBackingInt(@intCast((i orelse return .none) + 1));
3990 }
3991
3992 pub fn unwrap(i: @This()) ?u32 {
3993 return switch (i) {
3994 .none => null,
3995 _ => @backingInt(i) - 1,
3996 };
3997 }
3998 };
3999
4000 const PendingInputSection = struct {
4001 header: std.coff.SectionHeader,
4002 name: String,
4003 si: Symbol.Index,
4004 parent_si: Symbol.Index,
4005 psi: PendingSymbolIndex,
4006 num_symbols: u32,
4007 comdat: std.coff.ComdatSelection,
4008 comdat_psi: PendingSymbolIndex,
4009 comdat_crc: u32,
4010 comdat_association: Symbol.SectionNumber,
4011 comdat_result: union(enum) {
4012 pending,
4013 // Root of the association chain
4014 pending_association: Symbol.SectionNumber,
4015 include,
4016 skip,
4017 },
4018 };
4019
4020 const sections: []PendingInputSection = if (coff.isImage()) sections: {
4021 const sections = try gpa.alloc(PendingInputSection, header.number_of_sections);
4022 errdefer gpa.free(sections);
4023
4024 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));
4025 for (sections, 0..) |*section, section_i| {
4026 section.* = .{
4027 .header = try r.takeStruct(std.coff.SectionHeader, target_endian),
4028 .name = undefined,
4029 .si = .null,
4030 .parent_si = .null,
4031 .psi = .none,
4032 .num_symbols = 0,
4033 .comdat = .NONE,
4034 .comdat_psi = .none,
4035 .comdat_crc = 0,
4036 .comdat_association = .UNDEFINED,
4037 .comdat_result = .pending,
4038 };
4039
4040 const section_name_slice = if (section.header.name[0] == '/') name: {
4041 const offset_str = std.mem.sliceTo(section.header.name[1..], 0);
4042 const name_offset = std.fmt.parseUnsigned(u24, offset_str, 10) catch
4043 return diags.failParse(path, "ill-formed section name offset in section {d}: '{s}'", .{
4044 section_i,
4045 section.header.name[0 .. offset_str.len + 1],
4046 });
4047
4048 if (name_offset > string_table.len)
4049 return diags.failParse(
4050 path,
4051 "out-of-bounds section name offset in section {d}: {d}",
4052 .{ section_i, name_offset },
4053 );
4054
4055 break :name std.mem.sliceTo(string_table[name_offset..], 0);
4056 } else std.mem.sliceTo(&section.header.name, 0);
4057 section.name = coff.getOrPutStringAssumeCapacity(section_name_slice);
4058
4059 if (section.header.pointer_to_linenumbers +
4060 @as(u32, section.header.number_of_linenumbers) * std.coff.LineNumber.sizeOf() > fl.size)
4061 return diags.failParse(path, "bad line numbers location in section {d} `{s}`", .{
4062 section_i,
4063 section_name_slice,
4064 });
4065
4066 if (section.header.pointer_to_relocations +
4067 @as(u32, section.header.number_of_relocations) * std.coff.Relocation.sizeOf() > fl.size)
4068 return diags.failParse(path, "bad relocations location in section {d} `{s}`", .{
4069 section_i,
4070 section_name_slice,
4071 });
4072
4073 if (section.header.pointer_to_raw_data + section.header.size_of_raw_data > fl.size)
4074 return diags.failParse(path, "bad raw data location in section {d} `{s}`", .{
4075 section_i,
4076 section_name_slice,
4077 });
4078 }
4079
4080 break :sections sections;
4081 } else &.{};
4082 defer gpa.free(sections);
4083
4084 const mi = if (is_archive) mi: {
4085 try coff.nodes.ensureUnusedCapacity(gpa, 2);
4086 try coff.members.ensureUnusedCapacity(gpa, 1);
4087 const path_str = try path.toString(gpa);
4088 defer gpa.free(path_str);
4089
4090 const mi = try coff.addMemberAssumeCapacity(.coff, fl.size);
4091 const member = mi.get(coff);
4092 try member.initHeader(coff, path_str, header.time_date_stamp);
4093
4094 {
4095 // TODO: This should be deferred to an idle task (but resize it here!)
4096 var nw: MappedFile.Node.Writer = undefined;
4097 member.content_ni.writer(&coff.mf, gpa, &nw);
4098 defer nw.deinit();
4099
4100 try fr.seekTo(fl.offset);
4101 const written = nw.interface.sendFileAll(fr, .limited64(fl.size)) catch |err| switch (err) {
4102 error.WriteFailed => return nw.err.?,
4103 else => |e| return e,
4104 };
4105
4106 if (written != fl.size) return error.EndOfStream;
4107 }
4108
4109 break :mi mi;
4110 } else undefined;
4111
4112 try fr.seekTo(fl.offset + header.pointer_to_symbol_table);
4113 const symbol_size = std.coff.Symbol.sizeOf();
4114
4115 const PendingSymbol = struct {
4116 name: String,
4117 value: union(enum) {
4118 // Size of the section
4119 section: u32,
4120 // If section is absolute, the symbol value.
4121 // Otherwise, offset within the section.
4122 static: u32,
4123 // If section is undefined, the symbol size.
4124 // If section is absolute, the symbol value.
4125 // Otherwise offset within the section.
4126 external: u32,
4127 // The index of the target symbol of this weak external
4128 weak_external: u32,
4129 // Trails .weak_external
4130 weak_external_aux: WeakExternalStrat,
4131 },
4132 section_number: Symbol.SectionNumber,
4133 si: Symbol.Index,
4134 // If a weak external targets this symbol, the index of the weak external
4135 weak_external_psi: PendingSymbolIndex,
4136 };
4137
4138 var num_global_symbols: u32 = 0;
4139 var pending_symbols: std.array_hash_map.Auto(u32, PendingSymbol) = .empty;
4140 defer pending_symbols.deinit(gpa);
4141 if (!is_archive)
4142 try pending_symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
4143
4144 var section_merges: std.ArrayList(struct {
4145 from: String,
4146 to: String,
4147 }) = .empty;
4148 defer section_merges.deinit(gpa);
4149
4150 // Discover symbol names and COMDAT symbol mappings
4151 var symbol_i: u32 = 0;
4152 var num_included_symbols: u32 = 0;
4153 while (symbol_i < header.number_of_symbols) {
4154 var symbol: std.coff.Symbol = undefined;
4155 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));
4156 if (target_endian != native_endian)
4157 std.mem.byteSwapAllFields(std.coff.Symbol, &symbol);
4158
4159 const aux_symbols = if (symbol.number_of_aux_symbols > 0)
4160 try r.take(symbol_size * symbol.number_of_aux_symbols)
4161 else
4162 &.{};
4163 defer symbol_i += symbol.number_of_aux_symbols + 1;
4164
4165 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
4166 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);
4167 if (index >= string_table.len)
4168 return diags.failParse(path, "bad string offset for symbol 0x{x}", .{symbol_i});
4169 break :name string_table[index..];
4170 } else &symbol.name, 0);
4171
4172 if (is_archive) {
4173 if (switch (symbol.storage_class) {
4174 .WEAK_EXTERNAL => true,
4175 .EXTERNAL => symbol.section_number != .UNDEFINED,
4176 else => false,
4177 }) try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
4178
4179 continue;
4180 }
4181
4182 switch (symbol.section_number) {
4183 .UNDEFINED, .DEBUG, .ABSOLUTE => {},
4184 else => |sn| if (@backingInt(sn) > sections.len)
4185 return diags.failParse(path, "out-of-bounds section number {d} in symbol 0x{x}", .{ sn, symbol_i }),
4186 }
4187
4188 const psi: PendingSymbolIndex = .wrap(@intCast(pending_symbols.count()));
4189 const section_number: Symbol.SectionNumber = @fromBackingInt(@intCast(@backingInt(symbol.section_number)));
4190
4191 const values: []const @FieldType(PendingSymbol, "value") = pending_symbols: switch (symbol.storage_class) {
4192 .STATIC, .LABEL => |storage_class| switch (section_number) {
4193 // TODO: Do we need to do anything with @feat.00?
4194 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c392
4195 .UNDEFINED,
4196 .DEBUG,
4197 => &.{},
4198 .ABSOLUTE => &.{.{ .static = symbol.value }},
4199 else => |sn| {
4200 const section = &sections[sn.toIndex()];
4201
4202 // Section symbol
4203 const is_section = storage_class == .STATIC and
4204 symbol.value == 0 and
4205 symbol.type == std.coff.SymType{
4206 .complex_type = .NULL,
4207 .base_type = .NULL,
4208 } and
4209 symbol.number_of_aux_symbols > 0;
4210
4211 if (is_section) {
4212 if (symbol.number_of_aux_symbols > 1)
4213 return diags.failParse(path, "invalid number of aux symbols for section symbol 0x{x}: {d}", .{
4214 symbol_i,
4215 symbol.number_of_aux_symbols,
4216 });
4217
4218 var section_def: std.coff.SectionDefinition = undefined;
4219 @memcpy(std.mem.asBytes(&section_def)[0..symbol_size], aux_symbols[0..symbol_size]);
4220 if (target_endian != native_endian)
4221 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def);
4222
4223 if (section_def.number_of_relocations != section.header.number_of_relocations)
4224 return diags.failParse(
4225 path,
4226 "section aux symbol 0x{x} for '{s}' relocation count did not match section header: {d} vs {d}",
4227 .{ symbol_i + 1, name, section_def.number_of_relocations, section.header.number_of_relocations },
4228 );
4229
4230 if (section_def.number_of_linenumbers != section.header.number_of_linenumbers)
4231 return diags.failParse(
4232 path,
4233 "section aux symbol 0x{x} for '{s}' line number count did not match section header: {d} vs {d}",
4234 .{ symbol_i + 1, name, section_def.number_of_linenumbers, section.header.number_of_linenumbers },
4235 );
4236
4237 if (section.header.flags.LNK_COMDAT) {
4238 if (section_def.selection == .ASSOCIATIVE) {
4239 if (section_def.number == 0 or section_def.number > sections.len)
4240 return diags.failParse(
4241 path,
4242 "section aux symbol 0x{x} for '{s}' contained an invalid associated section number: 0x{x}",
4243 .{ symbol_i + 1, name, section_def.number },
4244 );
4245
4246 section.comdat_association = @fromBackingInt(@intCast(section_def.number));
4247 }
4248
4249 section.comdat = section_def.selection;
4250 section.comdat_crc = section_def.checksum;
4251 }
4252
4253 section.psi = psi;
4254 }
4255
4256 break :pending_symbols &.{if (is_section)
4257 .{ .section = section.header.size_of_raw_data }
4258 else
4259 .{ .static = symbol.value }};
4260 },
4261 },
4262 .WEAK_EXTERNAL => switch (symbol.section_number) {
4263 .UNDEFINED => {
4264 if (symbol.value != 0)
4265 return diags.failParse(
4266 path,
4267 "invalid value {d} for weak external symbol 0x{x}",
4268 .{ symbol.value, symbol_i },
4269 );
4270
4271 var weak_external: std.coff.WeakExternalDefinition = undefined;
4272 @memcpy(std.mem.asBytes(&weak_external)[0..symbol_size], aux_symbols[0..symbol_size]);
4273 if (target_endian != native_endian)
4274 std.mem.byteSwapAllFields(std.coff.WeakExternalDefinition, &weak_external);
4275
4276 if (weak_external.tag_index >= header.number_of_symbols)
4277 return diags.failParse(
4278 path,
4279 "invalid tag_index 0x{x} for weak external symbol 0x{x}",
4280 .{ weak_external.tag_index, symbol_i },
4281 );
4282
4283 break :pending_symbols switch (weak_external.flag) {
4284 else => |flag| &.{
4285 .{ .weak_external = weak_external.tag_index },
4286 .{ .weak_external_aux = WeakExternalStrat.fromFlag(flag) },
4287 },
4288 _ => return diags.failParse(
4289 path,
4290 "encountered unknown weak external characteristic 0x{x} for symbol 0x{x}",
4291 .{ weak_external.flag, symbol_i },
4292 ),
4293 };
4294 },
4295 else => |sn| return diags.failParse(
4296 path,
4297 "invalid section number {d} for weak external symbol 0x{x}",
4298 .{ sn, symbol_i },
4299 ),
4300 },
4301 .EXTERNAL => switch (section_number) {
4302 .UNDEFINED,
4303 .ABSOLUTE,
4304 => &.{.{ .external = symbol.value }},
4305 .DEBUG => return diags.failParse(
4306 path,
4307 "unexpected external symbol 0x{x} in DEBUG section: '{s}'",
4308 .{ symbol_i, name },
4309 ),
4310 else => &.{.{ .external = symbol.value }},
4311 },
4312 .FILE => {
4313 if (!std.mem.eql(u8, name, ".file"))
4314 return diags.failParse(
4315 path,
4316 "unexpected symbol name '{s}' for file symbol 0x{x}",
4317 .{ name, symbol_i },
4318 );
4319
4320 var file: std.coff.FileDefinition = undefined;
4321 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);
4322
4323 input.source_name = (try coff.getOrPutString(file.getFileName())).toOptional();
4324 break :pending_symbols &.{};
4325 },
4326 else => |storage_class| return diags.failParse(
4327 path,
4328 "TODO handle storage class {t} for symbol 0x{x}",
4329 .{ storage_class, symbol_i },
4330 ),
4331 };
4332
4333 for (values, 0..) |value, i| {
4334 if (section_number == .ABSOLUTE)
4335 num_included_symbols += 1;
4336
4337 switch (value) {
4338 .section => {},
4339 .static,
4340 .external,
4341 .weak_external,
4342 => {
4343 num_global_symbols += 1;
4344 if (section_number.hasIndex()) {
4345 const section = &sections[section_number.toIndex()];
4346 section.num_symbols += 1;
4347 if (section.header.flags.LNK_COMDAT and section.comdat_psi == .none)
4348 section.comdat_psi = psi;
4349 }
4350 },
4351 .weak_external_aux => {},
4352 }
4353
4354 const symbol_name = coff.getOrPutStringAssumeCapacity(name);
4355 pending_symbols.putAssumeCapacity(symbol_i + @as(u32, @intCast(i)), .{
4356 .name = symbol_name,
4357 .value = value,
4358 .section_number = section_number,
4359 .si = .null,
4360 .weak_external_psi = .none,
4361 });
4362 }
4363 }
4364
4365 try coff.globals.ensureUnusedCapacity(gpa, num_global_symbols);
4366 for (sections) |*section| {
4367 if (section.header.flags.LNK_INFO) {
4368 if (std.mem.eql(u8, &section.header.name, ".drectve")) {
4369 try fr.seekTo(fl.offset + section.header.pointer_to_raw_data);
4370 // TODO: Don't really want an additional buffer here, but want to limit to size_of_raw_data
4371 var buf: [128]u8 = undefined;
4372 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);
4373 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {
4374 error.StreamTooLong => return diags.failParse(path, "unexpectedly long .drectve argument", .{}),
4375 else => |e| return e,
4376 }) |arg| {
4377 // Microsoft tools emit 3 space characters into this section even with /Zl
4378 if (arg.len == 0) continue;
4379
4380 if (std.ascii.startsWithIgnoreCase(arg, "-exclude-symbols:")) {
4381 // TODO: When implementing mingw auto-exports (if at all?), track this to not export this symbol
4382 } else if (std.ascii.startsWithIgnoreCase(arg, "/include:")) {
4383 _ = try coff.globalSymbol(.{ .name = arg["/include:".len..] });
4384 } else if (std.ascii.startsWithIgnoreCase(arg, "/alternatename:")) {
4385 var split = std.mem.splitScalar(u8, arg["/alternatename:".len..], '=');
4386 const orig = split.first();
4387 const alt = split.next() orelse
4388 return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg});
4389
4390 try coff.ensureManyUnusedStringCapacity(2, orig.len + alt.len + 2);
4391 const orig_str = coff.getOrPutStringAssumeCapacity(orig);
4392 const alt_str = coff.getOrPutStringAssumeCapacity(alt);
4393 const gop = try coff.alternate_names.getOrPut(gpa, orig_str);
4394 if (!gop.found_existing) {
4395 log.debug("alternateName({s}={s})", .{ orig, alt });
4396 gop.value_ptr.* = alt_str;
4397 } else if (gop.value_ptr.* != alt_str)
4398 return diags.failParse(
4399 path,
4400 "conflicting /alternatename .drectve arguments: first seen as {s}={s}, now seen as {s}={s}",
4401 .{ orig, gop.value_ptr.toSlice(coff), orig, alt },
4402 );
4403 } else if (std.ascii.startsWithIgnoreCase(arg, "/guardsym:")) {
4404 // TODO: https://learn.microsoft.com/en-us/windows/win32/secbp/pe-metadata
4405 } else if (std.ascii.startsWithIgnoreCase(arg, "/merge:")) merge: {
4406 var split = std.mem.splitScalar(u8, arg["/merge:".len..], '=');
4407 const from = split.first();
4408 const to = split.next() orelse
4409 return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg});
4410 if (to.len > header_name_max_len)
4411 return diags.failParse(
4412 path,
4413 "/merge .drectve target exceeds max length of {d}: '{s}'",
4414 .{ header_name_max_len, arg },
4415 );
4416 if (std.mem.eql(u8, from, to)) break :merge;
4417
4418 try coff.ensureManyUnusedStringCapacity(2, from.len + to.len + 2);
4419 const from_str = coff.getOrPutStringAssumeCapacity(from);
4420 const to_str = coff.getOrPutStringAssumeCapacity(to);
4421
4422 {
4423 var iter = to_str;
4424 while (coff.section_merges.get(iter)) |next_to| {
4425 if (next_to == from_str)
4426 return diags.failParse(
4427 path,
4428 "/merge .drectve argument would create a cycle: {s}={s} leads to {s}={s}",
4429 .{ from, to, iter.toSlice(coff), to },
4430 );
4431
4432 iter = next_to;
4433 }
4434 }
4435
4436 try coff.section_merges.ensureUnusedCapacity(gpa, 1);
4437 const gop = coff.section_merges.getOrPutAssumeCapacity(from_str);
4438 if (!gop.found_existing) {
4439 coff.synth_prog_node.increaseEstimatedTotalItems(1);
4440 gop.value_ptr.* = to_str;
4441 } else if (gop.value_ptr.* != to_str)
4442 return diags.failParse(
4443 path,
4444 "conflicting /merge .drectve arguments: first seen as {s}={s}, now seen as {s}={s}",
4445 .{ from, gop.value_ptr.toSlice(coff), from, to },
4446 );
4447 } else if (std.ascii.startsWithIgnoreCase(arg, "/disallowlib:")) {
4448 const lib_name = arg["/disallowlib:".len..];
4449 // TODO: Track these and issue error in prelink if any match
4450 _ = lib_name;
4451 } else if (std.ascii.startsWithIgnoreCase(arg, "/defaultlib:")) {
4452 const lib_path = arg["/defaultlib:".len..];
4453 const trim = std.mem.trim(u8, lib_path, "\"");
4454 if (lib_path.len == trim.len or lib_path.len - 2 == trim.len) {
4455 if (!comp.config.link_libc or comp.libc_installation == null)
4456 return diags.failParse(path, "encountered /DEFAULTLIB .drectve argument when libc was not available: {s}", .{arg});
4457
4458 (try coff.pending_default_libs.addOne(gpa)).* = .{
4459 .path = try gpa.dupe(u8, lib_path),
4460 .ioi = ioi,
4461 };
4462 } else return diags.failParse(
4463 path,
4464 "malformed /DEFAULTLIB .drectve argument: `{s}`",
4465 .{arg},
4466 );
4467 } else return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
4468 }
4469 }
4470
4471 section.comdat_result = .skip;
4472 continue;
4473 }
4474
4475 if (section.header.flags.LNK_REMOVE or
4476 section.header.flags.MEM_DISCARDABLE)
4477 {
4478 // TODO: Convert .debug$* sections into PDB
4479 section.comdat_result = .skip;
4480 continue;
4481 }
4482
4483 section.comdat_result = comdat: switch (section.comdat) {
4484 .NONE => .include,
4485 .ASSOCIATIVE => {
4486 // Associative COMDAT sections have no COMDAT symbol.
4487 // They are linked if the assocated section is linked.
4488 var iter = section;
4489 var iter_sn = iter.comdat_association;
4490 while (iter.comdat == .ASSOCIATIVE) {
4491 iter = &sections[iter_sn.toIndex()];
4492 iter_sn = iter.comdat_association;
4493 if (iter == section)
4494 return diags.failParse(
4495 path,
4496 "circular COMDAT association loop detected, starting at symbol 0x{x}",
4497 .{pending_symbols.keys()[section.psi.unwrap().?]},
4498 );
4499 }
4500
4501 assert(iter != section);
4502 break :comdat switch (iter.comdat_result) {
4503 .pending => .{ .pending_association = iter_sn },
4504 else => |iter_result| iter_result,
4505 };
4506 },
4507 else => |comdat| {
4508 const psi = section.comdat_psi.unwrap() orelse section.psi.unwrap().?;
4509 const symbol = &pending_symbols.values()[psi];
4510 const si = existing: switch (symbol.value) {
4511 .weak_external => unreachable,
4512 .weak_external_aux => unreachable,
4513 .static => break :comdat .include,
4514 .section => {
4515 assert(section.comdat_psi == .none);
4516 if (coff.object_section_table.get(section.name)) |si|
4517 break :existing si
4518 else if (coff.pseudo_section_table.get(section.name)) |si|
4519 break :existing si
4520 else if (coff.section_table.get(section.name)) |s|
4521 break :existing s.si
4522 else
4523 break :comdat .include;
4524 },
4525 .external => {
4526 const global_gop = try coff.getOrPutGlobalSymbol(.{
4527 .name = symbol.name.toSlice(coff),
4528 });
4529
4530 // TODO: What if the same symbol is incorrectly defined twice in this obj?
4531 // Would need to mark this global as pending, or notice it later when .ni != none
4532 if (!global_gop.found_existing or global_gop.value_ptr.si.get(coff).ni == .none) {
4533 symbol.si = global_gop.value_ptr.si;
4534 break :comdat .include;
4535 }
4536
4537 break :existing global_gop.value_ptr.si;
4538 },
4539 };
4540
4541 const index = pending_symbols.keys()[psi];
4542 switch (comdat) {
4543 .NODUPLICATES => return coff.failMultipleDefinitions(
4544 path,
4545 member_name,
4546 symbol.name,
4547 index,
4548 si,
4549 .duplicate,
4550 ),
4551 .ANY => {
4552 symbol.si = si;
4553 break :comdat .skip;
4554 },
4555 .SAME_SIZE => {
4556 // TODO: Verify that this node isn't resized after creation
4557 _, const size = si.get(coff).ni.unwrap().?.location(&coff.mf).resolve(&coff.mf);
4558 if (size == section.header.size_of_raw_data) {
4559 symbol.si = si;
4560 break :comdat .skip;
4561 }
4562
4563 return coff.failMultipleDefinitions(
4564 path,
4565 member_name,
4566 symbol.name,
4567 index,
4568 si,
4569 .{ .size = .{ .a = size, .b = section.header.size_of_raw_data } },
4570 );
4571 },
4572 .EXACT_MATCH => {
4573 const sym = si.get(coff);
4574 const existing_crc = switch (coff.getNode(sym.ni.unwrap().?)) {
4575 .input_section => |isi| isi.inputSection(coff).crc,
4576 else => Crc32.hash(sym.ni.unwrap().?.sliceConst(&coff.mf)),
4577 };
4578
4579 if (existing_crc == section.comdat_crc) {
4580 symbol.si = si;
4581 break :comdat .skip;
4582 }
4583
4584 return coff.failMultipleDefinitions(
4585 path,
4586 member_name,
4587 symbol.name,
4588 index,
4589 si,
4590 .{ .crc = .{ .a = existing_crc, .b = section.comdat_crc } },
4591 );
4592 },
4593 .LARGEST => {
4594 // TODO: Resize existing .ni and replace with this section's contents
4595 // TODO: This will be tricky, what to do about existing InputSection?
4596 unreachable;
4597 },
4598 .NONE, .ASSOCIATIVE, _ => unreachable,
4599 }
4600 },
4601 };
4602 }
4603
4604 try coff.flushSectionMerges();
4605
4606 // Resolve pending associations, create parent sections
4607 var num_included_sections: u16 = 0;
4608 var num_included_relocs: u32 = 0;
4609 for (sections) |*section| {
4610 comdat: switch (section.comdat_result) {
4611 .pending_association => |root_assoc_sn| {
4612 const root_result = sections[root_assoc_sn.toIndex()].comdat_result;
4613 assert(root_result != .pending_association);
4614 section.comdat_result = root_result;
4615 continue :comdat root_result;
4616 },
4617 .include => {},
4618 .skip => {
4619 assert(switch (section.comdat) {
4620 .NONE, .ASSOCIATIVE => true,
4621 else => if (section.comdat_psi.unwrap()) |psi|
4622 pending_symbols.values()[psi].si != .null
4623 else
4624 pending_symbols.values()[section.psi.unwrap().?].si != .null,
4625 });
4626 continue;
4627 },
4628 .pending => unreachable,
4629 }
4630
4631 // Until we support sorting .pdata, we shouldn't merge these in, the result would be invalid
4632 const section_name = section.name.toSlice(coff);
4633 if (std.mem.startsWith(u8, section_name, ".pdata"))
4634 continue;
4635
4636 num_included_sections += 1;
4637 num_included_symbols += section.num_symbols;
4638 num_included_relocs += section.header.number_of_relocations;
4639
4640 section.parent_si = (try coff.objectSectionMapIndex(
4641 section.name,
4642 .fromByteUnits(section.header.flags.ALIGN.toByteUnits() orelse 1),
4643 .fromFlags(section.header.flags),
4644 )).symbol(coff);
4645 }
4646
4647 try coff.nodes.ensureUnusedCapacity(gpa, num_included_sections);
4648 try coff.relocs.ensureUnusedCapacity(gpa, num_included_relocs);
4649 try coff.symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
4650 try coff.input_sections.ensureUnusedCapacity(gpa, num_included_sections);
4651
4652 for (sections) |*section| {
4653 if (section.parent_si == .null) continue;
4654
4655 const alignment: Alignment = .fromByteUnits(section.header.flags.ALIGN.toByteUnits() orelse 1);
4656 const ni = try section.parent_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
4657 .size = alignment.forward(section.header.size_of_raw_data),
4658 .alignment = alignment,
4659 .moved = true,
4660 });
4661 coff.nodes.appendAssumeCapacity(.{ .input_section = @fromBackingInt(@intCast(coff.input_sections.items.len)) });
4662
4663 section.si = coff.addSymbolAssumeCapacity();
4664 if (section.psi.unwrap()) |psi|
4665 pending_symbols.values()[psi].si = section.si;
4666
4667 const sym = section.si.get(coff);
4668 sym.ni = .wrap(ni);
4669 sym.section_number = section.parent_si.get(coff).section_number;
4670
4671 coff.input_sections.addOneAssumeCapacity().* = .{
4672 .ioi = ioi,
4673 .si = section.si,
4674 .file_location = .{
4675 .offset = fl.offset + section.header.pointer_to_raw_data,
4676 .size = section.header.size_of_raw_data,
4677 },
4678 .first_li = @fromBackingInt(@intCast(coff.input_symbols.items.len)),
4679 .crc = section.comdat_crc,
4680 .comdat_si = if (section.comdat_psi.unwrap()) |psi|
4681 pending_symbols.values()[psi].si
4682 else
4683 .null,
4684 };
4685
4686 log.debug(
4687 "addInputSection({s}, 0x{x}) = {d}@{d}",
4688 .{ section.name.toSlice(coff), section.comdat_crc, section.si, sym.section_number },
4689 );
4690 coff.synth_prog_node.increaseEstimatedTotalItems(1);
4691 }
4692
4693 for (pending_symbols.values(), pending_symbols.keys(), 0..) |*symbol, index, i| {
4694 switch (symbol.value) {
4695 .weak_external_aux => continue,
4696 else => {},
4697 }
4698
4699 defer log.debug("addInputSymbol({s}, 0x{x}@{d}, {t}=0x{x}) = n{d} {d}@{d}", .{
4700 symbol.name.toSlice(coff),
4701 index,
4702 symbol.section_number,
4703 symbol.value,
4704 switch (symbol.value) {
4705 .weak_external_aux => unreachable,
4706 inline else => |v| v,
4707 },
4708 symbol.si.get(coff).ni,
4709 symbol.si,
4710 symbol.si.get(coff).section_number,
4711 });
4712
4713 const section = switch (symbol.section_number) {
4714 .UNDEFINED => switch (symbol.value) {
4715 .section,
4716 .static,
4717 .weak_external_aux,
4718 => unreachable,
4719 .external => {
4720 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
4721 // If the alias itself is an undef external, we need to wait until flushing the weak
4722 // external global before creating a global for the alias, as another input could
4723 // still provide the weak external.
4724 const weak_sym = pending_symbols.values()[weak_external_i].si.get(coff);
4725 weak_sym.setValue(.{ .weak_alias_name = symbol.name });
4726 weak_sym.flags.weak_external_strat = pending_symbols.values()[weak_external_i + 1].value.weak_external_aux;
4727 }
4728
4729 // Deferred until referenced by a reloc in this object.
4730 // vcruntime.lib defines symbols like this (ie. memcpy_$fo$) that are not referenced
4731 continue;
4732 },
4733 .weak_external => |alias_index| {
4734 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4735 symbol.si = global_gop.value_ptr.si;
4736 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
4737 const sym = symbol.si.get(coff);
4738 const alias = pending_symbols.getPtr(alias_index) orelse
4739 return diags.failParse(
4740 path,
4741 "weak external 0x{x} {s}{f} targets unknown symbol index 0x{x}",
4742 .{
4743 index,
4744 symbol.name.toSlice(coff),
4745 fmtMemberNameString(member_name),
4746 alias_index,
4747 },
4748 );
4749
4750 if (alias.si == .null and alias_index > index) {
4751 // Resolve this once we see alias
4752 alias.weak_external_psi = .wrap(@intCast(i));
4753 } else {
4754 sym.setValue(if (alias.si.unwrap()) |alias_si| .{
4755 .weak_alias_si = alias_si,
4756 } else .{
4757 .weak_alias_name = alias.name,
4758 });
4759 sym.flags.weak_external_strat = pending_symbols.values()[i + 1].value.weak_external_aux;
4760 }
4761 }
4762
4763 continue;
4764 },
4765 },
4766 .ABSOLUTE => {
4767 const value = sym: switch (symbol.value) {
4768 .static => |value| {
4769 symbol.si = coff.addSymbolAssumeCapacity();
4770 break :sym value;
4771 },
4772 .external => |value| {
4773 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4774 symbol.si = global_gop.value_ptr.si;
4775 if (global_gop.found_existing)
4776 return coff.failMultipleDefinitions(
4777 path,
4778 member_name,
4779 symbol.name,
4780 index,
4781 global_gop.value_ptr.si,
4782 .none,
4783 );
4784 break :sym value;
4785 },
4786 else => unreachable,
4787 };
4788
4789 const sym = symbol.si.get(coff);
4790 sym.rva = value;
4791 sym.section_number = .ABSOLUTE;
4792 continue;
4793 },
4794 .DEBUG => continue,
4795 else => |sn| &sections[sn.toIndex()],
4796 };
4797
4798 if (section.si == .null)
4799 continue;
4800
4801 if (symbol.si == .null) {
4802 switch (symbol.value) {
4803 .section => unreachable,
4804 .static => {
4805 symbol.si = coff.addSymbolAssumeCapacity();
4806 },
4807 .external => {
4808 assert(index != section.comdat_psi.unwrap());
4809 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4810 symbol.si = global_gop.value_ptr.si;
4811
4812 const sym = symbol.si.get(coff);
4813 if (global_gop.found_existing and sym.ni != .none)
4814 return coff.failMultipleDefinitions(
4815 path,
4816 member_name,
4817 symbol.name,
4818 index,
4819 global_gop.value_ptr.si,
4820 .none,
4821 );
4822 },
4823 .weak_external,
4824 .weak_external_aux,
4825 => unreachable,
4826 }
4827
4828 if (section.comdat_psi.unwrap() == @as(u32, @intCast(i)))
4829 coff.getNode(section.si.get(coff).ni.unwrap().?).input_section.inputSection(coff).comdat_si = symbol.si;
4830 }
4831
4832 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
4833 assert(symbol.si != .null);
4834 const weak_sym = pending_symbols.values()[weak_external_i].si.get(coff);
4835 weak_sym.setValue(.{ .weak_alias_si = symbol.si });
4836 weak_sym.flags.weak_external_strat = pending_symbols.values()[weak_external_i + 1].value.weak_external_aux;
4837 }
4838
4839 if (section.si != symbol.si) {
4840 const sym = symbol.si.get(coff);
4841 assert(sym.ni == .none);
4842 sym.ni = section.si.get(coff).ni;
4843 switch (symbol.value) {
4844 .section => |v| sym.setExtra(.{ .size = v }),
4845 .static => |v| sym.setValue(.{ .node_offset = v }),
4846 .external => |v| switch (symbol.section_number) {
4847 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
4848 else => sym.setValue(.{ .node_offset = v }),
4849 },
4850 .weak_external,
4851 .weak_external_aux,
4852 => unreachable,
4853 }
4854
4855 sym.section_number = section.si.get(coff).section_number;
4856 }
4857 }
4858
4859 const relocation_size = std.coff.Relocation.sizeOf();
4860 for (sections) |section| {
4861 if (section.si == .null) continue;
4862
4863 const loc_sym = section.si.get(coff);
4864 assert(loc_sym.loc_relocs == .none);
4865 loc_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
4866
4867 if (section.header.number_of_relocations == 0) continue;
4868
4869 try fr.seekTo(fl.offset + section.header.pointer_to_relocations);
4870 for (0..section.header.number_of_relocations) |reloc_i| {
4871 var reloc: std.coff.Relocation = undefined;
4872 @memcpy(std.mem.asBytes(&reloc)[0..relocation_size], try r.take(relocation_size));
4873 if (target_endian != native_endian)
4874 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);
4875
4876 const symbol = pending_symbols.getPtr(reloc.symbol_table_index) orelse
4877 return diags.failParse(
4878 path,
4879 "relocation 0x{x} in section '{s}' of {f}{f} targets invalid symbol index 0x{x}",
4880 .{
4881 reloc_i,
4882 section.name.toSlice(coff),
4883 path.fmtEscapeString(),
4884 fmtMemberNameString(member_name),
4885 reloc.symbol_table_index,
4886 },
4887 );
4888
4889 if (symbol.si == .null) {
4890 assert(symbol.section_number == .UNDEFINED);
4891 switch (symbol.value) {
4892 .external => |size| {
4893 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4894 symbol.si = global_gop.value_ptr.si;
4895 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
4896 const sym = symbol.si.get(coff);
4897 sym.setExtra(.{ .size = @max(sym.size(), size) });
4898 }
4899 },
4900 else => unreachable,
4901 }
4902 }
4903
4904 assert(symbol.si != .null);
4905 try coff.addReloc(
4906 section.si,
4907 reloc.virtual_address - section.header.virtual_address,
4908 symbol.si,
4909 .pending,
4910 .{ .u16 = reloc.type },
4911 );
4912 }
4913 }
4914
4915 // Set up contiguous symbol ranges in `input_symbols` for both symbols we just created,
4916 // and symbols that were previously created as undefined, but we just defined.
4917 const SortContext = struct {
4918 v: []const PendingSymbol,
4919
4920 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
4921 const lhs = &ctx.v[a_index];
4922 const rhs = &ctx.v[b_index];
4923 if (lhs.section_number == rhs.section_number)
4924 return @backingInt(lhs.si) < @backingInt(rhs.si);
4925 return @backingInt(lhs.section_number) < @backingInt(rhs.section_number);
4926 }
4927 };
4928
4929 pending_symbols.sortUnstable(SortContext{ .v = pending_symbols.values() });
4930
4931 try coff.input_symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
4932 var prev_sn: Symbol.SectionNumber = .DEBUG;
4933 var include_section = false;
4934 for (pending_symbols.values()) |symbol| {
4935 // The symbol may have not been included, or it's an undefined external / aux
4936 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
4937
4938 if (prev_sn != symbol.section_number) {
4939 prev_sn = symbol.section_number;
4940 if (symbol.section_number.hasIndex()) {
4941 const section = &sections[symbol.section_number.toIndex()];
4942 include_section = section.comdat_result == .include;
4943 if (include_section) {
4944 const isi = coff.getNode(section.si.get(coff).ni.unwrap().?).input_section;
4945 isi.inputSection(coff).first_li = @fromBackingInt(@intCast(coff.input_symbols.items.len));
4946 }
4947 }
4948 }
4949
4950 if (include_section) {
4951 assert(coff.getNode(symbol.si.get(coff).ni.unwrap().?) == .input_section);
4952 symbol.si.get(coff).setExtra(.{ .isli = @fromBackingInt(@intCast(coff.input_symbols.items.len)) });
4953 coff.input_symbols.addOneAssumeCapacity().* = .{
4954 .si = symbol.si,
4955 .name = symbol.name,
4956 };
4957 }
4958 }
4959}
4960
4961fn failMultipleDefinitions(
4962 coff: *Coff,
4963 path: std.Build.Cache.Path,
4964 member_name: ?[]const u8,
4965 name: String,
4966 index: u32,
4967 existing_si: Symbol.Index,
4968 comdat_reason: union(enum) {
4969 none: void,
4970 duplicate: void,
4971 size: struct { a: u64, b: u64 },
4972 crc: struct { a: u32, b: u32 },
4973 },
4974) error{ AlreadyReported, OutOfMemory } {
4975 const num_notes: usize = 2 + @as(usize, @intFromBool(comdat_reason != .none));
4976 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
4977 try err.addMsg("multiple definitions of '{s}'", .{name.toSlice(coff)});
4978
4979 switch (coff.getNode(existing_si.get(coff).ni.unwrap().?)) {
4980 .input_section => |isi| {
4981 const other_ioi = isi.input(coff);
4982 err.addNote("first seen in input '{f}{f}'", .{
4983 other_ioi.path(coff).fmtEscapeString(),
4984 fmtMemberNameString(other_ioi.memberName(coff)),
4985 });
4986 },
4987 .nav, .uav => err.addNote("first seen in module '{s}'", .{
4988 coff.base.comp.zcu.?.root_mod.fully_qualified_name,
4989 }),
4990 else => unreachable,
4991 }
4992
4993 err.addNote("defined again in input '{f}{f}' (0x{x}))", .{ path, fmtMemberNameString(member_name), index });
4994 switch (comdat_reason) {
4995 .none => {},
4996 .duplicate => err.addNote("COMDAT rule requires no duplicates", .{}),
4997 .size => |s| err.addNote(
4998 "COMDAT rule require duplicates to have the same size ({d} vs {d})",
4999 .{ s.a, s.b },
5000 ),
5001 .crc => |s| err.addNote(
5002 "COMDAT rule require duplicates to have the same CRC (0x{x} vs 0x{x})",
5003 .{ s.a, s.b },
5004 ),
5005 }
5006
5007 return error.AlreadyReported;
5008}
5009
5010const ArchiveMemberHeader = struct {
5011 name: []const u8,
5012 size: u34,
5013};
5014
5015/// Return value lifetime is that of `header`
5016fn parseArchiveMemberHeader(
5017 diags: *link.Diags,
5018 path: std.Build.Cache.Path,
5019 header: *const std.coff.ArchiveMemberHeader,
5020 opt_longnames: ?[]const u8,
5021) !ArchiveMemberHeader {
5022 return parseArchiveMemberHeaderInner(header, opt_longnames) catch |err| switch (err) {
5023 error.BadName => return diags.failParse(path, "malformed member name: '{s}'", .{&header.name}),
5024 error.BadSize => return diags.failParse(path, "malformed member size: '{s}'", .{&header.size}),
5025 error.BadEndOfHeader => return diags.failParse(path, "end of header was invalid", .{}),
5026 error.NoLongNames => return diags.failParse(path, "long name used without longnames member", .{}),
5027 };
5028}
5029
5030fn parseArchiveMemberHeaderInner(
5031 header: *const std.coff.ArchiveMemberHeader,
5032 opt_longnames: ?[]const u8,
5033) !ArchiveMemberHeader {
5034 const name = try header.parseName(opt_longnames);
5035 const size = header.parseSize() catch return error.BadSize;
5036
5037 if (!std.mem.eql(u8, &header.end_of_header, std.coff.archive_end_of_header))
5038 return error.BadEndOfHeader;
5039
5040 return .{
5041 .name = name,
5042 .size = size,
5043 };
5044}
5045
5046fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) LoadInputError!void {
5047 const comp = coff.base.comp;
5048 const gpa = comp.gpa;
5049 const diags = &comp.link_diags;
5050 const r = &fr.interface;
5051 const target_endian = coff.targetEndian();
5052
5053 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
5054
5055 const signature = try r.take(std.coff.archive_signature.len);
5056 if (!std.mem.eql(u8, signature, std.coff.archive_signature))
5057 return diags.failParse(path, "bad signature", .{});
5058
5059 var opt_expected_kind: ?std.coff.ArchiveMemberHeader.Kind = .first_linker;
5060 var opt_longnames: ?[]const u8 = null;
5061 defer if (opt_longnames) |l| gpa.free(l);
5062
5063 var members: std.ArrayList(struct {
5064 offset: u32,
5065 iami: ?InputArchive.Member.Index,
5066 }) = .empty;
5067 var symbol_member_indices: std.ArrayList(u32) = .empty;
5068
5069 const iai: InputArchive.Index = @fromBackingInt(@intCast(coff.input_archives.items.len));
5070 (try coff.input_archives.addOne(gpa)).* = .{
5071 .path = path,
5072 };
5073
5074 const first_iami = coff.input_archive_members.items.len;
5075 const first_iamsi = coff.input_archive_symbols.items.len;
5076 const first_symbol_indices_index = coff.input_archive_symbol_indices.count();
5077
5078 errdefer {
5079 for (coff.input_archive_symbol_indices.values()) |*v| {
5080 if (@backingInt(v.last) < first_iamsi) continue;
5081 if (@backingInt(v.first) >= first_iamsi) continue;
5082
5083 var iter = v.first;
5084 v.last = while (iter != v.last) {
5085 const sym = &coff.input_archive_symbols.items[@backingInt(iter)];
5086 if (@backingInt(sym.next) >= first_iamsi) {
5087 sym.next = iter;
5088 break iter;
5089 }
5090
5091 iter = sym.next;
5092 } else unreachable;
5093 }
5094
5095 // New entries in this map will only have pointed to iamsi we also just added
5096 coff.input_archive_symbol_indices.shrinkRetainingCapacity(first_symbol_indices_index);
5097 coff.input_archive_symbols.shrinkRetainingCapacity(first_iamsi);
5098 coff.input_archive_members.shrinkRetainingCapacity(first_iami);
5099 _ = coff.input_archives.pop();
5100 }
5101
5102 var pos = fr.logicalPos();
5103 const size = try fr.getSize();
5104 while (pos < size) : (pos = fr.logicalPos()) {
5105 if ((pos & 1) != 0) try r.discardAll(1);
5106 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);
5107 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);
5108
5109 const member_end = fr.logicalPos() + res.size;
5110 if (member_end > size)
5111 return diags.failParse(path, "out-of-bounds length 0x{x} in member '{s}'", .{ res.size, res.name });
5112
5113 log.debug("loadArchiveMember({s})", .{res.name});
5114
5115 if (opt_expected_kind) |expected_kind| switch (expected_kind) {
5116 .first_linker => {
5117 if (!std.mem.eql(u8, res.name, "/"))
5118 return diags.failParse(path, "expected first linker member, found '{s}'", .{res.name});
5119
5120 try fr.seekTo(fr.logicalPos() + res.size);
5121 opt_expected_kind = .second_linker;
5122 continue;
5123 },
5124 .second_linker => {
5125 if (!std.mem.eql(u8, res.name, "/"))
5126 return diags.failParse(path, "expected second linker member, found '{s}'", .{res.name});
5127
5128 const num_members = try r.takeInt(u32, target_endian);
5129 pos = fr.logicalPos();
5130 if (pos + num_members * @sizeOf(u32) > member_end)
5131 return diags.failParse(path, "invalid member count 0x{x} in second linker member", .{num_members});
5132
5133 try members.ensureTotalCapacity(gpa, num_members);
5134 for (0..num_members) |_|
5135 members.addOneAssumeCapacity().* = .{
5136 .offset = try r.takeInt(u32, target_endian),
5137 .iami = null,
5138 };
5139
5140 const num_symbols = try r.takeInt(u32, target_endian);
5141 pos = fr.logicalPos();
5142 if (pos + num_symbols * @sizeOf(u16) > member_end)
5143 return diags.failParse(path, "invalid symbol count 0x{x} in second linker member", .{num_symbols});
5144
5145 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);
5146 for (0..num_symbols) |_|
5147 symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, target_endian)) - 1;
5148
5149 pos = fr.logicalPos();
5150 try coff.ensureManyUnusedStringCapacity(num_symbols, @intCast(member_end - pos));
5151 try coff.input_archive_members.ensureUnusedCapacity(gpa, num_members);
5152 try coff.input_archive_symbols.ensureUnusedCapacity(gpa, num_symbols);
5153 try coff.input_archive_symbol_indices.ensureUnusedCapacity(gpa, num_symbols);
5154
5155 var symbol_i: u32 = 0;
5156 while (pos < member_end and symbol_i < num_symbols) : ({
5157 pos = fr.logicalPos();
5158 symbol_i += 1;
5159 }) {
5160 const name = if (r.takeDelimiter(0) catch |err| switch (err) {
5161 error.StreamTooLong => null,
5162 else => |e| return e,
5163 }) |n| n else return diags.failParse(path, "unterminated string found in second linker member", .{});
5164
5165 const string = coff.getOrPutStringAssumeCapacity(name);
5166 const iamsi: InputArchive.Member.Symbol.Index = @fromBackingInt(@intCast(coff.input_archive_symbols.items.len));
5167 const symbol_gop = coff.input_archive_symbol_indices.getOrPutAssumeCapacity(string);
5168 if (!symbol_gop.found_existing) {
5169 symbol_gop.value_ptr.* = .{
5170 .first = iamsi,
5171 .last = iamsi,
5172 };
5173 } else {
5174 coff.input_archive_symbols.items[@backingInt(symbol_gop.value_ptr.last)].next = iamsi;
5175 symbol_gop.value_ptr.last = iamsi;
5176 }
5177
5178 const iami = members.items[symbol_member_indices.items[symbol_i]].iami orelse iami: {
5179 const iami: InputArchive.Member.Index = @fromBackingInt(@intCast(coff.input_archive_members.items.len));
5180 const member_offset = members.items[symbol_member_indices.items[symbol_i]].offset;
5181 coff.input_archive_members.addOneAssumeCapacity().* = .{
5182 .iai = iai,
5183 .name = undefined,
5184 .content = .{
5185 .object = .{
5186 .offset = member_offset,
5187 .size = undefined,
5188 },
5189 },
5190 .flags = .{
5191 .is_loaded = false,
5192 },
5193 };
5194
5195 members.items[symbol_member_indices.items[symbol_i]].iami = iami;
5196 break :iami iami;
5197 };
5198
5199 log.debug("loadArchiveMemberSymbol({s}) = ({d}, {d}, {d})", .{ name, iai, iami, iamsi });
5200
5201 coff.input_archive_symbols.addOneAssumeCapacity().* = .{
5202 .iami = iami,
5203 .next = iamsi,
5204 };
5205 }
5206
5207 if (symbol_i != num_symbols)
5208 return diags.failParse(
5209 path,
5210 " expected {d} entries in second linker member string table, but found {d}",
5211 .{ num_symbols, symbol_i },
5212 );
5213
5214 try fr.seekTo(member_end);
5215 opt_expected_kind = .longnames;
5216 continue;
5217 },
5218 .longnames => {
5219 // This member is optional
5220 if (std.mem.eql(u8, res.name, "//"))
5221 opt_longnames = try r.readAlloc(gpa, @intCast(res.size));
5222
5223 opt_expected_kind = null;
5224 break;
5225 },
5226 else => unreachable,
5227 };
5228 }
5229
5230 if (opt_expected_kind) |expected_kind| switch (expected_kind) {
5231 .first_linker => return diags.failParse(path, "missing first linker member", .{}),
5232 .second_linker => return diags.failParse(path, "missing second linker member", .{}),
5233 else => {},
5234 };
5235
5236 // Validate / read names and sizes of all the referenced members, enumerate imports
5237 for (coff.input_archive_members.items[first_iami..]) |*member| {
5238 try fr.seekTo(member.content.object.offset);
5239
5240 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);
5241 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);
5242
5243 try coff.ensureUnusedStringCapacity(res.name.len);
5244 member.name = coff.getOrPutStringAssumeCapacity(res.name);
5245
5246 const member_sig = try r.peek(4);
5247 const machine: std.coff.IMAGE.FILE.MACHINE =
5248 @fromBackingInt(@intCast(std.mem.readInt(u16, member_sig[0..2], target_endian)));
5249 const sig = std.mem.readInt(u16, member_sig[2..4], target_endian);
5250
5251 log.debug("verifyArchiveMember({s}) = 0x{x}+{x}", .{
5252 res.name,
5253 member.content.object.offset,
5254 res.size,
5255 });
5256
5257 const expected_machine = comp.root_mod.resolved_target.result.toCoffMachine();
5258 if (machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff) {
5259 const import_header = try r.takeStruct(std.coff.ImportHeader, target_endian);
5260 const strings = r.take(import_header.size_of_data) catch |err| switch (err) {
5261 error.EndOfStream => return diags.failParse(path, "invalid data size in import header '{s}'", .{res.name}),
5262 else => |e| return e,
5263 };
5264
5265 var split = std.mem.splitScalar(u8, strings, 0);
5266 const symbol_name = split.next() orelse
5267 return diags.failParse(path, "invalid symbol name string in import header '{s}'", .{res.name});
5268 var lib_name = split.next() orelse
5269 return diags.failParse(path, "invalid dll name string in import header '{s}' ('{s}')", .{ res.name, symbol_name });
5270
5271 if (import_header.machine != expected_machine)
5272 return diags.failParse(path, "machine mismatch in import header '{s}' ('{s}'): expected {t}, found {t}", .{
5273 res.name,
5274 symbol_name,
5275 expected_machine,
5276 machine,
5277 });
5278
5279 const ext = ".dll";
5280 if (!std.mem.endsWith(u8, lib_name, ext))
5281 return diags.failParse(
5282 path,
5283 "unexpected extension for import '{s} ('{s}'): '{s}'",
5284 .{ res.name, symbol_name, lib_name },
5285 );
5286
5287 lib_name = lib_name[0 .. lib_name.len - ext.len];
5288 log.debug("verifyArchiveImportHeader({s}, {s}, {s}) = {t} ({t})", .{
5289 res.name,
5290 symbol_name,
5291 lib_name,
5292 import_header.types.type,
5293 import_header.types.name_type,
5294 });
5295
5296 try coff.ensureManyUnusedStringCapacity(2, strings.len - ext.len);
5297 member.content = .{
5298 .import = .{
5299 .symbol_name = coff.getOrPutStringAssumeCapacity(symbol_name),
5300 .lib_name = coff.getOrPutStringAssumeCapacity(lib_name),
5301 .import_ordinal_hint = import_header.hint,
5302 .type = import_header.types.type,
5303 .name_type = import_header.types.name_type,
5304 },
5305 };
5306 } else {
5307 member.content.object.size = res.size;
5308 // Microsoft's CRT contains members that set .UNKNOWN but do have undef symbols
5309 if (machine != expected_machine and machine != .UNKNOWN) {
5310 return diags.failParse(path, "machine mismatch in member header '{s}': expected {t}, found {t}", .{
5311 res.name,
5312 expected_machine,
5313 machine,
5314 });
5315 }
5316 }
5317 }
5318}
5319
5320fn loadRes(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) LoadInputError!void {
5321 const comp = coff.base.comp;
5322 const gpa = comp.gpa;
5323 const diags = &comp.link_diags;
5324 const r = &fr.interface;
5325
5326 log.debug("loadRes({f})", .{path.fmtEscapeString()});
5327
5328 _ = gpa;
5329 _ = diags;
5330 _ = r;
5331}
5332
5333fn loadDll(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) LoadInputError!void {
5334 const comp = coff.base.comp;
5335 const gpa = comp.gpa;
5336 const diags = &comp.link_diags;
5337 const r = &fr.interface;
5338
5339 log.debug("loadDll({f})", .{path.fmtEscapeString()});
5340
5341 _ = gpa;
5342 _ = diags;
5343 _ = r;
5344}
5345
5346pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
5347 const sub_prog_node = prog_node.start("COFF Prelink", 0);
5348 defer sub_prog_node.end();
5349
5350 const base = coff.base;
5351 const comp = base.comp;
5352
5353 log.debug("prelink()", .{});
5354
5355 if (coff.pending_default_libs.items.len > 0) {
5356 // Libs provided by /DEFAULTLIB arguments in objects are searched after all other inputs
5357 const gpa = comp.gpa;
5358 const arena = comp.arena;
5359 const target = &comp.root_mod.resolved_target.result;
5360
5361 defer {
5362 for (coff.pending_default_libs.items) |l| gpa.free(l.path);
5363 coff.pending_default_libs.clearAndFree(gpa);
5364 }
5365
5366 assert(comp.config.link_libc);
5367 const libc_installation = comp.libc_installation.?;
5368 const all_paths: [3]?[]const u8 = .{
5369 libc_installation.crt_dir,
5370 libc_installation.msvc_lib_dir,
5371 libc_installation.kernel32_lib_dir,
5372 };
5373 const search_paths = all_paths[0..if (target.abi == .msvc or target.abi == .itanium) 3 else 1];
5374 lib: for (coff.pending_default_libs.items) |lib| {
5375 if (!std.mem.eql(u8, std.fs.path.extension(lib.path), ".lib"))
5376 return comp.link_diags.failParse(
5377 lib.ioi.path(coff),
5378 "/DEFAULTLIB library '{s}' had unexpected extension",
5379 .{lib.path},
5380 );
5381
5382 log.debug("loadDefaultLib({s}, {f})", .{ lib.path, lib.ioi.path(coff) });
5383 for (search_paths) |opt_path| if (opt_path) |search_path| {
5384 const lib_path = try Path.initCwd(search_path).join(arena, lib.path);
5385 const archive = link.openObject(comp.io, lib_path, false, false) catch |err| switch (err) {
5386 error.FileNotFound => {
5387 arena.free(lib_path.sub_path);
5388 continue;
5389 },
5390 else => |e| return comp.link_diags.failParse(
5391 lib.ioi.path(coff),
5392 "error opening /DEFAULTLIB library '{s}': {t}",
5393 .{ lib.path, e },
5394 ),
5395 };
5396 errdefer archive.file.close(comp.io);
5397
5398 coff.loadInput(.{ .archive = archive }) catch |err| switch (err) {
5399 else => |e| return comp.link_diags.failParse(
5400 lib.ioi.path(coff),
5401 "error loading /DEFAULTLIB library '{s}': {t}",
5402 .{ lib.path, e },
5403 ),
5404 };
5405
5406 break :lib;
5407 };
5408
5409 return comp.link_diags.failParse(
5410 lib.ioi.path(coff),
5411 "/DEFAULTLIB library '{s}' was not found",
5412 .{lib.path},
5413 );
5414 }
5415 }
5416
5417 coff.inputs_complete = true;
5418 if (comp.zcu == null)
5419 coff.exports_complete = true;
5420}
5421
5422pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
5423 coff.updateNavInner(pt, nav_index) catch |err| switch (err) {
5424 error.MappedFileIo => return coff.base.cgFail(
5425 nav_index,
5426 "linker failed to update variable: {t}",
5427 .{coff.mf.io_err.?},
5428 ),
5429 else => |e| return e,
5430 };
5431}
5432fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
5433 const zcu = pt.zcu;
5434 const gpa = zcu.gpa;
5435 const ip = &zcu.intern_pool;
5436
5437 const nav = ip.getNav(nav_index);
5438 if (ip.indexToKey(nav.resolved.?.value) == .@"extern") return;
5439 if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) return;
5440
5441 const nmi = try coff.navMapIndex(zcu, nav_index);
5442 const si = nmi.symbol(coff);
5443 log.debug("updateNav({f}) = {d}", .{ nav.fqn.fmt(ip), si });
5444 const ni = ni: {
5445 switch (si.get(coff).ni) {
5446 .none => {
5447 const sec_si = try coff.navSection(zcu, nav.resolved.?);
5448 try coff.nodes.ensureUnusedCapacity(gpa, 1);
5449 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
5450 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
5451 .alignment = .fromIp(zcu.navAlignment(nav_index)),
5452 .moved = true,
5453 });
5454 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
5455 const sym = si.get(coff);
5456 sym.ni = .wrap(ni);
5457 sym.section_number = sec_si.get(coff).section_number;
5458 },
5459 else => si.deleteLocationRelocs(coff),
5460 }
5461 const sym = si.get(coff);
5462 assert(sym.loc_relocs == .none);
5463 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
5464 if (!isImage(coff) and sym.target_relocs != .none)
5465 try coff.pendingSymbolTableEntry(si);
5466
5467 break :ni sym.ni.unwrap().?;
5468 };
5469
5470 {
5471 var nw: MappedFile.Node.Writer = undefined;
5472 ni.writer(&coff.mf, gpa, &nw);
5473 defer nw.deinit();
5474 codegen.generateSymbol(
5475 &coff.base,
5476 pt,
5477 .fromInterned(nav.resolved.?.value),
5478 &nw.interface,
5479 .{ .atom_index = @fromBackingInt(@intCast(@backingInt(si))) },
5480 ) catch |err| switch (err) {
5481 error.WriteFailed => return nw.err.?,
5482 else => |e| return e,
5483 };
5484 si.get(coff).extra.size = @intCast(nw.interface.end);
5485 try si.applyLocationRelocs(coff);
5486 }
5487
5488 if (nav.resolved.?.@"linksection".unwrap()) |_| {
5489 try ni.resizeLeaf(&coff.mf, gpa, si.get(coff).extra.size);
5490 }
5491}
5492
5493pub fn lowerUav(
5494 coff: *Coff,
5495 pt: Zcu.PerThread,
5496 uav_val: InternPool.Index,
5497 uav_align: InternPool.Alignment,
5498) link.Error!link.File.SymbolId {
5499 const zcu = pt.zcu;
5500 const gpa = zcu.gpa;
5501
5502 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
5503 const umi = try coff.uavMapIndex(uav_val);
5504 const si = umi.symbol(coff);
5505 const need_update: bool = update: {
5506 const existing_ni = si.get(coff).ni.unwrap() orelse break :update true;
5507 break :update Alignment.compare(.fromIp(uav_align), .gt, existing_ni.alignment(&coff.mf));
5508 };
5509 if (need_update) {
5510 const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);
5511 if (gop.found_existing) {
5512 gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
5513 } else {
5514 gop.value_ptr.* = .{
5515 .alignment = uav_align,
5516 };
5517 coff.const_prog_node.increaseEstimatedTotalItems(1);
5518 }
5519 }
5520 return @fromBackingInt(@intCast(@backingInt(si)));
5521}
5522
5523pub fn updateFunc(
5524 coff: *Coff,
5525 pt: Zcu.PerThread,
5526 func_index: InternPool.Index,
5527 mir: *const codegen.AnyMir,
5528) link.Error!void {
5529 coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
5530 else => |e| return e,
5531 error.MappedFileIo => return coff.base.cgFail(
5532 pt.zcu.funcInfo(func_index).owner_nav,
5533 "linker failed to update function: {t}",
5534 .{coff.mf.io_err.?},
5535 ),
5536 };
5537}
5538fn updateFuncInner(
5539 coff: *Coff,
5540 pt: Zcu.PerThread,
5541 func_index: InternPool.Index,
5542 mir: *const codegen.AnyMir,
5543) !void {
5544 const zcu = pt.zcu;
5545 const gpa = zcu.gpa;
5546 const ip = &zcu.intern_pool;
5547 const func = zcu.funcInfo(func_index);
5548 const nav = ip.getNav(func.owner_nav);
5549
5550 const nmi = try coff.navMapIndex(zcu, func.owner_nav);
5551 const si = nmi.symbol(coff);
5552 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
5553 const ni = ni: {
5554 switch (si.get(coff).ni) {
5555 .none => {
5556 const sec_si = try coff.navSection(zcu, nav.resolved.?);
5557 try coff.nodes.ensureUnusedCapacity(gpa, 1);
5558 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
5559 const mod = zcu.navFileScope(func.owner_nav).mod.?;
5560 const target = &mod.resolved_target.result;
5561 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
5562 .alignment = switch (nav.resolved.?.@"align") {
5563 .none => switch (mod.optimize_mode) {
5564 .debug,
5565 .safe,
5566 .fast,
5567 => .fromIp(target_util.defaultFunctionAlignment(target)),
5568 .small => .fromIp(target_util.minFunctionAlignment(target)),
5569 },
5570 else => |a| .fromIp(a.maxStrict(target_util.minFunctionAlignment(target))),
5571 },
5572 .moved = true,
5573 });
5574 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
5575 const sym = si.get(coff);
5576 sym.ni = .wrap(ni);
5577 sym.section_number = sec_si.get(coff).section_number;
5578 },
5579 else => si.deleteLocationRelocs(coff),
5580 }
5581 const sym = si.get(coff);
5582 assert(sym.loc_relocs == .none);
5583 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
5584 if (!isImage(coff) and sym.target_relocs != .none)
5585 try coff.pendingSymbolTableEntry(si);
5586 break :ni sym.ni.unwrap().?;
5587 };
5588
5589 var nw: MappedFile.Node.Writer = undefined;
5590 ni.writer(&coff.mf, gpa, &nw);
5591 defer nw.deinit();
5592 codegen.emitFunction(
5593 &coff.base,
5594 pt,
5595 func_index,
5596 @fromBackingInt(@intCast(@backingInt(si))),
5597 mir,
5598 &nw.interface,
5599 .none,
5600 ) catch |err| switch (err) {
5601 error.WriteFailed => return nw.err.?,
5602 else => |e| return e,
5603 };
5604 si.get(coff).extra.size = @intCast(nw.interface.end);
5605 try si.applyLocationRelocs(coff);
5606}
5607
5608pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
5609 coff.flushLazy(pt, .{
5610 .kind = .const_data,
5611 .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return),
5612 }) catch |err| switch (err) {
5613 else => |e| return e,
5614 error.MappedFileIo => return coff.base.comp.link_diags.fail(
5615 "updateErrorData failed: {t}",
5616 .{coff.mf.io_err.?},
5617 ),
5618 };
5619}
5620
5621fn flushImplib(
5622 coff: *Coff,
5623 implib_file: []const u8,
5624) !void {
5625 // Emitting implibs is only valid for images
5626
5627 const comp = coff.base.comp;
5628 const gpa = comp.gpa;
5629 const io = comp.io;
5630
5631 const image_name = std.mem.sliceTo(
5632 coff.export_table.ni.slice(&coff.mf)[@sizeOf(std.coff.ExportDirectoryTable)..],
5633 0,
5634 );
5635 const machine_type = coff.targetLoad(&coff.headerPtr().machine);
5636 const members = members: {
5637 const def_arena: std.heap.ArenaAllocator = .init(gpa);
5638 var def: ModuleDefinition = .{
5639 .name = image_name,
5640 .arena = def_arena,
5641 .type = .mingw,
5642 };
5643 defer def.deinit();
5644
5645 try def.exports.ensureUnusedCapacity(
5646 def.arena.allocator(),
5647 coff.export_table.entries.count(),
5648 );
5649
5650 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
5651 for (coff.export_table.entries.values(), 0..) |entry, ord| {
5652 const name = name_table_slice[entry.name_index..][0..entry.name_len];
5653 const section_number = entry.si.get(coff).section_number;
5654 const import_type: std.coff.ImportType = switch (section_number.symbol(coff)) {
5655 .data, .rdata => .DATA,
5656 .text => .CODE,
5657 else => return comp.link_diags.fail(
5658 "unsupported section for export '{s}': {s}",
5659 .{ name, &section_number.header(coff).name },
5660 ),
5661 };
5662
5663 def.exports.appendAssumeCapacity(.{
5664 .name = name,
5665 .mangled_symbol_name = null,
5666 .ext_name = null,
5667 .import_name = null,
5668 .export_as = null,
5669 .no_name = false,
5670 .ordinal = @intCast(ord),
5671 .type = import_type,
5672 .private = false,
5673 });
5674 }
5675
5676 def.fixupForImportLibraryGeneration(machine_type);
5677 break :members try implib.getMembers(gpa, def, machine_type);
5678 };
5679 defer members.deinit();
5680
5681 const lib_sub_path = try std.fs.path.join(gpa, &.{
5682 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
5683 implib_file,
5684 });
5685 defer gpa.free(lib_sub_path);
5686
5687 const lib_final_file = try coff.base.emit.root_dir.handle.createFile(io, lib_sub_path, .{ .truncate = true });
5688 defer lib_final_file.close(io);
5689 var buffer: [1024]u8 = undefined;
5690 var file_writer = lib_final_file.writer(io, &buffer);
5691 try implib.writeCoffArchive(gpa, &file_writer.interface, members);
5692 try file_writer.interface.flush();
5693}
5694
5695fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5696 const comp = coff.base.comp;
5697 const gpa = comp.gpa;
5698 const max_notes = 4;
5699
5700 var undef_indices: std.ArrayList(u32) = .empty;
5701 for (coff.relocs.items, 0..) |reloc, reloc_i| {
5702 if (reloc.flags.free) continue;
5703 const target_sym = reloc.target.get(coff);
5704 switch (target_sym.ni) {
5705 .none => {
5706 assert(target_sym.gmi != .none);
5707 if (target_sym.section_number == .ABSOLUTE) continue;
5708 (try undef_indices.addOne(gpa)).* = @intCast(reloc_i);
5709 },
5710 else => continue,
5711 }
5712 }
5713
5714 if (undef_indices.items.len == 0) return;
5715
5716 const undefLessThan = struct {
5717 fn lessThan(ctx: *const Coff, lhs: u32, rhs: u32) bool {
5718 const reloc_l = &ctx.relocs.items[lhs];
5719 const reloc_r = &ctx.relocs.items[rhs];
5720 if (reloc_l.target == reloc_r.target)
5721 return @backingInt(reloc_l.loc) < @backingInt(reloc_r.loc)
5722 else
5723 return @backingInt(reloc_l.target) < @backingInt(reloc_r.target);
5724 }
5725 }.lessThan;
5726
5727 std.mem.sortUnstable(u32, undef_indices.items, coff, undefLessThan);
5728
5729 var start_i: usize = 0;
5730 var num_unique_references: usize = 1;
5731 for (0..undef_indices.items.len) |i| {
5732 const target = coff.relocs.items[undef_indices.items[start_i]].target;
5733 if (i == undef_indices.items.len - 1 or target != coff.relocs.items[undef_indices.items[i + 1]].target) {
5734 defer {
5735 start_i = i + 1;
5736 num_unique_references = 1;
5737 }
5738
5739 const num_full_notes = @min(max_notes, num_unique_references);
5740 var err = try comp.link_diags.addErrorWithNotes(
5741 num_full_notes + @intFromBool(num_unique_references > max_notes),
5742 );
5743 const target_sym = target.get(coff);
5744 try err.addMsg("undefined symbol: {s}", .{target_sym.gmi.name(coff).toSlice(coff)});
5745
5746 // TODO: If lib_name is set, show the user
5747
5748 var prev_loc_si: Symbol.Index = .null;
5749 for (undef_indices.items[start_i .. i + 1]) |reference_i| {
5750 if (err.note_slot == num_full_notes) break;
5751
5752 const reloc = &coff.relocs.items[reference_i];
5753 const loc_si = reloc.loc;
5754 if (loc_si == prev_loc_si) continue;
5755 defer prev_loc_si = loc_si;
5756
5757 const loc_sym = loc_si.get(coff);
5758
5759 // TODO: Make this a helper for anything that needs to report "referenced by" notes
5760 switch (coff.getNode(loc_sym.ni.unwrap().?)) {
5761 .data_directories => {
5762 const dir: std.coff.IMAGE.DIRECTORY_ENTRY =
5763 @fromBackingInt(@intCast(reloc.offset / @sizeOf(std.coff.ImageDataDirectory)));
5764 err.addNote("referenced by data directory entry: {t}", .{dir});
5765 },
5766 .optional_header => err.addNote("referenced by optional header field", .{}),
5767 .input_section => |isi| {
5768 const other_ioi = isi.input(coff);
5769 if (loc_sym.gmi == .none) {
5770 const section = isi.inputSection(coff);
5771 const section_name = coff.getNode(loc_sym.ni.unwrap().?.parent(&coff.mf).unwrap().?)
5772 .object_section.name(coff).toSlice(coff);
5773
5774 if (section.comdat_si != .null) {
5775 const comdat_sym = section.comdat_si.get(coff);
5776 const comdat_name = if (comdat_sym.gmi != .none)
5777 comdat_sym.gmi.name(coff).toSlice(coff)
5778 else
5779 comdat_sym.extra.isli.name(coff).toSlice(coff);
5780
5781 err.addNote("referenced by input COMDAT section '{s}={s}' '{f}{f}'", .{
5782 section_name,
5783 comdat_name,
5784 other_ioi.path(coff).fmtEscapeString(),
5785 fmtMemberNameString(other_ioi.memberName(coff)),
5786 });
5787 } else {
5788 err.addNote("referenced by input section '{s}' '{f}{f}'", .{
5789 section_name,
5790 other_ioi.path(coff).fmtEscapeString(),
5791 fmtMemberNameString(other_ioi.memberName(coff)),
5792 });
5793 }
5794 } else {
5795 err.addNote("referenced by input symbol '{s}' from '{f}{f}'", .{
5796 loc_sym.gmi.name(coff).toSlice(coff),
5797 other_ioi.path(coff).fmtEscapeString(),
5798 fmtMemberNameString(other_ioi.memberName(coff)),
5799 });
5800 }
5801 },
5802 .import_thunk => |gmi| err.addNote("referenced by import thunk for '{s}'", .{
5803 gmi.name(coff).toSlice(coff),
5804 }),
5805 inline .nav,
5806 .uav,
5807 .lazy_code,
5808 .lazy_const_data,
5809 => |val, tag| {
5810 err.addNote("referenced by '{f}'", .{
5811 format: switch (tag) {
5812 .nav => {
5813 const ip = &comp.zcu.?.intern_pool;
5814 break :format ip.getNav(val.navIndex(coff)).fqn.fmt(ip);
5815 },
5816 .uav => Value.fromInterned(val.uavValue(coff)).fmtValue(.{
5817 .zcu = coff.base.comp.zcu.?,
5818 .tid = tid,
5819 }),
5820 inline .lazy_code, .lazy_const_data => Type.fromInterned(val.lazySymbol(coff).ty).fmt(.{
5821 .zcu = coff.base.comp.zcu.?,
5822 .tid = tid,
5823 }),
5824 else => unreachable,
5825 },
5826 });
5827 },
5828 else => unreachable,
5829 }
5830 }
5831
5832 if (num_unique_references > max_notes)
5833 err.addNote("referenced {d} more times", .{num_unique_references - max_notes});
5834 } else if (i != start_i and
5835 coff.relocs.items[undef_indices.items[i - 1]].loc != coff.relocs.items[undef_indices.items[i]].loc)
5836 {
5837 num_unique_references += 1;
5838 }
5839 }
5840
5841 return error.AlreadyReported;
5842}
5843
5844pub fn flush(
5845 coff: *Coff,
5846 arena: std.mem.Allocator,
5847 tid: Zcu.PerThread.Id,
5848 prog_node: std.Progress.Node,
5849) link.Error!void {
5850 _ = arena;
5851 const sub_prog_node = prog_node.start("COFF Flush", 0);
5852 defer sub_prog_node.end();
5853
5854 const comp = coff.base.comp;
5855
5856 while (try coff.resolve(tid)) {}
5857 while (try coff.idle(tid)) {}
5858
5859 // This has to occur after all other flushMoved / flushResized have resolved,
5860 // but it will also generate one more set of resizes and moves.
5861 if (coff.symbol_table.pending_shrink) {
5862 coff.symbol_table.pending_shrink = false;
5863
5864 const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
5865 coff.symbol_table.ni.resizeLeaf(
5866 &coff.mf,
5867 comp.gpa,
5868 number_of_symbols * std.coff.Symbol.sizeOf(),
5869 ) catch |err| switch (err) {
5870 else => |e| return e,
5871 error.MappedFileIo => return comp.link_diags.fail(
5872 "linker failed to compact symbol table: {t}",
5873 .{coff.mf.io_err.?},
5874 ),
5875 };
5876 }
5877 while (try coff.idle(tid)) {}
5878
5879 if (coff.isImage())
5880 try coff.reportUndefs(tid);
5881
5882 if (comp.emit_implib) |implib_file|
5883 coff.flushImplib(implib_file) catch |err|
5884 return comp.link_diags.fail("flushing implib '{s}' failed: {t}", .{ implib_file, err });
5885
5886 coff.mf.flush() catch |err| switch (err) {
5887 error.Canceled => |e| return e,
5888 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
5889 };
5890
5891 if (coff.options.enable_link_snapshots)
5892 coff.dumpStderr(tid) catch |err|
5893 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
5894}
5895
5896/// Runs a single "resolution" task.
5897/// These are tasks that need to modify the node structure in some way.
5898/// They must run in a defined order with respect to linker tasks.
5899fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5900 const comp = coff.base.comp;
5901 task: {
5902 while (coff.section_merge_pending_index < coff.section_merges.count()) {
5903 defer coff.section_merge_pending_index += 1;
5904 const sub_prog_node = coff.synth_prog_node.start(
5905 coff.section_merges.keys()[coff.section_merge_pending_index].toSlice(coff),
5906 0,
5907 );
5908 defer sub_prog_node.end();
5909 coff.flushSectionMerge(coff.section_merge_pending_index) catch |err| switch (err) {
5910 //error.OutOfMemory => |e| return e,
5911 else => |e| return comp.link_diags.fail(
5912 "linker failed to merge section {s} into {s}: {t}",
5913 .{
5914 coff.section_merges.keys()[coff.section_merge_pending_index].toSlice(coff),
5915 coff.section_merges.values()[coff.section_merge_pending_index].toSlice(coff),
5916 e,
5917 },
5918 ),
5919 };
5920 break :task;
5921 }
5922 while (coff.pending_uavs.pop()) |pending_uav| {
5923 const sub_prog_node = coff.idleProgNode(tid, coff.const_prog_node, .{ .uav = pending_uav.key });
5924 defer sub_prog_node.end();
5925 coff.flushUav(
5926 .{ .zcu = comp.zcu.?, .tid = tid },
5927 pending_uav.key,
5928 pending_uav.value.alignment,
5929 ) catch |err| switch (err) {
5930 else => |e| return e,
5931 error.MappedFileIo => return comp.link_diags.fail(
5932 "linker failed to lower constant: {t}",
5933 .{coff.mf.io_err.?},
5934 ),
5935 };
5936 break :task;
5937 }
5938 if (coff.pending_input) |pending_iami| {
5939 const name_slice = pending_iami.member(coff).name.toSlice(coff);
5940 const sub_prog_node = coff.input_prog_node.start(
5941 name_slice,
5942 0,
5943 );
5944 defer sub_prog_node.end();
5945 coff.pending_input = null;
5946 coff.flushInputMember(pending_iami) catch |err| switch (err) {
5947 error.OutOfMemory => return error.OutOfMemory,
5948 else => |e| return comp.link_diags.fail(
5949 "linker failed to load archive member '{f}{f}': {t}",
5950 .{
5951 pending_iami.member(coff).iai.path(coff),
5952 fmtMemberNameString(name_slice),
5953 e,
5954 },
5955 ),
5956 };
5957 break :task;
5958 }
5959 if (coff.exports_complete and coff.global_pending_index < coff.globals.count()) {
5960 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
5961 const sub_prog_node = coff.synth_prog_node.start(
5962 gmi.name(coff).toSlice(coff),
5963 0,
5964 );
5965 defer sub_prog_node.end();
5966 if (coff.flushGlobal(gmi) catch |err| switch (err) {
5967 else => |e| return e,
5968 error.MappedFileIo => return comp.link_diags.fail(
5969 "linker failed to lower constant: {t}",
5970 .{coff.mf.io_err.?},
5971 ),
5972 }) coff.global_pending_index += 1;
5973 break :task;
5974 }
5975 if (coff.exports_complete and coff.pending_special_symbol != .none) {
5976 coff.pending_special_symbol = coff.flushSpecialSymbol(coff.pending_special_symbol) catch |err|
5977 switch (err) {
5978 error.OutOfMemory => |e| return e,
5979 else => |e| return comp.link_diags.fail(
5980 "linker failed to flush special symbols: {t}",
5981 .{e},
5982 ),
5983 };
5984 break :task;
5985 }
5986 var lazy_it = coff.lazy.iterator();
5987 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
5988 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };
5989 const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
5990 lazy.value.pending_index += 1;
5991 const kind = switch (lmr.kind) {
5992 .code => "code",
5993 .const_data => "data",
5994 };
5995 var name: [std.Progress.Node.max_name_len]u8 = undefined;
5996 const sub_prog_node = coff.synth_prog_node.start(
5997 std.mem.print(&name, "lazy {s} for {f}", .{
5998 kind,
5999 Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt),
6000 }) catch &name,
6001 0,
6002 );
6003 defer sub_prog_node.end();
6004 coff.flushLazy(pt, lmr) catch |err| switch (err) {
6005 else => |e| return e,
6006 error.MappedFileIo => return comp.link_diags.fail(
6007 "linker failed to lower lazy {s}: {t}",
6008 .{ kind, coff.mf.io_err.? },
6009 ),
6010 };
6011 break :task;
6012 };
6013 if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) {
6014 defer coff.symbol_table.pending_symbol_index += 1;
6015 const si = coff.symbol_table.symbols.keys()[coff.symbol_table.pending_symbol_index];
6016 const sym = si.get(coff);
6017 const sub_prog_node = coff.idleProgNode(
6018 tid,
6019 coff.symbol_prog_node,
6020 if (sym.ni.unwrap()) |sym_ni|
6021 coff.getNode(sym_ni)
6022 else
6023 .{ .import_thunk = sym.gmi },
6024 );
6025 defer sub_prog_node.end();
6026 coff.flushSymbolTableEntry(
6027 coff.symbol_table.pending_symbol_index,
6028 .{ .zcu = comp.zcu.?, .tid = tid },
6029 ) catch |err| switch (err) {
6030 error.OutOfMemory => return error.OutOfMemory,
6031 else => |e| return comp.link_diags.fail(
6032 "linker failed to flush symbol table entry: {t}",
6033 .{e},
6034 ),
6035 };
6036 break :task;
6037 }
6038 }
6039
6040 if (coff.section_merge_pending_index < coff.section_merges.count()) return true;
6041 if (coff.pending_uavs.count() > 0) return true;
6042 if (coff.pending_input != null) return true;
6043 if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true;
6044 assert(!coff.exports_complete or coff.inputs_complete);
6045 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
6046 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
6047 if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) return true;
6048 return false;
6049}
6050
6051pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
6052 // Idle tasks should not modify create / modify nodes, otherwise the output is not reproducible.
6053 coff.mf.nodes_lock.lock();
6054 defer coff.mf.nodes_lock.unlock();
6055
6056 const comp = coff.base.comp;
6057 task: {
6058 // TODO: Idle task for flushing obj into lib
6059 if (coff.input_section_pending_index < coff.input_sections.items.len) {
6060 const isi: Node.InputSection.Index = @fromBackingInt(@intCast(coff.input_section_pending_index));
6061 coff.input_section_pending_index += 1;
6062 const sub_prog_node = coff.idleProgNode(tid, coff.input_prog_node, coff.getNode(isi.symbol(coff).node(coff)));
6063 defer sub_prog_node.end();
6064 coff.flushInputSection(isi) catch |err| switch (err) {
6065 else => |e| {
6066 const ioi = isi.input(coff);
6067 return comp.link_diags.fail(
6068 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
6069 .{
6070 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
6071 ioi.path(coff).fmtEscapeString(),
6072 fmtMemberNameString(ioi.memberName(coff)),
6073 e,
6074 },
6075 );
6076 },
6077 };
6078 break :task;
6079 }
6080 while (coff.mf.updates.pop()) |ni| {
6081 const clean_moved = ni.cleanMoved(&coff.mf);
6082 const clean_resized = ni.cleanResized(&coff.mf);
6083 if (clean_moved or clean_resized) {
6084 const sub_prog_node =
6085 coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni));
6086 defer sub_prog_node.end();
6087 if (clean_moved) try coff.flushMoved(ni);
6088 if (clean_resized) try coff.flushResized(ni);
6089 break :task;
6090 } else coff.mf.update_prog_node.completeOne();
6091 }
6092 while (coff.pending_members.pop()) |pending_mi| {
6093 const sub_prog_node = coff.idleProgNode(
6094 tid,
6095 coff.symbol_prog_node,
6096 coff.getNode(pending_mi.key.get(coff).content_ni),
6097 );
6098 defer sub_prog_node.end();
6099 try coff.flushMember(pending_mi.key);
6100 break :task;
6101 }
6102 if (coff.exports_complete and coff.export_table.pending_sort) {
6103 defer coff.export_table.pending_sort = false;
6104 const sub_prog_node = coff.idleProgNode(
6105 tid,
6106 coff.synth_prog_node,
6107 coff.getNode(coff.export_table.ni),
6108 );
6109 defer sub_prog_node.end();
6110
6111 coff.flushExportsSort();
6112 break :task;
6113 }
6114 }
6115 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
6116 if (coff.mf.updates.items.len > 0) return true;
6117 if (coff.pending_members.count() > 0) return true;
6118 if (coff.exports_complete and coff.export_table.pending_sort) return true;
6119 return false;
6120}
6121
6122fn idleProgNode(
6123 coff: *Coff,
6124 tid: Zcu.PerThread.Id,
6125 prog_node: std.Progress.Node,
6126 node: Node,
6127) std.Progress.Node {
6128 var name: [std.Progress.Node.max_name_len]u8 = undefined;
6129 return prog_node.start(name: switch (node) {
6130 else => |tag| @tagName(tag),
6131 .image_section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
6132 inline .pseudo_section, .object_section => |smi| smi.name(coff).toSlice(coff),
6133 .input_section => |isi| {
6134 const ioi = isi.input(coff);
6135 break :name std.mem.print(&name, "{f}{f} {s}", .{
6136 ioi.path(coff).fmtEscapeString(),
6137 fmtMemberNameString(ioi.memberName(coff)),
6138 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf).unwrap().?).object_section.name(coff).toSlice(coff),
6139 }) catch &name;
6140 },
6141 .import_thunk => |gmi| gmi.name(coff).toSlice(coff),
6142 .nav => |nmi| {
6143 const ip = &coff.base.comp.zcu.?.intern_pool;
6144 break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
6145 },
6146 .uav => |umi| std.mem.print(&name, "{f}", .{
6147 Value.fromInterned(umi.uavValue(coff)).fmtValue(.{
6148 .zcu = coff.base.comp.zcu.?,
6149 .tid = tid,
6150 }),
6151 }) catch &name,
6152 .archive_member => |mi| &mi.get(coff).headerPtr(coff).name,
6153 }, 0);
6154}
6155
6156fn flushUav(
6157 coff: *Coff,
6158 pt: Zcu.PerThread,
6159 umi: Node.UavMapIndex,
6160 uav_align: InternPool.Alignment,
6161) !void {
6162 const zcu = pt.zcu;
6163 const gpa = zcu.gpa;
6164
6165 const uav_val = umi.uavValue(coff);
6166 const si = umi.symbol(coff);
6167 const ni = ni: {
6168 switch (si.get(coff).ni) {
6169 .none => {
6170 const sec_si = (try coff.objectSectionMapIndex(
6171 .@".rdata",
6172 coff.mf.flags.block_size,
6173 .{ .read = true, .initialized = true },
6174 )).symbol(coff);
6175 try coff.nodes.ensureUnusedCapacity(gpa, 1);
6176 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
6177 const sym = si.get(coff);
6178 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
6179 .alignment = .fromIp(uav_align),
6180 .moved = true,
6181 });
6182 coff.nodes.appendAssumeCapacity(.{ .uav = umi });
6183 sym.ni = .wrap(ni);
6184 sym.section_number = sec_si.get(coff).section_number;
6185 },
6186 else => {
6187 if (Alignment.compare(
6188 si.get(coff).ni.unwrap().?.alignment(&coff.mf),
6189 .gte,
6190 .fromIp(uav_align),
6191 )) {
6192 return;
6193 }
6194 si.deleteLocationRelocs(coff);
6195 },
6196 }
6197 const sym = si.get(coff);
6198 assert(sym.loc_relocs == .none);
6199 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6200 if (!isImage(coff) and sym.target_relocs != .none)
6201 try coff.pendingSymbolTableEntry(si);
6202
6203 break :ni sym.ni.unwrap().?;
6204 };
6205
6206 var nw: MappedFile.Node.Writer = undefined;
6207 ni.writer(&coff.mf, gpa, &nw);
6208 defer nw.deinit();
6209 codegen.generateSymbol(
6210 &coff.base,
6211 pt,
6212 .fromInterned(uav_val),
6213 &nw.interface,
6214 .{ .atom_index = @fromBackingInt(@intCast(@backingInt(si))) },
6215 ) catch |err| switch (err) {
6216 error.WriteFailed => return nw.err.?,
6217 else => |e| return e,
6218 };
6219 si.get(coff).extra.size = @intCast(nw.interface.end);
6220 try si.applyLocationRelocs(coff);
6221}
6222
6223fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !void {
6224 const si = gmi.symbol(coff);
6225 const sym = si.get(coff);
6226 const alias_sym = alias_si.get(coff);
6227 assert(sym.section_number == .UNDEFINED);
6228 assert(sym.loc_relocs == .none);
6229
6230 log.debug("aliasGlobal({s}, {?s}) {d}->{d} ({?s})", .{
6231 gmi.name(coff).toSlice(coff),
6232 gmi.libName(coff).toSlice(coff),
6233 si,
6234 alias_si,
6235 if (alias_sym.gmi != .none) alias_sym.gmi.name(coff).toSlice(coff) else null,
6236 });
6237
6238 var ri = sym.target_relocs;
6239 while (ri != .none) {
6240 const reloc = ri.get(coff);
6241 assert(reloc.target == si);
6242 reloc.target = alias_si;
6243 if (reloc.next == .none) {
6244 reloc.next = alias_sym.target_relocs;
6245 if (alias_sym.target_relocs != .none)
6246 alias_sym.target_relocs.get(coff).prev = ri;
6247 break;
6248 }
6249 ri = reloc.next;
6250 }
6251
6252 const prev_target_relocs = alias_sym.target_relocs;
6253 if (sym.target_relocs != .none)
6254 alias_sym.target_relocs = sym.target_relocs;
6255 sym.target_relocs = .none;
6256 sym.gmi = alias_sym.gmi;
6257 coff.globals.values()[gmi.unwrap().?].si = alias_si;
6258 // Only apply the new relocs
6259 try alias_si.applyTargetRelocs(coff, prev_target_relocs);
6260}
6261
6262fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6263 const comp = coff.base.comp;
6264 const gpa = comp.gpa;
6265 const name = gmi.name(coff);
6266 const si = gmi.symbol(coff);
6267
6268 log.debug(
6269 "flushGlobal({s}, {?s}) = n{d} {d}@{d}",
6270 .{
6271 name.toSlice(coff),
6272 gmi.libName(coff).toSlice(coff),
6273 si.get(coff).ni,
6274 si,
6275 si.get(coff).section_number,
6276 },
6277 );
6278
6279 if (!coff.isImage()) {
6280 try coff.pendingSymbolTableEntry(si);
6281 if (coff.isArchive() and si.get(coff).ni != .none)
6282 try coff.ensureMemberSymbol(
6283 coff.getNode(Node.known.zcu_member).archive_member,
6284 name,
6285 );
6286
6287 return true;
6288 }
6289
6290 if (si.get(coff).ni != .none)
6291 return true;
6292
6293 const Import = struct {
6294 lib_name: String,
6295 name: String.Optional,
6296 ordinal_hint: u16,
6297 kind: enum {
6298 iat_ptr,
6299 thunk,
6300 },
6301 };
6302
6303 const import: Import = import: {
6304 const sym = si.get(coff);
6305 const name_slice = name.toSlice(coff);
6306 const imp_match = std.mem.startsWith(u8, name_slice, imp_prefix);
6307
6308 // Globals may have the __imp_ prefix already if they are undef externals from another input.
6309 assert(sym.flags.dll_storage_class != .dllexport);
6310 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
6311 .{ name, imp_match }
6312 else name: {
6313 try coff.ensureUnusedStringCapacity(imp_prefix.len + name_slice.len);
6314 const imp_name = try std.fmt.allocPrint(gpa, imp_prefix ++ "{s}", .{name_slice});
6315 defer gpa.free(imp_name);
6316 break :name .{ coff.getOrPutStringAssumeCapacity(imp_name), true };
6317 };
6318
6319 const opt_alt_search_name = coff.alternate_names.get(search_name);
6320 const search_libs = switch (sym.flags.value_tag) {
6321 .weak_alias_si, .weak_alias_name => switch (sym.flags.weak_external_strat) {
6322 .none => unreachable,
6323 .no_library => false,
6324 .library,
6325 .alias,
6326 => true,
6327 .anti_dependency => return comp.link_diags.fail(
6328 // TODO: Figure out what the purpose of this is
6329 "TODO support anti_dependency weak external: {s}",
6330 .{name.toSlice(coff)},
6331 ),
6332 },
6333 else => true,
6334 };
6335
6336 const opt_indices_lists: []const ?InputArchive.SearchList = if (search_libs) &.{
6337 coff.input_archive_symbol_indices.get(search_name),
6338 if (opt_alt_search_name) |alt| coff.input_archive_symbol_indices.get(alt) else null,
6339 } else &.{};
6340
6341 for (opt_indices_lists) |opt_indices_list| {
6342 const indices_list = opt_indices_list orelse continue;
6343 var iter: InputArchive.Member.Symbol.Index = indices_list.first;
6344 while (true) {
6345 const archive_sym = &coff.input_archive_symbols.items[@backingInt(iter)];
6346 const member = &coff.input_archive_members.items[@backingInt(archive_sym.iami)];
6347 member: switch (member.content) {
6348 .object => if (!member.flags.is_loaded) {
6349 if (gmi.libName(coff).unwrap()) |lib_name|
6350 if (!std.ascii.eqlIgnoreCase(
6351 lib_name.toSlice(coff),
6352 member.iai.path(coff).stem(),
6353 )) break :member;
6354
6355 // Try loading the input member and then retry.
6356 // This could still be a member containing imports
6357 // that use the older non-IMPORT_HEADER method.
6358 coff.pending_input = archive_sym.iami;
6359 return false;
6360 },
6361 .import => |import| {
6362 if (gmi.libName(coff).unwrap()) |lib_name|
6363 if (!std.ascii.eqlIgnoreCase(
6364 import.lib_name.toSlice(coff),
6365 lib_name.toSlice(coff),
6366 )) break :member;
6367
6368 const imp_name: String.Optional = name: switch (import.name_type) {
6369 .NAME,
6370 .NAME_NOPREFIX,
6371 .NAME_UNDECORATE,
6372 => |tag| {
6373 const symbol_name: []const u8 = import.symbol_name.toSlice(coff);
6374 const end_match = std.mem.endsWith(u8, name_slice, symbol_name);
6375 const len_delta = name_slice.len -% symbol_name.len;
6376 if (!end_match or
6377 (!imp_match and len_delta != 0) or
6378 (imp_match and len_delta != imp_prefix.len))
6379 return comp.link_diags.fail(
6380 "global '{s}' has mismatched symbol name in import header: '{s}'",
6381 .{
6382 name.toSlice(coff),
6383 import.symbol_name.toSlice(coff),
6384 },
6385 );
6386
6387 const imp_name = if (tag == .NAME) import.symbol_name else undecorated: {
6388 var imp_name = std.mem.trimStart(u8, symbol_name, "?@_");
6389 if (tag == .NAME_UNDECORATE)
6390 imp_name = std.mem.sliceTo(imp_name, '@');
6391
6392 try coff.ensureUnusedStringCapacity(imp_name.len);
6393 break :undecorated coff.getOrPutStringAssumeCapacity(imp_name);
6394 };
6395
6396 break :name imp_name.toOptional();
6397 },
6398 .ORDINAL => break :name .none,
6399 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
6400 };
6401
6402 break :import .{
6403 .lib_name = import.lib_name,
6404 .name = imp_name,
6405 .ordinal_hint = import.import_ordinal_hint,
6406 .kind = if (import.type == .CODE and !is_imp) .thunk else .iat_ptr,
6407 };
6408 },
6409 }
6410
6411 if (archive_sym.next == iter) break;
6412 iter = archive_sym.next;
6413 }
6414 }
6415
6416 switch (sym.flags.value_tag) {
6417 .weak_alias_si => {
6418 try coff.aliasGlobal(gmi, sym.value.weak_alias_si);
6419 return true;
6420 },
6421 .weak_alias_name => {
6422 // Convert an unresolved weak external that itself refers to an undef external
6423 // into a (possibly new) global, so it can be resolved separately.
6424 const alias_gop = try coff.getOrPutGlobalSymbol(.{
6425 .name = sym.value.weak_alias_name.toSlice(coff),
6426 });
6427 try coff.aliasGlobal(gmi, alias_gop.value_ptr.si);
6428 return true;
6429 },
6430 else => {},
6431 }
6432
6433 // If there was an object that had the alternate name, we've attempted to load it
6434 if (opt_alt_search_name) |alt_search_name| {
6435 if (coff.globals.get(alt_search_name)) |alias_global| {
6436 try coff.aliasGlobal(gmi, alias_global.si);
6437 return true;
6438 }
6439 }
6440
6441 // Allow importing symbols with no implib entry, if a lib_name was specified.
6442 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,
6443 // which are not in the implib.
6444 if (sym.flags.type != .unknown) {
6445 if (gmi.libName(coff).unwrap()) |lib_name| break :import .{
6446 .lib_name = lib_name,
6447 .name = name.toOptional(),
6448 .ordinal_hint = 0,
6449 .kind = if (sym.flags.type == .code) .thunk else .iat_ptr,
6450 };
6451 }
6452
6453 return true;
6454 };
6455
6456 try coff.nodes.ensureUnusedCapacity(gpa, 4);
6457 try coff.symbols.ensureUnusedCapacity(gpa, 2);
6458
6459 const target_endian = coff.targetEndian();
6460 const addr_info = coff.targetAddrInfo();
6461 const lib_name = import.lib_name.toSlice(coff);
6462 const gop = try coff.import_table.entries.getOrPutAdapted(
6463 gpa,
6464 lib_name,
6465 ImportTable.Adapter{ .coff = coff },
6466 );
6467 const import_hint_name_align: Alignment = .@"2";
6468 if (!gop.found_existing) {
6469 errdefer _ = coff.import_table.entries.pop();
6470 try coff.import_table.ni.resizeLeaf(
6471 &coff.mf,
6472 gpa,
6473 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
6474 );
6475 const import_hint_name_table_len =
6476 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
6477 const idata_section_ni = coff.import_table.ni.parent(&coff.mf).unwrap().?;
6478 const import_lookup_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{
6479 .size = addr_info.size * 2,
6480 .alignment = addr_info.alignment,
6481 .moved = true,
6482 });
6483 const import_address_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{
6484 .size = addr_info.size * 2,
6485 .alignment = addr_info.alignment,
6486 .moved = true,
6487 });
6488 const import_address_table_si = coff.addSymbolAssumeCapacity();
6489 {
6490 const import_address_table_sym = import_address_table_si.get(coff);
6491 import_address_table_sym.ni = .wrap(import_address_table_ni);
6492 assert(import_address_table_sym.loc_relocs == .none);
6493 import_address_table_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6494 import_address_table_sym.section_number =
6495 coff.getNode(idata_section_ni).object_section.symbol(coff).get(coff).section_number;
6496 }
6497 const import_hint_name_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{
6498 .size = import_hint_name_table_len,
6499 .alignment = import_hint_name_align,
6500 .moved = true,
6501 });
6502 gop.value_ptr.* = .{
6503 .import_lookup_table_ni = import_lookup_table_ni,
6504 .import_address_table_si = import_address_table_si,
6505 .import_hint_name_table_ni = import_hint_name_table_ni,
6506 .import_address_table_symbols = .empty,
6507 .len = 0,
6508 .hint_name_len = @intCast(import_hint_name_table_len),
6509 };
6510 const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
6511 @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
6512 @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
6513 @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
6514 coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @fromBackingInt(@intCast(gop.index)) });
6515 coff.nodes.appendAssumeCapacity(.{ .import_address_table = @fromBackingInt(@intCast(gop.index)) });
6516 coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @fromBackingInt(@intCast(gop.index)) });
6517
6518 const import_directory_entries = coff.importDirectoryTableSlice()[gop.index..][0..2];
6519 import_directory_entries.* = .{ .{
6520 .import_lookup_table_rva = coff.computeNodeRva(import_lookup_table_ni),
6521 .time_date_stamp = 0,
6522 .forwarder_chain = 0,
6523 .name_rva = coff.computeNodeRva(import_hint_name_table_ni),
6524 .import_address_table_rva = coff.computeNodeRva(import_address_table_ni),
6525 }, .{
6526 .import_lookup_table_rva = 0,
6527 .time_date_stamp = 0,
6528 .forwarder_chain = 0,
6529 .name_rva = 0,
6530 .import_address_table_rva = 0,
6531 } };
6532 if (target_endian != native_endian)
6533 std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries);
6534 }
6535
6536 log.debug(
6537 "flushGlobalImport({s}, {?s}, {d}, {s})",
6538 .{ name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name },
6539 );
6540
6541 const iat_symbol_gop = try coff.import_table.iat_symbol_indices.getOrPut(gpa, .{
6542 .iti = @fromBackingInt(@intCast(gop.index)),
6543 .name = import.name,
6544 .ordinal_hint = import.ordinal_hint,
6545 });
6546 if (!iat_symbol_gop.found_existing) {
6547 const import_symbol_index = gop.value_ptr.len;
6548 iat_symbol_gop.value_ptr.* = import_symbol_index;
6549
6550 gop.value_ptr.len = import_symbol_index + 1;
6551 const new_symbol_table_size = addr_info.size * (import_symbol_index + 2);
6552
6553 try gop.value_ptr.import_lookup_table_ni.resizeLeaf(&coff.mf, gpa, new_symbol_table_size);
6554 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
6555 try import_address_table_ni.resizeLeaf(&coff.mf, gpa, new_symbol_table_size);
6556
6557 const opt_imp_name = import.name.toSlice(coff);
6558 const opt_import_hint_name_index = if (opt_imp_name) |imp_name| blk: {
6559 const import_hint_name_index = gop.value_ptr.hint_name_len;
6560 gop.value_ptr.hint_name_len = @intCast(
6561 import_hint_name_align.forward(import_hint_name_index + 2 + imp_name.len + 1),
6562 );
6563 try gop.value_ptr.import_hint_name_table_ni.resizeLeaf(&coff.mf, gpa, gop.value_ptr.hint_name_len);
6564 break :blk import_hint_name_index;
6565 } else null;
6566
6567 const import_hint_name_rva = if (opt_import_hint_name_index) |import_hint_name_index| blk: {
6568 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
6569 const ordinal_hint: *u16 = @ptrCast(@alignCast(import_hint_name_slice[import_hint_name_index..][0..2]));
6570 ordinal_hint.* = std.mem.nativeTo(u16, import.ordinal_hint, target_endian);
6571 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..opt_imp_name.?.len], opt_imp_name.?);
6572 @memset(import_hint_name_slice[import_hint_name_index + 2 + opt_imp_name.?.len ..], 0);
6573 break :blk coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
6574 } else 0;
6575
6576 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
6577 const import_address_slice = import_address_table_ni.slice(&coff.mf);
6578 switch (addr_info.magic) {
6579 _ => unreachable,
6580 inline .PE32, .@"PE32+" => |ct_magic| {
6581 const Entry = std.coff.ImportLookupTableEntry(ct_magic);
6582 const import_lookup_table: []Entry = @ptrCast(@alignCast(import_lookup_slice));
6583 const import_address_table: []Entry = @ptrCast(@alignCast(import_address_slice));
6584 var import_hint_name_rvas: [2]Entry = .{
6585 .{
6586 .payload = if (import.name == .none)
6587 .{ .ordinal = .{ .ordinal = import.ordinal_hint } }
6588 else
6589 .{ .hint_name_rva = @intCast(import_hint_name_rva) },
6590 .is_ordinal = import.name == .none,
6591 },
6592 @bitCast(@as(@typeInfo(Entry).@"struct".backing_integer.?, 0)),
6593 };
6594 if (native_endian != target_endian)
6595 for (&import_hint_name_rvas) |*v| std.mem.byteSwapAllFields(Entry, v);
6596
6597 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
6598 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
6599 },
6600 }
6601 }
6602
6603 const sym = si.get(coff);
6604 assert(sym.loc_relocs == .none);
6605 const iat_offset: u32 = @intCast(addr_info.size * iat_symbol_gop.value_ptr.*);
6606 switch (import.kind) {
6607 .iat_ptr => {
6608 const iat_sym = gop.value_ptr.import_address_table_si.get(coff);
6609 sym.section_number = iat_sym.section_number;
6610 sym.ni = iat_sym.ni;
6611 sym.setValue(.{ .node_offset = iat_offset });
6612 (try gop.value_ptr.import_address_table_symbols.addOne(gpa)).* = si;
6613 },
6614 .thunk => {
6615 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6616
6617 const target = &comp.root_mod.resolved_target.result;
6618 const alignment: Alignment = switch (comp.root_mod.optimize_mode) {
6619 .debug,
6620 .safe,
6621 .fast,
6622 => .fromIp(target_util.defaultFunctionAlignment(target)),
6623 .small => .fromIp(target_util.minFunctionAlignment(target)),
6624 };
6625 const parent_si = (try coff.pseudoSectionMapIndex(
6626 .@".thunks",
6627 alignment,
6628 .{ .execute = true, .read = true },
6629 )).symbol(coff);
6630
6631 const parent_sym = parent_si.get(coff);
6632 sym.section_number = parent_sym.section_number;
6633
6634 switch (coff.targetLoad(&coff.headerPtr().machine)) {
6635 else => |tag| @panic(@tagName(tag)),
6636 .AMD64 => {
6637 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
6638 const ni = try parent_sym.ni.unwrap().?.addFloatingChild(&coff.mf, gpa, .{
6639 .alignment = alignment,
6640 .size = alignment.forward(init.len),
6641 });
6642 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
6643 sym.ni = .wrap(ni);
6644 sym.extra.size = init.len;
6645 try coff.addReloc(
6646 si,
6647 init.len - 4,
6648 gop.value_ptr.import_address_table_si,
6649 .{ .known = iat_offset },
6650 .{ .AMD64 = .REL32 },
6651 );
6652 },
6653 }
6654 coff.nodes.appendAssumeCapacity(.{ .import_thunk = gmi });
6655 },
6656 }
6657
6658 try si.flushMoved(coff);
6659 return true;
6660}
6661
6662fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol {
6663 const comp = coff.base.comp;
6664
6665 if (!coff.isImage()) return .none;
6666 const gpa = comp.gpa;
6667 const machine = coff.targetLoad(&coff.headerPtr().machine);
6668 const target = &comp.root_mod.resolved_target.result;
6669
6670 return next: switch (pending) {
6671 .entry => {
6672 // TODO: Use explicitly specified entry if set, add err if not found
6673 const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe())
6674 if (comp.config.link_libc) switch (coff.optionalHeaderField(.subsystem)) {
6675 .WINDOWS_CUI => &.{
6676 .{ "main", "mainCRTStartup" },
6677 .{ "wmain", "wmainCRTStartup" },
6678 },
6679 .WINDOWS_GUI => &.{
6680 .{ "WinMain", "WinMainCRTStartup" },
6681 .{ "wWinMain", "wWinMainCRTStartup" },
6682 },
6683 else => unreachable,
6684 } else &.{
6685 .{ "wWinMainCRTStartup", "wWinMainCRTStartup" },
6686 }
6687 else
6688 &.{.{ null, if (target.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup" }};
6689
6690 const entry_si = for (entries) |entry| {
6691 if (entry[0]) |required_name|
6692 if (coff.getDefinedGlobal(required_name) == .null) continue;
6693
6694 break try coff.globalSymbol(.{ .name = entry[1], .type = .code });
6695 } else .null;
6696
6697 if (entry_si != .null) {
6698 log.debug(
6699 "entry({s}, {d})",
6700 .{ entry_si.get(coff).gmi.name(coff).toSlice(coff), entry_si },
6701 );
6702
6703 try coff.symbols.ensureUnusedCapacity(gpa, 1);
6704 const optional_hdr_si = coff.addSymbolAssumeCapacity();
6705 const optional_hdr_sym = optional_hdr_si.get(coff);
6706 optional_hdr_sym.ni = .wrap(Node.known.optional_header);
6707 assert(optional_hdr_sym.loc_relocs == .none);
6708 optional_hdr_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6709
6710 const optional_hdr = coff.optionalHeaderStandardPtr();
6711 optional_hdr.address_of_entry_point = std.mem.nativeTo(
6712 u32,
6713 entry_si.get(coff).rva,
6714 coff.targetEndian(),
6715 );
6716
6717 try coff.addReloc(
6718 optional_hdr_si,
6719 @intFromPtr(&optional_hdr.address_of_entry_point) - @intFromPtr(optional_hdr),
6720 entry_si,
6721 .{ .known = 0 },
6722 switch (machine) {
6723 else => |tag| @panic(@tagName(tag)),
6724 .AMD64 => .{ .AMD64 = .ADDR32NB },
6725 .I386 => .{ .I386 = .DIR32NB },
6726 },
6727 );
6728 }
6729
6730 // Referencing the startup functions may trigger loading the object containing them,
6731 // we need to wait until that is done before looking for further symbols.
6732 break :next .tls;
6733 },
6734 .tls => {
6735 if (coff.getDefinedGlobal("_tls_used").unwrap()) |tls_used_si| {
6736 log.debug("tlsDir({d})", .{tls_used_si});
6737
6738 const tls_directory = coff.dataDirectoryPtr(.TLS);
6739 tls_directory.* = .{
6740 .virtual_address = tls_used_si.get(coff).rva,
6741 .size = switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
6742 _ => unreachable,
6743 .PE32 => 24,
6744 .@"PE32+" => 40,
6745 },
6746 };
6747 if (coff.targetEndian() != native_endian)
6748 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
6749
6750 try coff.symbols.ensureUnusedCapacity(gpa, 1);
6751 const data_dir_si = coff.addSymbolAssumeCapacity();
6752 const data_dir_sym = data_dir_si.get(coff);
6753 data_dir_sym.ni = .wrap(Node.known.data_directories);
6754 assert(data_dir_sym.loc_relocs == .none);
6755 data_dir_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6756
6757 try coff.addReloc(
6758 data_dir_si,
6759 @intFromPtr(&tls_directory.virtual_address) - @intFromPtr(coff.dataDirectorySlice().ptr),
6760 tls_used_si,
6761 .{ .known = 0 },
6762 switch (machine) {
6763 else => |tag| @panic(@tagName(tag)),
6764 .AMD64 => .{ .AMD64 = .ADDR32NB },
6765 .I386 => .{ .I386 = .DIR32NB },
6766 },
6767 );
6768 }
6769
6770 break :next .none;
6771 },
6772 .none => unreachable,
6773 };
6774}
6775
6776fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
6777 const zcu = pt.zcu;
6778 const gpa = zcu.gpa;
6779
6780 const lazy = lmr.lazySymbol(coff);
6781 const si = lmr.symbol(coff);
6782 const ni = ni: {
6783 const sym = si.get(coff);
6784 switch (sym.ni) {
6785 .none => {
6786 try coff.nodes.ensureUnusedCapacity(gpa, 1);
6787 const sec_si: Symbol.Index = switch (lazy.kind) {
6788 .code => .text,
6789 .const_data => .rdata,
6790 };
6791 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ .moved = true });
6792 coff.nodes.appendAssumeCapacity(switch (lazy.kind) {
6793 .code => .{ .lazy_code = @fromBackingInt(@intCast(lmr.index)) },
6794 .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(lmr.index)) },
6795 });
6796 sym.ni = .wrap(ni);
6797 sym.section_number = sec_si.get(coff).section_number;
6798 },
6799 else => si.deleteLocationRelocs(coff),
6800 }
6801 assert(sym.loc_relocs == .none);
6802 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6803 if (!isImage(coff) and sym.target_relocs != .none)
6804 try coff.pendingSymbolTableEntry(si);
6805
6806 break :ni sym.ni.unwrap().?;
6807 };
6808
6809 var required_alignment: InternPool.Alignment = .none;
6810 var nw: MappedFile.Node.Writer = undefined;
6811 ni.writer(&coff.mf, gpa, &nw);
6812 defer nw.deinit();
6813 codegen.generateLazySymbol(
6814 &coff.base,
6815 pt,
6816 lazy,
6817 &required_alignment,
6818 &nw.interface,
6819 .none,
6820 .{ .atom_index = @fromBackingInt(@intCast(@backingInt(si))) },
6821 ) catch |err| switch (err) {
6822 error.WriteFailed => return nw.err.?,
6823 else => |e| return e,
6824 };
6825 si.get(coff).extra.size = @intCast(nw.interface.end);
6826 try si.applyLocationRelocs(coff);
6827}
6828
6829fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6830 log.debug("flushMoved({s}, n{d})", .{ @tagName(coff.getNode(ni)), ni });
6831 switch (coff.getNode(ni)) {
6832 .file,
6833 .header,
6834 .signature,
6835 => unreachable,
6836 .coff_header,
6837 .optional_header,
6838 .data_directories,
6839 .section_table,
6840 .placeholder,
6841 => assert(!coff.isImage()),
6842 .symbol_table,
6843 .string_table,
6844 => |_, tag| {
6845 if (tag == .symbol_table)
6846 coff.targetStore(
6847 &coff.headerPtr().pointer_to_symbol_table,
6848 @intCast(ni.location(&coff.mf).resolve(&coff.mf)[0]),
6849 );
6850
6851 if (!coff.symbol_table.pending_shrink) {
6852 const symbol_table_loc, const symbol_table_size = coff.symbol_table.ni.location(&coff.mf).resolve(&coff.mf);
6853 const string_table_offset, _ = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf);
6854 coff.symbol_table.pending_shrink = string_table_offset - (symbol_table_loc + symbol_table_size) > 0;
6855 }
6856 },
6857 .relocation_table => |sn| {
6858 coff.targetStore(
6859 &sn.header(coff).pointer_to_relocations,
6860 @intCast(ni.location(&coff.mf).resolve(&coff.mf)[0]),
6861 );
6862 },
6863 .relocation_table_entry => {},
6864 .archive_member_header => |mi| {
6865 const member = mi.get(coff);
6866 switch (member.kind) {
6867 .first_linker, .second_linker, .longnames => {},
6868 else => coff.targetStore(
6869 &coff.secondLinkerMemberOffsetsSlice()[@backingInt(mi) - Member.Index.known_count],
6870 @intCast(ni.fileLocation(&coff.mf, false).offset),
6871 ),
6872 }
6873
6874 if (member.kind == .coff)
6875 try coff.pending_members.put(coff.base.comp.gpa, mi, {});
6876 },
6877 .archive_member,
6878 => {},
6879 .image_section => |si| {
6880 const sym = si.get(coff);
6881 const flags = coff.targetLoad(&sym.section_number.header(coff).flags);
6882 if (!flags.CNT_UNINITIALIZED_DATA) {
6883 const file_offset = if (isArchive(coff))
6884 sym.ni.unwrap().?.location(&coff.mf).resolve(&coff.mf)[0]
6885 else
6886 ni.fileLocation(&coff.mf, false).offset;
6887
6888 return coff.targetStore(
6889 &sym.section_number.header(coff).pointer_to_raw_data,
6890 @intCast(file_offset),
6891 );
6892 }
6893 },
6894 .input_section => |isi| {
6895 try isi.symbol(coff).flushMoved(coff);
6896 for (coff.input_symbols.items[@backingInt(isi.firstSymbol(coff))..]) |input_symbol| {
6897 if (input_symbol.si.get(coff).ni != ni.toOptional()) break;
6898 try input_symbol.si.flushMoved(coff);
6899 }
6900 },
6901 .import_directory_table => {
6902 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
6903 if (size > 0)
6904 coff.targetStore(
6905 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
6906 coff.computeNodeRva(ni),
6907 );
6908 },
6909 .import_lookup_table => |import_index| coff.targetStore(
6910 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
6911 coff.computeNodeRva(ni),
6912 ),
6913 .import_address_table => |import_index| {
6914 const entry = import_index.get(coff);
6915 const import_address_table_si = entry.import_address_table_si;
6916 try import_address_table_si.flushMoved(coff);
6917 coff.targetStore(
6918 &coff.importDirectoryEntryPtr(import_index).import_address_table_rva,
6919 import_address_table_si.get(coff).rva,
6920 );
6921
6922 for (entry.import_address_table_symbols.items) |iat_ptr_si|
6923 try iat_ptr_si.flushMoved(coff);
6924 },
6925 .import_hint_name_table => |import_index| {
6926 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
6927 const import_hint_name_rva = coff.computeNodeRva(ni);
6928 coff.targetStore(
6929 &coff.importDirectoryEntryPtr(import_index).name_rva,
6930 import_hint_name_rva,
6931 );
6932 const import_entry = import_index.get(coff);
6933 const import_lookup_slice = import_entry.import_lookup_table_ni.slice(&coff.mf);
6934 const import_address_slice =
6935 import_entry.import_address_table_si.node(coff).slice(&coff.mf);
6936 const import_hint_name_slice = ni.slice(&coff.mf);
6937 const import_hint_name_align = ni.alignment(&coff.mf);
6938
6939 var import_hint_name_index: u32 = 0;
6940 for (0..import_entry.len) |import_symbol_index| {
6941 switch (magic) {
6942 _ => unreachable,
6943 inline .PE32, .@"PE32+" => |ct_magic| {
6944 const Entry = std.coff.ImportLookupTableEntry(ct_magic);
6945 const import_lookup_table: []Entry = @ptrCast(@alignCast(import_lookup_slice));
6946 const import_address_table: []Entry = @ptrCast(@alignCast(import_address_slice));
6947
6948 var entry = coff.targetLoad(&import_lookup_table[import_symbol_index]);
6949 if (entry.is_ordinal)
6950 continue;
6951
6952 import_hint_name_index = @intCast(import_hint_name_align.forward(
6953 std.mem.findScalarPos(
6954 u8,
6955 import_hint_name_slice,
6956 import_hint_name_index,
6957 0,
6958 ).? + 1,
6959 ));
6960
6961 entry.payload.hint_name_rva = @intCast(import_hint_name_rva + import_hint_name_index);
6962 import_hint_name_index += 2;
6963
6964 coff.targetStore(&import_lookup_table[import_symbol_index], entry);
6965 coff.targetStore(&import_address_table[import_symbol_index], entry);
6966 },
6967 }
6968 }
6969 },
6970 .export_directory_table => {
6971 const rva = coff.computeNodeRva(ni);
6972 coff.targetStore(&coff.dataDirectoryPtr(.EXPORT).virtual_address, rva);
6973 coff.targetStore(&coff.exportDirectoryTable().name_rva, rva + @sizeOf(std.coff.ExportDirectoryTable));
6974 },
6975 .export_address_table => {
6976 try coff.export_table.export_address_table_si.flushMoved(coff);
6977
6978 // These relocs are applied directly here instead of via the above flushMoved call as
6979 // they are non-contiguous, and not tracked under export_address_table_si.
6980 for (coff.export_table.entries.values()) |entry|
6981 try entry.export_address_table_ri.get(coff).apply(coff);
6982
6983 coff.targetStore(
6984 &coff.exportDirectoryTable().export_address_table_rva,
6985 coff.computeNodeRva(ni),
6986 );
6987 },
6988 .export_name_pointer_table => coff.targetStore(
6989 &coff.exportDirectoryTable().name_pointer_table_rva,
6990 coff.computeNodeRva(ni),
6991 ),
6992 .export_ordinal_table => coff.targetStore(
6993 &coff.exportDirectoryTable().ordinal_table_rva,
6994 coff.computeNodeRva(ni),
6995 ),
6996 .export_name_table => {
6997 const name_table_rva = coff.computeNodeRva(coff.export_table.name_table_ni);
6998 for (
6999 coff.exportNamePointerTableSlice(),
7000 coff.exportOrdinalTableSlice(),
7001 ) |*np, target_ord| {
7002 const ord: ExportTable.Ordinal = @fromBackingInt(@intCast(coff.targetLoad(&target_ord.unbiased_ordinal)));
7003 const entry = ord.get(coff);
7004 coff.targetStore(
7005 &np.name_rva,
7006 @intCast(name_table_rva + entry.name_index),
7007 );
7008 }
7009 },
7010 inline .pseudo_section,
7011 .object_section,
7012 .import_thunk,
7013 .nav,
7014 .uav,
7015 .lazy_code,
7016 .lazy_const_data,
7017 => |mi| try mi.symbol(coff).flushMoved(coff),
7018 .builtin => |si| try si.flushMoved(coff),
7019 }
7020 try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
7021}
7022
7023fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
7024 const offset, const size = ni.location(&coff.mf).resolve(&coff.mf);
7025 log.debug("flushResized({s}, n{d}, 0x{x})", .{ @tagName(coff.getNode(ni)), ni, size });
7026
7027 switch (coff.getNode(ni)) {
7028 .file => {
7029 if (coff.isArchive() and coff.members.items.len > 0) {
7030 const last_member = coff.members.items[coff.members.items.len - 1];
7031 // See .archive_member branch for reasoning
7032 assert(Node.known.file.last(&coff.mf).unwrap().? == last_member.content_ni);
7033 try coff.flushResized(last_member.content_ni);
7034 }
7035 },
7036 .header => {
7037 if (coff.isImage()) {
7038 switch (coff.optionalHeaderPtr()) {
7039 inline else => |optional_header| coff.targetStore(
7040 &optional_header.size_of_headers,
7041 @intCast(size),
7042 ),
7043 }
7044
7045 if (size > coff.section_table.values()[0].si.get(coff).rva) try coff.virtualSlide(
7046 0,
7047 std.mem.alignForward(
7048 u32,
7049 @intCast(size * 4),
7050 coff.optionalHeaderField(.section_alignment),
7051 ),
7052 );
7053 }
7054 },
7055 .signature,
7056 .archive_member_header,
7057 => unreachable,
7058 .archive_member => |mi| {
7059 const content_ni = mi.get(coff).content_ni;
7060 const content_offset, _ = content_ni.location(&coff.mf).resolve(&coff.mf);
7061 const next_offset = if (content_ni.next(&coff.mf).unwrap()) |next_ni| offset: {
7062 assert(coff.getNode(next_ni) == .archive_member_header);
7063 break :offset next_ni.location(&coff.mf).resolve(&coff.mf)[0];
7064 } else offset: {
7065 assert(content_ni.parent(&coff.mf) == Node.known.file.toOptional());
7066 // This must take into account the final file size. If there are trailing
7067 // bytes, they will be expected to contain another valid member header
7068 break :offset coff.mf.memory_map.memory.len;
7069 };
7070
7071 // Not inserting IMAGE_ARCHIVE_PAD `\n` byte here, because we are expanding to full size
7072 Member.storeHeaderDecimalStr(&mi.get(coff).headerPtr(coff).size, next_offset - content_offset);
7073 },
7074 .coff_header,
7075 .optional_header,
7076 .data_directories,
7077 => unreachable,
7078 .section_table => {},
7079 .symbol_table => {
7080 assert(!coff.isImage());
7081 if (!coff.symbol_table.pending_shrink) {
7082 const string_table_offset, _ = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf);
7083 coff.symbol_table.pending_shrink =
7084 size > coff.targetLoad(&coff.headerPtr().number_of_symbols) * std.coff.Symbol.sizeOf() or
7085 string_table_offset - (offset + size) > 0;
7086 }
7087 },
7088 .string_table => {
7089 assert(!coff.isImage());
7090 coff.targetStore(coff.symbolTableStringLenPtr(), @intCast(size));
7091 },
7092 .relocation_table,
7093 .relocation_table_entry,
7094 => assert(!coff.isImage()),
7095 .image_section => |si| {
7096 const sym = si.get(coff);
7097 const section_index = sym.section_number.toIndex();
7098 const section = &coff.sectionTableSlice()[section_index];
7099 coff.targetStore(&section.size_of_raw_data, @intCast(size));
7100 if (coff.isImage() and size > coff.targetLoad(&section.virtual_size)) {
7101 const virtual_size = std.mem.alignForward(
7102 u32,
7103 @intCast(size * 4),
7104 coff.optionalHeaderField(.section_alignment),
7105 );
7106 coff.targetStore(&section.virtual_size, virtual_size);
7107 try coff.virtualSlide(section_index + 1, sym.rva + virtual_size);
7108 }
7109
7110 if (!coff.isImage()) {
7111 if (coff.symbolTableSectionAuxEntryPtr(si.sti(coff))) |aux_ptr|
7112 coff.targetStore(&aux_ptr.length, @intCast(size));
7113 }
7114 },
7115 .input_section => {},
7116 .import_directory_table => {
7117 const prev_size = coff.targetLoad(&coff.dataDirectoryPtr(.IMPORT).size);
7118 coff.targetStore(
7119 &coff.dataDirectoryPtr(.IMPORT).size,
7120 @intCast(size),
7121 );
7122 if (prev_size == 0) try coff.flushMoved(ni);
7123 },
7124 .import_lookup_table,
7125 .import_address_table,
7126 .import_hint_name_table,
7127 => {},
7128 .export_directory_table => unreachable,
7129 .export_address_table,
7130 .export_name_pointer_table,
7131 .export_ordinal_table,
7132 .export_name_table,
7133 => {},
7134 inline .pseudo_section,
7135 .object_section,
7136 => |smi, tag| {
7137 if (tag == .pseudo_section and smi.name(coff) == .@".edata") {
7138 coff.targetStore(
7139 &coff.dataDirectoryPtr(.EXPORT).size,
7140 @intCast(size),
7141 );
7142 }
7143
7144 var sym = smi.symbol(coff).get(coff);
7145 while (sym.flags.extra_tag == .next_alias_si)
7146 sym = sym.extra.next_alias_si.get(coff);
7147
7148 sym.extra.size = @intCast(size);
7149 },
7150 .import_thunk,
7151 .nav,
7152 .uav,
7153 .lazy_code,
7154 .lazy_const_data,
7155 .builtin,
7156 => {},
7157 .placeholder,
7158 => unreachable,
7159 }
7160}
7161
7162fn flushMember(coff: *Coff, mi: Member.Index) !void {
7163 const member = mi.get(coff);
7164 switch (member.kind) {
7165 .first_linker,
7166 .longnames,
7167 .import,
7168 => unreachable,
7169 .second_linker => {
7170 const Context = struct {
7171 coff: *Coff,
7172 indices: []u16,
7173 strings: []String,
7174
7175 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
7176 return std.mem.lessThan(
7177 u8,
7178 ctx.strings[lhs].toSlice(ctx.coff),
7179 ctx.strings[rhs].toSlice(ctx.coff),
7180 );
7181 }
7182
7183 pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void {
7184 std.mem.swap(u16, &ctx.indices[lhs], &ctx.indices[rhs]);
7185 std.mem.swap(String, &ctx.strings[lhs], &ctx.strings[rhs]);
7186 }
7187 };
7188
7189 // TODO: Does this sort need to also sort by linker input order (if names equal)?
7190 std.sort.pdqContext(0, coff.lib_string_table.items.len, Context{
7191 .coff = coff,
7192 .indices = coff.secondLinkerMemberIndicesSlice(),
7193 .strings = coff.lib_string_table.items,
7194 });
7195
7196 var offset: usize = 0;
7197 var string_table = coff.secondLinkerMemberStringsSlice();
7198 for (coff.lib_string_table.items) |string| {
7199 const str = string.toSlice(coff);
7200 @memcpy(string_table[offset..][0..str.len], str);
7201 string_table[offset + str.len] = 0;
7202 offset += str.len + 1;
7203 }
7204 },
7205 .coff => {
7206 const file_offset: u32 = @intCast(member.header_ni.fileLocation(&coff.mf, false).offset);
7207 const first_linker_offsets = coff.firstLinkerMemberOffsetsSlice();
7208 for (member.first_linker_indices.values()) |mfli|
7209 first_linker_offsets[@backingInt(mfli)] = std.mem.nativeTo(u32, file_offset, .big);
7210 },
7211 }
7212}
7213
7214fn flushExportsSort(coff: *Coff) void {
7215 const Context = struct {
7216 coff: *Coff,
7217 np: []std.coff.ExportNamePointerTableEntry,
7218 ord: []std.coff.ExportOrdinalTableEntry,
7219 entries: []ExportTable.Entry,
7220 nt: []const u8,
7221
7222 pub fn lessThan(ctx: *const @This(), lhs: usize, rhs: usize) bool {
7223 const lhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[lhs].unbiased_ordinal)];
7224 const rhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[rhs].unbiased_ordinal)];
7225 return std.mem.lessThan(
7226 u8,
7227 ctx.nt[lhs_entry.name_index..][0..lhs_entry.name_len],
7228 ctx.nt[rhs_entry.name_index..][0..rhs_entry.name_len],
7229 );
7230 }
7231
7232 pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void {
7233 std.mem.swap(std.coff.ExportNamePointerTableEntry, &ctx.np[lhs], &ctx.np[rhs]);
7234 std.mem.swap(std.coff.ExportOrdinalTableEntry, &ctx.ord[lhs], &ctx.ord[rhs]);
7235 }
7236 };
7237
7238 std.sort.pdqContext(0, coff.export_table.entries.count(), &Context{
7239 .coff = coff,
7240 .np = coff.exportNamePointerTableSlice(),
7241 .ord = coff.exportOrdinalTableSlice(),
7242 .entries = coff.export_table.entries.values(),
7243 .nt = coff.export_table.name_table_ni.slice(&coff.mf),
7244 });
7245}
7246
7247fn flushSectionMerges(coff: *Coff) !void {
7248 while (coff.section_merge_pending_index < coff.section_merges.count()) : (coff.section_merge_pending_index += 1)
7249 try coff.flushSectionMerge(coff.section_merge_pending_index);
7250}
7251
7252fn flushSectionMerge(coff: *Coff, index: u32) !void {
7253 assert(coff.isImage());
7254 const from = coff.section_merges.keys()[index];
7255 const to = coff.section_merges.values()[index];
7256 assert(from != to);
7257
7258 log.debug("flushSectionMerge({s}->{s})", .{ from.toSlice(coff), to.toSlice(coff) });
7259
7260 const opt_to_sec = coff.section_table.getPtr(to);
7261 if (coff.section_table.getPtr(from)) |from_sec| {
7262 const from_sym = from_sec.si.get(coff);
7263 if (opt_to_sec) |to_sec| {
7264 const to_sym = to_sec.si.get(coff);
7265
7266 // TODO: Create a pseudo-section named `from` in `to`, copy `from_sec` ni into that pseudo section
7267 // TODO: Update .section_number for all contained syms
7268 // TODO: Remove `from_sec` from section table (set size = 0 and can do it in flushResized?).
7269 // This is non-trivial as we can't leave holes in the section table.
7270 // TODO: Merge section flags
7271 _ = to_sym;
7272 return coff.base.comp.link_diags.fail("TODO implement section to section merge", .{});
7273 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
7274 const to_sym = to_ps_si.get(coff);
7275 if (from_sym.section_number == to_sym.section_number)
7276 return;
7277
7278 // TODO: Same as above, except place `from` into a node in `to_psmi`'s parent
7279 return coff.base.comp.link_diags.fail("TODO implement section to pseudosection merge", .{});
7280 }
7281
7282 // If `to` doesn't exist, /MERGE is defined as renaming `from` to `to`.
7283 // No other path will create image-level sections, so we can safely rename this now
7284 const from_name = &from_sec.si.get(coff).section_number.header(coff).name;
7285 const to_slice = to.toSlice(coff);
7286 @memcpy(from_name[0..to_slice.len], to_slice);
7287 @memset(from_name[to_slice.len..], 0);
7288 } else if (coff.pseudo_section_table.getIndex(from)) |from_index| {
7289 const from_psmi: Node.PseudoSectionMapIndex = @fromBackingInt(@intCast(from_index));
7290 const from_sym = from_psmi.symbol(coff).get(coff);
7291 if (opt_to_sec) |to_sec| {
7292 const to_sym = to_sec.si.get(coff);
7293 if (from_sym.section_number == to_sym.section_number)
7294 return;
7295
7296 // TODO: Move from_psmi's node into to_sec
7297 // TODO: Update .section_number for all contained syms
7298 // TODO: Merge section flags
7299 return coff.base.comp.link_diags.fail("TODO implement pseudosection to section merge", .{});
7300 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
7301 const to_sym = to_ps_si.get(coff);
7302 if (from_sym.section_number == to_sym.section_number)
7303 return;
7304
7305 // TODO: Same as above, but move from_psmi's node after to_psmi's node in its parent
7306 return coff.base.comp.link_diags.fail("TODO implement pseudosection to pseudosection merge", .{});
7307 }
7308
7309 // Renaming pseudo-sections have no effect on the output, so this is a no-op.
7310 }
7311}
7312
7313fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
7314 var rva = start_rva;
7315 for (
7316 coff.section_table.values()[start_section_index..],
7317 coff.sectionTableSlice()[start_section_index..],
7318 ) |*section, *header| {
7319 const section_sym = section.si.get(coff);
7320 section_sym.rva = rva;
7321 coff.targetStore(&header.virtual_address, rva);
7322 try section_sym.ni.unwrap().?.childrenMoved(coff.base.comp.gpa, &coff.mf);
7323 rva += coff.targetLoad(&header.virtual_size);
7324 }
7325 switch (coff.optionalHeaderPtr()) {
7326 inline else => |optional_header| coff.targetStore(
7327 &optional_header.size_of_image,
7328 @intCast(rva),
7329 ),
7330 }
7331}
7332
7333pub fn updateExports(
7334 coff: *Coff,
7335 pt: Zcu.PerThread,
7336 export_indices: []const Zcu.Export.Index,
7337) link.Error!void {
7338 // TODO: delete old exports from first/second linker member table
7339 // TODO: delete old exports from symbol table inside section
7340 const diags = &coff.base.comp.link_diags;
7341 var alias_syms: std.array_hash_map.Auto(Symbol.Index, Symbol.Index) = .empty;
7342 defer alias_syms.deinit(coff.base.comp.gpa);
7343 for (export_indices) |export_index| {
7344 coff.updateExportInner(pt, export_index, &alias_syms) catch |err| switch (err) {
7345 error.MappedFileIo => return diags.fail(
7346 "failed to write output file: {t}",
7347 .{coff.mf.io_err.?},
7348 ),
7349 else => |e| return e,
7350 };
7351 }
7352 coff.exports_complete = true;
7353}
7354fn updateExportInner(
7355 coff: *Coff,
7356 pt: Zcu.PerThread,
7357 export_index: Zcu.Export.Index,
7358 alias_syms: *std.array_hash_map.Auto(Symbol.Index, Symbol.Index),
7359) !void {
7360 const zcu = pt.zcu;
7361 const gpa = zcu.gpa;
7362 const ip = &zcu.intern_pool;
7363
7364 const exp = export_index.ptr(zcu);
7365
7366 try coff.symbols.ensureUnusedCapacity(gpa, 1);
7367 const exported_si: Symbol.Index = switch (exp.exported) {
7368 .nav => |nav| try coff.navSymbol(zcu, nav),
7369 .uav => |uav| @fromBackingInt(@intCast(@backingInt(try coff.lowerUav(
7370 pt,
7371 uav,
7372 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
7373 )))),
7374 };
7375 switch (exp.exported) {
7376 .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }),
7377 .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{
7378 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
7379 Value.fromInterned(uav).fmtValue(pt),
7380 exported_si,
7381 }),
7382 }
7383 while (try coff.resolve(pt.tid)) {}
7384 while (try coff.idle(pt.tid)) {}
7385
7386 const machine = coff.targetLoad(&coff.headerPtr().machine);
7387 const exported_ni = exported_si.node(coff);
7388 const exported_sym = exported_si.get(coff);
7389
7390 const @"export" = export_index.ptr(zcu);
7391 const name = @"export".opts.name.toSlice(ip);
7392
7393 // TODO: add an errMsg if this conflicts with an existing symbol
7394 const export_si = try coff.globalSymbol(.{ .name = name });
7395 const export_sym = export_si.get(coff);
7396 export_sym.ni = .wrap(exported_ni);
7397 export_sym.rva = exported_sym.rva;
7398 export_sym.section_number = exported_sym.section_number;
7399 if (@"export".opts.linkage == .weak and !coff.isImage()) {
7400 // exported_si needs to be ahead of export_si in the symbol table,
7401 // so that its sti is known when creating the weak external aux entry
7402 try coff.pendingSymbolTableEntry(exported_si);
7403 export_sym.flags.weak_external_strat = .alias;
7404 export_sym.setValue(.{ .weak_alias_si = exported_si });
7405 }
7406 defer export_si.applyTargetRelocs(coff, .none) catch unreachable;
7407
7408 const prev_alias_si: Symbol.Index = si: {
7409 const gop = try alias_syms.getOrPut(gpa, exported_si);
7410 const prev_alias_si = if (gop.found_existing) gop.value_ptr.* else exported_si;
7411 gop.value_ptr.* = export_si;
7412 break :si prev_alias_si;
7413 };
7414
7415 // The last symbol in the alias list holds the size
7416 const prev_alias_sym = prev_alias_si.get(coff);
7417 switch (prev_alias_sym.flags.extra_tag) {
7418 .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),
7419 // This export should have been deleted
7420 .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si),
7421 else => unreachable,
7422 }
7423
7424 prev_alias_sym.setExtra(.{ .next_alias_si = export_si });
7425
7426 if (!coff.isImage()) return;
7427
7428 const entries_ctx = ExportTable.Adapter{ .coff = coff };
7429 const gop = try coff.export_table.entries.getOrPutAdapted(
7430 gpa,
7431 name,
7432 entries_ctx,
7433 );
7434
7435 if (!gop.found_existing) {
7436 errdefer _ = coff.export_table.entries.pop();
7437
7438 const export_count = coff.export_table.entries.count();
7439 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
7440 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
7441
7442 const name_index: u32 = @intCast(coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1]);
7443 const new_name_table_size = name_index + name.len + 1;
7444 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
7445 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
7446
7447 try coff.export_table.name_table_ni.resizeLeaf(&coff.mf, gpa, new_name_table_size);
7448
7449 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
7450 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
7451
7452 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort
7453 {
7454 const ordinal_table_slice = coff.exportOrdinalTableSlice();
7455 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
7456 const tail_index: ExportTable.Ordinal =
7457 @fromBackingInt(@intCast(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal));
7458 const tail_entry = tail_index.get(coff);
7459 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];
7460 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);
7461 }
7462 }
7463
7464 const edt = coff.exportDirectoryTable();
7465 coff.targetStore(&edt.number_of_names, @intCast(export_count));
7466 edt.number_of_entries = edt.number_of_names;
7467
7468 // TODO: These should all be resized ahead of time to fit all exports
7469 // after https://github.com/ziglang/zig/issues/23616
7470 try coff.export_table.export_address_table_si.node(coff).resizeLeaf(
7471 &coff.mf,
7472 gpa,
7473 export_count * @sizeOf(std.coff.ExportAddressTableEntry),
7474 );
7475
7476 try coff.export_table.name_pointer_table_ni.resizeLeaf(
7477 &coff.mf,
7478 gpa,
7479 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
7480 );
7481
7482 try coff.export_table.ordinal_table_ni.resizeLeaf(
7483 &coff.mf,
7484 gpa,
7485 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
7486 );
7487
7488 coff.targetStore(
7489 &coff.exportNamePointerTableSlice()[gop.index].name_rva,
7490 @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index),
7491 );
7492 coff.targetStore(
7493 &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal,
7494 @intCast(gop.index),
7495 );
7496
7497 gop.value_ptr.* = .{
7498 .si = export_si,
7499 .name_index = @intCast(name_index),
7500 .name_len = @intCast(name.len),
7501 .export_address_table_ri = @fromBackingInt(@intCast(coff.relocs.items.len)),
7502 };
7503
7504 try coff.addReloc(
7505 coff.export_table.export_address_table_si,
7506 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),
7507 export_si,
7508 .{ .known = 0 },
7509 switch (machine) {
7510 else => |tag| @panic(@tagName(tag)),
7511 .AMD64 => .{ .AMD64 = .ADDR32NB },
7512 .I386 => .{ .I386 = .DIR32NB },
7513 },
7514 );
7515 } else {
7516 gop.value_ptr.si = export_si;
7517 const reloc = gop.value_ptr.*.export_address_table_ri.get(coff);
7518 reloc.target = export_si;
7519 }
7520}
7521
7522fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
7523 const comp = coff.base.comp;
7524 const io = comp.io;
7525 var buffer: [512]u8 = undefined;
7526 const stderr = try io.lockStderr(&buffer, null);
7527 defer io.unlockStderr();
7528 const w = &stderr.file_writer.interface;
7529 _ = try coff.dump(w, tid);
7530}
7531
7532pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
7533 if (coff.options.enable_link_snapshots) {
7534 try coff.printNode(tid, w, .root, 0);
7535 try w.writeAll("Section table:\n");
7536 for (coff.section_table.keys(), coff.section_table.values()) |name, sec|
7537 try coff.printSection(w, name, sec.si);
7538 try w.writeAll("Symbol table:\n");
7539 for (1..coff.symbols.items.len) |si|
7540 try coff.printSymbol(w, tid, @fromBackingInt(@intCast(si)));
7541
7542 return .enabled;
7543 }
7544 return .disabled;
7545}
7546
7547fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !void {
7548 const sym = si.get(coff);
7549 try w.print("{d:0>6}@{d:0>2} {x:08} n{d:0>8} | {s}\n", .{
7550 si,
7551 sym.section_number,
7552 if (sym.flags.extra_tag == .size) sym.extra.size else 0,
7553 sym.ni,
7554 name.toSlice(coff),
7555 });
7556}
7557
7558fn printSymbol(
7559 coff: *Coff,
7560 w: *Io.Writer,
7561 tid: Zcu.PerThread.Id,
7562 si: Symbol.Index,
7563) !void {
7564 const sym = si.get(coff);
7565 try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} {s} n{d:0>8}+{x:08}:{s: <26} | {x:08} ", .{
7566 si,
7567 sym.section_number,
7568 if (sym.flags.extra_tag == .size)
7569 @as(u64, sym.extra.size)
7570 else if (sym.ni.unwrap()) |ni|
7571 ni.location(&coff.mf).resolve(&coff.mf)[1]
7572 else
7573 0,
7574 switch (sym.flags.value_tag) {
7575 .none => "xx",
7576 .weak_alias_name => "an",
7577 .weak_alias_si => "as",
7578 .node_offset => "no",
7579 },
7580 switch (sym.flags.extra_tag) {
7581 .size => "sz",
7582 .isli => "li",
7583 .next_alias_si => "na",
7584 },
7585 switch (sym.flags.type) {
7586 .unknown => "u",
7587 .code => "c",
7588 .data => "d",
7589 },
7590 sym.ni,
7591 if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0,
7592 if (sym.ni.unwrap()) |ni| @tagName(coff.getNode(ni)) else "",
7593 sym.rva,
7594 });
7595
7596 if (sym.gmi != .none) {
7597 try w.print("G {f}\n", .{fmtGlobalName(coff, sym.gmi)});
7598 } else {
7599 try w.writeAll("| ");
7600 try coff.printNodeName(w, tid, coff.getNode(sym.ni.unwrap().?));
7601 if (sym.flags.extra_tag == .isli)
7602 try w.print(" | {s}", .{sym.extra.isli.name(coff).toSlice(coff)});
7603 try w.writeByte('\n');
7604 }
7605}
7606
7607const FmtGlobalName = struct { coff: *Coff, gmi: Node.GlobalMapIndex };
7608
7609fn fmtGlobalName(coff: *Coff, gmi: Node.GlobalMapIndex) std.fmt.Alt(FmtGlobalName, globalNameEscape) {
7610 return .{ .data = .{ .coff = coff, .gmi = gmi } };
7611}
7612
7613fn globalNameEscape(data: FmtGlobalName, w: *std.Io.Writer) std.Io.Writer.Error!void {
7614 if (data.gmi == .none) return;
7615 try w.writeAll(data.gmi.name(data.coff).toSlice(data.coff));
7616 if (data.gmi.libName(data.coff).unwrap()) |lib_name|
7617 try w.print("({s})", .{lib_name.toSlice(data.coff)});
7618}
7619
7620fn printNodeName(
7621 coff: *Coff,
7622 w: *std.Io.Writer,
7623 tid: Zcu.PerThread.Id,
7624 node: Node,
7625) !void {
7626 switch (node) {
7627 else => {},
7628 .image_section => |si| try w.print("({s})", .{
7629 std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
7630 }),
7631 .input_section => |isi| {
7632 const ioi = isi.input(coff);
7633 const is = isi.inputSection(coff);
7634 try w.print("({f}{f}, {s}", .{
7635 ioi.path(coff).fmtEscapeString(),
7636 fmtMemberNameString(ioi.memberName(coff)),
7637 coff.getNode(is.si.node(coff).parent(&coff.mf).unwrap().?).object_section.name(coff).toSlice(coff),
7638 });
7639 if (is.comdat_si != .null) {
7640 const comdat_sym = is.comdat_si.get(coff);
7641 const comdat_name = if (comdat_sym.gmi != .none)
7642 comdat_sym.gmi.name(coff).toSlice(coff)
7643 else
7644 coff.input_symbols.items[@backingInt(comdat_sym.extra.isli)].name.toSlice(coff);
7645
7646 try w.print("={s}", .{comdat_name});
7647 }
7648 try w.writeAll(")");
7649 },
7650 .import_lookup_table,
7651 .import_address_table,
7652 .import_hint_name_table,
7653 => |import_index| try w.print("({s})", .{
7654 std.mem.sliceTo(import_index.get(coff).import_hint_name_table_ni.sliceConst(&coff.mf), 0),
7655 }),
7656 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
7657 smi.name(coff).toSlice(coff),
7658 }),
7659 .import_thunk,
7660 => |gmi| {
7661 try w.writeByte('(');
7662 if (gmi.libName(coff).toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
7663 try w.print("{s})", .{gmi.name(coff).toSlice(coff)});
7664 },
7665 .nav => |nmi| {
7666 const zcu = coff.base.comp.zcu.?;
7667 const ip = &zcu.intern_pool;
7668 const nav = ip.getNav(nmi.navIndex(coff));
7669 try w.print("({f}, {f})", .{
7670 Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }),
7671 nav.fqn.fmt(ip),
7672 });
7673 },
7674 .uav => |umi| {
7675 const zcu = coff.base.comp.zcu.?;
7676 const val: Value = .fromInterned(umi.uavValue(coff));
7677 try w.print("({f}, {f})", .{
7678 val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
7679 val.fmtValue(.{ .zcu = zcu, .tid = tid }),
7680 });
7681 },
7682 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
7683 Type.fromInterned(lmi.lazySymbol(coff).ty).fmt(.{
7684 .zcu = coff.base.comp.zcu.?,
7685 .tid = tid,
7686 }),
7687 }),
7688 .builtin => |si| {
7689 const sym = si.get(coff);
7690 if (sym.gmi != .none) {
7691 try w.writeByte('(');
7692 if (sym.gmi.libName(coff).toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
7693 try w.print("{s})", .{sym.gmi.name(coff).toSlice(coff)});
7694 }
7695 },
7696 }
7697}
7698
7699pub fn printNode(
7700 coff: *Coff,
7701 tid: Zcu.PerThread.Id,
7702 w: *Io.Writer,
7703 ni: MappedFile.Node.Index,
7704 indent: usize,
7705) !void {
7706 const node = coff.getNode(ni);
7707 try w.splatByteAll(' ', indent);
7708 try w.writeAll(@tagName(node));
7709 try coff.printNodeName(w, tid, node);
7710 {
7711 const mf_node = &coff.mf.nodes.items[@backingInt(ni)];
7712 const off, const size = mf_node.location().resolve(&coff.mf);
7713 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}\n", .{
7714 @backingInt(ni),
7715 off,
7716 size,
7717 mf_node.flags.alignment.toByteUnits(),
7718 mf_node.flags.position,
7719 if (mf_node.flags.moved) " moved" else "",
7720 if (mf_node.flags.resized) " resized" else "",
7721 if (mf_node.flags.has_content) " has_content" else "",
7722 });
7723 }
7724 if (ni.first(&coff.mf).unwrap()) |first_ni| {
7725 // non-leaf, just print children
7726 var child_ni = first_ni;
7727 while (true) {
7728 try coff.printNode(tid, w, child_ni, indent + 1);
7729 child_ni = child_ni.next(&coff.mf).unwrap() orelse break;
7730 }
7731 return;
7732 }
7733 const file_loc = ni.fileLocation(&coff.mf, false);
7734 if (file_loc.size == 0) return;
7735 var address = file_loc.offset;
7736 const line_len = 0x10;
7737 var line_it = std.mem.window(
7738 u8,
7739 coff.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
7740 line_len,
7741 line_len,
7742 );
7743 while (line_it.next()) |line_bytes| : (address += line_len) {
7744 try w.splatByteAll(' ', indent + 1);
7745 try w.print("{x:0>8} ", .{address});
7746 for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
7747 try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
7748 for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
7749 try w.writeByte('\n');
7750 }
7751}