authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-07 02:52:34-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:27:17-04:00
logae41a2b8acfdab6cfd40333f2f188fcc6f3ee2bf
tree2c63644a8c3d30d278efa222cded764113d61fc5
parent1b74bc22e969e708d5ac6cb34a7b9cd069b71e80

Coff: fix .ctor / .dtor generation, the length fields need to be their own nodes, otherwise child nodes will overwrite them

test/link: add .ctor / .dtor tests for mingw test/link: include optimize_mode in the target, and only enable .Debug targets for now

5 files changed, 158 insertions(+), 87 deletions(-)

src/link/Coff.zig+104-85
...@@ -65,8 +65,6 @@ section_merge_pending_index: u32,...@@ -65,8 +65,6 @@ section_merge_pending_index: u32,
65symbols: std.ArrayList(Symbol),65symbols: std.ArrayList(Symbol),
66globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),66globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),
67global_pending_index: u32,67global_pending_index: u32,
68late_globals: std.ArrayList(Node.GlobalMapIndex),
69late_globals_pending_index: u32,
70navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),68navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),
71uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),69uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),
72lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {70lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
...@@ -188,17 +186,16 @@ pub const Node = union(enum) {...@@ -188,17 +186,16 @@ pub const Node = union(enum) {
188 archive_member: Member.Index,186 archive_member: Member.Index,
189187
190 coff_header,188 coff_header,
189
191 /// Image only190 /// Image only
192 optional_header,191 optional_header,
193 /// Image only
194 data_directories,192 data_directories,
195193
196 section_table,194 section_table,
197 // Archives and objects only195
196 /// Archives and objects only
198 symbol_table,197 symbol_table,
199 // Archives and objects only
200 string_table,198 string_table,
201 // Archives and objects only
202 relocation_table: Symbol.SectionNumber,199 relocation_table: Symbol.SectionNumber,
203 relocation_table_entry: Reloc.Index,200 relocation_table_entry: Reloc.Index,
204201
...@@ -220,11 +217,12 @@ pub const Node = union(enum) {...@@ -220,11 +217,12 @@ pub const Node = union(enum) {
220 pseudo_section: PseudoSectionMapIndex,217 pseudo_section: PseudoSectionMapIndex,
221 object_section: ObjectSectionMapIndex,218 object_section: ObjectSectionMapIndex,
222 input_section: InputSection.Index,219 input_section: InputSection.Index,
223 import_thunk: GlobalMapIndex, // TODO: Rename to import_thunk220 import_thunk: GlobalMapIndex,
224 nav: NavMapIndex,221 nav: NavMapIndex,
225 uav: UavMapIndex,222 uav: UavMapIndex,
226 lazy_code: LazyMapRef.Index(.code),223 lazy_code: LazyMapRef.Index(.code),
227 lazy_const_data: LazyMapRef.Index(.const_data),224 lazy_const_data: LazyMapRef.Index(.const_data),
225 builtin: Symbol.Index,
228226
229 /// Takes the place of a known node index when that node is not present in the output227 /// Takes the place of a known node index when that node is not present in the output
230 placeholder,228 placeholder,
...@@ -945,7 +943,7 @@ pub const Symbol = struct {...@@ -945,7 +943,7 @@ pub const Symbol = struct {
945 // The size of the symbol943 // The size of the symbol
946 size: u32,944 size: u32,
947 /// Only valid when .ni == .input_section and .value_tag == .node_offset945 /// Only valid when .ni == .input_section and .value_tag == .node_offset
948 /// TODO: This is only used for name lookups, could just be String?946 /// TODO: This is only used for name lookups, could just be String, remove `input_symbols`?
949 isli: Node.InputSection.LocalIndex,947 isli: Node.InputSection.LocalIndex,
950 /// The next symbol in the list of aliases of this symbol.948 /// The next symbol in the list of aliases of this symbol.
951 next_alias_si: Symbol.Index,949 next_alias_si: Symbol.Index,
...@@ -1322,7 +1320,8 @@ pub const Reloc = extern struct {...@@ -1322,7 +1320,8 @@ pub const Reloc = extern struct {
1322 switch (target_machine) {1320 switch (target_machine) {
1323 else => |machine| @panic(@tagName(machine)),1321 else => |machine| @panic(@tagName(machine)),
1324 .AMD64 => switch (reloc.type.AMD64) {1322 .AMD64 => switch (reloc.type.AMD64) {
1325 // TODO: Report these later, in reportUndefs -> reportRelocErrs ?1323 // TODO: Could wait to report these later, in reportUndefs -> reportRelocErrs,
1324 // so that this function doesn't return an err
1326 else => |kind| return coff.base.comp.link_diags.fail(1325 else => |kind| return coff.base.comp.link_diags.fail(
1327 "absolute symbol '{s}' targeted by invalid relocation type: {t}",1326 "absolute symbol '{s}' targeted by invalid relocation type: {t}",
1328 .{ target_sym.gmi.globalName(coff).name.toSlice(coff), kind },1327 .{ target_sym.gmi.globalName(coff).name.toSlice(coff), kind },
...@@ -1475,8 +1474,10 @@ pub const Reloc = extern struct {...@@ -1475,8 +1474,10 @@ pub const Reloc = extern struct {
1475 pub fn delete(reloc: *Reloc, coff: *Coff) void {1474 pub fn delete(reloc: *Reloc, coff: *Coff) void {
1476 if (reloc.sri != .none) {1475 if (reloc.sri != .none) {
1477 // TODO: Need to remove this from the COFF relocation table (maybe removeswap?)1476 // TODO: Need to remove this from the COFF relocation table (maybe removeswap?)
1478 // TODO: If this was the last reloc causing something to be in the symbol table, we should remove the sti1477 // TODO: If this was the last reloc causing something to be in the symbol table, we should remove
1479 // That will require flushSymbolTableIndex on the swapped symbol if we exchange indices1478 // the symbol table entry (and unset sti). That will require flushSymbolTableIndex on the
1479 // swapped symbol if we exchange indices
1480 unreachable;
1480 }1481 }
14811482
1482 switch (reloc.prev) {1483 switch (reloc.prev) {
...@@ -1623,8 +1624,6 @@ fn create(...@@ -1623,8 +1624,6 @@ fn create(
1623 .symbols = .empty,1624 .symbols = .empty,
1624 .globals = .empty,1625 .globals = .empty,
1625 .global_pending_index = 0,1626 .global_pending_index = 0,
1626 .late_globals = .empty,
1627 .late_globals_pending_index = 0,
1628 .navs = .empty,1627 .navs = .empty,
1629 .uavs = .empty,1628 .uavs = .empty,
1630 .lazy = .initFill(.{1629 .lazy = .initFill(.{
...@@ -1698,7 +1697,6 @@ pub fn deinit(coff: *Coff) void {...@@ -1698,7 +1697,6 @@ pub fn deinit(coff: *Coff) void {
1698 coff.object_section_table.deinit(gpa);1697 coff.object_section_table.deinit(gpa);
1699 coff.symbols.deinit(gpa);1698 coff.symbols.deinit(gpa);
1700 coff.globals.deinit(gpa);1699 coff.globals.deinit(gpa);
1701 coff.late_globals.deinit(gpa);
1702 coff.navs.deinit(gpa);1700 coff.navs.deinit(gpa);
1703 coff.uavs.deinit(gpa);1701 coff.uavs.deinit(gpa);
1704 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);1702 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
...@@ -2109,7 +2107,7 @@ fn initHeaders(...@@ -2109,7 +2107,7 @@ fn initHeaders(
2109 });2107 });
2110 }2108 }
21112109
2112 // TODO: Lazily initialize this instead?2110 // TODO: Lazily initialize this instead, avoid the extra logic for this in flushMoved / flushResized
2113 coff.import_table.ni = try coff.mf.addLastChildNode(2111 coff.import_table.ni = try coff.mf.addLastChildNode(
2114 gpa,2112 gpa,
2115 (try coff.objectSectionMapIndex(2113 (try coff.objectSectionMapIndex(
...@@ -2225,18 +2223,27 @@ pub fn initBuiltins(coff: *Coff) !void {...@@ -2225,18 +2223,27 @@ pub fn initBuiltins(coff: *Coff) !void {
2225 sym.ni = Node.known.header;2223 sym.ni = Node.known.header;
2226 }2224 }
22272225
2226 defer coff.flushSectionMerges() catch unreachable;
2228 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {2227 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {
2229 try coff.symbols.ensureUnusedCapacity(gpa, 6);2228 try coff.symbols.ensureUnusedCapacity(gpa, 8);
2230 try coff.globals.ensureUnusedCapacity(gpa, 2);2229 try coff.globals.ensureUnusedCapacity(gpa, 2);
2231 try coff.nodes.ensureUnusedCapacity(gpa, 6);2230 try coff.nodes.ensureUnusedCapacity(gpa, 8);
2231 try coff.section_merges.ensureUnusedCapacity(gpa, 2);
22322232
2233 const lists: []const struct { global: []const u8, start: String, end: String } = &.{2233 const lists: []const struct { global: []const u8, start: String, end: String } = &.{
2234 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },2234 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },
2235 .{ .global = "__DTOR_LIST__", .start = .@".dtors", .end = .@".dtors$ZZZ" },2235 .{ .global = "__DTOR_LIST__", .start = .@".dtors", .end = .@".dtors$ZZZ" },
2236 };2236 };
22372237
2238 // We need to explicitly merge these into .rdata as in objects they can be marked
2239 // as MEM_WRITE, and would have mismatced section flags.
2240 try coff.section_merges.put(gpa, .@".ctors", .@".rdata");
2241 try coff.section_merges.put(gpa, .@".dtors", .@".rdata");
2242
2238 for (lists) |list| {2243 for (lists) |list| {
2239 const addr_info = coff.targetAddrInfo();2244 const addr_info = coff.targetAddrInfo();
2245
2246 // Any .(c|d)tor$(.*) input sections will merge in between these sections
2240 const start_osmi = try coff.objectSectionMapIndex(2247 const start_osmi = try coff.objectSectionMapIndex(
2241 list.start,2248 list.start,
2242 addr_info.alignment,2249 addr_info.alignment,
...@@ -2248,32 +2255,46 @@ pub fn initBuiltins(coff: *Coff) !void {...@@ -2248,32 +2255,46 @@ pub fn initBuiltins(coff: *Coff) !void {
2248 .{ .read = true, .initialized = true },2255 .{ .read = true, .initialized = true },
2249 );2256 );
22502257
2258 // Additional nodes are used here, instead of just adding the sentinel
2259 // directly to the section data, since once input sections are added
2260 // as children, they would overwrite that data.
2251 const start_sym = start_osmi.symbol(coff).get(coff);2261 const start_sym = start_osmi.symbol(coff).get(coff);
2252 try start_sym.ni.resize(&coff.mf, gpa, addr_info.size);2262 const list_len_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });
2253 const start_slice = start_sym.ni.slice(&coff.mf);2263 const list_len_sym = list_len_si.get(coff);
2264 list_len_sym.setExtra(.{ .size = addr_info.size });
2265 list_len_sym.ni = try coff.mf.addFirstChildNode(gpa, start_sym.ni, .{
2266 .size = addr_info.size,
2267 .fixed = true,
2268 });
2269 coff.nodes.appendAssumeCapacity(.{ .builtin = list_len_si });
2270 list_len_sym.section_number = start_sym.section_number;
2271
2272 const start_slice = list_len_sym.ni.slice(&coff.mf);
2254 switch (addr_info.magic) {2273 switch (addr_info.magic) {
2255 _ => unreachable,2274 _ => unreachable,
2256 inline .PE32, .@"PE32+" => |t| {2275 inline .PE32, .@"PE32+" => |t| {
2257 const addr: *TargetAddr(t) = @ptrCast(@alignCast(start_slice));2276 const addr: *TargetAddr(t) = @ptrCast(@alignCast(start_slice));
2258 // For __CTOR_LIST__ -1 indicates that the list is null terminated.2277 // For __CTOR_LIST__ -1 indicates that the list is null terminated.
2259 // For __DTOR_LIST__, this value is ignored.2278 // For __DTOR_LIST__, this value is ignored, the list is always null terminated
2260 coff.targetStore(addr, std.math.maxInt(TargetAddr(t)));2279 coff.targetStore(addr, std.math.maxInt(TargetAddr(t)));
2261 },2280 },
2262 }2281 }
22632282
2264 // Any .(c|d)tor$(.*) input sections will merge in between these sections
2265 // TODO: is it guaranteed that there will be no padding between those nodes?
2266
2267 const end_sym = end_osmi.symbol(coff).get(coff);2283 const end_sym = end_osmi.symbol(coff).get(coff);
2268 try end_sym.ni.resize(&coff.mf, gpa, addr_info.size);2284 const list_end_si = coff.addSymbolAssumeCapacity();
2269 @memset(end_sym.ni.slice(&coff.mf), 0);2285 const list_end_sym = list_end_si.get(coff);
2286 list_end_sym.setExtra(.{ .size = addr_info.size });
2287 list_end_sym.ni = try coff.mf.addFirstChildNode(gpa, end_sym.ni, .{
2288 .size = addr_info.size,
2289 .fixed = true,
2290 });
2291 coff.nodes.appendAssumeCapacity(.{ .builtin = list_end_si });
2292 list_end_sym.section_number = start_sym.section_number;
22702293
2271 const list_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });2294 @memset(list_end_sym.ni.slice(&coff.mf), 0);
2272 const list_sym = list_si.get(coff);
2273 list_sym.ni = start_sym.ni;
2274 list_sym.section_number = start_sym.section_number;
22752295
2276 start_sym.setExtra(.{ .next_alias_si = list_si });2296 try list_len_si.flushMoved(coff);
2297 try list_end_si.flushMoved(coff);
2277 }2298 }
2278 }2299 }
2279}2300}
...@@ -2284,7 +2305,6 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {...@@ -2284,7 +2305,6 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
2284 coff.synth_prog_node = prog_node.start("Synthetics", count: {2305 coff.synth_prog_node = prog_node.start("Synthetics", count: {
2285 var count =2306 var count =
2286 coff.globals.count() - coff.global_pending_index +2307 coff.globals.count() - coff.global_pending_index +
2287 coff.late_globals.items.len - coff.late_globals_pending_index +
2288 coff.section_merges.count() - coff.section_merge_pending_index;2308 coff.section_merges.count() - coff.section_merge_pending_index;
22892309
2290 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;2310 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
...@@ -2344,6 +2364,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {...@@ -2344,6 +2364,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
2344 .relocation_table,2364 .relocation_table,
2345 .relocation_table_entry,2365 .relocation_table_entry,
2346 .input_section,2366 .input_section,
2367 .builtin,
2347 => unreachable,2368 => unreachable,
2348 .image_section => |si| si,2369 .image_section => |si| si,
2349 .import_directory_table => break :parent_rva coff.targetLoad(2370 .import_directory_table => break :parent_rva coff.targetLoad(
...@@ -2404,7 +2425,7 @@ pub inline fn targetEndian(_: *const Coff) std.lang.Endian {...@@ -2404,7 +2425,7 @@ pub inline fn targetEndian(_: *const Coff) std.lang.Endian {
2404}2425}
24052426
2406fn targetAddrInfo(coff: *Coff) struct {2427fn targetAddrInfo(coff: *Coff) struct {
2407 size: u64,2428 size: u8,
2408 alignment: std.mem.Alignment,2429 alignment: std.mem.Alignment,
2409 magic: std.coff.OptionalHeader.Magic,2430 magic: std.coff.OptionalHeader.Magic,
2410} {2431} {
...@@ -3450,9 +3471,9 @@ fn pseudoSectionMapIndex(...@@ -3450,9 +3471,9 @@ fn pseudoSectionMapIndex(
3450 } else pseudo_section_gop.value_ptr.get(coff).section_number;3471 } else pseudo_section_gop.value_ptr.get(coff).section_number;
34513472
3452 try coff.verifyParentSectionAttributes(3473 try coff.verifyParentSectionAttributes(
3453 .pseudo,3474 parent_sn,
3454 parent_sn.name(coff),
3455 name,3475 name,
3476 .pseudo,
3456 .fromFlags(parent_sn.header(coff).flags),3477 .fromFlags(parent_sn.header(coff).flags),
3457 attributes,3478 attributes,
3458 );3479 );
...@@ -3478,6 +3499,7 @@ fn objectSectionMapIndex(...@@ -3478,6 +3499,7 @@ fn objectSectionMapIndex(
3478) !Node.ObjectSectionMapIndex {3499) !Node.ObjectSectionMapIndex {
3479 const gpa = coff.base.comp.gpa;3500 const gpa = coff.base.comp.gpa;
3480 const name_slice = name.toSlice(coff);3501 const name_slice = name.toSlice(coff);
3502 // TODO: Should this be a section merge instead?
3481 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name_slice, ".tls")) attr: {3503 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name_slice, ".tls")) attr: {
3482 // In images, the .tls section is a read-only template3504 // In images, the .tls section is a read-only template
3483 var attr = attributes;3505 var attr = attributes;
...@@ -3541,9 +3563,9 @@ fn objectSectionMapIndex(...@@ -3541,9 +3563,9 @@ fn objectSectionMapIndex(
3541 }3563 }
35423564
3543 try coff.verifyParentSectionAttributes(3565 try coff.verifyParentSectionAttributes(
3544 .object,3566 sym.section_number,
3545 sym.section_number.name(coff),
3546 name,3567 name,
3568 .object,
3547 .fromFlags(sym.section_number.header(coff).flags),3569 .fromFlags(sym.section_number.header(coff).flags),
3548 effective_attributes,3570 effective_attributes,
3549 );3571 );
...@@ -3554,21 +3576,34 @@ fn objectSectionMapIndex(...@@ -3554,21 +3576,34 @@ fn objectSectionMapIndex(
3554// TODO: Include align in attrs and verify the current align is >= requested3576// TODO: Include align in attrs and verify the current align is >= requested
3555fn verifyParentSectionAttributes(3577fn verifyParentSectionAttributes(
3556 coff: *Coff,3578 coff: *Coff,
3557 kind: enum { pseudo, object },3579 parent: Symbol.SectionNumber,
3558 parent_name: String,
3559 child_name: String,3580 child_name: String,
3581 child_kind: enum { pseudo, object },
3560 parent_attrs: ObjectSectionAttributes,3582 parent_attrs: ObjectSectionAttributes,
3561 child_attrs: ObjectSectionAttributes,3583 child_attrs: ObjectSectionAttributes,
3562) !void {3584) !void {
3563 if (parent_attrs == child_attrs) return;3585 if (parent_attrs == child_attrs) return;
35643586
3587 const was_merged = switch (child_kind) {
3588 .pseudo => coff.section_merges.contains(child_name),
3589 .object => if (coff.getString(
3590 coff.objectSectionParentName(child_name.toSlice(coff)),
3591 ).unwrap()) |pseudo_name|
3592 coff.section_merges.contains(pseudo_name)
3593 else
3594 false,
3595 };
3596
3597 // The section was intentionally merged by the user or builtin rule
3598 if (was_merged) return;
3599
3565 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;3600 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;
3566 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));3601 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));
3567 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);3602 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
3568 try err.addMsg("{t} section '{s}' was placed in parent section '{s}' with mismatched flags", .{3603 try err.addMsg("{t} section '{s}' was placed in parent section '{s}' with mismatched flags", .{
3569 kind,3604 child_kind,
3570 child_name.toSlice(coff),3605 child_name.toSlice(coff),
3571 parent_name.toSlice(coff),3606 parent.name(coff).toSlice(coff),
3572 });3607 });
35733608
3574 inline for (comptime std.meta.fieldNames(ObjectSectionAttributes)) |field| {3609 inline for (comptime std.meta.fieldNames(ObjectSectionAttributes)) |field| {
...@@ -3578,7 +3613,7 @@ fn verifyParentSectionAttributes(...@@ -3578,7 +3613,7 @@ fn verifyParentSectionAttributes(
3578 @intFromBool(@field(child_attrs, field)),3613 @intFromBool(@field(child_attrs, field)),
3579 child_name.toSlice(coff),3614 child_name.toSlice(coff),
3580 @intFromBool(@field(parent_attrs, field)),3615 @intFromBool(@field(parent_attrs, field)),
3581 parent_name.toSlice(coff),3616 parent.name(coff).toSlice(coff),
3582 });3617 });
3583 }3618 }
3584 }3619 }
...@@ -4094,6 +4129,7 @@ fn loadObject(...@@ -4094,6 +4129,7 @@ fn loadObject(
40944129
4095 // Discover symbol names and COMDAT symbol mappings4130 // Discover symbol names and COMDAT symbol mappings
4096 var symbol_i: u32 = 0;4131 var symbol_i: u32 = 0;
4132 var num_included_symbols: u32 = 0;
4097 while (symbol_i < header.number_of_symbols) {4133 while (symbol_i < header.number_of_symbols) {
4098 var symbol: std.coff.Symbol = undefined;4134 var symbol: std.coff.Symbol = undefined;
4099 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));4135 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));
...@@ -4275,6 +4311,9 @@ fn loadObject(...@@ -4275,6 +4311,9 @@ fn loadObject(
4275 };4311 };
42764312
4277 for (values, 0..) |value, i| {4313 for (values, 0..) |value, i| {
4314 if (section_number == .ABSOLUTE)
4315 num_included_symbols += 1;
4316
4278 switch (value) {4317 switch (value) {
4279 .section => {},4318 .section => {},
4280 .static,4319 .static,
...@@ -4545,12 +4584,10 @@ fn loadObject(...@@ -4545,12 +4584,10 @@ fn loadObject(
4545 };4584 };
4546 }4585 }
45474586
4548 while (coff.section_merge_pending_index < coff.section_merges.count()) : (coff.section_merge_pending_index += 1)4587 try coff.flushSectionMerges();
4549 try coff.flushSectionMerge(coff.section_merge_pending_index);
45504588
4551 // Resolve pending associations, create parent sections4589 // Resolve pending associations, create parent sections
4552 var num_included_sections: u16 = 0;4590 var num_included_sections: u16 = 0;
4553 var num_included_symbols: u32 = 0;
4554 var num_included_relocs: u32 = 0;4591 var num_included_relocs: u32 = 0;
4555 for (sections) |*section| {4592 for (sections) |*section| {
4556 comdat: switch (section.comdat_result) {4593 comdat: switch (section.comdat_result) {
...@@ -5916,22 +5953,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5916,22 +5953,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5916 }) coff.global_pending_index += 1;5953 }) coff.global_pending_index += 1;
5917 break :task;5954 break :task;
5918 }5955 }
5919 if (coff.exports_complete and coff.late_globals_pending_index < coff.late_globals.items.len) {
5920 const gmi: Node.GlobalMapIndex = coff.late_globals.items[coff.late_globals_pending_index];
5921 const sub_prog_node = coff.synth_prog_node.start(
5922 gmi.globalName(coff).name.toSlice(coff),
5923 0,
5924 );
5925 defer sub_prog_node.end();
5926 if (coff.flushGlobal(gmi) catch |err| switch (err) {
5927 error.OutOfMemory => |e| return e,
5928 else => |e| return comp.link_diags.fail(
5929 "linker failed to lower constant: {t}",
5930 .{e},
5931 ),
5932 }) coff.late_globals_pending_index += 1;
5933 break :task;
5934 }
5935 if (coff.exports_complete and coff.pending_special_symbol != .none) {5956 if (coff.exports_complete and coff.pending_special_symbol != .none) {
5936 coff.pending_special_symbol = coff.flushSpecialSymbol(coff.pending_special_symbol) catch |err|5957 coff.pending_special_symbol = coff.flushSpecialSymbol(coff.pending_special_symbol) catch |err|
5937 switch (err) {5958 switch (err) {
...@@ -6002,7 +6023,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -6002,7 +6023,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
6002 if (coff.pending_input != null) return true;6023 if (coff.pending_input != null) return true;
6003 if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true;6024 if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true;
6004 assert(!coff.exports_complete or coff.inputs_complete);6025 assert(!coff.exports_complete or coff.inputs_complete);
6005 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
6006 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;6026 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
6007 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;6027 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
6008 if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) return true;6028 if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) return true;
...@@ -6223,11 +6243,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6223,11 +6243,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6223 const gpa = comp.gpa;6243 const gpa = comp.gpa;
6224 const gn = gmi.globalName(coff);6244 const gn = gmi.globalName(coff);
6225 const si = gmi.symbol(coff);6245 const si = gmi.symbol(coff);
6226 const is_late = gmi.unwrap().? < coff.global_pending_index;
62276246
6228 log.debug(6247 log.debug(
6229 "flushGlobal({s}, {?s}, {}) = n{d} {d}@{d}",6248 "flushGlobal({s}, {?s}) = n{d} {d}@{d}",
6230 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), is_late, si.get(coff).ni, si, si.get(coff).section_number },6249 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), si.get(coff).ni, si, si.get(coff).section_number },
6231 );6250 );
62326251
6233 if (!coff.isImage()) {6252 if (!coff.isImage()) {
...@@ -6271,7 +6290,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6271,7 +6290,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6271 };6290 };
62726291
6273 const opt_alt_search_name = coff.alternate_names.get(search_name);6292 const opt_alt_search_name = coff.alternate_names.get(search_name);
6274 const search_libs = if (is_late) switch (sym.flags.value_tag) {6293 const search_libs = switch (sym.flags.value_tag) {
6275 .weak_alias_si, .weak_alias_name => switch (sym.flags.weak_external_strat) {6294 .weak_alias_si, .weak_alias_name => switch (sym.flags.weak_external_strat) {
6276 .none => unreachable,6295 .none => unreachable,
6277 .no_library => false,6296 .no_library => false,
...@@ -6285,18 +6304,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6285,18 +6304,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6285 ),6304 ),
6286 },6305 },
6287 else => true,6306 else => true,
6288 } else search_libs: {
6289 if (switch (sym.flags.value_tag) {
6290 .weak_alias_si, .weak_alias_name => true,
6291 else => opt_alt_search_name != null,
6292 }) {
6293 // We need to wait until all exports are known before resolving these
6294 coff.synth_prog_node.increaseEstimatedTotalItems(1);
6295 (try coff.late_globals.addOne(gpa)).* = gmi;
6296 return true;
6297 }
6298
6299 break :search_libs true;
6300 };6307 };
63016308
6302 const opt_indices_lists: []const ?InputArchive.SearchList = if (search_libs) &.{6309 const opt_indices_lists: []const ?InputArchive.SearchList = if (search_libs) &.{
...@@ -6381,12 +6388,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6381,12 +6388,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
63816388
6382 switch (sym.flags.value_tag) {6389 switch (sym.flags.value_tag) {
6383 .weak_alias_si => {6390 .weak_alias_si => {
6384 assert(is_late);
6385 try coff.aliasGlobal(gmi, sym.value.weak_alias_si);6391 try coff.aliasGlobal(gmi, sym.value.weak_alias_si);
6386 return true;6392 return true;
6387 },6393 },
6388 .weak_alias_name => {6394 .weak_alias_name => {
6389 assert(is_late);
6390 // Convert an unresolved weak external that itself refers to an undef external6395 // Convert an unresolved weak external that itself refers to an undef external
6391 // into a (possibly new) global, so it can be resolved separately.6396 // into a (possibly new) global, so it can be resolved separately.
6392 const alias_gop = try coff.getOrPutGlobalSymbol(.{6397 const alias_gop = try coff.getOrPutGlobalSymbol(.{
...@@ -6400,7 +6405,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6400,7 +6405,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64006405
6401 // If there was an object that had the alternate name, we've attempted to load it6406 // If there was an object that had the alternate name, we've attempted to load it
6402 if (opt_alt_search_name) |alt_search_name| {6407 if (opt_alt_search_name) |alt_search_name| {
6403 assert(is_late);
6404 if (coff.globals.get(.{ .name = alt_search_name, .lib_name = .none })) |alias_si| {6408 if (coff.globals.get(.{ .name = alt_search_name, .lib_name = .none })) |alias_si| {
6405 try coff.aliasGlobal(gmi, alias_si);6409 try coff.aliasGlobal(gmi, alias_si);
6406 return true;6410 return true;
...@@ -6986,6 +6990,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6986,6 +6990,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6986 .lazy_code,6990 .lazy_code,
6987 .lazy_const_data,6991 .lazy_const_data,
6988 => |mi| try mi.symbol(coff).flushMoved(coff),6992 => |mi| try mi.symbol(coff).flushMoved(coff),
6993 .builtin => |si| try si.flushMoved(coff),
6989 }6994 }
6990 try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);6995 try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
6991}6996}
...@@ -7126,8 +7131,10 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -7126,8 +7131,10 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
7126 .uav,7131 .uav,
7127 .lazy_code,7132 .lazy_code,
7128 .lazy_const_data,7133 .lazy_const_data,
7134 .builtin,
7129 => {},7135 => {},
7130 .placeholder => unreachable,7136 .placeholder,
7137 => unreachable,
7131 }7138 }
7132}7139}
71337140
...@@ -7217,6 +7224,11 @@ fn flushExportsSort(coff: *Coff) void {...@@ -7217,6 +7224,11 @@ fn flushExportsSort(coff: *Coff) void {
7217 });7224 });
7218}7225}
72197226
7227fn flushSectionMerges(coff: *Coff) !void {
7228 while (coff.section_merge_pending_index < coff.section_merges.count()) : (coff.section_merge_pending_index += 1)
7229 try coff.flushSectionMerge(coff.section_merge_pending_index);
7230}
7231
7220fn flushSectionMerge(coff: *Coff, index: u32) !void {7232fn flushSectionMerge(coff: *Coff, index: u32) !void {
7221 assert(coff.isImage());7233 assert(coff.isImage());
7222 const from = coff.section_merges.keys()[index];7234 const from = coff.section_merges.keys()[index];
...@@ -7237,7 +7249,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {...@@ -7237,7 +7249,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {
7237 // This is non-trivial as we can't leave holes in the section table.7249 // This is non-trivial as we can't leave holes in the section table.
7238 // TODO: Merge section flags7250 // TODO: Merge section flags
7239 _ = to_sym;7251 _ = to_sym;
7240
7241 return coff.base.comp.link_diags.fail("TODO implement section to section merge", .{});7252 return coff.base.comp.link_diags.fail("TODO implement section to section merge", .{});
7242 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {7253 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
7243 const to_sym = to_ps_si.get(coff);7254 const to_sym = to_ps_si.get(coff);
...@@ -7265,7 +7276,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {...@@ -7265,7 +7276,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {
7265 // TODO: Move from_psmi's node into to_sec7276 // TODO: Move from_psmi's node into to_sec
7266 // TODO: Update .section_number for all contained syms7277 // TODO: Update .section_number for all contained syms
7267 // TODO: Merge section flags7278 // TODO: Merge section flags
7268
7269 return coff.base.comp.link_diags.fail("TODO implement pseudosection to section merge", .{});7279 return coff.base.comp.link_diags.fail("TODO implement pseudosection to section merge", .{});
7270 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {7280 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
7271 const to_sym = to_ps_si.get(coff);7281 const to_sym = to_ps_si.get(coff);
...@@ -7273,7 +7283,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {...@@ -7273,7 +7283,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {
7273 return;7283 return;
72747284
7275 // TODO: Same as above, but move from_psmi's node after to_psmi's node in its parent7285 // TODO: Same as above, but move from_psmi's node after to_psmi's node in its parent
7276
7277 return coff.base.comp.link_diags.fail("TODO implement pseudosection to pseudosection merge", .{});7286 return coff.base.comp.link_diags.fail("TODO implement pseudosection to pseudosection merge", .{});
7278 }7287 }
72797288
...@@ -7626,7 +7635,8 @@ fn printNodeName(...@@ -7626,7 +7635,8 @@ fn printNodeName(
7626 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{7635 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
7627 smi.name(coff).toSlice(coff),7636 smi.name(coff).toSlice(coff),
7628 }),7637 }),
7629 .import_thunk => |gmi| {7638 .import_thunk,
7639 => |gmi| {
7630 const gn = gmi.globalName(coff);7640 const gn = gmi.globalName(coff);
7631 try w.writeByte('(');7641 try w.writeByte('(');
7632 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});7642 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
...@@ -7655,6 +7665,15 @@ fn printNodeName(...@@ -7655,6 +7665,15 @@ fn printNodeName(
7655 .tid = tid,7665 .tid = tid,
7656 }),7666 }),
7657 }),7667 }),
7668 .builtin => |si| {
7669 const sym = si.get(coff);
7670 if (sym.gmi != .none) {
7671 const gn = sym.gmi.globalName(coff);
7672 try w.writeByte('(');
7673 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
7674 try w.print("{s})", .{gn.name.toSlice(coff)});
7675 }
7676 },
7658 }7677 }
7659}7678}
76607679
test/link.zig+3
...@@ -1,4 +1,7 @@...@@ -1,4 +1,7 @@
1pub fn addCases(ctx: *LinkContext) void {1pub fn addCases(ctx: *LinkContext) void {
2 if (ctx.target.result.isMinGW())
3 @import("link/mingw.zig").addCases(ctx);
4
2 if (ctx.includeTest("static-lib")) |case| {5 if (ctx.includeTest("static-lib")) |case| {
3 const obj1 = case.addObject(.{6 const obj1 = case.addObject(.{
4 .name = "obj1",7 .name = "obj1",
test/link/mingw.zig created+48
...@@ -0,0 +1,48 @@
1pub fn addCases(ctx: *LinkContext) void {
2 if (ctx.includeTest("ctor-dtor")) |case| {
3 if (!ctx.link_libc) return;
4
5 const obj = case.addObject(.{
6 .name = "obj",
7 .use_llvm = true,
8 .use_lld = true,
9 .c_source_bytes =
10 \\#include <stdlib.h>
11 \\int foo;
12 \\__attribute__((constructor))
13 \\static void init_foo() {
14 \\ foo = 42;
15 \\}
16 \\__attribute__((destructor))
17 \\static void deinit_foo() {
18 \\ exit(42);
19 \\}
20 ,
21 });
22
23 const lib = case.addLibrary(.static, .{
24 .name = "lib",
25 .name_prefix = false,
26 .name_target = false,
27 });
28 lib.root_module.addObject(obj);
29
30 const exe = case.addExecutable(.{
31 .name = "test",
32 .zig_source_bytes =
33 \\extern var foo: u32;
34 \\pub fn main() !u8 {
35 \\ if (foo != 42) return 1;
36 \\ return 2;
37 \\}
38 ,
39 });
40 exe.root_module.addObject(obj);
41
42 const run = case.addRunArtifact(exe);
43 run.addCheck(.{ .expect_term = .{ .exited = 42 } });
44 }
45}
46
47const LinkContext = @import("../tests.zig").LinkContext;
48const std = @import("std");
test/src/Link.zig-1
...@@ -117,7 +117,6 @@ pub const Case = struct {...@@ -117,7 +117,6 @@ pub const Case = struct {
117 /// contains the expected output. Snapshots alias between all build117 /// contains the expected output. Snapshots alias between all build
118 /// configurations by default, but by specifying fields in `scope`,118 /// configurations by default, but by specifying fields in `scope`,
119 /// unique snapshot names are generated for each value of that field.119 /// unique snapshot names are generated for each value of that field.
120 ///
121 pub fn verifyObjdump(120 pub fn verifyObjdump(
122 self: *const Case,121 self: *const Case,
123 file: Build.LazyPath,122 file: Build.LazyPath,
test/tests.zig+3-1
...@@ -2063,6 +2063,7 @@ const c_abi_targets = blk: {...@@ -2063,6 +2063,7 @@ const c_abi_targets = blk: {
20632063
2064const LinkTarget = struct {2064const LinkTarget = struct {
2065 target: std.Target.Query = .{},2065 target: std.Target.Query = .{},
2066 optimize_mode: std.builtin.OptimizeMode = .Debug,
2066 link_libc: bool = false,2067 link_libc: bool = false,
2067 use_llvm: bool = false,2068 use_llvm: bool = false,
2068 use_lld: bool = false,2069 use_lld: bool = false,
...@@ -3207,9 +3208,10 @@ pub fn addLinkTests(b: *std.Build, options: LinkTestOptions) *Step {...@@ -3207,9 +3208,10 @@ pub fn addLinkTests(b: *std.Build, options: LinkTestOptions) *Step {
3207 }3208 }
32083209
3209 for (options.optimize_modes) |optimize_mode| {3210 for (options.optimize_modes) |optimize_mode| {
3211 if (link_target.optimize_mode != optimize_mode) continue;
3212 if (link_target.link_libc and target.abi == .msvc and b.graph.host.result.os.tag != .windows) continue;
3210 const would_use_llvm = wouldUseLlvm(link_target.use_llvm, link_target.target, optimize_mode);3213 const would_use_llvm = wouldUseLlvm(link_target.use_llvm, link_target.target, optimize_mode);
3211 if (options.skip_llvm and would_use_llvm) continue;3214 if (options.skip_llvm and would_use_llvm) continue;
3212 if (link_target.link_libc and target.abi == .msvc and b.graph.host.result.os.tag != .windows) continue;
32133215
3214 const opt_update_step = if (update_snapshots) update: {3216 const opt_update_step = if (update_snapshots) update: {
3215 const update_step = Step.UpdateSourceFiles.create(b);3217 const update_step = Step.UpdateSourceFiles.create(b);