authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:34-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:22:42-04:00
log6ff2b9b9137c514b2750e40bf345824461a5aabf
tree0c2f62f0cb42b711cfdb8d17767d5958e2c7144b
parentc7c1d65372019199824d5f2284a63031383f0512

Coff: handle COMDAT sections and weak externals

- Rework object loading to handle COMDAT sections - Add better error output for multiple definitions - Track all symbols associated with input sections in a flat array - Resolve weak externals to their alias if nothing defines the weak symbol - Fixup incorrect signs of reloc addends in the logic that saves / restores them

2 files changed, 752 insertions(+), 335 deletions(-)

lib/std/coff.zig+4
......@@ -654,6 +654,10 @@ pub const SectionHeader = extern struct {
654654 std.debug.assert(std.math.isPowerOfTwo(n));
655655 return @enumFromInt(@ctz(n) + 1);
656656 }
657
658 pub fn alignment(a: Align) ?std.mem.Alignment {
659 return .fromByteUnitsOptional(a.toByteUnits() orelse null);
660 }
657661 };
658662 };
659663};
src/link/Coff.zig+748-335
......@@ -40,9 +40,10 @@ input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {
4040}),
4141pending_input: ?InputArchive.Member.Index,
4242inputs: std.ArrayList(Input),
43input_resolved: std.ArrayList(Symbol.Index),
43input_symbols: std.ArrayList(Symbol.Index),
4444input_sections: std.ArrayList(Node.InputSection),
4545input_section_pending_index: u32,
46inputs_complete: bool,
4647strings: std.HashMapUnmanaged(
4748 u32,
4849 void,
......@@ -292,25 +293,14 @@ pub const Node = union(enum) {
292293 pub fn memberName(ii: InputIndex, coff: *const Coff) ?[]const u8 {
293294 return coff.inputs.items[@intFromEnum(ii)].member_name;
294295 }
295
296 pub fn firstSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
297 return coff.inputs.items[@intFromEnum(ii)].first_si;
298 }
299
300 pub fn endSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
301 return coff.inputs.items[@intFromEnum(ii)].end_si;
302 }
303
304 pub fn firstResolvedGlobal(ii: InputIndex, coff: *const Coff) Input.ResolvedIndex {
305 return coff.inputs.items[@intFromEnum(ii)].first_iri;
306 }
307296 };
308297
309298 const InputSection = struct {
310299 ii: Node.InputIndex,
311300 si: Symbol.Index,
312301 file_location: MappedFile.Node.FileLocation,
313 first_iri: Node.InputSection.ResolvedIndex,
302 first_li: Node.InputSection.LocalIndex,
303 crc: u32,
314304
315305 pub const Index = enum(u32) {
316306 _,
......@@ -331,12 +321,12 @@ pub const Node = union(enum) {
331321 return coff.input_sections.items[@intFromEnum(isi)].si;
332322 }
333323
334 pub fn firstResolvedSymbol(isi: Index, coff: *const Coff) ResolvedIndex {
335 return coff.input_sections.items[@intFromEnum(isi)].first_iri;
324 pub fn firstSymbol(isi: Index, coff: *const Coff) LocalIndex {
325 return coff.input_sections.items[@intFromEnum(isi)].first_li;
336326 }
337327 };
338328
339 const ResolvedIndex = enum(u32) {
329 const LocalIndex = enum(u32) {
340330 _,
341331 };
342332 };
......@@ -449,8 +439,7 @@ pub const InputArchive = struct {
449439pub const Input = struct {
450440 path: std.Build.Cache.Path,
451441 member_name: ?[]const u8,
452 first_si: Symbol.Index,
453 end_si: Symbol.Index,
442 source_name: String.Optional,
454443};
455444
456445pub const Member = struct {
......@@ -814,11 +803,14 @@ pub const GlobalName = struct { name: String, lib_name: String.Optional };
814803pub const Symbol = struct {
815804 ni: MappedFile.Node.Index,
816805 rva: u32,
817 value: union {
818 /// For generated symbols, this is their size
819 size: u32,
820 /// For symbols from input sections, this is the offset within the input section
806 value: union(enum) {
807 /// For .ni == .input_section, this is the offset of this symbol within the input section
821808 input_offset: u32,
809 /// For .ni == none and .gmi != .none, this is a weak alias
810 /// that should replace this symbol, or .null if none exists
811 alias_si: Symbol.Index,
812 /// Otherwise, this is the symbol size if known
813 size: u32,
822814 },
823815 /// Relocations contained within this symbol
824816 loc_relocs: Reloc.Index,
......@@ -828,7 +820,6 @@ pub const Symbol = struct {
828820 /// Only used when outputting objects
829821 sti: SymbolTable.Index,
830822 gmi: Node.GlobalMapIndex,
831 unused0: u16 = 0,
832823
833824 pub const SectionNumber = enum(i16) {
834825 UNDEFINED = 0,
......@@ -840,6 +831,13 @@ pub const Symbol = struct {
840831 return @intCast(@intFromEnum(sn) - 1);
841832 }
842833
834 fn hasIndex(sn: SectionNumber) bool {
835 return switch (sn) {
836 .UNDEFINED, .ABSOLUTE, .DEBUG => false,
837 else => true,
838 };
839 }
840
843841 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
844842 return sn.section(coff).si;
845843 }
......@@ -1021,15 +1019,21 @@ pub const Reloc = extern struct {
10211019 ),
10221020 .ADDR32,
10231021 .ADDR32NB,
1022 .SECREL,
1023 => std.mem.writeInt(
1024 u32,
1025 loc_slice[0..4],
1026 @intCast(reloc.addend),
1027 target_endian,
1028 ),
10241029 .REL32,
10251030 .REL32_1,
10261031 .REL32_2,
10271032 .REL32_3,
10281033 .REL32_4,
10291034 .REL32_5,
1030 .SECREL,
10311035 => std.mem.writeInt(
1032 u32,
1036 i32,
10331037 loc_slice[0..4],
10341038 @intCast(reloc.addend),
10351039 target_endian,
......@@ -1039,16 +1043,21 @@ pub const Reloc = extern struct {
10391043 else => |kind| @panic(@tagName(kind)),
10401044 .ABSOLUTE => {},
10411045 .DIR16,
1042 .REL16,
10431046 => std.mem.writeInt(
10441047 u16,
10451048 loc_slice[0..2],
10461049 @intCast(reloc.addend),
10471050 target_endian,
10481051 ),
1052 .REL16,
1053 => std.mem.writeInt(
1054 i16,
1055 loc_slice[0..2],
1056 @intCast(reloc.addend),
1057 target_endian,
1058 ),
10491059 .DIR32,
10501060 .DIR32NB,
1051 .REL32,
10521061 .SECREL,
10531062 => std.mem.writeInt(
10541063 u32,
......@@ -1056,6 +1065,13 @@ pub const Reloc = extern struct {
10561065 @intCast(reloc.addend),
10571066 target_endian,
10581067 ),
1068 .REL32,
1069 => std.mem.writeInt(
1070 i32,
1071 loc_slice[0..4],
1072 @intCast(reloc.addend),
1073 target_endian,
1074 ),
10591075 },
10601076 }
10611077
......@@ -1074,15 +1090,20 @@ pub const Reloc = extern struct {
10741090 )),
10751091 .ADDR32,
10761092 .ADDR32NB,
1093 .SECREL,
1094 => std.mem.readInt(
1095 u32,
1096 loc_slice[0..4],
1097 target_endian,
1098 ),
10771099 .REL32,
10781100 .REL32_1,
10791101 .REL32_2,
10801102 .REL32_3,
10811103 .REL32_4,
10821104 .REL32_5,
1083 .SECREL,
10841105 => std.mem.readInt(
1085 u32,
1106 i32,
10861107 loc_slice[0..4],
10871108 target_endian,
10881109 ),
......@@ -1091,21 +1112,31 @@ pub const Reloc = extern struct {
10911112 else => |kind| @panic(@tagName(kind)),
10921113 .ABSOLUTE => 0,
10931114 .DIR16,
1094 .REL16,
10951115 => std.mem.readInt(
10961116 u16,
10971117 loc_slice[0..2],
10981118 target_endian,
10991119 ),
1120 .REL16,
1121 => std.mem.readInt(
1122 i16,
1123 loc_slice[0..2],
1124 target_endian,
1125 ),
11001126 .DIR32,
11011127 .DIR32NB,
1102 .REL32,
11031128 .SECREL,
11041129 => std.mem.readInt(
11051130 u32,
11061131 loc_slice[0..4],
11071132 target_endian,
11081133 ),
1134 .REL32,
1135 => std.mem.readInt(
1136 i32,
1137 loc_slice[0..4],
1138 target_endian,
1139 ),
11091140 },
11101141 };
11111142 }
......@@ -1356,9 +1387,10 @@ fn create(
13561387 .input_archive_symbol_indices = .empty,
13571388 .pending_input = null,
13581389 .inputs = .empty,
1359 .input_resolved = .empty,
1390 .input_symbols = .empty,
13601391 .input_sections = .empty,
13611392 .input_section_pending_index = 0,
1393 .inputs_complete = false,
13621394 .strings = .empty,
13631395 .string_bytes = .empty,
13641396 .section_table = .empty,
......@@ -1420,7 +1452,7 @@ pub fn deinit(coff: *Coff) void {
14201452 coff.input_archive_symbols.deinit(gpa);
14211453 coff.input_archive_symbol_indices.deinit(gpa);
14221454 coff.inputs.deinit(gpa);
1423 coff.input_resolved.deinit(gpa);
1455 coff.input_symbols.deinit(gpa);
14241456 coff.input_sections.deinit(gpa);
14251457 coff.strings.deinit(gpa);
14261458 coff.string_bytes.deinit(gpa);
......@@ -2238,13 +2270,6 @@ fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
22382270 return si;
22392271}
22402272
2241fn initInputSectionSymbol(coff: *Coff, sym: *Symbol, section_si: Symbol.Index, value: u32) void {
2242 const section_sym = section_si.get(coff);
2243 sym.ni = section_sym.ni;
2244 sym.value = .{ .input_offset = value };
2245 sym.section_number = section_sym.section_number;
2246}
2247
22482273fn getOrPutString(coff: *Coff, string: []const u8) !String {
22492274 try coff.ensureUnusedStringCapacity(string.len);
22502275 return coff.getOrPutStringAssumeCapacity(string);
......@@ -2315,7 +2340,7 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
23152340}
23162341
23172342const GlobalOptions = struct {
2318 name: []const u8,
2343 name: []const u8, // TODO: Union with String
23192344 lib_name: ?[]const u8 = null,
23202345};
23212346
......@@ -2986,7 +3011,8 @@ fn objectSectionMapIndex(
29863011 attributes: ObjectSectionAttributes,
29873012) !Node.ObjectSectionMapIndex {
29883013 const gpa = coff.base.comp.gpa;
2989 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name.toSlice(coff), ".tls")) attr: {
3014 const name_slice = name.toSlice(coff);
3015 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name_slice, ".tls")) attr: {
29903016 // In images, the .tls section is a read-only template
29913017 var attr = attributes;
29923018 attr.write = false;
......@@ -2996,8 +3022,7 @@ fn objectSectionMapIndex(
29963022 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
29973023 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
29983024 const sn = if (!object_section_gop.found_existing) sn: {
2999 try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);
3000 const name_slice = name.toSlice(coff);
3025 try coff.ensureUnusedStringCapacity(name_slice.len);
30013026 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
30023027 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
30033028 try coff.nodes.ensureUnusedCapacity(gpa, 1);
......@@ -3047,6 +3072,7 @@ fn objectSectionMapIndex(
30473072 return osmi;
30483073}
30493074
3075// TODO: Include align in attrs and verify the current align is >= requested
30503076fn verifyParentSectionAttributes(
30513077 coff: *Coff,
30523078 kind: enum { pseudo, object },
......@@ -3096,7 +3122,8 @@ pub fn addReloc(
30963122 const gpa = coff.base.comp.gpa;
30973123 const target = target_si.get(coff);
30983124
3099 log.debug("addReloc({d}@{d}+{d} -> {d}@{d}+{d}{s})", .{
3125 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
3126 log.debug("addReloc({d}@{d}+{d} -> {d}@{d}+{d}{s}) = {d}", .{
31003127 loc_si,
31013128 loc_si.get(coff).section_number,
31023129 offset,
......@@ -3104,6 +3131,7 @@ pub fn addReloc(
31043131 target_si.get(coff).section_number,
31053132 if (addend == .pending) 0 else addend.known,
31063133 if (addend == .pending) "p" else "k",
3134 ri,
31073135 });
31083136
31093137 try coff.relocs.ensureUnusedCapacity(gpa, 1);
......@@ -3166,7 +3194,6 @@ pub fn addReloc(
31663194 },
31673195 };
31683196
3169 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
31703197 coff.relocs.addOneAssumeCapacity().* = .{
31713198 .type = @"type",
31723199 .prev = .none,
......@@ -3319,8 +3346,7 @@ fn loadObject(
33193346 input.* = .{
33203347 .path = path,
33213348 .member_name = if (member_name) |m| try gpa.dupe(u8, m) else null,
3322 .first_si = @enumFromInt(coff.symbols.items.len),
3323 .end_si = @enumFromInt(coff.symbols.items.len),
3349 .source_name = .none,
33243350 };
33253351
33263352 const string_table = string_table: {
......@@ -3336,30 +3362,60 @@ fn loadObject(
33363362 string_table_len - @sizeOf(u32),
33373363 );
33383364
3339 const InputSection = struct {
3365 const PendingSymbolIndex = enum(u32) {
3366 none,
3367 _,
3368
3369 pub fn wrap(i: ?u32) @This() {
3370 return @enumFromInt((i orelse return .none) + 1);
3371 }
3372
3373 pub fn unwrap(i: @This()) ?u32 {
3374 return switch (i) {
3375 .none => null,
3376 _ => @intFromEnum(i) - 1,
3377 };
3378 }
3379 };
3380
3381 const PendingInputSection = struct {
33403382 header: std.coff.SectionHeader,
33413383 name: String,
33423384 si: Symbol.Index,
3385 parent_si: Symbol.Index,
3386 psi: PendingSymbolIndex,
3387 num_symbols: u32,
3388 comdat: std.coff.ComdatSelection,
3389 comdat_psi: PendingSymbolIndex,
3390 comdat_crc: u32,
3391 comdat_association: Symbol.SectionNumber,
3392 comdat_result: union(enum) {
3393 pending,
3394 // Root of the association chain
3395 pending_association: Symbol.SectionNumber,
3396 include,
3397 skip,
3398 },
33433399 };
33443400
3345 const sections: []const InputSection = if (coff.isImage()) sections: {
3346 const sections = try gpa.alloc(InputSection, header.number_of_sections);
3401 const sections: []PendingInputSection = if (coff.isImage()) sections: {
3402 const sections = try gpa.alloc(PendingInputSection, header.number_of_sections);
33473403 errdefer gpa.free(sections);
33483404
3349 var num_input_sections: u16 = 0;
3350 var reqd_object_sections: std.AutoArrayHashMapUnmanaged(String, void) = .empty;
3351 defer reqd_object_sections.deinit(gpa);
3352 var reqd_pseudo_sections: std.StringArrayHashMapUnmanaged(void) = .empty;
3353 defer reqd_pseudo_sections.deinit(gpa);
3354 try reqd_object_sections.ensureUnusedCapacity(gpa, sections.len);
3355 try reqd_pseudo_sections.ensureUnusedCapacity(gpa, sections.len);
3356
33573405 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));
33583406 for (sections, 0..) |*section, section_i| {
33593407 section.* = .{
33603408 .header = try r.takeStruct(std.coff.SectionHeader, target_endian),
33613409 .name = undefined,
33623410 .si = .null,
3411 .parent_si = .null,
3412 .psi = .none,
3413 .num_symbols = 0,
3414 .comdat = .NONE,
3415 .comdat_psi = .none,
3416 .comdat_crc = 0,
3417 .comdat_association = .UNDEFINED,
3418 .comdat_result = .pending,
33633419 };
33643420
33653421 const section_name_slice = if (section.header.name[0] == '/') name: {
......@@ -3371,7 +3427,11 @@ fn loadObject(
33713427 });
33723428
33733429 if (name_offset > string_table.len)
3374 return diags.failParse(path, "out-of-bounds section name offset in section {d}: {d}", .{ section_i, name_offset });
3430 return diags.failParse(
3431 path,
3432 "out-of-bounds section name offset in section {d}: {d}",
3433 .{ section_i, name_offset },
3434 );
33753435
33763436 break :name std.mem.sliceTo(string_table[name_offset..], 0);
33773437 } else std.mem.sliceTo(&section.header.name, 0);
......@@ -3403,109 +3463,6 @@ fn loadObject(
34033463 // TODO: Convert .debug$* sections into PDB
34043464 continue;
34053465 }
3406
3407 num_input_sections += 1;
3408 _ = reqd_object_sections.getOrPutAssumeCapacity(section.name);
3409 _ = reqd_pseudo_sections.getOrPutAssumeCapacity(
3410 coff.objectSectionParentName(section.name.toSlice(coff)),
3411 );
3412 }
3413
3414 var symbol_capacity: u16 = num_input_sections;
3415 var node_capacity: u16 = 0;
3416 {
3417 var iter = reqd_object_sections.count();
3418 while (iter > 0) {
3419 iter -= 1;
3420 if (coff.object_section_table.contains(reqd_object_sections.keys()[iter]))
3421 reqd_object_sections.swapRemoveAt(iter);
3422 }
3423
3424 node_capacity += @intCast(reqd_object_sections.count());
3425 symbol_capacity += @intCast(reqd_object_sections.count());
3426 }
3427
3428 {
3429 var iter = reqd_pseudo_sections.count();
3430 while (iter > 0) {
3431 // TODO: Track the extra number of strings and their length and reserve? These have not been reserved as
3432 // part of the ensureManyUnusedStringCapacity call above
3433 iter -= 1;
3434 const name = coff.getString(reqd_pseudo_sections.keys()[iter]) orelse continue;
3435 if (coff.pseudo_section_table.contains(name))
3436 reqd_pseudo_sections.swapRemoveAt(iter);
3437 }
3438
3439 node_capacity += @intCast(reqd_pseudo_sections.count());
3440 symbol_capacity += @intCast(reqd_pseudo_sections.count());
3441 }
3442
3443 try coff.nodes.ensureUnusedCapacity(gpa, node_capacity);
3444 try coff.symbols.ensureUnusedCapacity(gpa, symbol_capacity + num_input_sections);
3445 try coff.input_sections.ensureUnusedCapacity(gpa, num_input_sections);
3446
3447 for (sections) |*section| {
3448 if (section.header.flags.LNK_INFO) {
3449 if (std.mem.eql(u8, &section.header.name, ".drectve")) {
3450 try fr.seekTo(fl.offset + section.header.pointer_to_raw_data);
3451 var buf: [128]u8 = undefined;
3452 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);
3453 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {
3454 error.StreamTooLong => return diags.failParse(path, "unexpectedly long .drectve argument", .{}),
3455 else => |e| return e,
3456 }) |arg| {
3457 // Microsoft tools emit 3 space characters into this section even with /Zl
3458 if (arg.len > 0)
3459 return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
3460 }
3461 }
3462
3463 continue;
3464 }
3465
3466 if (section.header.flags.LNK_REMOVE or
3467 section.header.flags.MEM_DISCARDABLE)
3468 {
3469 continue;
3470 }
3471
3472 if (section.header.flags.LNK_COMDAT)
3473 // This will be necessary if we do the equivalent of /Gy for compiler-rt
3474 return diags.failParse(path, "TODO handle COMDAT sections in input objects", .{});
3475
3476 const parent_osmi = try coff.objectSectionMapIndex(
3477 section.name,
3478 coff.mf.flags.block_size,
3479 .fromFlags(section.header.flags),
3480 );
3481 const parent_si = parent_osmi.symbol(coff);
3482 const ni = try coff.mf.addLastChildNode(gpa, parent_si.node(coff), .{
3483 .size = section.header.size_of_raw_data,
3484 .alignment = if (section.header.flags.ALIGN.toByteUnits()) |align_bytes|
3485 .fromByteUnits(align_bytes)
3486 else
3487 .@"1",
3488 .moved = true,
3489 });
3490 coff.nodes.appendAssumeCapacity(.{ .input_section = @enumFromInt(coff.input_sections.items.len) });
3491
3492 section.si = coff.addSymbolAssumeCapacity();
3493 const sym = section.si.get(coff);
3494 sym.ni = ni;
3495 sym.section_number = parent_si.get(coff).section_number;
3496
3497 coff.input_sections.addOneAssumeCapacity().* = .{
3498 .ii = ii,
3499 .si = section.si,
3500 .file_location = .{
3501 .offset = fl.offset + section.header.pointer_to_raw_data,
3502 .size = section.header.size_of_raw_data,
3503 },
3504 .first_iri = @enumFromInt(coff.input_resolved.items.len),
3505 };
3506
3507 log.debug("loadInputSection({s}) = {d}@{d}", .{ section.name.toSlice(coff), section.si, sym.section_number });
3508 coff.synth_prog_node.increaseEstimatedTotalItems(1);
35093466 }
35103467
35113468 break :sections sections;
......@@ -3522,9 +3479,8 @@ fn loadObject(
35223479 const member = mi.get(coff);
35233480 try member.initHeader(coff, path_str, header.time_date_stamp);
35243481
3525 // TODO: This could be deferred to an idle task?
3526
35273482 {
3483 // TODO: This could be deferred to an idle task?
35283484 var nw: MappedFile.Node.Writer = undefined;
35293485 member.content_ni.writer(&coff.mf, gpa, &nw);
35303486 defer nw.deinit();
......@@ -3537,18 +3493,35 @@ fn loadObject(
35373493 break :mi mi;
35383494 } else undefined;
35393495
3540 // TODO: Also reserve memory for the symbols / globals / relocs within each section
3541
35423496 try fr.seekTo(fl.offset + header.pointer_to_symbol_table);
3543 const symbol_size = comptime std.coff.Symbol.sizeOf();
3497 const symbol_size = std.coff.Symbol.sizeOf();
35443498
3545 var symbols: std.ArrayList(Symbol.Index) = .empty;
3546 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
3547 var num_resolved: u32 = 0;
3499 const PendingSymbol = struct {
3500 name: String,
3501 value: union(enum) {
3502 // Size of the section
3503 section: u32,
3504 // Offset within the section
3505 static: u32,
3506 // If section is defined, the symbol size. Otherwise offset within the section.
3507 external: u32,
3508 // The index of the target symbol of this alias
3509 weak_external: u32,
3510 },
3511 section_number: Symbol.SectionNumber,
3512 si: Symbol.Index,
3513 // The index of the weak_external that targest this symbol
3514 weak_external_psi: PendingSymbolIndex,
3515 };
3516
3517 var num_global_symbols: u32 = 0;
3518 var pending_symbols: std.AutoArrayHashMapUnmanaged(u32, PendingSymbol) = .empty;
3519 defer pending_symbols.deinit(gpa);
35483520
3549 input.first_si = @enumFromInt(coff.symbols.items.len);
3550 defer input.end_si = @enumFromInt(coff.symbols.items.len);
3521 if (!is_archive)
3522 try pending_symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
35513523
3524 // Discover symbol names and COMDAT symbol mappings
35523525 var symbol_i: u32 = 0;
35533526 while (symbol_i < header.number_of_symbols) {
35543527 var symbol: std.coff.Symbol = undefined;
......@@ -3556,10 +3529,11 @@ fn loadObject(
35563529 if (target_endian != native_endian)
35573530 std.mem.byteSwapAllFields(std.coff.Symbol, &symbol);
35583531
3559 defer {
3560 r.toss(symbol.number_of_aux_symbols * symbol_size);
3561 symbol_i += symbol.number_of_aux_symbols + 1;
3562 }
3532 const aux_symbols = if (symbol.number_of_aux_symbols > 0)
3533 try r.take(symbol_size * symbol.number_of_aux_symbols)
3534 else
3535 &.{};
3536 defer symbol_i += symbol.number_of_aux_symbols + 1;
35633537
35643538 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
35653539 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);
......@@ -3568,145 +3542,499 @@ fn loadObject(
35683542 break :name string_table[index..];
35693543 } else &symbol.name, 0);
35703544
3571 const si_slice = symbols.addManyAsSliceAssumeCapacity(1 + symbol.number_of_aux_symbols);
3572 @memset(si_slice, .null);
3573
3574 defer log.debug("loadInputSymbol({s}, 0x{x}) = {d}@{d}", .{
3575 name,
3576 symbol.value,
3577 si_slice[0],
3578 if (si_slice[0] == .null) .UNDEFINED else si_slice[0].get(coff).section_number,
3579 });
3580
35813545 if (is_archive) {
3582 if (symbol.storage_class == .EXTERNAL and symbol.section_number != .UNDEFINED)
3583 try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
3546 if (switch (symbol.storage_class) {
3547 .WEAK_EXTERNAL => true,
3548 .EXTERNAL => symbol.section_number != .UNDEFINED,
3549 else => false,
3550 }) try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
35843551
35853552 continue;
35863553 }
35873554
3588 switch (symbol.storage_class) {
3589 .STATIC, .LABEL => |storage_class| switch (symbol.section_number) {
3590 .UNDEFINED, .DEBUG, .ABSOLUTE => {
3591 // TODO: Do we need to do anything with @feat.00?
3592 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c392
3593 },
3555 switch (symbol.section_number) {
3556 .UNDEFINED, .DEBUG, .ABSOLUTE => {},
3557 else => |sn| if (@intFromEnum(sn) > sections.len)
3558 return diags.failParse(path, "out-of-bounds section number {d} in symbol 0x{x}", .{ sn, symbol_i }),
3559 }
3560
3561 const psi: PendingSymbolIndex = .wrap(@intCast(pending_symbols.count()));
3562 const section_number: Symbol.SectionNumber = @enumFromInt(@intFromEnum(symbol.section_number));
3563 const opt_value: ?@FieldType(PendingSymbol, "value") = pending_symbol: switch (symbol.storage_class) {
3564 .STATIC, .LABEL => |storage_class| switch (section_number) {
3565 // TODO: Do we need to do anything with @feat.00?
3566 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c392
3567 .UNDEFINED, .DEBUG, .ABSOLUTE => null,
35943568 else => |sn| {
3569 const section = &sections[sn.toIndex()];
3570
35953571 // Section symbol
3596 if (storage_class == .STATIC and
3572 const is_section = storage_class == .STATIC and
35973573 symbol.value == 0 and
35983574 symbol.type == std.coff.SymType{
35993575 .complex_type = .NULL,
36003576 .base_type = .NULL,
36013577 } and
3602 symbol.number_of_aux_symbols > 0)
3603 {
3578 symbol.number_of_aux_symbols > 0;
3579
3580 if (is_section) {
36043581 if (symbol.number_of_aux_symbols > 1)
3605 return diags.failParse(path, "invalid number of aux symbols for section 0x{x}: {d}", .{
3582 return diags.failParse(path, "invalid number of aux symbols for section symbol 0x{x}: {d}", .{
36063583 symbol_i,
36073584 symbol.number_of_aux_symbols,
36083585 });
36093586
36103587 var section_def: std.coff.SectionDefinition = undefined;
3611 @memcpy(std.mem.asBytes(&section_def)[0..symbol_size], try r.peek(symbol_size));
3588 @memcpy(std.mem.asBytes(&section_def)[0..symbol_size], aux_symbols[0..symbol_size]);
36123589 if (target_endian != native_endian)
36133590 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def);
36143591
3615 // TODO: Extract the COMDAT section info
3616
3617 if (section_def.number > sections.len)
3618 return diags.failParse(
3619 path,
3620 "section symbol for '{s}' contained an out of bounds section number: 0x{x}",
3621 .{ name, section_def.number },
3622 );
3623
3624 // It's valid for this to not match the symbol's section number (ie. .drectve sets this)
3625 if (section_def.number == 0)
3626 continue;
3627
3628 const section = &sections[section_def.number - 1];
36293592 if (section_def.number_of_relocations != section.header.number_of_relocations)
36303593 return diags.failParse(
36313594 path,
3632 "section symbol for '{s}' relocation count did not match section header: {d} vs {d}",
3633 .{ name, section_def.number_of_relocations, section.header.number_of_relocations },
3595 "section aux symbol 0x{x} for '{s}' relocation count did not match section header: {d} vs {d}",
3596 .{ symbol_i + 1, name, section_def.number_of_relocations, section.header.number_of_relocations },
36343597 );
36353598
36363599 if (section_def.number_of_linenumbers != section.header.number_of_linenumbers)
36373600 return diags.failParse(
36383601 path,
3639 "section symbol for '{s}' line number count did not match section header: {d} vs {d}",
3640 .{ name, section_def.number_of_linenumbers, section.header.number_of_linenumbers },
3602 "section aux symbol 0x{x} for '{s}' line number count did not match section header: {d} vs {d}",
3603 .{ symbol_i + 1, name, section_def.number_of_linenumbers, section.header.number_of_linenumbers },
36413604 );
36423605
3643 @memset(si_slice, section.si);
3644 } else {
3645 try coff.symbols.ensureUnusedCapacity(gpa, 1);
3646 si_slice[0] = coff.addSymbolAssumeCapacity();
3647 const sym = si_slice[0].get(coff);
3648 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);
3606 if (section.header.flags.LNK_COMDAT) {
3607 if (section_def.selection == .ASSOCIATIVE) {
3608 if (section_def.number == 0 or section_def.number > sections.len)
3609 return diags.failParse(
3610 path,
3611 "section aux symbol 0x{x} for '{s}' contained an invalid associated section number: 0x{x}",
3612 .{ symbol_i + 1, name, section_def.number },
3613 );
3614
3615 section.comdat_association = @enumFromInt(section_def.number);
3616 }
3617
3618 section.comdat = section_def.selection;
3619 section.comdat_crc = section_def.checksum;
3620 }
3621
3622 section.psi = psi;
36493623 }
3624
3625 break :pending_symbol if (is_section)
3626 .{ .section = section.header.size_of_raw_data }
3627 else
3628 .{ .static = symbol.value };
36503629 },
36513630 },
3652 .EXTERNAL => switch (symbol.section_number) {
3631 .WEAK_EXTERNAL => switch (symbol.section_number) {
36533632 .UNDEFINED => {
3654 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
3655 si_slice[0] = global_gop.value_ptr.*;
3656 if (!global_gop.found_existing) {
3657 const sym = si_slice[0].get(coff);
3658 sym.value = .{ .size = symbol.value };
3659 }
3633 if (symbol.value != 0)
3634 return diags.failParse(
3635 path,
3636 "invalid value {d} for weak external symbol 0x{x}",
3637 .{ symbol.value, symbol_i },
3638 );
3639
3640 var weak_external: std.coff.WeakExternalDefinition = undefined;
3641 @memcpy(std.mem.asBytes(&weak_external)[0..symbol_size], aux_symbols[0..symbol_size]);
3642 if (target_endian != native_endian)
3643 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &weak_external);
3644
3645 if (weak_external.tag_index >= header.number_of_symbols)
3646 return diags.failParse(
3647 path,
3648 "invalid tag_index 0x{x} for weak external symbol 0x{x}",
3649 .{ weak_external.tag_index, symbol_i },
3650 );
3651
3652 break :pending_symbol switch (weak_external.flag) {
3653 .SEARCH_NOLIBRARY,
3654 .SEARCH_LIBRARY,
3655 => return diags.failParse(
3656 path,
3657 "TODO handle weak external characteristic 0x{x} for symbol 0x{x}",
3658 .{ weak_external.flag, symbol_i },
3659 ),
3660 .SEARCH_ALIAS => .{ .weak_external = weak_external.tag_index },
3661 else => return diags.failParse(
3662 path,
3663 "encountered unknown weak external characteristic 0x{x} for symbol 0x{x}",
3664 .{ weak_external.flag, symbol_i },
3665 ),
3666 };
36603667 },
3668 else => |sn| return diags.failParse(
3669 path,
3670 "invalid section number {d} for weak external symbol 0x{x}",
3671 .{ sn, symbol_i },
3672 ),
3673 },
3674 .EXTERNAL => switch (section_number) {
3675 .UNDEFINED => .{ .external = symbol.value },
36613676 .ABSOLUTE => return diags.failParse(
36623677 path,
3663 "TODO unhandled external absolute symbol: '{s}'",
3664 .{name},
3678 "TODO unhandled external absolute symbol 0x{x}: '{s}'",
3679 .{ symbol_i, name },
36653680 ),
36663681 .DEBUG => return diags.failParse(
36673682 path,
3668 "unexpected external symbol in DEBUG section: '{s}'",
3669 .{name},
3683 "unexpected external symbol 0x{x} in DEBUG section: '{s}'",
3684 .{ symbol_i, name },
36703685 ),
3671 else => |sn| {
3672 // TODO: Should this use archive name as lib_name as well?
3673 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
3674 si_slice[0] = global_gop.value_ptr.*;
3675 const sym = si_slice[0].get(coff);
3676 if (global_gop.found_existing and sym.ni != .none) {
3677 // TODO: Need corresponding logic later if we try to make a global already defined by an input
3678 var err = try diags.addErrorWithNotes(2);
3679 try err.addMsg("multiple definitions of '{s}'", .{name});
3680 switch (coff.getNode(sym.ni)) {
3681 .input_section => |isi| {
3682 const other_ii = isi.input(coff);
3683 err.addNote("first seen in input '{f}{f}'", .{
3684 other_ii.path(coff).fmtEscapeString(),
3685 fmtMemberNameString(other_ii.memberName(coff)),
3686 });
3687 },
3688 .nav, .uav => err.addNote("first seen in module '{s}'", .{
3689 comp.zcu.?.root_mod.fully_qualified_name,
3690 }),
3691 else => unreachable,
3692 }
3693 err.addNote("defined again in input '{f}'", .{path});
3694 return error.LinkFailure;
3686 else => .{ .external = symbol.value },
3687 },
3688 .FILE => {
3689 if (!std.mem.eql(u8, name, ".file"))
3690 return diags.failParse(
3691 path,
3692 "unexpected symbol name '{s}' for file symbol 0x{x}",
3693 .{ name, symbol_i },
3694 );
3695
3696 var file: std.coff.FileDefinition = undefined;
3697 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);
3698
3699 input.source_name = (try coff.getOrPutString(file.getFileName())).toOptional();
3700 break :pending_symbol null;
3701 },
3702 else => |storage_class| return diags.failParse(
3703 path,
3704 "TODO handle storage class {t} for symbol 0x{x}",
3705 .{ storage_class, symbol_i },
3706 ),
3707 };
3708
3709 if (opt_value) |value| {
3710 switch (value) {
3711 .section => {},
3712 .static, .external, .weak_external => {
3713 num_global_symbols += 1;
3714 if (section_number.hasIndex()) {
3715 const section = &sections[section_number.toIndex()];
3716 section.num_symbols += 1;
3717 if (section.header.flags.LNK_COMDAT and section.comdat_psi == .none)
3718 section.comdat_psi = psi;
36953719 }
3720 },
3721 }
3722
3723 const symbol_name = coff.getOrPutStringAssumeCapacity(name);
3724 pending_symbols.putAssumeCapacity(symbol_i, .{
3725 .name = symbol_name,
3726 .value = value,
3727 .section_number = section_number,
3728 .si = .null,
3729 .weak_external_psi = .none,
3730 });
3731 }
3732 }
3733
3734 try coff.globals.ensureUnusedCapacity(gpa, num_global_symbols);
3735 for (sections) |*section| {
3736 if (section.header.flags.LNK_INFO) {
3737 if (std.mem.eql(u8, &section.header.name, ".drectve")) {
3738 try fr.seekTo(fl.offset + section.header.pointer_to_raw_data);
3739 var buf: [128]u8 = undefined;
3740 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);
3741 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {
3742 error.StreamTooLong => return diags.failParse(path, "unexpectedly long .drectve argument", .{}),
3743 else => |e| return e,
3744 }) |arg| {
3745 // Microsoft tools emit 3 space characters into this section even with /Zl
3746 if (arg.len > 0)
3747 return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
3748 }
3749 }
3750
3751 section.comdat_result = .skip;
3752 continue;
3753 }
3754
3755 if (section.header.flags.LNK_REMOVE or
3756 section.header.flags.MEM_DISCARDABLE)
3757 {
3758 section.comdat_result = .skip;
3759 continue;
3760 }
3761
3762 section.comdat_result = comdat: switch (section.comdat) {
3763 .NONE => .include,
3764 .ASSOCIATIVE => {
3765 // Associative COMDAT sections have no COMDAT symbol.
3766 // They are linked if the assocated section is linked.
3767 var iter = section;
3768 var iter_sn = iter.comdat_association;
3769 while (iter.comdat == .ASSOCIATIVE) {
3770 iter = &sections[iter_sn.toIndex()];
3771 iter_sn = iter.comdat_association;
3772 if (iter == section)
3773 return diags.failParse(
3774 path,
3775 "circular COMDAT association loop detected, starting at symbol 0x{x}",
3776 .{pending_symbols.keys()[section.psi.unwrap().?]},
3777 );
3778 }
3779
3780 assert(iter != section);
3781 break :comdat switch (iter.comdat_result) {
3782 .pending => .{ .pending_association = iter_sn },
3783 else => |iter_result| iter_result,
3784 };
3785 },
3786 else => |comdat| {
3787 const psi = section.comdat_psi.unwrap() orelse
3788 return diags.failParse(
3789 path,
3790 "COMDAT section symbol 0x{x} had no COMDAT symbol",
3791 .{pending_symbols.keys()[section.psi.unwrap().?]},
3792 );
36963793
3697 if (global_gop.found_existing)
3698 num_resolved += 1;
3794 const symbol = &pending_symbols.values()[psi];
3795 switch (symbol.value) {
3796 .section, .weak_external => unreachable,
3797 .static => break :comdat .include,
3798 else => {},
3799 }
3800
3801 // TODO: Do we need to use lib_name here?
3802 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff), .lib_name = null });
3803 if (!global_gop.found_existing) {
3804 symbol.si = global_gop.value_ptr.*;
3805 break :comdat .include;
3806 }
3807
3808 const index = pending_symbols.keys()[psi];
3809 const si = global_gop.value_ptr.*;
3810 switch (comdat) {
3811 .NODUPLICATES => return coff.failMultipleDefinitions(
3812 path,
3813 member_name,
3814 symbol.name,
3815 index,
3816 si,
3817 .duplicate,
3818 ),
3819 .ANY => break :comdat .skip,
3820 .SAME_SIZE => {
3821 // TODO: Verify that this node isn't resized after creation
3822 _, const size = si.get(coff).ni.location(&coff.mf).resolve(&coff.mf);
3823 if (size == section.header.size_of_raw_data)
3824 break :comdat .skip;
3825
3826 return coff.failMultipleDefinitions(
3827 path,
3828 member_name,
3829 symbol.name,
3830 index,
3831 si,
3832 .{ .size = .{ .a = size, .b = section.header.size_of_raw_data } },
3833 );
3834 },
3835 .EXACT_MATCH => {
3836 const sym = si.get(coff);
3837 const existing_crc = switch (coff.getNode(sym.ni)) {
3838 .input_section => |isi| isi.inputSection(coff).crc,
3839 // TODO: Should this result be cached somewhere?
3840 else => std.hash.crc.Crc32Jamcrc.hash(sym.ni.slice(&coff.mf)),
3841 };
3842
3843 if (existing_crc == section.comdat_crc)
3844 break :comdat .skip;
3845
3846 return coff.failMultipleDefinitions(
3847 path,
3848 member_name,
3849 symbol.name,
3850 index,
3851 si,
3852 .{ .crc = .{ .a = existing_crc, .b = section.comdat_crc } },
3853 );
3854 },
3855 .LARGEST => {
3856 // TODO: Resize existing .ni and replace with this section's contents
3857 // TODO: This will be tricky, what to do about existing InputSection?
3858 unreachable; // TODO
3859 },
3860 .NONE, .ASSOCIATIVE, _ => unreachable,
3861 }
3862 },
3863 };
3864 }
3865
3866 // Resolve pending associations, create parent sections
3867 var num_included_sections: u16 = 0;
3868 var num_included_symbols: u32 = 0;
3869 var num_included_relocs: u32 = 0;
3870 for (sections) |*section| {
3871 comdat: switch (section.comdat_result) {
3872 .pending_association => |root_assoc_sn| {
3873 const root_result = sections[root_assoc_sn.toIndex()].comdat_result;
3874 assert(root_result != .pending_association);
3875 section.comdat_result = root_result;
3876 continue :comdat root_result;
3877 },
3878 .include => {},
3879 .skip => continue,
3880 .pending => unreachable,
3881 }
3882
3883 num_included_sections += 1;
3884 num_included_symbols += section.num_symbols;
3885 num_included_relocs += section.header.number_of_relocations;
3886
3887 section.parent_si = (try coff.objectSectionMapIndex(
3888 section.name,
3889 section.header.flags.ALIGN.alignment() orelse .@"1",
3890 .fromFlags(section.header.flags),
3891 )).symbol(coff);
3892 }
3893
3894 try coff.nodes.ensureUnusedCapacity(gpa, num_included_sections);
3895 try coff.relocs.ensureUnusedCapacity(gpa, num_included_relocs);
3896 try coff.symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
3897 try coff.input_sections.ensureUnusedCapacity(gpa, num_included_sections);
3898
3899 for (sections) |*section| {
3900 if (section.comdat_result != .include) continue;
3901
3902 const ni = try coff.mf.addLastChildNode(gpa, section.parent_si.node(coff), .{
3903 .size = section.header.size_of_raw_data,
3904 .alignment = section.header.flags.ALIGN.alignment() orelse .@"1",
3905 .moved = true,
3906 });
3907 coff.nodes.appendAssumeCapacity(.{ .input_section = @enumFromInt(coff.input_sections.items.len) });
3908
3909 section.si = coff.addSymbolAssumeCapacity();
3910 if (section.psi.unwrap()) |psi|
3911 pending_symbols.values()[psi].si = section.si;
3912
3913 const sym = section.si.get(coff);
3914 sym.ni = ni;
3915 sym.section_number = section.parent_si.get(coff).section_number;
3916
3917 coff.input_sections.addOneAssumeCapacity().* = .{
3918 .ii = ii,
3919 .si = section.si,
3920 .file_location = .{
3921 .offset = fl.offset + section.header.pointer_to_raw_data,
3922 .size = section.header.size_of_raw_data,
3923 },
3924 .first_li = @enumFromInt(coff.input_symbols.items.len),
3925 .crc = section.comdat_crc,
3926 };
3927
3928 log.debug(
3929 "addInputSection({s}, 0x{x}) = {d}@{d}",
3930 .{ section.name.toSlice(coff), section.comdat_crc, section.si, sym.section_number },
3931 );
3932 coff.synth_prog_node.increaseEstimatedTotalItems(1);
3933 }
3934
3935 for (pending_symbols.values(), pending_symbols.keys(), 0..) |*symbol, index, psi| {
3936 const section = switch (symbol.section_number) {
3937 .UNDEFINED => switch (symbol.value) {
3938 .section,
3939 .static,
3940 => unreachable,
3941 .external,
3942 .weak_external,
3943 => |value, tag| {
3944 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
3945 symbol.si = global_gop.value_ptr.*;
3946 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
3947 const sym = symbol.si.get(coff);
3948 if (tag == .external) {
3949 // TOOD: Is it valid to encounter multiple external definitions with different sizes?
3950 assert(sym.value == .size);
3951 sym.value = .{ .size = @max(sym.value.size, value) };
3952 } else {
3953 const alias = pending_symbols.getPtr(value) orelse
3954 return diags.failParse(
3955 path,
3956 "weak external 0x{x} {s}{f} targets unknown symbol index 0x{x}",
3957 .{
3958 index,
3959 symbol.name.toSlice(coff),
3960 fmtMemberNameString(member_name),
3961 value,
3962 },
3963 );
3964
3965 if (alias.si == .null) {
3966 alias.weak_external_psi = .wrap(@intCast(psi));
3967 } else {
3968 sym.value = .{ .alias_si = alias.si };
3969 }
3970 }
3971 }
36993972
3700 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);
3973 continue;
37013974 },
37023975 },
3703 else => {},
3976 .ABSOLUTE, .DEBUG => continue,
3977 else => |sn| &sections[sn.toIndex()],
3978 };
3979
3980 if (section.si == .null)
3981 continue;
3982
3983 if (symbol.si == .null) {
3984 switch (symbol.value) {
3985 .section => unreachable,
3986 .static => {
3987 symbol.si = coff.addSymbolAssumeCapacity();
3988 },
3989 .external => {
3990 // COMDAT symbols were created when enumerating the sections
3991 assert(section.comdat == .NONE);
3992 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
3993 symbol.si = global_gop.value_ptr.*;
3994
3995 const sym = symbol.si.get(coff);
3996 if (global_gop.found_existing and sym.ni != .none)
3997 return coff.failMultipleDefinitions(path, member_name, symbol.name, index, global_gop.value_ptr.*, .none);
3998 },
3999 .weak_external => unreachable,
4000 }
4001 }
4002
4003 if (symbol.weak_external_psi.unwrap()) |i| {
4004 assert(symbol.si != .null);
4005 pending_symbols.values()[i].si.get(coff).value = .{ .alias_si = symbol.si };
4006 }
4007
4008 if (section.si != symbol.si) {
4009 const sym = symbol.si.get(coff);
4010 sym.ni = section.si.get(coff).ni;
4011 sym.value = switch (symbol.value) {
4012 .section => |v| .{ .size = v },
4013 .static => |v| .{ .input_offset = v },
4014 .external => |v| switch (symbol.section_number) {
4015 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
4016 else => .{ .input_offset = v },
4017 },
4018 .weak_external => unreachable,
4019 };
4020 sym.section_number = symbol.section_number;
37044021 }
4022
4023 defer log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
4024 symbol.name.toSlice(coff),
4025 index,
4026 symbol.value,
4027 switch (symbol.value) {
4028 inline else => |v| v,
4029 },
4030 symbol.si,
4031 section.si.get(coff).section_number,
4032 });
37054033 }
37064034
37074035 const relocation_size = std.coff.Relocation.sizeOf();
37084036 for (sections) |section| {
3709 if (section.si == .null) continue;
4037 if (section.comdat_result != .include) continue;
37104038
37114039 const loc_sym = section.si.get(coff);
37124040 assert(loc_sym.loc_relocs == .none);
......@@ -3714,7 +4042,6 @@ fn loadObject(
37144042
37154043 if (section.header.number_of_relocations == 0) continue;
37164044
3717 try coff.relocs.ensureUnusedCapacity(gpa, section.header.number_of_relocations);
37184045 try fr.seekTo(fl.offset + section.header.pointer_to_relocations);
37194046 for (0..section.header.number_of_relocations) |reloc_i| {
37204047 var reloc: std.coff.Relocation = undefined;
......@@ -3722,57 +4049,110 @@ fn loadObject(
37224049 if (target_endian != native_endian)
37234050 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);
37244051
3725 if (reloc.symbol_table_index >= symbols.items.len)
4052 // TODO: This error should show member name for lib
4053 const symbol = pending_symbols.get(reloc.symbol_table_index) orelse
37264054 return diags.failParse(
37274055 path,
3728 "relocation 0x{x} in section '{s}' targets invalid symbol index 0x{x}",
3729 .{ reloc_i, section.name.toSlice(coff), reloc.symbol_table_index },
4056 "relocation 0x{x} in section '{s}'{f} targets invalid symbol index 0x{x}",
4057 .{ reloc_i, section.name.toSlice(coff), fmtMemberNameString(member_name), reloc.symbol_table_index },
37304058 );
37314059
3732 assert(symbols.items[reloc.symbol_table_index] != .null);
4060 assert(symbol.si != .null);
37334061 try coff.addReloc(
37344062 section.si,
37354063 reloc.virtual_address - section.header.virtual_address,
3736 symbols.items[reloc.symbol_table_index],
4064 symbol.si,
37374065 .pending,
37384066 @bitCast(reloc.type),
37394067 );
37404068 }
37414069 }
37424070
3743 const symbolLessThan = struct {
3744 fn lessThan(ctx: *Coff, lhs: Symbol.Index, rhs: Symbol.Index) bool {
3745 const lhs_sn = @intFromEnum(if (lhs == .null) .UNDEFINED else lhs.get(ctx).section_number);
3746 const rhs_sn = @intFromEnum(if (rhs == .null) .UNDEFINED else rhs.get(ctx).section_number);
3747 if (lhs_sn == rhs_sn) return @intFromEnum(lhs) < @intFromEnum(rhs);
3748 return lhs_sn < rhs_sn;
4071 // Set up contiguous symbol ranges in `input_symbols` for both symbols we just created,
4072 // and symbols that were previously created as undefined, but we just defined.
4073 const SortContext = struct {
4074 v: []const PendingSymbol,
4075
4076 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
4077 const lhs = &ctx.v[a_index];
4078 const rhs = &ctx.v[b_index];
4079 if (lhs.section_number == rhs.section_number)
4080 return @intFromEnum(lhs.si) < @intFromEnum(rhs.si);
4081 return @intFromEnum(lhs.section_number) < @intFromEnum(rhs.section_number);
37494082 }
3750 }.lessThan;
4083 };
4084
4085 pending_symbols.sortUnstable(SortContext{ .v = pending_symbols.values() });
4086
4087 try coff.input_symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
4088 var prev_sn: Symbol.SectionNumber = .UNDEFINED;
4089 for (pending_symbols.values()) |symbol| {
4090 // The symbol may have not been included, or it's an undefined external
4091 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
4092 assert(coff.getNode(symbol.si.get(coff).ni) == .input_section);
37514093
3752 std.mem.sortUnstable(Symbol.Index, symbols.items, coff, symbolLessThan);
3753
3754 // Any symbols that we resolved (used to be undefined but are now defined) in this pass need to be
3755 // added to contigous ranges in `input_resolved` so they can be visited in `flushMoved`, as they
3756 // are not part of the contiguous ii.first_si / ii.last_si range.
3757 //
3758 // TODO: Should we just use this array for all symbols in this input? More memory but less get().ni misses in flushMoved
3759 try coff.input_resolved.ensureUnusedCapacity(gpa, num_resolved);
3760 var prev_isi: ?Node.InputSection.Index = null;
3761 for (symbols.items) |si| {
3762 if (si == .null or @intFromEnum(si) >= @intFromEnum(input.end_si)) continue;
3763 const ni = si.get(coff).ni;
3764 if (ni == .none) continue;
3765
3766 const isi = coff.getNode(ni).input_section;
3767 if (prev_isi != isi) {
3768 isi.inputSection(coff).first_iri = @enumFromInt(coff.input_resolved.items.len);
3769 prev_isi = isi;
4094 if (prev_sn != symbol.section_number) {
4095 prev_sn = symbol.section_number;
4096
4097 const section = &sections[symbol.section_number.toIndex()];
4098 const isi = coff.getNode(section.si.get(coff).ni).input_section;
4099 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
37704100 }
37714101
3772 coff.input_resolved.addOneAssumeCapacity().* = si;
4102 coff.input_symbols.addOneAssumeCapacity().* = symbol.si;
37734103 }
37744104}
37754105
4106fn failMultipleDefinitions(
4107 coff: *Coff,
4108 path: std.Build.Cache.Path,
4109 member_name: ?[]const u8,
4110 name: String,
4111 index: u32,
4112 existing_si: Symbol.Index,
4113 comdat_reason: union(enum) {
4114 none: void,
4115 duplicate: void,
4116 size: struct { a: u64, b: u64 },
4117 crc: struct { a: u32, b: u32 },
4118 },
4119) error{ LinkFailure, OutOfMemory } {
4120 const num_notes: usize = 2 + @as(usize, @intFromBool(comdat_reason != .none));
4121 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
4122 try err.addMsg("multiple definitions of '{s}'", .{name.toSlice(coff)});
4123
4124 switch (coff.getNode(existing_si.get(coff).ni)) {
4125 .input_section => |isi| {
4126 const other_ii = isi.input(coff);
4127 err.addNote("first seen in input '{f}{f}'", .{
4128 other_ii.path(coff).fmtEscapeString(),
4129 fmtMemberNameString(other_ii.memberName(coff)),
4130 });
4131 },
4132 .nav, .uav => err.addNote("first seen in module '{s}'", .{
4133 coff.base.comp.zcu.?.root_mod.fully_qualified_name,
4134 }),
4135 //else => |_, tag| err.addNote("TODO multiple def for {t}", .{tag}),
4136 else => unreachable,
4137 }
4138
4139 err.addNote("defined again in input '{f}{f}' (0x{x}))", .{ path, fmtMemberNameString(member_name), index });
4140 switch (comdat_reason) {
4141 .none => {},
4142 .duplicate => err.addNote("COMDAT rule requires no duplicates", .{}),
4143 .size => |s| err.addNote(
4144 "COMDAT rule require duplicates to have the same size ({d} vs {d})",
4145 .{ s.a, s.b },
4146 ),
4147 .crc => |s| err.addNote(
4148 "COMDAT rule require duplicates to have the same CRC (0x{x} vs 0x{x})",
4149 .{ s.a, s.b },
4150 ),
4151 }
4152
4153 return error.LinkFailure;
4154}
4155
37764156const ArchiveMemberHeader = struct {
37774157 name: []const u8,
37784158 size: u34,
......@@ -4078,10 +4458,10 @@ fn loadDll(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
40784458}
40794459
40804460pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
4081 _ = coff;
40824461 _ = prog_node;
4083
40844462 log.debug("prelink()", .{});
4463
4464 coff.inputs_complete = true;
40854465}
40864466
40874467pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
......@@ -4410,17 +4790,16 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
44104790 num_unique_references = 1;
44114791 }
44124792
4413 const num_notes =
4414 @min(max_notes, num_unique_references) +
4415 @intFromBool(num_unique_references > max_notes);
4416
4417 var err = try comp.link_diags.addErrorWithNotes(num_notes);
4793 const num_full_notes = @min(max_notes, num_unique_references);
4794 var err = try comp.link_diags.addErrorWithNotes(
4795 num_full_notes + @intFromBool(num_unique_references > max_notes),
4796 );
44184797 const target_sym = target.get(coff);
44194798 try err.addMsg("undefined symbol: {s}", .{target_sym.gmi.globalName(coff).name.toSlice(coff)});
44204799
44214800 var prev_loc_si: Symbol.Index = .null;
44224801 for (undef_indices.items[start_i..][0..@max(1, i - start_i)]) |reference_i| {
4423 if (err.note_slot == num_notes) break;
4802 if (err.note_slot == num_full_notes) break;
44244803
44254804 const loc_si = coff.relocs.items[reference_i].loc;
44264805 if (loc_si == prev_loc_si) continue;
......@@ -4537,18 +4916,27 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
45374916 break :task;
45384917 }
45394918 if (coff.pending_input) |pending_iami| {
4540 // TODO: Prog node?
4919 const name_slice = pending_iami.member(coff).name.toSlice(coff);
4920 const sub_prog_node = coff.input_prog_node.start(
4921 name_slice,
4922 0,
4923 );
4924 defer sub_prog_node.end();
45414925 coff.pending_input = null;
45424926 coff.flushInputMember(pending_iami) catch |err| switch (err) {
45434927 error.OutOfMemory => return error.OutOfMemory,
45444928 else => |e| return comp.link_diags.fail(
4545 "linker failed to load archive member: {t}",
4546 .{e},
4929 "linker failed to load archive member '{f}{f}': {t}",
4930 .{
4931 pending_iami.member(coff).iai.path(coff),
4932 fmtMemberNameString(name_slice),
4933 e,
4934 },
45474935 ),
45484936 };
45494937 break :task;
45504938 }
4551 if (coff.global_pending_index < coff.globals.count()) {
4939 if (coff.inputs_complete and coff.global_pending_index < coff.globals.count()) {
45524940 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
45534941 const sub_prog_node = coff.synth_prog_node.start(
45544942 gmi.globalName(coff).name.toSlice(coff),
......@@ -4699,7 +5087,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
46995087 }
47005088 if (coff.pending_uavs.count() > 0) return true;
47015089 if (coff.pending_input != null) return true;
4702 if (coff.globals.count() > coff.global_pending_index) return true;
5090 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
47035091 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
47045092 if (coff.symbol_table.pending.count() > 0) return true;
47055093 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
......@@ -4810,12 +5198,17 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
48105198 const comp = coff.base.comp;
48115199 const gpa = comp.gpa;
48125200 const gn = gmi.globalName(coff);
4813 log.debug("flushGlobal({s}, {?s}) = {d}", .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), gmi.symbol(coff) });
5201 const si = gmi.symbol(coff);
5202 const sym = si.get(coff);
5203
5204 log.debug(
5205 "flushGlobal({s}, {?s}) = {d} ({d})",
5206 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), si, sym.ni },
5207 );
48145208
48155209 if (!coff.isImage()) {
4816 const si = gmi.symbol(coff);
48175210 try coff.pendingSymbolTableEntry(si);
4818 if (coff.isArchive() and si.get(coff).ni != .none)
5211 if (coff.isArchive() and sym.ni != .none)
48195212 try coff.ensureMemberSymbol(
48205213 coff.getNode(Node.known.zcu_member).archive_member,
48215214 gn.name,
......@@ -4945,8 +5338,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
49455338 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
49465339 },
49475340 }
4948 const si = gmi.symbol(coff);
4949 const sym = si.get(coff);
49505341 sym.section_number = Symbol.Index.text.get(coff).section_number;
49515342 assert(sym.loc_relocs == .none);
49525343 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
......@@ -4980,14 +5371,49 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
49805371 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
49815372 sym.rva = coff.computeNodeRva(sym.ni);
49825373 si.applyLocationRelocs(coff);
4983 } else {
4984 if (coff.input_archive_symbol_indices.get(gn.name)) |index| {
5374 } else if (sym.ni == .none) {
5375 switch (sym.value) {
5376 .alias_si => |alias_si| {
5377 assert(sym.section_number == .UNDEFINED);
5378 assert(sym.loc_relocs == .none);
5379
5380 const alias_sym = alias_si.get(coff);
5381 var ri = sym.target_relocs;
5382 while (ri != .none) {
5383 const reloc = ri.get(coff);
5384 assert(reloc.target == si);
5385 reloc.target = alias_si;
5386 if (reloc.next == .none) {
5387 reloc.next = alias_sym.target_relocs;
5388 if (alias_sym.target_relocs != .none)
5389 alias_sym.target_relocs.get(coff).prev = ri;
5390 }
5391 ri = reloc.next;
5392 }
5393
5394 sym.target_relocs = .none;
5395 coff.globals.values()[gmi.unwrap().?] = alias_si;
5396 alias_si.applyTargetRelocs(coff);
5397
5398 log.debug("flushGlobal({s}, {?s}) alias {d}->{d}", .{
5399 gmi.globalName(coff).name.toSlice(coff),
5400 gmi.globalName(coff).lib_name.toSlice(coff),
5401 si,
5402 alias_si,
5403 });
5404
5405 return true;
5406 },
5407 .size => {},
5408 .input_offset => unreachable,
5409 }
5410
5411 if (coff.input_archive_symbol_indices.get(gmi.globalName(coff).name)) |index| {
49855412 var iter: InputArchive.Member.Symbol.Index = index.first;
49865413 while (true) {
49875414 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
4988
4989 // TODO: This implies that loading an input failing is not fatal
49905415 if (!coff.input_archive_members.items[@intFromEnum(archive_sym.iami)].flags.is_loaded) {
5416 // Try loading the input member and then retry
49915417 coff.pending_input = archive_sym.iami;
49925418 return false;
49935419 }
......@@ -4996,8 +5422,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
49965422 iter = archive_sym.next;
49975423 }
49985424 }
4999
5000 // TODO: Check if we can get flushGlobal before prelink, that would cause a problem
50015425 }
50025426
50035427 return true;
......@@ -5116,19 +5540,8 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
51165540 );
51175541 },
51185542 .input_section => |isi| {
5119 const ii = isi.input(coff);
51205543 isi.symbol(coff).flushMoved(coff);
5121
5122 {
5123 var si = ii.firstSymbol(coff);
5124 const end_si = ii.endSymbol(coff);
5125 while (@intFromEnum(si) < @intFromEnum(end_si)) : (si = si.next()) {
5126 if (si.get(coff).ni != ni) continue;
5127 si.flushMoved(coff);
5128 }
5129 }
5130
5131 for (coff.input_resolved.items[@intFromEnum(isi.firstResolvedSymbol(coff))..]) |si| {
5544 for (coff.input_symbols.items[@intFromEnum(isi.firstSymbol(coff))..]) |si| {
51325545 if (si.get(coff).ni != ni) break;
51335546 si.flushMoved(coff);
51345547 }
......@@ -5444,7 +5857,7 @@ fn flushExportsSort(coff: *Coff) void {
54445857 entries: []ExportTable.Entry,
54455858 nt: []const u8,
54465859
5447 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
5860 pub fn lessThan(ctx: *const @This(), lhs: usize, rhs: usize) bool {
54485861 const lhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[lhs].unbiased_ordinal)];
54495862 const rhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[rhs].unbiased_ordinal)];
54505863 return std.mem.lessThan(
......@@ -5460,7 +5873,7 @@ fn flushExportsSort(coff: *Coff) void {
54605873 }
54615874 };
54625875
5463 std.sort.pdqContext(0, coff.export_table.entries.count(), Context{
5876 std.sort.pdqContext(0, coff.export_table.entries.count(), &Context{
54645877 .coff = coff,
54655878 .np = coff.exportNamePointerTableSlice(),
54665879 .ord = coff.exportOrdinalTableSlice(),