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,
6565symbols: std.ArrayList(Symbol),
6666globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),
6767global_pending_index: u32,
68late_globals: std.ArrayList(Node.GlobalMapIndex),
69late_globals_pending_index: u32,
7068navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),
7169uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),
7270lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
......@@ -188,17 +186,16 @@ pub const Node = union(enum) {
188186 archive_member: Member.Index,
189187
190188 coff_header,
189
191190 /// Image only
192191 optional_header,
193 /// Image only
194192 data_directories,
195193
196194 section_table,
197 // Archives and objects only
195
196 /// Archives and objects only
198197 symbol_table,
199 // Archives and objects only
200198 string_table,
201 // Archives and objects only
202199 relocation_table: Symbol.SectionNumber,
203200 relocation_table_entry: Reloc.Index,
204201
......@@ -220,11 +217,12 @@ pub const Node = union(enum) {
220217 pseudo_section: PseudoSectionMapIndex,
221218 object_section: ObjectSectionMapIndex,
222219 input_section: InputSection.Index,
223 import_thunk: GlobalMapIndex, // TODO: Rename to import_thunk
220 import_thunk: GlobalMapIndex,
224221 nav: NavMapIndex,
225222 uav: UavMapIndex,
226223 lazy_code: LazyMapRef.Index(.code),
227224 lazy_const_data: LazyMapRef.Index(.const_data),
225 builtin: Symbol.Index,
228226
229227 /// Takes the place of a known node index when that node is not present in the output
230228 placeholder,
......@@ -945,7 +943,7 @@ pub const Symbol = struct {
945943 // The size of the symbol
946944 size: u32,
947945 /// 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`?
949947 isli: Node.InputSection.LocalIndex,
950948 /// The next symbol in the list of aliases of this symbol.
951949 next_alias_si: Symbol.Index,
......@@ -1322,7 +1320,8 @@ pub const Reloc = extern struct {
13221320 switch (target_machine) {
13231321 else => |machine| @panic(@tagName(machine)),
13241322 .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
13261325 else => |kind| return coff.base.comp.link_diags.fail(
13271326 "absolute symbol '{s}' targeted by invalid relocation type: {t}",
13281327 .{ target_sym.gmi.globalName(coff).name.toSlice(coff), kind },
......@@ -1475,8 +1474,10 @@ pub const Reloc = extern struct {
14751474 pub fn delete(reloc: *Reloc, coff: *Coff) void {
14761475 if (reloc.sri != .none) {
14771476 // 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 sti
1479 // That will require flushSymbolTableIndex on the swapped symbol if we exchange indices
1477 // TODO: If this was the last reloc causing something to be in the symbol table, we should remove
1478 // the symbol table entry (and unset sti). That will require flushSymbolTableIndex on the
1479 // swapped symbol if we exchange indices
1480 unreachable;
14801481 }
14811482
14821483 switch (reloc.prev) {
......@@ -1623,8 +1624,6 @@ fn create(
16231624 .symbols = .empty,
16241625 .globals = .empty,
16251626 .global_pending_index = 0,
1626 .late_globals = .empty,
1627 .late_globals_pending_index = 0,
16281627 .navs = .empty,
16291628 .uavs = .empty,
16301629 .lazy = .initFill(.{
......@@ -1698,7 +1697,6 @@ pub fn deinit(coff: *Coff) void {
16981697 coff.object_section_table.deinit(gpa);
16991698 coff.symbols.deinit(gpa);
17001699 coff.globals.deinit(gpa);
1701 coff.late_globals.deinit(gpa);
17021700 coff.navs.deinit(gpa);
17031701 coff.uavs.deinit(gpa);
17041702 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
......@@ -2109,7 +2107,7 @@ fn initHeaders(
21092107 });
21102108 }
21112109
2112 // TODO: Lazily initialize this instead?
2110 // TODO: Lazily initialize this instead, avoid the extra logic for this in flushMoved / flushResized
21132111 coff.import_table.ni = try coff.mf.addLastChildNode(
21142112 gpa,
21152113 (try coff.objectSectionMapIndex(
......@@ -2225,18 +2223,27 @@ pub fn initBuiltins(coff: *Coff) !void {
22252223 sym.ni = Node.known.header;
22262224 }
22272225
2226 defer coff.flushSectionMerges() catch unreachable;
22282227 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);
22302229 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
22332233 const lists: []const struct { global: []const u8, start: String, end: String } = &.{
22342234 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },
22352235 .{ .global = "__DTOR_LIST__", .start = .@".dtors", .end = .@".dtors$ZZZ" },
22362236 };
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
22382243 for (lists) |list| {
22392244 const addr_info = coff.targetAddrInfo();
2245
2246 // Any .(c|d)tor$(.*) input sections will merge in between these sections
22402247 const start_osmi = try coff.objectSectionMapIndex(
22412248 list.start,
22422249 addr_info.alignment,
......@@ -2248,32 +2255,46 @@ pub fn initBuiltins(coff: *Coff) !void {
22482255 .{ .read = true, .initialized = true },
22492256 );
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.
22512261 const start_sym = start_osmi.symbol(coff).get(coff);
2252 try start_sym.ni.resize(&coff.mf, gpa, addr_info.size);
2253 const start_slice = start_sym.ni.slice(&coff.mf);
2262 const list_len_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });
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);
22542273 switch (addr_info.magic) {
22552274 _ => unreachable,
22562275 inline .PE32, .@"PE32+" => |t| {
22572276 const addr: *TargetAddr(t) = @ptrCast(@alignCast(start_slice));
22582277 // 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
22602279 coff.targetStore(addr, std.math.maxInt(TargetAddr(t)));
22612280 },
22622281 }
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
22672283 const end_sym = end_osmi.symbol(coff).get(coff);
2268 try end_sym.ni.resize(&coff.mf, gpa, addr_info.size);
2269 @memset(end_sym.ni.slice(&coff.mf), 0);
2284 const list_end_si = coff.addSymbolAssumeCapacity();
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 });
2272 const list_sym = list_si.get(coff);
2273 list_sym.ni = start_sym.ni;
2274 list_sym.section_number = start_sym.section_number;
2294 @memset(list_end_sym.ni.slice(&coff.mf), 0);
22752295
2276 start_sym.setExtra(.{ .next_alias_si = list_si });
2296 try list_len_si.flushMoved(coff);
2297 try list_end_si.flushMoved(coff);
22772298 }
22782299 }
22792300}
......@@ -2284,7 +2305,6 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
22842305 coff.synth_prog_node = prog_node.start("Synthetics", count: {
22852306 var count =
22862307 coff.globals.count() - coff.global_pending_index +
2287 coff.late_globals.items.len - coff.late_globals_pending_index +
22882308 coff.section_merges.count() - coff.section_merge_pending_index;
22892309
22902310 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 {
23442364 .relocation_table,
23452365 .relocation_table_entry,
23462366 .input_section,
2367 .builtin,
23472368 => unreachable,
23482369 .image_section => |si| si,
23492370 .import_directory_table => break :parent_rva coff.targetLoad(
......@@ -2404,7 +2425,7 @@ pub inline fn targetEndian(_: *const Coff) std.lang.Endian {
24042425}
24052426
24062427fn targetAddrInfo(coff: *Coff) struct {
2407 size: u64,
2428 size: u8,
24082429 alignment: std.mem.Alignment,
24092430 magic: std.coff.OptionalHeader.Magic,
24102431} {
......@@ -3450,9 +3471,9 @@ fn pseudoSectionMapIndex(
34503471 } else pseudo_section_gop.value_ptr.get(coff).section_number;
34513472
34523473 try coff.verifyParentSectionAttributes(
3453 .pseudo,
3454 parent_sn.name(coff),
3474 parent_sn,
34553475 name,
3476 .pseudo,
34563477 .fromFlags(parent_sn.header(coff).flags),
34573478 attributes,
34583479 );
......@@ -3478,6 +3499,7 @@ fn objectSectionMapIndex(
34783499) !Node.ObjectSectionMapIndex {
34793500 const gpa = coff.base.comp.gpa;
34803501 const name_slice = name.toSlice(coff);
3502 // TODO: Should this be a section merge instead?
34813503 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name_slice, ".tls")) attr: {
34823504 // In images, the .tls section is a read-only template
34833505 var attr = attributes;
......@@ -3541,9 +3563,9 @@ fn objectSectionMapIndex(
35413563 }
35423564
35433565 try coff.verifyParentSectionAttributes(
3544 .object,
3545 sym.section_number.name(coff),
3566 sym.section_number,
35463567 name,
3568 .object,
35473569 .fromFlags(sym.section_number.header(coff).flags),
35483570 effective_attributes,
35493571 );
......@@ -3554,21 +3576,34 @@ fn objectSectionMapIndex(
35543576// TODO: Include align in attrs and verify the current align is >= requested
35553577fn verifyParentSectionAttributes(
35563578 coff: *Coff,
3557 kind: enum { pseudo, object },
3558 parent_name: String,
3579 parent: Symbol.SectionNumber,
35593580 child_name: String,
3581 child_kind: enum { pseudo, object },
35603582 parent_attrs: ObjectSectionAttributes,
35613583 child_attrs: ObjectSectionAttributes,
35623584) !void {
35633585 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
35653600 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;
35663601 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));
35673602 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
35683603 try err.addMsg("{t} section '{s}' was placed in parent section '{s}' with mismatched flags", .{
3569 kind,
3604 child_kind,
35703605 child_name.toSlice(coff),
3571 parent_name.toSlice(coff),
3606 parent.name(coff).toSlice(coff),
35723607 });
35733608
35743609 inline for (comptime std.meta.fieldNames(ObjectSectionAttributes)) |field| {
......@@ -3578,7 +3613,7 @@ fn verifyParentSectionAttributes(
35783613 @intFromBool(@field(child_attrs, field)),
35793614 child_name.toSlice(coff),
35803615 @intFromBool(@field(parent_attrs, field)),
3581 parent_name.toSlice(coff),
3616 parent.name(coff).toSlice(coff),
35823617 });
35833618 }
35843619 }
......@@ -4094,6 +4129,7 @@ fn loadObject(
40944129
40954130 // Discover symbol names and COMDAT symbol mappings
40964131 var symbol_i: u32 = 0;
4132 var num_included_symbols: u32 = 0;
40974133 while (symbol_i < header.number_of_symbols) {
40984134 var symbol: std.coff.Symbol = undefined;
40994135 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));
......@@ -4275,6 +4311,9 @@ fn loadObject(
42754311 };
42764312
42774313 for (values, 0..) |value, i| {
4314 if (section_number == .ABSOLUTE)
4315 num_included_symbols += 1;
4316
42784317 switch (value) {
42794318 .section => {},
42804319 .static,
......@@ -4545,12 +4584,10 @@ fn loadObject(
45454584 };
45464585 }
45474586
4548 while (coff.section_merge_pending_index < coff.section_merges.count()) : (coff.section_merge_pending_index += 1)
4549 try coff.flushSectionMerge(coff.section_merge_pending_index);
4587 try coff.flushSectionMerges();
45504588
45514589 // Resolve pending associations, create parent sections
45524590 var num_included_sections: u16 = 0;
4553 var num_included_symbols: u32 = 0;
45544591 var num_included_relocs: u32 = 0;
45554592 for (sections) |*section| {
45564593 comdat: switch (section.comdat_result) {
......@@ -5916,22 +5953,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
59165953 }) coff.global_pending_index += 1;
59175954 break :task;
59185955 }
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 }
59355956 if (coff.exports_complete and coff.pending_special_symbol != .none) {
59365957 coff.pending_special_symbol = coff.flushSpecialSymbol(coff.pending_special_symbol) catch |err|
59375958 switch (err) {
......@@ -6002,7 +6023,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
60026023 if (coff.pending_input != null) return true;
60036024 if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true;
60046025 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;
60066026 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
60076027 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
60086028 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 {
62236243 const gpa = comp.gpa;
62246244 const gn = gmi.globalName(coff);
62256245 const si = gmi.symbol(coff);
6226 const is_late = gmi.unwrap().? < coff.global_pending_index;
62276246
62286247 log.debug(
6229 "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 },
6248 "flushGlobal({s}, {?s}) = n{d} {d}@{d}",
6249 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), si.get(coff).ni, si, si.get(coff).section_number },
62316250 );
62326251
62336252 if (!coff.isImage()) {
......@@ -6271,7 +6290,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
62716290 };
62726291
62736292 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) {
62756294 .weak_alias_si, .weak_alias_name => switch (sym.flags.weak_external_strat) {
62766295 .none => unreachable,
62776296 .no_library => false,
......@@ -6285,18 +6304,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
62856304 ),
62866305 },
62876306 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;
63006307 };
63016308
63026309 const opt_indices_lists: []const ?InputArchive.SearchList = if (search_libs) &.{
......@@ -6381,12 +6388,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
63816388
63826389 switch (sym.flags.value_tag) {
63836390 .weak_alias_si => {
6384 assert(is_late);
63856391 try coff.aliasGlobal(gmi, sym.value.weak_alias_si);
63866392 return true;
63876393 },
63886394 .weak_alias_name => {
6389 assert(is_late);
63906395 // Convert an unresolved weak external that itself refers to an undef external
63916396 // into a (possibly new) global, so it can be resolved separately.
63926397 const alias_gop = try coff.getOrPutGlobalSymbol(.{
......@@ -6400,7 +6405,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64006405
64016406 // If there was an object that had the alternate name, we've attempted to load it
64026407 if (opt_alt_search_name) |alt_search_name| {
6403 assert(is_late);
64046408 if (coff.globals.get(.{ .name = alt_search_name, .lib_name = .none })) |alias_si| {
64056409 try coff.aliasGlobal(gmi, alias_si);
64066410 return true;
......@@ -6986,6 +6990,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
69866990 .lazy_code,
69876991 .lazy_const_data,
69886992 => |mi| try mi.symbol(coff).flushMoved(coff),
6993 .builtin => |si| try si.flushMoved(coff),
69896994 }
69906995 try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
69916996}
......@@ -7126,8 +7131,10 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
71267131 .uav,
71277132 .lazy_code,
71287133 .lazy_const_data,
7134 .builtin,
71297135 => {},
7130 .placeholder => unreachable,
7136 .placeholder,
7137 => unreachable,
71317138 }
71327139}
71337140
......@@ -7217,6 +7224,11 @@ fn flushExportsSort(coff: *Coff) void {
72177224 });
72187225}
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
72207232fn flushSectionMerge(coff: *Coff, index: u32) !void {
72217233 assert(coff.isImage());
72227234 const from = coff.section_merges.keys()[index];
......@@ -7237,7 +7249,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {
72377249 // This is non-trivial as we can't leave holes in the section table.
72387250 // TODO: Merge section flags
72397251 _ = to_sym;
7240
72417252 return coff.base.comp.link_diags.fail("TODO implement section to section merge", .{});
72427253 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
72437254 const to_sym = to_ps_si.get(coff);
......@@ -7265,7 +7276,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {
72657276 // TODO: Move from_psmi's node into to_sec
72667277 // TODO: Update .section_number for all contained syms
72677278 // TODO: Merge section flags
7268
72697279 return coff.base.comp.link_diags.fail("TODO implement pseudosection to section merge", .{});
72707280 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
72717281 const to_sym = to_ps_si.get(coff);
......@@ -7273,7 +7283,6 @@ fn flushSectionMerge(coff: *Coff, index: u32) !void {
72737283 return;
72747284
72757285 // TODO: Same as above, but move from_psmi's node after to_psmi's node in its parent
7276
72777286 return coff.base.comp.link_diags.fail("TODO implement pseudosection to pseudosection merge", .{});
72787287 }
72797288
......@@ -7626,7 +7635,8 @@ fn printNodeName(
76267635 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
76277636 smi.name(coff).toSlice(coff),
76287637 }),
7629 .import_thunk => |gmi| {
7638 .import_thunk,
7639 => |gmi| {
76307640 const gn = gmi.globalName(coff);
76317641 try w.writeByte('(');
76327642 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
......@@ -7655,6 +7665,15 @@ fn printNodeName(
76557665 .tid = tid,
76567666 }),
76577667 }),
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 },
76587677 }
76597678}
76607679
test/link.zig+3
......@@ -1,4 +1,7 @@
11pub fn addCases(ctx: *LinkContext) void {
2 if (ctx.target.result.isMinGW())
3 @import("link/mingw.zig").addCases(ctx);
4
25 if (ctx.includeTest("static-lib")) |case| {
36 const obj1 = case.addObject(.{
47 .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 {
117117 /// contains the expected output. Snapshots alias between all build
118118 /// configurations by default, but by specifying fields in `scope`,
119119 /// unique snapshot names are generated for each value of that field.
120 ///
121120 pub fn verifyObjdump(
122121 self: *const Case,
123122 file: Build.LazyPath,
test/tests.zig+3-1
......@@ -2063,6 +2063,7 @@ const c_abi_targets = blk: {
20632063
20642064const LinkTarget = struct {
20652065 target: std.Target.Query = .{},
2066 optimize_mode: std.builtin.OptimizeMode = .Debug,
20662067 link_libc: bool = false,
20672068 use_llvm: bool = false,
20682069 use_lld: bool = false,
......@@ -3207,9 +3208,10 @@ pub fn addLinkTests(b: *std.Build, options: LinkTestOptions) *Step {
32073208 }
32083209
32093210 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;
32103213 const would_use_llvm = wouldUseLlvm(link_target.use_llvm, link_target.target, optimize_mode);
32113214 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
32143216 const opt_update_step = if (update_snapshots) update: {
32153217 const update_step = Step.UpdateSourceFiles.create(b);