commit 7be8e660acaa56d2be110d59b058ff2a31b05e23 (tree)
parent 2c3f42724d5131d826674357cecb5731d858ad27
Author: kcbanner <kcbanner@gmail.com>
Date: Fri, 5 Jun 2026 01:55:36 -0400
Coff: value / extra rework to allow symbols to have both an alias and symbol table entry
Coff: move pending_shrink handling to flush, as it's a special case of a resolve task that generates idle tasks (moves / resizes). It's also redundant to do this operation more than once.
Diffstat:
| M | src/link/Coff.zig | | | 260 | ++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- |
1 file changed, 152 insertions(+), 108 deletions(-)
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
@@ -874,14 +874,15 @@ pub const Symbol = struct {
ni: MappedFile.Node.Index,
rva: u32,
value: std.meta.BareUnion(Symbol.Value),
+ extra: std.meta.BareUnion(Symbol.Extra),
flags: packed struct(u16) {
value_tag: ValueTag,
+ extra_tag: ExtraTag,
type: Symbol.Type,
dll_storage_class: DllStorageClass,
// Only defined for .alias_si and .alias_name
weak_external_strat: WeakExternalStrat,
- has_alias: bool,
- _: u7 = 0,
+ _: u6 = 0,
},
/// Relocations contained within this symbol
loc_relocs: Reloc.Index,
@@ -889,16 +890,6 @@ pub const Symbol = struct {
target_relocs: Reloc.Index,
section_number: SectionNumber,
gmi: Node.GlobalMapIndex,
- extra: union {
- /// Only valid when outputting objects
- sti: SymbolTable.Index,
- /// Only valid when .ni == .input_section and .value_tag == .node_offset
- /// TODO: This is only used for name lookups, could just be String?
- isli: Node.InputSection.LocalIndex,
- /// Only valid if flags.has_alias is set.
- /// The next symbol in the list of aliases of this symbol.
- next_alias_si: Symbol.Index,
- },
pub const DllStorageClass = enum(u2) {
default,
@@ -916,23 +907,41 @@ pub const Symbol = struct {
node_offset,
weak_alias_si,
weak_alias_name,
- size,
+ sti,
};
pub const Value = union(ValueTag) {
/// The offset of the symbol within its node. Used with symbols that
/// don't create their own nodes: .input_section, .import_address_table
+ /// Images only.
node_offset: u32,
/// This is a weak alias that can replace this symbol
- /// Globals only.
+ /// Globals only, images only.
weak_alias_si: Symbol.Index,
/// For weak externals that have an alias that is also an undef
/// external, this is the name of the alias global that should
/// be generated if this symbol is not resolved.
- /// Globals only.
+ /// Globals only, images only.
weak_alias_name: String,
- /// The symbol size, or 0 if unknown
+ /// Index of this symbol in the symbol table
+ /// Only used when outputting objects
+ sti: SymbolTable.Index,
+ };
+
+ const ExtraTag = enum(u2) {
+ size,
+ isli,
+ next_alias_si,
+ };
+
+ pub const Extra = union(ExtraTag) {
+ // The size of the symbol
size: u32,
+ /// Only valid when .ni == .input_section and .value_tag == .node_offset
+ /// TODO: This is only used for name lookups, could just be String?
+ isli: Node.InputSection.LocalIndex,
+ /// The next symbol in the list of aliases of this symbol.
+ next_alias_si: Symbol.Index,
};
pub fn setValue(sym: *Symbol, value: Symbol.Value) void {
@@ -946,6 +955,17 @@ pub const Symbol = struct {
};
}
+ pub fn setExtra(sym: *Symbol, extra: Symbol.Extra) void {
+ sym.flags.extra_tag = std.meta.activeTag(extra);
+ sym.extra = switch (sym.flags.extra_tag) {
+ inline else => |t| @unionInit(
+ @FieldType(Symbol, "extra"),
+ @tagName(t),
+ @field(extra, @tagName(t)),
+ ),
+ };
+ }
+
pub fn nodeOffset(sym: *const Symbol, coff: *Coff) u32 {
return switch (sym.flags.value_tag) {
.node_offset => offset: {
@@ -961,7 +981,7 @@ pub const Symbol = struct {
}
pub fn size(sym: *const Symbol) u32 {
- return if (sym.flags.value_tag == .size) sym.value.size else 0;
+ return if (sym.flags.extra_tag == .size) sym.extra.size else 0;
}
pub const SectionNumber = enum(i16) {
@@ -1038,7 +1058,7 @@ pub const Symbol = struct {
si.applyTargetRelocs(coff, .none);
var alias_sym = sym;
- while (alias_sym.flags.has_alias) {
+ while (alias_sym.flags.extra_tag == .next_alias_si) {
const alias_si = alias_sym.extra.next_alias_si;
alias_sym = alias_si.get(coff);
assert(alias_sym.ni == sym.ni);
@@ -1049,7 +1069,7 @@ pub const Symbol = struct {
pub fn flushSymbolTableIndex(si: Symbol.Index, coff: *Coff) void {
const sym = si.get(coff);
- const index = sym.extra.sti.unwrap() orelse return;
+ const index = sym.value.sti.unwrap() orelse return;
var ri = sym.target_relocs;
while (ri != .none) {
const reloc = ri.get(coff);
@@ -1989,6 +2009,7 @@ fn initHeaders(
.resized = true,
});
coff.nodes.appendAssumeCapacity(.string_table);
+ coff.targetStore(coff.symbolTableStringLenPtr(), @sizeOf(u32));
}
try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);
@@ -2193,8 +2214,7 @@ pub fn initBuiltins(coff: *Coff) !void {
list_sym.ni = start_sym.ni;
list_sym.section_number = start_sym.section_number;
- start_sym.extra = .{ .next_alias_si = list_si };
- start_sym.flags.has_alias = true;
+ start_sym.setExtra(.{ .next_alias_si = list_si });
}
}
}
@@ -2483,11 +2503,14 @@ pub fn symbolTableEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.c
return null;
}
-pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, si: Symbol.Index) *align(2) std.coff.SectionDefinition {
- const sti = si.get(coff).extra.sti;
- const entry = symbolTableEntryPtr(coff, sti).?;
- assert(entry.storage_class == .STATIC and entry.number_of_aux_symbols == 1);
- return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1)));
+pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, si: Symbol.Index) ?*align(2) std.coff.SectionDefinition {
+ const sti = si.get(coff).value.sti;
+ if (symbolTableEntryPtr(coff, sti)) |entry| {
+ assert(entry.storage_class == .STATIC and entry.number_of_aux_symbols == 1);
+ return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1)));
+ } else {
+ return null;
+ }
}
pub fn symbolTableStringLenPtr(coff: *Coff) *align(1) u32 {
@@ -2524,19 +2547,19 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
defer coff.symbols.addOneAssumeCapacity().* = .{
.ni = .none,
.rva = 0,
- .value = .{ .size = 0 },
+ .value = .{ .sti = .none },
+ .extra = .{ .size = 0 },
.flags = .{
- .value_tag = .size,
+ .value_tag = .sti,
+ .extra_tag = .size,
.type = .unknown,
.dll_storage_class = .default,
.weak_external_strat = undefined,
- .has_alias = false,
},
.loc_relocs = .none,
.target_relocs = .none,
.section_number = .UNDEFINED,
.gmi = .none,
- .extra = .{ .sti = .none },
};
return @enumFromInt(coff.symbols.items.len);
}
@@ -2975,7 +2998,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
const sym = si.get(coff);
assert(sym.ni != .none or sym.gmi != .none);
- const entry = coff.symbolTableEntryPtr(sym.extra.sti) orelse entry: {
+ const entry = coff.symbolTableEntryPtr(sym.value.sti) orelse entry: {
var buf: [15]u8 = undefined;
const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
if (sym.gmi != .none) blk: {
@@ -3040,10 +3063,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
try coff.symbol_table.ni.resize(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());
coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);
- sym.extra = .{ .sti = .wrap(old_num_symbols) };
+
+ sym.value.sti = .wrap(old_num_symbols);
si.flushSymbolTableIndex(coff);
- const entry = coff.symbolTableEntryPtr(sym.extra.sti).?;
+ const entry = coff.symbolTableEntryPtr(sym.value.sti).?;
symbol_name.store(coff, &entry.name);
entry.section_number = @enumFromInt(@intFromEnum(sym.section_number));
@@ -3056,8 +3080,31 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
if (coff.targetEndian() != native_endian)
std.mem.byteSwapAllFieldsAligned(std.coff.Symbol, .@"2", entry);
- for (1..num_aux_symbols + 1) |aux_index|
- @memset(coff.symbolTableEntryStoragePtr(@intCast(old_num_symbols + aux_index)), 0);
+ if (num_aux_symbols > 0) aux_init: {
+ if (sym.gmi == .none) switch (coff.getNode(sym.ni)) {
+ .image_section => |sec_si| {
+ assert(si == sec_si);
+ const header = sym.section_number.header(coff);
+ const aux_ptr = coff.symbolTableSectionAuxEntryPtr(si).?;
+ aux_ptr.* = .{
+ .length = @intCast(sym.ni.location(&coff.mf).resolve(&coff.mf)[1]),
+ .number_of_relocations = header.number_of_relocations,
+ .number_of_linenumbers = header.number_of_linenumbers,
+ .checksum = 0,
+ .number = 0,
+ .selection = .NONE,
+ .unused = @splat(0),
+ };
+ if (coff.targetEndian() != native_endian)
+ std.mem.byteSwapAllFields(std.coff.SectionDefinition, .@"2", aux_ptr);
+
+ break :aux_init;
+ },
+ else => {},
+ };
+
+ unreachable;
+ }
break :entry entry;
};
@@ -3073,7 +3120,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
},
});
- log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.extra.sti });
+ log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.value.sti });
}
fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
@@ -3478,8 +3525,8 @@ pub fn addReloc(
else => |loc_sn| sri: {
// The target may not have a node yet, or it could be an extern that will never
// have a node. In that case, flushGlobal will create the symbol table entry.
- const sti: SymbolTable.Index = if (target.extra.sti != .none)
- target.extra.sti
+ const sti: SymbolTable.Index = if (target.value.sti != .none)
+ target.value.sti
else if (target.ni != .none) sti: {
try coff.pendingSymbolTableEntry(target_si);
break :sti .none;
@@ -3503,14 +3550,9 @@ pub fn addReloc(
try section.relocation_table_ni.resize(&coff.mf, gpa, new_size);
}
- coff.targetStore(
- &header.number_of_relocations,
- new_num_relocations,
- );
- coff.targetStore(
- &coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff)).number_of_relocations,
- new_num_relocations,
- );
+ coff.targetStore(&header.number_of_relocations, new_num_relocations);
+ if (coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff))) |aux_ptr|
+ coff.targetStore(&aux_ptr.number_of_relocations, new_num_relocations);
// TODO: These need to allocate from a free list (once deleting relocs is supported) (or can we just remove swap?)
const sri: Section.RelocationIndex = .wrap(old_num_relocations);
@@ -3643,7 +3685,7 @@ fn loadObject(
log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberNameString(member_name) });
- const header = try r.peekStruct(std.coff.Header, .little());
+ const header = try r.peekStruct(std.coff.Header, .little);
if (header.machine != target.toCoffMachine())
return diags.failParse(path, "machine mismatch: expected {t}, found {t}", .{
target.toCoffMachine(),
@@ -3678,7 +3720,7 @@ fn loadObject(
const string_table_len = try r.peekInt(u32, target_endian);
if (string_table_len < @sizeOf(u32) or
symbol_table_end + string_table_len > fl.size)
- return diags.failParse(path, "bad string table", .{});
+ return diags.failParse(path, "bad string table length: 0x{x}", .{string_table_len});
const ioi: InputObject.Index = @enumFromInt(coff.input_objects.items.len);
try coff.input_objects.ensureUnusedCapacity(gpa, 1);
@@ -4519,17 +4561,18 @@ fn loadObject(
const sym = symbol.si.get(coff);
assert(sym.ni == .none);
sym.ni = section.si.get(coff).ni;
- sym.setValue(switch (symbol.value) {
- .section => |v| .{ .size = v },
- .static => |v| .{ .node_offset = v },
+ switch (symbol.value) {
+ .section => |v| sym.setExtra(.{ .size = v }),
+ .static => |v| sym.setValue(.{ .node_offset = v }),
.external => |v| switch (symbol.section_number) {
.UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
- else => .{ .node_offset = v },
+ else => sym.setValue(.{ .node_offset = v }),
},
.weak_external,
.weak_external_aux,
=> unreachable,
- });
+ }
+
sym.section_number = section.si.get(coff).section_number;
}
}
@@ -4572,7 +4615,7 @@ fn loadObject(
symbol.si = global_gop.value_ptr.*;
if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
const sym = symbol.si.get(coff);
- sym.setValue(.{ .size = @max(sym.size(), size) });
+ sym.setExtra(.{ .size = @max(sym.size(), size) });
}
},
else => unreachable,
@@ -5156,12 +5199,12 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
error.WriteFailed => return nw.err.?,
else => |e| return e,
};
- si.get(coff).value.size = @intCast(nw.interface.end);
+ si.get(coff).extra.size = @intCast(nw.interface.end);
si.applyLocationRelocs(coff);
}
if (nav.resolved.?.@"linksection".unwrap()) |_| {
- try ni.resize(&coff.mf, gpa, si.get(coff).value.size);
+ try ni.resize(&coff.mf, gpa, si.get(coff).extra.size);
var parent_ni = ni;
while (true) {
parent_ni = parent_ni.parent(&coff.mf);
@@ -5289,7 +5332,7 @@ fn updateFuncInner(
error.WriteFailed => return nw.err.?,
else => |e| return e,
};
- si.get(coff).value.size = @intCast(nw.interface.end);
+ si.get(coff).extra.size = @intCast(nw.interface.end);
si.applyLocationRelocs(coff);
}
@@ -5533,6 +5576,7 @@ pub fn flush(
) !void {
_ = arena;
_ = prog_node;
+ const comp = coff.base.comp;
// TODO: When https://github.com/ziglang/zig/issues/23617 is in,
// this should be set after updateExports instead
@@ -5541,11 +5585,30 @@ pub fn flush(
while (try coff.resolve(tid)) {}
while (try coff.idle(tid)) {}
+ // This has to occur after all other flushMoved / flushResized have resolved,
+ // but it will also generate one more set of resizes and moves.
+ if (coff.symbol_table.pending_shrink) {
+ coff.symbol_table.pending_shrink = false;
+
+ 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},
+ ),
+ };
+ }
+ while (try coff.idle(tid)) {}
+
if (coff.isImage())
try coff.reportUndefs(tid);
- const comp = coff.base.comp;
-
// Implib generation should instead be done via building a MappedFile progressively
if (comp.emit_implib) |implib_file|
coff.flushImplib(implib_file) catch |err|
@@ -5717,31 +5780,6 @@ fn resolve(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;
@@ -5753,7 +5791,6 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
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;
}
@@ -5922,7 +5959,7 @@ fn flushUav(
error.WriteFailed => return nw.err.?,
else => |e| return e,
};
- si.get(coff).value.size = @intCast(nw.interface.end);
+ si.get(coff).extra.size = @intCast(nw.interface.end);
si.applyLocationRelocs(coff);
}
@@ -6358,7 +6395,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
});
@memcpy(ni.slice(&coff.mf)[0..init.len], &init);
sym.ni = ni;
- sym.setValue(.{ .size = init.len });
+ sym.extra.size = init.len;
try coff.addReloc(
si,
init.len - 4,
@@ -6539,7 +6576,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
error.WriteFailed => return nw.err.?,
else => |e| return e,
};
- si.get(coff).value.size = @intCast(nw.interface.end);
+ si.get(coff).extra.size = @intCast(nw.interface.end);
si.applyLocationRelocs(coff);
}
@@ -6556,13 +6593,15 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
.section_table,
.placeholder,
=> assert(!coff.isImage()),
- .symbol_table => {
- coff.targetStore(
- &coff.headerPtr().pointer_to_symbol_table,
- @intCast(ni.location(&coff.mf).resolve(&coff.mf)[0]),
- );
- },
- .string_table => {
+ .symbol_table,
+ .string_table,
+ => |_, tag| {
+ if (tag == .symbol_table)
+ coff.targetStore(
+ &coff.headerPtr().pointer_to_symbol_table,
+ @intCast(ni.location(&coff.mf).resolve(&coff.mf)[0]),
+ );
+
if (!coff.symbol_table.pending_shrink) {
const symbol_table_loc, const symbol_table_size = coff.symbol_table.ni.location(&coff.mf).resolve(&coff.mf);
const string_table_offset, _ = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf);
@@ -6822,10 +6861,8 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
}
if (!coff.isImage()) {
- coff.targetStore(
- &coff.symbolTableSectionAuxEntryPtr(si).length,
- @intCast(size),
- );
+ if (coff.symbolTableSectionAuxEntryPtr(si)) |aux_ptr|
+ coff.targetStore(&aux_ptr.length, @intCast(size));
}
},
.input_section => {},
@@ -6853,7 +6890,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
);
}
- smi.symbol(coff).get(coff).value.size = @intCast(size);
+ smi.symbol(coff).get(coff).extra.size = @intCast(size);
},
.import_thunk,
.nav,
@@ -7091,14 +7128,16 @@ fn updateExportsInner(
const export_sym = export_si.get(coff);
export_sym.ni = exported_ni;
export_sym.rva = exported_sym.rva;
- export_sym.setValue(.{ .size = exported_sym.value.size });
export_sym.section_number = exported_sym.section_number;
defer export_si.applyTargetRelocs(coff, .none);
const prev_alias_sym = prev_alias_si.get(coff);
- assert(!prev_alias_sym.flags.has_alias);
- prev_alias_sym.extra = .{ .next_alias_si = export_si };
- prev_alias_sym.flags.has_alias = true;
+ switch (prev_alias_sym.flags.extra_tag) {
+ .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),
+ else => unreachable,
+ }
+
+ prev_alias_sym.setExtra(.{ .next_alias_si = export_si });
prev_alias_si = export_si;
if (!coff.isImage()) continue;
@@ -7237,7 +7276,7 @@ fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !voi
try w.print("{d:0>6}@{d:0>2} {x:08} n{d:0>8} | {s}\n", .{
si,
sym.section_number,
- if (sym.flags.value_tag == .size) sym.value.size else 0,
+ if (sym.flags.extra_tag == .size) sym.extra.size else 0,
sym.ni,
name.toSlice(coff),
});
@@ -7251,11 +7290,11 @@ fn printSymbol(
) !void {
const sym = si.get(coff);
const node = coff.getNode(sym.ni);
- try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} n{d:0>8}+{x:08}:{t: <26} | {x:08} ", .{
+ try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} {s} n{d:0>8}+{x:08}:{t: <26} | {x:08} ", .{
si,
sym.section_number,
- if (sym.flags.value_tag == .size)
- @as(u64, sym.value.size)
+ if (sym.flags.extra_tag == .size)
+ @as(u64, sym.extra.size)
else if (sym.ni != .none)
sym.ni.location(&coff.mf).resolve(&coff.mf)[1]
else
@@ -7264,7 +7303,12 @@ fn printSymbol(
.weak_alias_name => "an",
.weak_alias_si => "as",
.node_offset => "no",
+ .sti => "st",
+ },
+ switch (sym.flags.extra_tag) {
.size => "sz",
+ .isli => "li",
+ .next_alias_si => "na",
},
switch (sym.flags.type) {
.unknown => "u",