commit bed106e5bf298adf8ce3f2c937f8fcaab14e21db (tree)
parent 6057145533c877ff5682e0dd08fc11b68ee8850c
Author: kcbanner <kcbanner@gmail.com>
Date: Fri, 5 Jun 2026 01:55:36 -0400
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
Diffstat:
2 files changed, 80 insertions(+), 40 deletions(-)
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
@@ -2663,11 +2663,28 @@ fn getOrPutGlobalSymbol(
coff: *Coff,
opts: GlobalOptions,
) !std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index).GetOrPutResult {
- const gpa = coff.base.comp.gpa;
+ const comp = coff.base.comp;
+ const gpa = comp.gpa;
try coff.symbols.ensureUnusedCapacity(gpa, 1);
+
+ const lib_name = if (opts.lib_name) |lib_name| lib_name: {
+ const is_libc = std.zig.target.isLibCLibName(&comp.root_mod.resolved_target.result, lib_name);
+ if (is_libc) {
+ // This is guaranteed by Sema.handleExternLibName
+ if (!comp.config.link_libc) unreachable;
+
+ // TODO: The user has requested this symbol come from libc, but this logic allows
+ // it to come from anywhere. We need to know what inputs are libc inputs,
+ // and set a flag to only search them for this symbol.
+ break :lib_name null;
+ }
+
+ break :lib_name lib_name;
+ } else null;
+
const sym_gop = try coff.globals.getOrPut(gpa, .{
.name = try coff.getOrPutString(opts.name),
- .lib_name = try coff.getOrPutOptionalString(opts.lib_name),
+ .lib_name = try coff.getOrPutOptionalString(lib_name),
});
if (!sym_gop.found_existing) {
const si = coff.addSymbolAssumeCapacity();
@@ -5576,6 +5593,7 @@ pub fn flush(
// this should be set after updateExports instead
coff.exports_complete = true;
+ while (try coff.resolve(tid)) {}
while (try coff.idle(tid)) {}
if (coff.isImage())
@@ -5598,7 +5616,10 @@ pub fn flush(
return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
}
-pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
+/// Runs a single "resolution" task.
+/// These are tasks that need to modify the node structure in some way.
+/// They must run in a defined order with respect to linker tasks.
+fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
const comp = coff.base.comp;
task: {
while (coff.section_merge_pending_index < coff.section_merges.count()) {
@@ -5658,7 +5679,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
};
break :task;
}
- if (coff.inputs_complete and coff.global_pending_index < coff.globals.count()) {
+ if (coff.exports_complete and coff.global_pending_index < coff.globals.count()) {
const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
const sub_prog_node = coff.synth_prog_node.start(
gmi.globalName(coff).name.toSlice(coff),
@@ -5751,6 +5772,53 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
};
break :task;
}
+ if (coff.symbol_table.pending_shrink) {
+ defer coff.symbol_table.pending_shrink = false;
+ const sub_prog_node = coff.idleProgNode(
+ tid,
+ coff.symbol_prog_node,
+ coff.getNode(coff.symbol_table.ni),
+ );
+ defer sub_prog_node.end();
+
+ const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
+ coff.symbol_table.ni.shrink(
+ &coff.mf,
+ comp.gpa,
+ number_of_symbols * std.coff.Symbol.sizeOf(),
+ true,
+ ) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => |e| return comp.link_diags.fail(
+ "linker failed to compact symbol table: {t}",
+ .{e},
+ ),
+ };
+
+ break :task;
+ }
+ }
+
+ if (coff.section_merge_pending_index < coff.section_merges.count()) return true;
+ if (coff.pending_uavs.count() > 0) return true;
+ if (coff.pending_input != null) return true;
+ if (coff.exports_complete and coff.globals.count() > coff.global_pending_index) return true;
+ assert(!coff.exports_complete or coff.inputs_complete);
+ if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
+ if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
+ for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
+ if (coff.symbol_table.pending.count() > 0) return true;
+ if (coff.symbol_table.pending_shrink) return true;
+ return false;
+}
+
+pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
+ // Idle tasks should not modify create / modify nodes, otherwise the output is not reproducible.
+ coff.mf.nodes_lock.lock();
+ defer coff.mf.nodes_lock.unlock();
+
+ const comp = coff.base.comp;
+ task: {
// TODO: Idle task for flushing obj into lib
if (coff.input_section_pending_index < coff.input_sections.items.len) {
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 {
coff.flushExportsSort();
break :task;
}
- if (coff.symbol_table.pending_shrink) {
- defer coff.symbol_table.pending_shrink = false;
- const sub_prog_node = coff.idleProgNode(
- tid,
- coff.symbol_prog_node,
- coff.getNode(coff.symbol_table.ni),
- );
- defer sub_prog_node.end();
-
- const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
- coff.symbol_table.ni.shrink(
- &coff.mf,
- comp.gpa,
- number_of_symbols * std.coff.Symbol.sizeOf(),
- true,
- ) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => |e| return comp.link_diags.fail(
- "linker failed to compact symbol table: {t}",
- .{e},
- ),
- };
-
- break :task;
- }
}
- if (coff.section_merge_pending_index < coff.section_merges.count()) return true;
- if (coff.pending_uavs.count() > 0) return true;
- if (coff.pending_input != null) return true;
- if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
- assert(!coff.exports_complete or coff.inputs_complete);
- if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
- if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
- for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
- if (coff.symbol_table.pending.count() > 0) return true;
if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
if (coff.mf.updates.items.len > 0) return true;
if (coff.pending_members.count() > 0) return true;
if (coff.export_table.pending_sort) return true;
- if (coff.symbol_table.pending_shrink) return true;
return false;
}
@@ -6924,7 +6957,6 @@ fn flushMember(coff: *Coff, mi: Member.Index) !void {
});
var offset: u64 = 0;
-
var string_table = coff.secondLinkerMemberStringsSlice();
for (coff.lib_string_table.items) |string| {
const str = string.toSlice(coff);
@@ -7096,6 +7128,7 @@ fn updateExportsInner(
Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
))),
};
+ while (try coff.resolve(pt.tid)) {}
while (try coff.idle(pt.tid)) {}
const machine = coff.targetLoad(&coff.headerPtr().machine);
diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig
@@ -26,6 +26,9 @@ updates: std.ArrayList(Node.Index),
update_prog_node: std.Progress.Node,
writers: std.SinglyLinkedList,
io_err: ?IoError,
+/// If locked, modifying the node layout is not allowed.
+/// Modifying node content is always allowed.
+nodes_lock: std.debug.SafetyLock = .{},
pub const growth_factor = 4;
@@ -556,6 +559,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
add_node: AddNodeOptions,
}) Error!Node.Index {
if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
+ mf.nodes_lock.assertUnlocked();
const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
if (opts.parent != .none) {
const new_end = offset + opts.add_node.size;
@@ -715,6 +719,7 @@ fn shrinkNode(
size: u64,
shift_next: bool,
) !void {
+ mf.nodes_lock.assertUnlocked();
const node = ni.get(mf);
const old_offset, _ = node.location().resolve(mf);
@@ -753,6 +758,7 @@ fn shrinkNode(
}
fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void {
+ mf.nodes_lock.assertUnlocked();
const io = mf.io;
const node = ni.get(mf);
const old_offset, const old_size = node.location().resolve(mf);
@@ -1023,6 +1029,7 @@ fn realignNode(
set_alignment: bool,
) (Allocator.Error || Io.Cancelable || IoError)!void {
assert(ni != Node.Index.root); // currently unsupported
+ mf.nodes_lock.assertUnlocked();
const node = ni.get(mf);
const old_offset, const size = node.location().resolve(mf);