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 {...@@ -654,6 +654,10 @@ pub const SectionHeader = extern struct {
654 std.debug.assert(std.math.isPowerOfTwo(n));654 std.debug.assert(std.math.isPowerOfTwo(n));
655 return @enumFromInt(@ctz(n) + 1);655 return @enumFromInt(@ctz(n) + 1);
656 }656 }
657
658 pub fn alignment(a: Align) ?std.mem.Alignment {
659 return .fromByteUnitsOptional(a.toByteUnits() orelse null);
660 }
657 };661 };
658 };662 };
659};663};
src/link/Coff.zig+748-335
...@@ -40,9 +40,10 @@ input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {...@@ -40,9 +40,10 @@ input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {
40}),40}),
41pending_input: ?InputArchive.Member.Index,41pending_input: ?InputArchive.Member.Index,
42inputs: std.ArrayList(Input),42inputs: std.ArrayList(Input),
43input_resolved: std.ArrayList(Symbol.Index),43input_symbols: std.ArrayList(Symbol.Index),
44input_sections: std.ArrayList(Node.InputSection),44input_sections: std.ArrayList(Node.InputSection),
45input_section_pending_index: u32,45input_section_pending_index: u32,
46inputs_complete: bool,
46strings: std.HashMapUnmanaged(47strings: std.HashMapUnmanaged(
47 u32,48 u32,
48 void,49 void,
...@@ -292,25 +293,14 @@ pub const Node = union(enum) {...@@ -292,25 +293,14 @@ pub const Node = union(enum) {
292 pub fn memberName(ii: InputIndex, coff: *const Coff) ?[]const u8 {293 pub fn memberName(ii: InputIndex, coff: *const Coff) ?[]const u8 {
293 return coff.inputs.items[@intFromEnum(ii)].member_name;294 return coff.inputs.items[@intFromEnum(ii)].member_name;
294 }295 }
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 }
307 };296 };
308297
309 const InputSection = struct {298 const InputSection = struct {
310 ii: Node.InputIndex,299 ii: Node.InputIndex,
311 si: Symbol.Index,300 si: Symbol.Index,
312 file_location: MappedFile.Node.FileLocation,301 file_location: MappedFile.Node.FileLocation,
313 first_iri: Node.InputSection.ResolvedIndex,302 first_li: Node.InputSection.LocalIndex,
303 crc: u32,
314304
315 pub const Index = enum(u32) {305 pub const Index = enum(u32) {
316 _,306 _,
...@@ -331,12 +321,12 @@ pub const Node = union(enum) {...@@ -331,12 +321,12 @@ pub const Node = union(enum) {
331 return coff.input_sections.items[@intFromEnum(isi)].si;321 return coff.input_sections.items[@intFromEnum(isi)].si;
332 }322 }
333323
334 pub fn firstResolvedSymbol(isi: Index, coff: *const Coff) ResolvedIndex {324 pub fn firstSymbol(isi: Index, coff: *const Coff) LocalIndex {
335 return coff.input_sections.items[@intFromEnum(isi)].first_iri;325 return coff.input_sections.items[@intFromEnum(isi)].first_li;
336 }326 }
337 };327 };
338328
339 const ResolvedIndex = enum(u32) {329 const LocalIndex = enum(u32) {
340 _,330 _,
341 };331 };
342 };332 };
...@@ -449,8 +439,7 @@ pub const InputArchive = struct {...@@ -449,8 +439,7 @@ pub const InputArchive = struct {
449pub const Input = struct {439pub const Input = struct {
450 path: std.Build.Cache.Path,440 path: std.Build.Cache.Path,
451 member_name: ?[]const u8,441 member_name: ?[]const u8,
452 first_si: Symbol.Index,442 source_name: String.Optional,
453 end_si: Symbol.Index,
454};443};
455444
456pub const Member = struct {445pub const Member = struct {
...@@ -814,11 +803,14 @@ pub const GlobalName = struct { name: String, lib_name: String.Optional };...@@ -814,11 +803,14 @@ pub const GlobalName = struct { name: String, lib_name: String.Optional };
814pub const Symbol = struct {803pub const Symbol = struct {
815 ni: MappedFile.Node.Index,804 ni: MappedFile.Node.Index,
816 rva: u32,805 rva: u32,
817 value: union {806 value: union(enum) {
818 /// For generated symbols, this is their size807 /// For .ni == .input_section, this is the offset of this symbol within the input section
819 size: u32,
820 /// For symbols from input sections, this is the offset within the input section
821 input_offset: u32,808 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,
822 },814 },
823 /// Relocations contained within this symbol815 /// Relocations contained within this symbol
824 loc_relocs: Reloc.Index,816 loc_relocs: Reloc.Index,
...@@ -828,7 +820,6 @@ pub const Symbol = struct {...@@ -828,7 +820,6 @@ pub const Symbol = struct {
828 /// Only used when outputting objects820 /// Only used when outputting objects
829 sti: SymbolTable.Index,821 sti: SymbolTable.Index,
830 gmi: Node.GlobalMapIndex,822 gmi: Node.GlobalMapIndex,
831 unused0: u16 = 0,
832823
833 pub const SectionNumber = enum(i16) {824 pub const SectionNumber = enum(i16) {
834 UNDEFINED = 0,825 UNDEFINED = 0,
...@@ -840,6 +831,13 @@ pub const Symbol = struct {...@@ -840,6 +831,13 @@ pub const Symbol = struct {
840 return @intCast(@intFromEnum(sn) - 1);831 return @intCast(@intFromEnum(sn) - 1);
841 }832 }
842833
834 fn hasIndex(sn: SectionNumber) bool {
835 return switch (sn) {
836 .UNDEFINED, .ABSOLUTE, .DEBUG => false,
837 else => true,
838 };
839 }
840
843 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {841 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
844 return sn.section(coff).si;842 return sn.section(coff).si;
845 }843 }
...@@ -1021,15 +1019,21 @@ pub const Reloc = extern struct {...@@ -1021,15 +1019,21 @@ pub const Reloc = extern struct {
1021 ),1019 ),
1022 .ADDR32,1020 .ADDR32,
1023 .ADDR32NB,1021 .ADDR32NB,
1022 .SECREL,
1023 => std.mem.writeInt(
1024 u32,
1025 loc_slice[0..4],
1026 @intCast(reloc.addend),
1027 target_endian,
1028 ),
1024 .REL32,1029 .REL32,
1025 .REL32_1,1030 .REL32_1,
1026 .REL32_2,1031 .REL32_2,
1027 .REL32_3,1032 .REL32_3,
1028 .REL32_4,1033 .REL32_4,
1029 .REL32_5,1034 .REL32_5,
1030 .SECREL,
1031 => std.mem.writeInt(1035 => std.mem.writeInt(
1032 u32,1036 i32,
1033 loc_slice[0..4],1037 loc_slice[0..4],
1034 @intCast(reloc.addend),1038 @intCast(reloc.addend),
1035 target_endian,1039 target_endian,
...@@ -1039,16 +1043,21 @@ pub const Reloc = extern struct {...@@ -1039,16 +1043,21 @@ pub const Reloc = extern struct {
1039 else => |kind| @panic(@tagName(kind)),1043 else => |kind| @panic(@tagName(kind)),
1040 .ABSOLUTE => {},1044 .ABSOLUTE => {},
1041 .DIR16,1045 .DIR16,
1042 .REL16,
1043 => std.mem.writeInt(1046 => std.mem.writeInt(
1044 u16,1047 u16,
1045 loc_slice[0..2],1048 loc_slice[0..2],
1046 @intCast(reloc.addend),1049 @intCast(reloc.addend),
1047 target_endian,1050 target_endian,
1048 ),1051 ),
1052 .REL16,
1053 => std.mem.writeInt(
1054 i16,
1055 loc_slice[0..2],
1056 @intCast(reloc.addend),
1057 target_endian,
1058 ),
1049 .DIR32,1059 .DIR32,
1050 .DIR32NB,1060 .DIR32NB,
1051 .REL32,
1052 .SECREL,1061 .SECREL,
1053 => std.mem.writeInt(1062 => std.mem.writeInt(
1054 u32,1063 u32,
...@@ -1056,6 +1065,13 @@ pub const Reloc = extern struct {...@@ -1056,6 +1065,13 @@ pub const Reloc = extern struct {
1056 @intCast(reloc.addend),1065 @intCast(reloc.addend),
1057 target_endian,1066 target_endian,
1058 ),1067 ),
1068 .REL32,
1069 => std.mem.writeInt(
1070 i32,
1071 loc_slice[0..4],
1072 @intCast(reloc.addend),
1073 target_endian,
1074 ),
1059 },1075 },
1060 }1076 }
10611077
...@@ -1074,15 +1090,20 @@ pub const Reloc = extern struct {...@@ -1074,15 +1090,20 @@ pub const Reloc = extern struct {
1074 )),1090 )),
1075 .ADDR32,1091 .ADDR32,
1076 .ADDR32NB,1092 .ADDR32NB,
1093 .SECREL,
1094 => std.mem.readInt(
1095 u32,
1096 loc_slice[0..4],
1097 target_endian,
1098 ),
1077 .REL32,1099 .REL32,
1078 .REL32_1,1100 .REL32_1,
1079 .REL32_2,1101 .REL32_2,
1080 .REL32_3,1102 .REL32_3,
1081 .REL32_4,1103 .REL32_4,
1082 .REL32_5,1104 .REL32_5,
1083 .SECREL,
1084 => std.mem.readInt(1105 => std.mem.readInt(
1085 u32,1106 i32,
1086 loc_slice[0..4],1107 loc_slice[0..4],
1087 target_endian,1108 target_endian,
1088 ),1109 ),
...@@ -1091,21 +1112,31 @@ pub const Reloc = extern struct {...@@ -1091,21 +1112,31 @@ pub const Reloc = extern struct {
1091 else => |kind| @panic(@tagName(kind)),1112 else => |kind| @panic(@tagName(kind)),
1092 .ABSOLUTE => 0,1113 .ABSOLUTE => 0,
1093 .DIR16,1114 .DIR16,
1094 .REL16,
1095 => std.mem.readInt(1115 => std.mem.readInt(
1096 u16,1116 u16,
1097 loc_slice[0..2],1117 loc_slice[0..2],
1098 target_endian,1118 target_endian,
1099 ),1119 ),
1120 .REL16,
1121 => std.mem.readInt(
1122 i16,
1123 loc_slice[0..2],
1124 target_endian,
1125 ),
1100 .DIR32,1126 .DIR32,
1101 .DIR32NB,1127 .DIR32NB,
1102 .REL32,
1103 .SECREL,1128 .SECREL,
1104 => std.mem.readInt(1129 => std.mem.readInt(
1105 u32,1130 u32,
1106 loc_slice[0..4],1131 loc_slice[0..4],
1107 target_endian,1132 target_endian,
1108 ),1133 ),
1134 .REL32,
1135 => std.mem.readInt(
1136 i32,
1137 loc_slice[0..4],
1138 target_endian,
1139 ),
1109 },1140 },
1110 };1141 };
1111 }1142 }
...@@ -1356,9 +1387,10 @@ fn create(...@@ -1356,9 +1387,10 @@ fn create(
1356 .input_archive_symbol_indices = .empty,1387 .input_archive_symbol_indices = .empty,
1357 .pending_input = null,1388 .pending_input = null,
1358 .inputs = .empty,1389 .inputs = .empty,
1359 .input_resolved = .empty,1390 .input_symbols = .empty,
1360 .input_sections = .empty,1391 .input_sections = .empty,
1361 .input_section_pending_index = 0,1392 .input_section_pending_index = 0,
1393 .inputs_complete = false,
1362 .strings = .empty,1394 .strings = .empty,
1363 .string_bytes = .empty,1395 .string_bytes = .empty,
1364 .section_table = .empty,1396 .section_table = .empty,
...@@ -1420,7 +1452,7 @@ pub fn deinit(coff: *Coff) void {...@@ -1420,7 +1452,7 @@ pub fn deinit(coff: *Coff) void {
1420 coff.input_archive_symbols.deinit(gpa);1452 coff.input_archive_symbols.deinit(gpa);
1421 coff.input_archive_symbol_indices.deinit(gpa);1453 coff.input_archive_symbol_indices.deinit(gpa);
1422 coff.inputs.deinit(gpa);1454 coff.inputs.deinit(gpa);
1423 coff.input_resolved.deinit(gpa);1455 coff.input_symbols.deinit(gpa);
1424 coff.input_sections.deinit(gpa);1456 coff.input_sections.deinit(gpa);
1425 coff.strings.deinit(gpa);1457 coff.strings.deinit(gpa);
1426 coff.string_bytes.deinit(gpa);1458 coff.string_bytes.deinit(gpa);
...@@ -2238,13 +2270,6 @@ fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {...@@ -2238,13 +2270,6 @@ fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
2238 return si;2270 return si;
2239}2271}
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
2248fn getOrPutString(coff: *Coff, string: []const u8) !String {2273fn getOrPutString(coff: *Coff, string: []const u8) !String {
2249 try coff.ensureUnusedStringCapacity(string.len);2274 try coff.ensureUnusedStringCapacity(string.len);
2250 return coff.getOrPutStringAssumeCapacity(string);2275 return coff.getOrPutStringAssumeCapacity(string);
...@@ -2315,7 +2340,7 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {...@@ -2315,7 +2340,7 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
2315}2340}
23162341
2317const GlobalOptions = struct {2342const GlobalOptions = struct {
2318 name: []const u8,2343 name: []const u8, // TODO: Union with String
2319 lib_name: ?[]const u8 = null,2344 lib_name: ?[]const u8 = null,
2320};2345};
23212346
...@@ -2986,7 +3011,8 @@ fn objectSectionMapIndex(...@@ -2986,7 +3011,8 @@ fn objectSectionMapIndex(
2986 attributes: ObjectSectionAttributes,3011 attributes: ObjectSectionAttributes,
2987) !Node.ObjectSectionMapIndex {3012) !Node.ObjectSectionMapIndex {
2988 const gpa = coff.base.comp.gpa;3013 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: {
2990 // In images, the .tls section is a read-only template3016 // In images, the .tls section is a read-only template
2991 var attr = attributes;3017 var attr = attributes;
2992 attr.write = false;3018 attr.write = false;
...@@ -2996,8 +3022,7 @@ fn objectSectionMapIndex(...@@ -2996,8 +3022,7 @@ fn objectSectionMapIndex(
2996 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);3022 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
2997 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);3023 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
2998 const sn = if (!object_section_gop.found_existing) sn: {3024 const sn = if (!object_section_gop.found_existing) sn: {
2999 try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);3025 try coff.ensureUnusedStringCapacity(name_slice.len);
3000 const name_slice = name.toSlice(coff);
3001 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));3026 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
3002 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);3027 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
3003 try coff.nodes.ensureUnusedCapacity(gpa, 1);3028 try coff.nodes.ensureUnusedCapacity(gpa, 1);
...@@ -3047,6 +3072,7 @@ fn objectSectionMapIndex(...@@ -3047,6 +3072,7 @@ fn objectSectionMapIndex(
3047 return osmi;3072 return osmi;
3048}3073}
30493074
3075// TODO: Include align in attrs and verify the current align is >= requested
3050fn verifyParentSectionAttributes(3076fn verifyParentSectionAttributes(
3051 coff: *Coff,3077 coff: *Coff,
3052 kind: enum { pseudo, object },3078 kind: enum { pseudo, object },
...@@ -3096,7 +3122,8 @@ pub fn addReloc(...@@ -3096,7 +3122,8 @@ pub fn addReloc(
3096 const gpa = coff.base.comp.gpa;3122 const gpa = coff.base.comp.gpa;
3097 const target = target_si.get(coff);3123 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}", .{
3100 loc_si,3127 loc_si,
3101 loc_si.get(coff).section_number,3128 loc_si.get(coff).section_number,
3102 offset,3129 offset,
...@@ -3104,6 +3131,7 @@ pub fn addReloc(...@@ -3104,6 +3131,7 @@ pub fn addReloc(
3104 target_si.get(coff).section_number,3131 target_si.get(coff).section_number,
3105 if (addend == .pending) 0 else addend.known,3132 if (addend == .pending) 0 else addend.known,
3106 if (addend == .pending) "p" else "k",3133 if (addend == .pending) "p" else "k",
3134 ri,
3107 });3135 });
31083136
3109 try coff.relocs.ensureUnusedCapacity(gpa, 1);3137 try coff.relocs.ensureUnusedCapacity(gpa, 1);
...@@ -3166,7 +3194,6 @@ pub fn addReloc(...@@ -3166,7 +3194,6 @@ pub fn addReloc(
3166 },3194 },
3167 };3195 };
31683196
3169 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
3170 coff.relocs.addOneAssumeCapacity().* = .{3197 coff.relocs.addOneAssumeCapacity().* = .{
3171 .type = @"type",3198 .type = @"type",
3172 .prev = .none,3199 .prev = .none,
...@@ -3319,8 +3346,7 @@ fn loadObject(...@@ -3319,8 +3346,7 @@ fn loadObject(
3319 input.* = .{3346 input.* = .{
3320 .path = path,3347 .path = path,
3321 .member_name = if (member_name) |m| try gpa.dupe(u8, m) else null,3348 .member_name = if (member_name) |m| try gpa.dupe(u8, m) else null,
3322 .first_si = @enumFromInt(coff.symbols.items.len),3349 .source_name = .none,
3323 .end_si = @enumFromInt(coff.symbols.items.len),
3324 };3350 };
33253351
3326 const string_table = string_table: {3352 const string_table = string_table: {
...@@ -3336,30 +3362,60 @@ fn loadObject(...@@ -3336,30 +3362,60 @@ fn loadObject(
3336 string_table_len - @sizeOf(u32),3362 string_table_len - @sizeOf(u32),
3337 );3363 );
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 {
3340 header: std.coff.SectionHeader,3382 header: std.coff.SectionHeader,
3341 name: String,3383 name: String,
3342 si: Symbol.Index,3384 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 },
3343 };3399 };
33443400
3345 const sections: []const InputSection = if (coff.isImage()) sections: {3401 const sections: []PendingInputSection = if (coff.isImage()) sections: {
3346 const sections = try gpa.alloc(InputSection, header.number_of_sections);3402 const sections = try gpa.alloc(PendingInputSection, header.number_of_sections);
3347 errdefer gpa.free(sections);3403 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
3357 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));3405 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));
3358 for (sections, 0..) |*section, section_i| {3406 for (sections, 0..) |*section, section_i| {
3359 section.* = .{3407 section.* = .{
3360 .header = try r.takeStruct(std.coff.SectionHeader, target_endian),3408 .header = try r.takeStruct(std.coff.SectionHeader, target_endian),
3361 .name = undefined,3409 .name = undefined,
3362 .si = .null,3410 .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,
3363 };3419 };
33643420
3365 const section_name_slice = if (section.header.name[0] == '/') name: {3421 const section_name_slice = if (section.header.name[0] == '/') name: {
...@@ -3371,7 +3427,11 @@ fn loadObject(...@@ -3371,7 +3427,11 @@ fn loadObject(
3371 });3427 });
33723428
3373 if (name_offset > string_table.len)3429 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
3376 break :name std.mem.sliceTo(string_table[name_offset..], 0);3436 break :name std.mem.sliceTo(string_table[name_offset..], 0);
3377 } else std.mem.sliceTo(&section.header.name, 0);3437 } else std.mem.sliceTo(&section.header.name, 0);
...@@ -3403,109 +3463,6 @@ fn loadObject(...@@ -3403,109 +3463,6 @@ fn loadObject(
3403 // TODO: Convert .debug$* sections into PDB3463 // TODO: Convert .debug$* sections into PDB
3404 continue;3464 continue;
3405 }3465 }
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);
3509 }3466 }
35103467
3511 break :sections sections;3468 break :sections sections;
...@@ -3522,9 +3479,8 @@ fn loadObject(...@@ -3522,9 +3479,8 @@ fn loadObject(
3522 const member = mi.get(coff);3479 const member = mi.get(coff);
3523 try member.initHeader(coff, path_str, header.time_date_stamp);3480 try member.initHeader(coff, path_str, header.time_date_stamp);
35243481
3525 // TODO: This could be deferred to an idle task?
3526
3527 {3482 {
3483 // TODO: This could be deferred to an idle task?
3528 var nw: MappedFile.Node.Writer = undefined;3484 var nw: MappedFile.Node.Writer = undefined;
3529 member.content_ni.writer(&coff.mf, gpa, &nw);3485 member.content_ni.writer(&coff.mf, gpa, &nw);
3530 defer nw.deinit();3486 defer nw.deinit();
...@@ -3537,18 +3493,35 @@ fn loadObject(...@@ -3537,18 +3493,35 @@ fn loadObject(
3537 break :mi mi;3493 break :mi mi;
3538 } else undefined;3494 } else undefined;
35393495
3540 // TODO: Also reserve memory for the symbols / globals / relocs within each section
3541
3542 try fr.seekTo(fl.offset + header.pointer_to_symbol_table);3496 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;3499 const PendingSymbol = struct {
3546 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);3500 name: String,
3547 var num_resolved: u32 = 0;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);3521 if (!is_archive)
3550 defer input.end_si = @enumFromInt(coff.symbols.items.len);3522 try pending_symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
35513523
3524 // Discover symbol names and COMDAT symbol mappings
3552 var symbol_i: u32 = 0;3525 var symbol_i: u32 = 0;
3553 while (symbol_i < header.number_of_symbols) {3526 while (symbol_i < header.number_of_symbols) {
3554 var symbol: std.coff.Symbol = undefined;3527 var symbol: std.coff.Symbol = undefined;
...@@ -3556,10 +3529,11 @@ fn loadObject(...@@ -3556,10 +3529,11 @@ fn loadObject(
3556 if (target_endian != native_endian)3529 if (target_endian != native_endian)
3557 std.mem.byteSwapAllFields(std.coff.Symbol, &symbol);3530 std.mem.byteSwapAllFields(std.coff.Symbol, &symbol);
35583531
3559 defer {3532 const aux_symbols = if (symbol.number_of_aux_symbols > 0)
3560 r.toss(symbol.number_of_aux_symbols * symbol_size);3533 try r.take(symbol_size * symbol.number_of_aux_symbols)
3561 symbol_i += symbol.number_of_aux_symbols + 1;3534 else
3562 }3535 &.{};
3536 defer symbol_i += symbol.number_of_aux_symbols + 1;
35633537
3564 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {3538 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
3565 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);3539 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);
...@@ -3568,145 +3542,499 @@ fn loadObject(...@@ -3568,145 +3542,499 @@ fn loadObject(
3568 break :name string_table[index..];3542 break :name string_table[index..];
3569 } else &symbol.name, 0);3543 } 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
3581 if (is_archive) {3545 if (is_archive) {
3582 if (symbol.storage_class == .EXTERNAL and symbol.section_number != .UNDEFINED)3546 if (switch (symbol.storage_class) {
3583 try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));3547 .WEAK_EXTERNAL => true,
3548 .EXTERNAL => symbol.section_number != .UNDEFINED,
3549 else => false,
3550 }) try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
35843551
3585 continue;3552 continue;
3586 }3553 }
35873554
3588 switch (symbol.storage_class) {3555 switch (symbol.section_number) {
3589 .STATIC, .LABEL => |storage_class| switch (symbol.section_number) {3556 .UNDEFINED, .DEBUG, .ABSOLUTE => {},
3590 .UNDEFINED, .DEBUG, .ABSOLUTE => {3557 else => |sn| if (@intFromEnum(sn) > sections.len)
3591 // TODO: Do we need to do anything with @feat.00?3558 return diags.failParse(path, "out-of-bounds section number {d} in symbol 0x{x}", .{ sn, symbol_i }),
3592 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c3923559 }
3593 },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,
3594 else => |sn| {3568 else => |sn| {
3569 const section = &sections[sn.toIndex()];
3570
3595 // Section symbol3571 // Section symbol
3596 if (storage_class == .STATIC and3572 const is_section = storage_class == .STATIC and
3597 symbol.value == 0 and3573 symbol.value == 0 and
3598 symbol.type == std.coff.SymType{3574 symbol.type == std.coff.SymType{
3599 .complex_type = .NULL,3575 .complex_type = .NULL,
3600 .base_type = .NULL,3576 .base_type = .NULL,
3601 } and3577 } and
3602 symbol.number_of_aux_symbols > 0)3578 symbol.number_of_aux_symbols > 0;
3603 {3579
3580 if (is_section) {
3604 if (symbol.number_of_aux_symbols > 1)3581 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}", .{
3606 symbol_i,3583 symbol_i,
3607 symbol.number_of_aux_symbols,3584 symbol.number_of_aux_symbols,
3608 });3585 });
36093586
3610 var section_def: std.coff.SectionDefinition = undefined;3587 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]);
3612 if (target_endian != native_endian)3589 if (target_endian != native_endian)
3613 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def);3590 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];
3629 if (section_def.number_of_relocations != section.header.number_of_relocations)3592 if (section_def.number_of_relocations != section.header.number_of_relocations)
3630 return diags.failParse(3593 return diags.failParse(
3631 path,3594 path,
3632 "section symbol for '{s}' relocation count did not match section header: {d} vs {d}",3595 "section aux symbol 0x{x} for '{s}' relocation count did not match section header: {d} vs {d}",
3633 .{ name, section_def.number_of_relocations, section.header.number_of_relocations },3596 .{ symbol_i + 1, name, section_def.number_of_relocations, section.header.number_of_relocations },
3634 );3597 );
36353598
3636 if (section_def.number_of_linenumbers != section.header.number_of_linenumbers)3599 if (section_def.number_of_linenumbers != section.header.number_of_linenumbers)
3637 return diags.failParse(3600 return diags.failParse(
3638 path,3601 path,
3639 "section symbol for '{s}' line number count did not match section header: {d} vs {d}",3602 "section aux symbol 0x{x} 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 },3603 .{ symbol_i + 1, name, section_def.number_of_linenumbers, section.header.number_of_linenumbers },
3641 );3604 );
36423605
3643 @memset(si_slice, section.si);3606 if (section.header.flags.LNK_COMDAT) {
3644 } else {3607 if (section_def.selection == .ASSOCIATIVE) {
3645 try coff.symbols.ensureUnusedCapacity(gpa, 1);3608 if (section_def.number == 0 or section_def.number > sections.len)
3646 si_slice[0] = coff.addSymbolAssumeCapacity();3609 return diags.failParse(
3647 const sym = si_slice[0].get(coff);3610 path,
3648 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);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;
3649 }3623 }
3624
3625 break :pending_symbol if (is_section)
3626 .{ .section = section.header.size_of_raw_data }
3627 else
3628 .{ .static = symbol.value };
3650 },3629 },
3651 },3630 },
3652 .EXTERNAL => switch (symbol.section_number) {3631 .WEAK_EXTERNAL => switch (symbol.section_number) {
3653 .UNDEFINED => {3632 .UNDEFINED => {
3654 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });3633 if (symbol.value != 0)
3655 si_slice[0] = global_gop.value_ptr.*;3634 return diags.failParse(
3656 if (!global_gop.found_existing) {3635 path,
3657 const sym = si_slice[0].get(coff);3636 "invalid value {d} for weak external symbol 0x{x}",
3658 sym.value = .{ .size = symbol.value };3637 .{ symbol.value, symbol_i },
3659 }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 };
3660 },3667 },
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 },
3661 .ABSOLUTE => return diags.failParse(3676 .ABSOLUTE => return diags.failParse(
3662 path,3677 path,
3663 "TODO unhandled external absolute symbol: '{s}'",3678 "TODO unhandled external absolute symbol 0x{x}: '{s}'",
3664 .{name},3679 .{ symbol_i, name },
3665 ),3680 ),
3666 .DEBUG => return diags.failParse(3681 .DEBUG => return diags.failParse(
3667 path,3682 path,
3668 "unexpected external symbol in DEBUG section: '{s}'",3683 "unexpected external symbol 0x{x} in DEBUG section: '{s}'",
3669 .{name},3684 .{ symbol_i, name },
3670 ),3685 ),
3671 else => |sn| {3686 else => .{ .external = symbol.value },
3672 // TODO: Should this use archive name as lib_name as well?3687 },
3673 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });3688 .FILE => {
3674 si_slice[0] = global_gop.value_ptr.*;3689 if (!std.mem.eql(u8, name, ".file"))
3675 const sym = si_slice[0].get(coff);3690 return diags.failParse(
3676 if (global_gop.found_existing and sym.ni != .none) {3691 path,
3677 // TODO: Need corresponding logic later if we try to make a global already defined by an input3692 "unexpected symbol name '{s}' for file symbol 0x{x}",
3678 var err = try diags.addErrorWithNotes(2);3693 .{ name, symbol_i },
3679 try err.addMsg("multiple definitions of '{s}'", .{name});3694 );
3680 switch (coff.getNode(sym.ni)) {3695
3681 .input_section => |isi| {3696 var file: std.coff.FileDefinition = undefined;
3682 const other_ii = isi.input(coff);3697 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);
3683 err.addNote("first seen in input '{f}{f}'", .{3698
3684 other_ii.path(coff).fmtEscapeString(),3699 input.source_name = (try coff.getOrPutString(file.getFileName())).toOptional();
3685 fmtMemberNameString(other_ii.memberName(coff)),3700 break :pending_symbol null;
3686 });3701 },
3687 },3702 else => |storage_class| return diags.failParse(
3688 .nav, .uav => err.addNote("first seen in module '{s}'", .{3703 path,
3689 comp.zcu.?.root_mod.fully_qualified_name,3704 "TODO handle storage class {t} for symbol 0x{x}",
3690 }),3705 .{ storage_class, symbol_i },
3691 else => unreachable,3706 ),
3692 }3707 };
3693 err.addNote("defined again in input '{f}'", .{path});3708
3694 return error.LinkFailure;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;
3695 }3719 }
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)3794 const symbol = &pending_symbols.values()[psi];
3698 num_resolved += 1;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;
3701 },3974 },
3702 },3975 },
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;
3704 }4021 }
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 });
3705 }4033 }
37064034
3707 const relocation_size = std.coff.Relocation.sizeOf();4035 const relocation_size = std.coff.Relocation.sizeOf();
3708 for (sections) |section| {4036 for (sections) |section| {
3709 if (section.si == .null) continue;4037 if (section.comdat_result != .include) continue;
37104038
3711 const loc_sym = section.si.get(coff);4039 const loc_sym = section.si.get(coff);
3712 assert(loc_sym.loc_relocs == .none);4040 assert(loc_sym.loc_relocs == .none);
...@@ -3714,7 +4042,6 @@ fn loadObject(...@@ -3714,7 +4042,6 @@ fn loadObject(
37144042
3715 if (section.header.number_of_relocations == 0) continue;4043 if (section.header.number_of_relocations == 0) continue;
37164044
3717 try coff.relocs.ensureUnusedCapacity(gpa, section.header.number_of_relocations);
3718 try fr.seekTo(fl.offset + section.header.pointer_to_relocations);4045 try fr.seekTo(fl.offset + section.header.pointer_to_relocations);
3719 for (0..section.header.number_of_relocations) |reloc_i| {4046 for (0..section.header.number_of_relocations) |reloc_i| {
3720 var reloc: std.coff.Relocation = undefined;4047 var reloc: std.coff.Relocation = undefined;
...@@ -3722,57 +4049,110 @@ fn loadObject(...@@ -3722,57 +4049,110 @@ fn loadObject(
3722 if (target_endian != native_endian)4049 if (target_endian != native_endian)
3723 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);4050 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
3726 return diags.failParse(4054 return diags.failParse(
3727 path,4055 path,
3728 "relocation 0x{x} in section '{s}' targets invalid symbol index 0x{x}",4056 "relocation 0x{x} in section '{s}'{f} targets invalid symbol index 0x{x}",
3729 .{ reloc_i, section.name.toSlice(coff), reloc.symbol_table_index },4057 .{ reloc_i, section.name.toSlice(coff), fmtMemberNameString(member_name), reloc.symbol_table_index },
3730 );4058 );
37314059
3732 assert(symbols.items[reloc.symbol_table_index] != .null);4060 assert(symbol.si != .null);
3733 try coff.addReloc(4061 try coff.addReloc(
3734 section.si,4062 section.si,
3735 reloc.virtual_address - section.header.virtual_address,4063 reloc.virtual_address - section.header.virtual_address,
3736 symbols.items[reloc.symbol_table_index],4064 symbol.si,
3737 .pending,4065 .pending,
3738 @bitCast(reloc.type),4066 @bitCast(reloc.type),
3739 );4067 );
3740 }4068 }
3741 }4069 }
37424070
3743 const symbolLessThan = struct {4071 // Set up contiguous symbol ranges in `input_symbols` for both symbols we just created,
3744 fn lessThan(ctx: *Coff, lhs: Symbol.Index, rhs: Symbol.Index) bool {4072 // and symbols that were previously created as undefined, but we just defined.
3745 const lhs_sn = @intFromEnum(if (lhs == .null) .UNDEFINED else lhs.get(ctx).section_number);4073 const SortContext = struct {
3746 const rhs_sn = @intFromEnum(if (rhs == .null) .UNDEFINED else rhs.get(ctx).section_number);4074 v: []const PendingSymbol,
3747 if (lhs_sn == rhs_sn) return @intFromEnum(lhs) < @intFromEnum(rhs);4075
3748 return lhs_sn < rhs_sn;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);
3749 }4082 }
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);4094 if (prev_sn != symbol.section_number) {
37534095 prev_sn = symbol.section_number;
3754 // Any symbols that we resolved (used to be undefined but are now defined) in this pass need to be4096
3755 // added to contigous ranges in `input_resolved` so they can be visited in `flushMoved`, as they4097 const section = &sections[symbol.section_number.toIndex()];
3756 // are not part of the contiguous ii.first_si / ii.last_si range.4098 const isi = coff.getNode(section.si.get(coff).ni).input_section;
3757 //4099 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
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;
3770 }4100 }
37714101
3772 coff.input_resolved.addOneAssumeCapacity().* = si;4102 coff.input_symbols.addOneAssumeCapacity().* = symbol.si;
3773 }4103 }
3774}4104}
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
3776const ArchiveMemberHeader = struct {4156const ArchiveMemberHeader = struct {
3777 name: []const u8,4157 name: []const u8,
3778 size: u34,4158 size: u34,
...@@ -4078,10 +4458,10 @@ fn loadDll(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -4078,10 +4458,10 @@ fn loadDll(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
4078}4458}
40794459
4080pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {4460pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
4081 _ = coff;
4082 _ = prog_node;4461 _ = prog_node;
4083
4084 log.debug("prelink()", .{});4462 log.debug("prelink()", .{});
4463
4464 coff.inputs_complete = true;
4085}4465}
40864466
4087pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {4467pub 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 {...@@ -4410,17 +4790,16 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
4410 num_unique_references = 1;4790 num_unique_references = 1;
4411 }4791 }
44124792
4413 const num_notes =4793 const num_full_notes = @min(max_notes, num_unique_references);
4414 @min(max_notes, num_unique_references) +4794 var err = try comp.link_diags.addErrorWithNotes(
4415 @intFromBool(num_unique_references > max_notes);4795 num_full_notes + @intFromBool(num_unique_references > max_notes),
44164796 );
4417 var err = try comp.link_diags.addErrorWithNotes(num_notes);
4418 const target_sym = target.get(coff);4797 const target_sym = target.get(coff);
4419 try err.addMsg("undefined symbol: {s}", .{target_sym.gmi.globalName(coff).name.toSlice(coff)});4798 try err.addMsg("undefined symbol: {s}", .{target_sym.gmi.globalName(coff).name.toSlice(coff)});
44204799
4421 var prev_loc_si: Symbol.Index = .null;4800 var prev_loc_si: Symbol.Index = .null;
4422 for (undef_indices.items[start_i..][0..@max(1, i - start_i)]) |reference_i| {4801 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
4425 const loc_si = coff.relocs.items[reference_i].loc;4804 const loc_si = coff.relocs.items[reference_i].loc;
4426 if (loc_si == prev_loc_si) continue;4805 if (loc_si == prev_loc_si) continue;
...@@ -4537,18 +4916,27 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -4537,18 +4916,27 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
4537 break :task;4916 break :task;
4538 }4917 }
4539 if (coff.pending_input) |pending_iami| {4918 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();
4541 coff.pending_input = null;4925 coff.pending_input = null;
4542 coff.flushInputMember(pending_iami) catch |err| switch (err) {4926 coff.flushInputMember(pending_iami) catch |err| switch (err) {
4543 error.OutOfMemory => return error.OutOfMemory,4927 error.OutOfMemory => return error.OutOfMemory,
4544 else => |e| return comp.link_diags.fail(4928 else => |e| return comp.link_diags.fail(
4545 "linker failed to load archive member: {t}",4929 "linker failed to load archive member '{f}{f}': {t}",
4546 .{e},4930 .{
4931 pending_iami.member(coff).iai.path(coff),
4932 fmtMemberNameString(name_slice),
4933 e,
4934 },
4547 ),4935 ),
4548 };4936 };
4549 break :task;4937 break :task;
4550 }4938 }
4551 if (coff.global_pending_index < coff.globals.count()) {4939 if (coff.inputs_complete and coff.global_pending_index < coff.globals.count()) {
4552 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);4940 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
4553 const sub_prog_node = coff.synth_prog_node.start(4941 const sub_prog_node = coff.synth_prog_node.start(
4554 gmi.globalName(coff).name.toSlice(coff),4942 gmi.globalName(coff).name.toSlice(coff),
...@@ -4699,7 +5087,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -4699,7 +5087,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
4699 }5087 }
4700 if (coff.pending_uavs.count() > 0) return true;5088 if (coff.pending_uavs.count() > 0) return true;
4701 if (coff.pending_input != null) return true;5089 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;
4703 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;5091 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
4704 if (coff.symbol_table.pending.count() > 0) return true;5092 if (coff.symbol_table.pending.count() > 0) return true;
4705 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;5093 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 {...@@ -4810,12 +5198,17 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
4810 const comp = coff.base.comp;5198 const comp = coff.base.comp;
4811 const gpa = comp.gpa;5199 const gpa = comp.gpa;
4812 const gn = gmi.globalName(coff);5200 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
4815 if (!coff.isImage()) {5209 if (!coff.isImage()) {
4816 const si = gmi.symbol(coff);
4817 try coff.pendingSymbolTableEntry(si);5210 try coff.pendingSymbolTableEntry(si);
4818 if (coff.isArchive() and si.get(coff).ni != .none)5211 if (coff.isArchive() and sym.ni != .none)
4819 try coff.ensureMemberSymbol(5212 try coff.ensureMemberSymbol(
4820 coff.getNode(Node.known.zcu_member).archive_member,5213 coff.getNode(Node.known.zcu_member).archive_member,
4821 gn.name,5214 gn.name,
...@@ -4945,8 +5338,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -4945,8 +5338,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
4945 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;5338 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
4946 },5339 },
4947 }5340 }
4948 const si = gmi.symbol(coff);
4949 const sym = si.get(coff);
4950 sym.section_number = Symbol.Index.text.get(coff).section_number;5341 sym.section_number = Symbol.Index.text.get(coff).section_number;
4951 assert(sym.loc_relocs == .none);5342 assert(sym.loc_relocs == .none);
4952 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);5343 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
...@@ -4980,14 +5371,49 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -4980,14 +5371,49 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
4980 coff.nodes.appendAssumeCapacity(.{ .global = gmi });5371 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
4981 sym.rva = coff.computeNodeRva(sym.ni);5372 sym.rva = coff.computeNodeRva(sym.ni);
4982 si.applyLocationRelocs(coff);5373 si.applyLocationRelocs(coff);
4983 } else {5374 } else if (sym.ni == .none) {
4984 if (coff.input_archive_symbol_indices.get(gn.name)) |index| {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| {
4985 var iter: InputArchive.Member.Symbol.Index = index.first;5412 var iter: InputArchive.Member.Symbol.Index = index.first;
4986 while (true) {5413 while (true) {
4987 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];5414 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
4988
4989 // TODO: This implies that loading an input failing is not fatal
4990 if (!coff.input_archive_members.items[@intFromEnum(archive_sym.iami)].flags.is_loaded) {5415 if (!coff.input_archive_members.items[@intFromEnum(archive_sym.iami)].flags.is_loaded) {
5416 // Try loading the input member and then retry
4991 coff.pending_input = archive_sym.iami;5417 coff.pending_input = archive_sym.iami;
4992 return false;5418 return false;
4993 }5419 }
...@@ -4996,8 +5422,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -4996,8 +5422,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
4996 iter = archive_sym.next;5422 iter = archive_sym.next;
4997 }5423 }
4998 }5424 }
4999
5000 // TODO: Check if we can get flushGlobal before prelink, that would cause a problem
5001 }5425 }
50025426
5003 return true;5427 return true;
...@@ -5116,19 +5540,8 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -5116,19 +5540,8 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
5116 );5540 );
5117 },5541 },
5118 .input_section => |isi| {5542 .input_section => |isi| {
5119 const ii = isi.input(coff);
5120 isi.symbol(coff).flushMoved(coff);5543 isi.symbol(coff).flushMoved(coff);
51215544 for (coff.input_symbols.items[@intFromEnum(isi.firstSymbol(coff))..]) |si| {
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| {
5132 if (si.get(coff).ni != ni) break;5545 if (si.get(coff).ni != ni) break;
5133 si.flushMoved(coff);5546 si.flushMoved(coff);
5134 }5547 }
...@@ -5444,7 +5857,7 @@ fn flushExportsSort(coff: *Coff) void {...@@ -5444,7 +5857,7 @@ fn flushExportsSort(coff: *Coff) void {
5444 entries: []ExportTable.Entry,5857 entries: []ExportTable.Entry,
5445 nt: []const u8,5858 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 {
5448 const lhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[lhs].unbiased_ordinal)];5861 const lhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[lhs].unbiased_ordinal)];
5449 const rhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[rhs].unbiased_ordinal)];5862 const rhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[rhs].unbiased_ordinal)];
5450 return std.mem.lessThan(5863 return std.mem.lessThan(
...@@ -5460,7 +5873,7 @@ fn flushExportsSort(coff: *Coff) void {...@@ -5460,7 +5873,7 @@ fn flushExportsSort(coff: *Coff) void {
5460 }5873 }
5461 };5874 };
54625875
5463 std.sort.pdqContext(0, coff.export_table.entries.count(), Context{5876 std.sort.pdqContext(0, coff.export_table.entries.count(), &Context{
5464 .coff = coff,5877 .coff = coff,
5465 .np = coff.exportNamePointerTableSlice(),5878 .np = coff.exportNamePointerTableSlice(),
5466 .ord = coff.exportOrdinalTableSlice(),5879 .ord = coff.exportOrdinalTableSlice(),