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(...@@ -2663,11 +2663,28 @@ fn getOrPutGlobalSymbol(
2663 coff: *Coff,2663 coff: *Coff,
2664 opts: GlobalOptions,2664 opts: GlobalOptions,
2665) !std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index).GetOrPutResult {2665) !std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index).GetOrPutResult {
2666 const gpa = coff.base.comp.gpa;2666 const comp = coff.base.comp;
2667 const gpa = comp.gpa;
2667 try coff.symbols.ensureUnusedCapacity(gpa, 1);2668 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
2668 const sym_gop = try coff.globals.getOrPut(gpa, .{2685 const sym_gop = try coff.globals.getOrPut(gpa, .{
2669 .name = try coff.getOrPutString(opts.name),2686 .name = try coff.getOrPutString(opts.name),
2670 .lib_name = try coff.getOrPutOptionalString(opts.lib_name),2687 .lib_name = try coff.getOrPutOptionalString(lib_name),
2671 });2688 });
2672 if (!sym_gop.found_existing) {2689 if (!sym_gop.found_existing) {
2673 const si = coff.addSymbolAssumeCapacity();2690 const si = coff.addSymbolAssumeCapacity();
...@@ -5576,6 +5593,7 @@ pub fn flush(...@@ -5576,6 +5593,7 @@ pub fn flush(
5576 // this should be set after updateExports instead5593 // this should be set after updateExports instead
5577 coff.exports_complete = true;5594 coff.exports_complete = true;
55785595
5596 while (try coff.resolve(tid)) {}
5579 while (try coff.idle(tid)) {}5597 while (try coff.idle(tid)) {}
55805598
5581 if (coff.isImage())5599 if (coff.isImage())
...@@ -5598,7 +5616,10 @@ pub fn flush(...@@ -5598,7 +5616,10 @@ pub fn flush(
5598 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});5616 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
5599}5617}
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 {
5602 const comp = coff.base.comp;5623 const comp = coff.base.comp;
5603 task: {5624 task: {
5604 while (coff.section_merge_pending_index < coff.section_merges.count()) {5625 while (coff.section_merge_pending_index < coff.section_merges.count()) {
...@@ -5658,7 +5679,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5658,7 +5679,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5658 };5679 };
5659 break :task;5680 break :task;
5660 }5681 }
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()) {
5662 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);5683 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
5663 const sub_prog_node = coff.synth_prog_node.start(5684 const sub_prog_node = coff.synth_prog_node.start(
5664 gmi.globalName(coff).name.toSlice(coff),5685 gmi.globalName(coff).name.toSlice(coff),
...@@ -5751,6 +5772,53 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5751,6 +5772,53 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5751 };5772 };
5752 break :task;5773 break :task;
5753 }5774 }
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: {
5754 // TODO: Idle task for flushing obj into lib5822 // TODO: Idle task for flushing obj into lib
5755 if (coff.input_section_pending_index < coff.input_sections.items.len) {5823 if (coff.input_section_pending_index < coff.input_sections.items.len) {
5756 const isi: Node.InputSection.Index = @enumFromInt(coff.input_section_pending_index);5824 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 {...@@ -5809,46 +5877,11 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5809 coff.flushExportsSort();5877 coff.flushExportsSort();
5810 break :task;5878 break :task;
5811 }5879 }
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 }
5837 }5880 }
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;
5847 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;5881 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
5848 if (coff.mf.updates.items.len > 0) return true;5882 if (coff.mf.updates.items.len > 0) return true;
5849 if (coff.pending_members.count() > 0) return true;5883 if (coff.pending_members.count() > 0) return true;
5850 if (coff.export_table.pending_sort) return true;5884 if (coff.export_table.pending_sort) return true;
5851 if (coff.symbol_table.pending_shrink) return true;
5852 return false;5885 return false;
5853}5886}
58545887
...@@ -6924,7 +6957,6 @@ fn flushMember(coff: *Coff, mi: Member.Index) !void {...@@ -6924,7 +6957,6 @@ fn flushMember(coff: *Coff, mi: Member.Index) !void {
6924 });6957 });
69256958
6926 var offset: u64 = 0;6959 var offset: u64 = 0;
6927
6928 var string_table = coff.secondLinkerMemberStringsSlice();6960 var string_table = coff.secondLinkerMemberStringsSlice();
6929 for (coff.lib_string_table.items) |string| {6961 for (coff.lib_string_table.items) |string| {
6930 const str = string.toSlice(coff);6962 const str = string.toSlice(coff);
...@@ -7096,6 +7128,7 @@ fn updateExportsInner(...@@ -7096,6 +7128,7 @@ fn updateExportsInner(
7096 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),7128 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
7097 ))),7129 ))),
7098 };7130 };
7131 while (try coff.resolve(pt.tid)) {}
7099 while (try coff.idle(pt.tid)) {}7132 while (try coff.idle(pt.tid)) {}
71007133
7101 const machine = coff.targetLoad(&coff.headerPtr().machine);7134 const machine = coff.targetLoad(&coff.headerPtr().machine);
src/link/MappedFile.zig+7
...@@ -26,6 +26,9 @@ updates: std.ArrayList(Node.Index),...@@ -26,6 +26,9 @@ updates: std.ArrayList(Node.Index),
26update_prog_node: std.Progress.Node,26update_prog_node: std.Progress.Node,
27writers: std.SinglyLinkedList,27writers: std.SinglyLinkedList,
28io_err: ?IoError,28io_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
30pub const growth_factor = 4;33pub const growth_factor = 4;
3134
...@@ -556,6 +559,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {...@@ -556,6 +559,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
556 add_node: AddNodeOptions,559 add_node: AddNodeOptions,
557}) Error!Node.Index {560}) Error!Node.Index {
558 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);561 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
562 mf.nodes_lock.assertUnlocked();
559 const offset = opts.add_node.alignment.forward(@intCast(opts.offset));563 const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
560 if (opts.parent != .none) {564 if (opts.parent != .none) {
561 const new_end = offset + opts.add_node.size;565 const new_end = offset + opts.add_node.size;
...@@ -715,6 +719,7 @@ fn shrinkNode(...@@ -715,6 +719,7 @@ fn shrinkNode(
715 size: u64,719 size: u64,
716 shift_next: bool,720 shift_next: bool,
717) !void {721) !void {
722 mf.nodes_lock.assertUnlocked();
718 const node = ni.get(mf);723 const node = ni.get(mf);
719 const old_offset, _ = node.location().resolve(mf);724 const old_offset, _ = node.location().resolve(mf);
720725
...@@ -753,6 +758,7 @@ fn shrinkNode(...@@ -753,6 +758,7 @@ fn shrinkNode(
753}758}
754759
755fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void {760fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void {
761 mf.nodes_lock.assertUnlocked();
756 const io = mf.io;762 const io = mf.io;
757 const node = ni.get(mf);763 const node = ni.get(mf);
758 const old_offset, const old_size = node.location().resolve(mf);764 const old_offset, const old_size = node.location().resolve(mf);
...@@ -1023,6 +1029,7 @@ fn realignNode(...@@ -1023,6 +1029,7 @@ fn realignNode(
1023 set_alignment: bool,1029 set_alignment: bool,
1024) (Allocator.Error || Io.Cancelable || IoError)!void {1030) (Allocator.Error || Io.Cancelable || IoError)!void {
1025 assert(ni != Node.Index.root); // currently unsupported1031 assert(ni != Node.Index.root); // currently unsupported
1032 mf.nodes_lock.assertUnlocked();
10261033
1027 const node = ni.get(mf);1034 const node = ni.get(mf);
1028 const old_offset, const size = node.location().resolve(mf);1035 const old_offset, const size = node.location().resolve(mf);