authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:36-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:56-04:00
logbed106e5bf298adf8ce3f2c937f8fcaab14e21db
tree83b8fb6e5a1daca9cc77b52c5ae798add5f44319
parent6057145533c877ff5682e0dd08fc11b68ee8850c

Coff: introduce resolve()

- Move all idle() tasks that were modifying the node structure into resolve() - Add asserts to verify no node modification from idle() tasks - Fixup lib_name == "c" pulling in a non-existant c.dll, instead clear the lib_name and assume it comes from libc

2 files changed, 80 insertions(+), 40 deletions(-)

src/link/Coff.zig+73-40
......@@ -2663,11 +2663,28 @@ fn getOrPutGlobalSymbol(
26632663 coff: *Coff,
26642664 opts: GlobalOptions,
26652665) !std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index).GetOrPutResult {
2666 const gpa = coff.base.comp.gpa;
2666 const comp = coff.base.comp;
2667 const gpa = comp.gpa;
26672668 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2669
2670 const lib_name = if (opts.lib_name) |lib_name| lib_name: {
2671 const is_libc = std.zig.target.isLibCLibName(&comp.root_mod.resolved_target.result, lib_name);
2672 if (is_libc) {
2673 // This is guaranteed by Sema.handleExternLibName
2674 if (!comp.config.link_libc) unreachable;
2675
2676 // TODO: The user has requested this symbol come from libc, but this logic allows
2677 // it to come from anywhere. We need to know what inputs are libc inputs,
2678 // and set a flag to only search them for this symbol.
2679 break :lib_name null;
2680 }
2681
2682 break :lib_name lib_name;
2683 } else null;
2684
26682685 const sym_gop = try coff.globals.getOrPut(gpa, .{
26692686 .name = try coff.getOrPutString(opts.name),
2670 .lib_name = try coff.getOrPutOptionalString(opts.lib_name),
2687 .lib_name = try coff.getOrPutOptionalString(lib_name),
26712688 });
26722689 if (!sym_gop.found_existing) {
26732690 const si = coff.addSymbolAssumeCapacity();
......@@ -5576,6 +5593,7 @@ pub fn flush(
55765593 // this should be set after updateExports instead
55775594 coff.exports_complete = true;
55785595
5596 while (try coff.resolve(tid)) {}
55795597 while (try coff.idle(tid)) {}
55805598
55815599 if (coff.isImage())
......@@ -5598,7 +5616,10 @@ pub fn flush(
55985616 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
55995617}
56005618
5601pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5619/// Runs a single "resolution" task.
5620/// These are tasks that need to modify the node structure in some way.
5621/// They must run in a defined order with respect to linker tasks.
5622fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
56025623 const comp = coff.base.comp;
56035624 task: {
56045625 while (coff.section_merge_pending_index < coff.section_merges.count()) {
......@@ -5658,7 +5679,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
56585679 };
56595680 break :task;
56605681 }
5661 if (coff.inputs_complete and coff.global_pending_index < coff.globals.count()) {
5682 if (coff.exports_complete and coff.global_pending_index < coff.globals.count()) {
56625683 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
56635684 const sub_prog_node = coff.synth_prog_node.start(
56645685 gmi.globalName(coff).name.toSlice(coff),
......@@ -5751,6 +5772,53 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
57515772 };
57525773 break :task;
57535774 }
5775 if (coff.symbol_table.pending_shrink) {
5776 defer coff.symbol_table.pending_shrink = false;
5777 const sub_prog_node = coff.idleProgNode(
5778 tid,
5779 coff.symbol_prog_node,
5780 coff.getNode(coff.symbol_table.ni),
5781 );
5782 defer sub_prog_node.end();
5783
5784 const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
5785 coff.symbol_table.ni.shrink(
5786 &coff.mf,
5787 comp.gpa,
5788 number_of_symbols * std.coff.Symbol.sizeOf(),
5789 true,
5790 ) catch |err| switch (err) {
5791 error.OutOfMemory => return error.OutOfMemory,
5792 else => |e| return comp.link_diags.fail(
5793 "linker failed to compact symbol table: {t}",
5794 .{e},
5795 ),
5796 };
5797
5798 break :task;
5799 }
5800 }
5801
5802 if (coff.section_merge_pending_index < coff.section_merges.count()) return true;
5803 if (coff.pending_uavs.count() > 0) return true;
5804 if (coff.pending_input != null) return true;
5805 if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true;
5806 assert(!coff.exports_complete or coff.inputs_complete);
5807 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
5808 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
5809 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
5810 if (coff.symbol_table.pending.count() > 0) return true;
5811 if (coff.symbol_table.pending_shrink) return true;
5812 return false;
5813}
5814
5815pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5816 // Idle tasks should not modify create / modify nodes, otherwise the output is not reproducible.
5817 coff.mf.nodes_lock.lock();
5818 defer coff.mf.nodes_lock.unlock();
5819
5820 const comp = coff.base.comp;
5821 task: {
57545822 // TODO: Idle task for flushing obj into lib
57555823 if (coff.input_section_pending_index < coff.input_sections.items.len) {
57565824 const isi: Node.InputSection.Index = @enumFromInt(coff.input_section_pending_index);
......@@ -5809,46 +5877,11 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
58095877 coff.flushExportsSort();
58105878 break :task;
58115879 }
5812 if (coff.symbol_table.pending_shrink) {
5813 defer coff.symbol_table.pending_shrink = false;
5814 const sub_prog_node = coff.idleProgNode(
5815 tid,
5816 coff.symbol_prog_node,
5817 coff.getNode(coff.symbol_table.ni),
5818 );
5819 defer sub_prog_node.end();
5820
5821 const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
5822 coff.symbol_table.ni.shrink(
5823 &coff.mf,
5824 comp.gpa,
5825 number_of_symbols * std.coff.Symbol.sizeOf(),
5826 true,
5827 ) catch |err| switch (err) {
5828 error.OutOfMemory => return error.OutOfMemory,
5829 else => |e| return comp.link_diags.fail(
5830 "linker failed to compact symbol table: {t}",
5831 .{e},
5832 ),
5833 };
5834
5835 break :task;
5836 }
58375880 }
5838 if (coff.section_merge_pending_index < coff.section_merges.count()) return true;
5839 if (coff.pending_uavs.count() > 0) return true;
5840 if (coff.pending_input != null) return true;
5841 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
5842 assert(!coff.exports_complete or coff.inputs_complete);
5843 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
5844 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
5845 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
5846 if (coff.symbol_table.pending.count() > 0) return true;
58475881 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
58485882 if (coff.mf.updates.items.len > 0) return true;
58495883 if (coff.pending_members.count() > 0) return true;
58505884 if (coff.export_table.pending_sort) return true;
5851 if (coff.symbol_table.pending_shrink) return true;
58525885 return false;
58535886}
58545887
......@@ -6924,7 +6957,6 @@ fn flushMember(coff: *Coff, mi: Member.Index) !void {
69246957 });
69256958
69266959 var offset: u64 = 0;
6927
69286960 var string_table = coff.secondLinkerMemberStringsSlice();
69296961 for (coff.lib_string_table.items) |string| {
69306962 const str = string.toSlice(coff);
......@@ -7096,6 +7128,7 @@ fn updateExportsInner(
70967128 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
70977129 ))),
70987130 };
7131 while (try coff.resolve(pt.tid)) {}
70997132 while (try coff.idle(pt.tid)) {}
71007133
71017134 const machine = coff.targetLoad(&coff.headerPtr().machine);
src/link/MappedFile.zig+7
......@@ -26,6 +26,9 @@ updates: std.ArrayList(Node.Index),
2626update_prog_node: std.Progress.Node,
2727writers: std.SinglyLinkedList,
2828io_err: ?IoError,
29/// If locked, modifying the node layout is not allowed.
30/// Modifying node content is always allowed.
31nodes_lock: std.debug.SafetyLock = .{},
2932
3033pub const growth_factor = 4;
3134
......@@ -556,6 +559,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
556559 add_node: AddNodeOptions,
557560}) Error!Node.Index {
558561 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
562 mf.nodes_lock.assertUnlocked();
559563 const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
560564 if (opts.parent != .none) {
561565 const new_end = offset + opts.add_node.size;
......@@ -715,6 +719,7 @@ fn shrinkNode(
715719 size: u64,
716720 shift_next: bool,
717721) !void {
722 mf.nodes_lock.assertUnlocked();
718723 const node = ni.get(mf);
719724 const old_offset, _ = node.location().resolve(mf);
720725
......@@ -753,6 +758,7 @@ fn shrinkNode(
753758}
754759
755760fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void {
761 mf.nodes_lock.assertUnlocked();
756762 const io = mf.io;
757763 const node = ni.get(mf);
758764 const old_offset, const old_size = node.location().resolve(mf);
......@@ -1023,6 +1029,7 @@ fn realignNode(
10231029 set_alignment: bool,
10241030) (Allocator.Error || Io.Cancelable || IoError)!void {
10251031 assert(ni != Node.Index.root); // currently unsupported
1032 mf.nodes_lock.assertUnlocked();
10261033
10271034 const node = ni.get(mf);
10281035 const old_offset, const size = node.location().resolve(mf);