authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:35-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:55-04:00
logf5a2bfd95ee75a5ddd57071567052e3482683925
tree4c41508a08ca290726e5e888f59ab358b10682cc
parent22a22ceaeb3f693207fc4821b55959ba0732d7ed

Coff: supply builtins that mingw libc expects

- Fix incorrect .alias_si init - Fix not logging addInputSymbol for weak externals - Add builtin init for `__ImageBase` and `__(C|D)TOR_LIST_` when linking mingw libc - Fix up incorrectly concurrently calling `buildMingwImportLib` - Fix `reportUndef` not logging the last undef if there was more than one

3 files changed, 119 insertions(+), 52 deletions(-)

src/Compilation.zig+11-8
......@@ -4475,14 +4475,16 @@ fn performAllTheWork(
44754475
44764476 comp.link_queue.finishZcuQueue(comp);
44774477
4478 // Main thread work is all done, now just wait for all async work.
4479 try misc_group.await(io);
4480
44784481 // This has to happen again after the main semantic analysis loop because it is possible for Sema to
44794482 // call `addLinkLib` and hence add more items to `comp.windows_libs`.
44804483 for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |lib_name|
4481 comp.buildMingwImportLib(lib_name, false, main_progress_node);
4484 misc_group.async(io, buildMingwImportLib, .{ comp, lib_name, false, main_progress_node });
44824485 comp.windows_libs_num_done = @intCast(comp.windows_libs.count());
4483
4484 // Main thread work is all done, now just wait for all async work.
44854486 try misc_group.await(io);
4487
44864488 comp.link_queue.wait(io);
44874489}
44884490
......@@ -4685,11 +4687,12 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node
46854687 }
46864688
46874689 while (comp.windows_libs_num_done < comp.windows_libs.count()) {
4688 prelink_group.async(
4689 io,
4690 buildMingwImportLib,
4691 .{ comp, comp.windows_libs.keys()[comp.windows_libs_num_done], true, main_progress_node },
4692 );
4690 prelink_group.async(io, buildMingwImportLib, .{
4691 comp,
4692 comp.windows_libs.keys()[comp.windows_libs_num_done],
4693 true,
4694 main_progress_node,
4695 });
46934696 comp.windows_libs_num_done += 1;
46944697 }
46954698
src/libs/mingw.zig+2
......@@ -210,6 +210,8 @@ fn addCrtCcArgs(
210210pub fn buildImportLib(comp: *Compilation, lib_name: []const u8, prog_node: std.Progress.Node) !Cache.Path {
211211 dev.check(.build_import_lib);
212212
213 log.debug("buildImportLib({s})", .{lib_name});
214
213215 const sub_node = prog_node.start(lib_name, 0);
214216 defer sub_node.end();
215217
src/link/Coff.zig+106-44
......@@ -753,6 +753,10 @@ pub const String = enum(u32) {
753753 @".text" = 20,
754754 @".tls$" = 26,
755755 @".edata" = 32,
756 @".ctors" = 39,
757 @".ctors$ZZZ" = 46,
758 @".dtors" = 57,
759 @".dtors$ZZZ" = 64,
756760 _,
757761
758762 pub const Optional = enum(u32) {
......@@ -761,6 +765,8 @@ pub const String = enum(u32) {
761765 @".text" = @intFromEnum(String.@".text"),
762766 @".tls$" = @intFromEnum(String.@".tls$"),
763767 @".edata" = @intFromEnum(String.@".edata"),
768 @".dtors" = @intFromEnum(String.@".dtors"),
769 @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"),
764770 none = std.math.maxInt(u32),
765771 _,
766772
......@@ -1506,6 +1512,7 @@ fn create(
15061512 section_align,
15071513 std.fs.path.basename(path.sub_path),
15081514 );
1515 try coff.initBuiltins();
15091516 return coff;
15101517}
15111518
......@@ -2017,15 +2024,58 @@ fn initHeaders(
20172024 .{ .read = true, .write = !is_image },
20182025 );
20192026 }
2027}
20202028
2021 // Linker-supplied symbols
2022 {
2023 const target = &comp.root_mod.resolved_target.result;
2024 if (is_image and target.isMinGW()) {
2029pub fn initBuiltins(coff: *Coff) !void {
2030 const comp = coff.base.comp;
2031 const gpa = comp.gpa;
2032 const target = &comp.root_mod.resolved_target.result;
2033 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {
2034 try coff.symbols.ensureUnusedCapacity(gpa, 5);
2035 try coff.globals.ensureUnusedCapacity(gpa, 2);
2036 try coff.nodes.ensureUnusedCapacity(gpa, 3);
2037
2038 {
20252039 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
20262040 const sym = si.get(coff);
20272041 sym.ni = Node.known.header;
20282042 }
2043
2044 const lists: []const struct { global: []const u8, start: String, end: String } = &.{
2045 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },
2046 .{ .global = "__DTOR_LIST__", .start = .@".dtors", .end = .@".dtors$ZZZ" },
2047 };
2048
2049 for (lists) |list| {
2050 const addr_info = coff.targetAddrInfo();
2051 const start_osmi = try coff.objectSectionMapIndex(list.start, addr_info.alignment, .{ .read = true });
2052 const end_osmi = try coff.objectSectionMapIndex(list.end, addr_info.alignment, .{ .read = true });
2053
2054 const start_sym = start_osmi.symbol(coff).get(coff);
2055 try start_sym.ni.resize(&coff.mf, gpa, addr_info.size);
2056 const start_slice = start_sym.ni.slice(&coff.mf);
2057 switch (addr_info.magic) {
2058 _ => unreachable,
2059 inline .PE32, .@"PE32+" => |t| {
2060 const addr: *TargetAddr(t) = @ptrCast(@alignCast(start_slice));
2061 // For __CTOR_LIST__ -1 indicates that the list is null terminated.
2062 // For __DTOR_LIST__, this value is ignored.
2063 coff.targetStore(addr, std.math.maxInt(TargetAddr(t)));
2064 },
2065 }
2066
2067 // Any .(c|d)tor$(.*) input sections will merge in between these sections
2068 // TODO: is it guaranteed that there will be no padding between those nodes?
2069
2070 const end_sym = end_osmi.symbol(coff).get(coff);
2071 try end_sym.ni.resize(&coff.mf, gpa, addr_info.size);
2072 @memset(end_sym.ni.slice(&coff.mf), 0);
2073
2074 const list_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });
2075 const list_sym = list_si.get(coff);
2076 list_sym.ni = start_sym.ni;
2077 list_sym.section_number = start_sym.section_number;
2078 }
20292079 }
20302080}
20312081
......@@ -2146,6 +2196,28 @@ fn computeSymbolSectionOffset(coff: *Coff, sym: *const Symbol) u32 {
21462196pub inline fn targetEndian(_: *const Coff) std.lang.Endian {
21472197 return .little;
21482198}
2199
2200fn targetAddrInfo(coff: *Coff) struct {
2201 size: u64,
2202 alignment: std.mem.Alignment,
2203 magic: std.coff.OptionalHeader.Magic,
2204} {
2205 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
2206 switch (magic) {
2207 _ => unreachable,
2208 .PE32 => return .{ .size = 4, .alignment = .@"4", .magic = magic },
2209 .@"PE32+" => return .{ .size = 8, .alignment = .@"8", .magic = magic },
2210 }
2211}
2212
2213fn TargetAddr(comptime magic: std.coff.OptionalHeader.Magic) type {
2214 return switch (magic) {
2215 _ => comptime unreachable,
2216 .PE32 => u32,
2217 .@"PE32+" => u64,
2218 };
2219}
2220
21492221fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
21502222 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
21512223 return switch (@typeInfo(Child)) {
......@@ -4072,7 +4144,19 @@ fn loadObject(
40724144 coff.synth_prog_node.increaseEstimatedTotalItems(1);
40734145 }
40744146
4075 for (pending_symbols.values(), pending_symbols.keys(), 0..) |*symbol, index, psi| {
4147 for (pending_symbols.values(), pending_symbols.keys(), 0..) |*symbol, index, i| {
4148 defer log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}, {d}) = {d}@{d}", .{
4149 symbol.name.toSlice(coff),
4150 index,
4151 symbol.value,
4152 symbol.section_number,
4153 switch (symbol.value) {
4154 inline else => |v| v,
4155 },
4156 symbol.si,
4157 symbol.si.get(coff).section_number,
4158 });
4159
40764160 const section = switch (symbol.section_number) {
40774161 .UNDEFINED => switch (symbol.value) {
40784162 .section,
......@@ -4101,7 +4185,7 @@ fn loadObject(
41014185 );
41024186
41034187 if (alias.si == .null) {
4104 alias.weak_external_psi = .wrap(@intCast(psi));
4188 alias.weak_external_psi = .wrap(@intCast(i));
41054189 } else {
41064190 sym.setValue(.{ .alias_si = alias.si });
41074191 }
......@@ -4137,9 +4221,9 @@ fn loadObject(
41374221 }
41384222 }
41394223
4140 if (symbol.weak_external_psi.unwrap()) |i| {
4224 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
41414225 assert(symbol.si != .null);
4142 pending_symbols.values()[i].si.get(coff).value = .{ .alias_si = symbol.si };
4226 pending_symbols.values()[weak_external_i].si.get(coff).setValue(.{ .alias_si = symbol.si });
41434227 }
41444228
41454229 if (section.si != symbol.si) {
......@@ -4157,17 +4241,6 @@ fn loadObject(
41574241 });
41584242 sym.section_number = section.si.get(coff).section_number;
41594243 }
4160
4161 log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
4162 symbol.name.toSlice(coff),
4163 index,
4164 symbol.value,
4165 switch (symbol.value) {
4166 inline else => |v| v,
4167 },
4168 symbol.si,
4169 section.si.get(coff).section_number,
4170 });
41714244 }
41724245
41734246 const relocation_size = std.coff.Relocation.sizeOf();
......@@ -4979,11 +5052,11 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
49795052
49805053 var start_i: usize = 0;
49815054 var num_unique_references: usize = 1;
4982 for (undef_indices.items[0..], 0..) |reloc_i, i| {
5055 for (0..undef_indices.items.len) |i| {
49835056 const target = coff.relocs.items[undef_indices.items[start_i]].target;
4984 if (target != coff.relocs.items[reloc_i].target or i == undef_indices.items.len - 1) {
5057 if (i == undef_indices.items.len - 1 or target != coff.relocs.items[undef_indices.items[i + 1]].target) {
49855058 defer {
4986 start_i = i;
5059 start_i = i + 1;
49875060 num_unique_references = 1;
49885061 }
49895062
......@@ -4995,7 +5068,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
49955068 try err.addMsg("undefined symbol: {s}", .{target_sym.gmi.globalName(coff).name.toSlice(coff)});
49965069
49975070 var prev_loc_si: Symbol.Index = .null;
4998 for (undef_indices.items[start_i..][0..@max(1, i - start_i)]) |reference_i| {
5071 for (undef_indices.items[start_i .. i + 1]) |reference_i| {
49995072 if (err.note_slot == num_full_notes) break;
50005073
50015074 const loc_si = coff.relocs.items[reference_i].loc;
......@@ -5007,7 +5080,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
50075080 .input_section => |isi| {
50085081 const other_ioi = isi.input(coff);
50095082 if (loc_sym.gmi == .none) {
5010 // TODO: We could report the name here if we interned it in loadObject
5083 // TODO: We could report non-global names here if we intern them in loadObject
50115084 err.addNote("referenced by input '{f}{f}'", .{
50125085 other_ioi.path(coff).fmtEscapeString(),
50135086 fmtMemberNameString(other_ioi.memberName(coff)),
......@@ -5022,8 +5095,6 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
50225095 },
50235096 .import_thunk => |gmi| err.addNote("referenced by import thunk for '{s}'", .{
50245097 gmi.globalName(coff).name.toSlice(coff),
5025 // TODO: This won't always have a ZCU
5026 //comp.zcu.?.root_mod.fully_qualified_name,
50275098 }),
50285099 inline .nav,
50295100 .uav,
......@@ -5583,12 +5654,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
55835654 try coff.symbols.ensureUnusedCapacity(gpa, 1);
55845655
55855656 const target_endian = coff.targetEndian();
5586 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
5587 const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
5588 _ => unreachable,
5589 .PE32 => .{ 4, .@"4" },
5590 .@"PE32+" => .{ 8, .@"8" },
5591 };
5657 const addr_info = coff.targetAddrInfo();
55925658 const gop = try coff.import_table.entries.getOrPutAdapted(
55935659 gpa,
55945660 lib_name,
......@@ -5606,13 +5672,13 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
56065672 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
56075673 const idata_section_ni = coff.import_table.ni.parent(&coff.mf);
56085674 const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
5609 .size = addr_size * 2,
5610 .alignment = addr_align,
5675 .size = addr_info.size * 2,
5676 .alignment = addr_info.alignment,
56115677 .moved = true,
56125678 });
56135679 const import_address_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
5614 .size = addr_size * 2,
5615 .alignment = addr_align,
5680 .size = addr_info.size * 2,
5681 .alignment = addr_info.alignment,
56165682 .moved = true,
56175683 });
56185684 const import_address_table_si = coff.addSymbolAssumeCapacity();
......@@ -5677,7 +5743,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
56775743 iat_symbol_gop.value_ptr.* = import_symbol_index;
56785744
56795745 gop.value_ptr.len = import_symbol_index + 1;
5680 const new_symbol_table_size = addr_size * (import_symbol_index + 2);
5746 const new_symbol_table_size = addr_info.size * (import_symbol_index + 2);
56815747
56825748 const opt_name = import.name.toSlice(coff);
56835749 const opt_import_hint_name_index = if (opt_name) |name| blk: {
......@@ -5704,7 +5770,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
57045770
57055771 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
57065772 const import_address_slice = import_address_table_ni.slice(&coff.mf);
5707 switch (magic) {
5773 switch (addr_info.magic) {
57085774 _ => unreachable,
57095775 inline .PE32, .@"PE32+" => |ct_magic| {
57105776 const Payload = packed union(u31) {
......@@ -5749,7 +5815,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
57495815 }
57505816
57515817 assert(sym.loc_relocs == .none);
5752 const iat_offset: u32 = @intCast(addr_size * iat_symbol_gop.value_ptr.*);
5818 const iat_offset: u32 = @intCast(addr_info.size * iat_symbol_gop.value_ptr.*);
57535819 switch (import.kind) {
57545820 .iat_ptr => {
57555821 const iat_sym = gop.value_ptr.import_address_table_si.get(coff);
......@@ -5960,11 +6026,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
59606026 switch (magic) {
59616027 _ => unreachable,
59626028 inline .PE32, .@"PE32+" => |ct_magic| {
5963 const Addr = switch (ct_magic) {
5964 _ => comptime unreachable,
5965 .PE32 => u32,
5966 .@"PE32+" => u64,
5967 };
6029 const Addr = TargetAddr(ct_magic);
59686030 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
59696031 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
59706032 const rva = std.mem.nativeTo(