zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 537d7b74274c31ea4240870724ffd41e0e5de8dd (tree)
parent bd50917c0d86cc45c46cb10868e5e4da6d87c58f
Author: kcbanner <kcbanner@gmail.com>
Date:   Fri,  5 Jun 2026 01:55:37 -0400

Coff: rework symbol table generation

Instead of popping from the end of a map of pending symbols, use the map as the storage and flush symbols in order. This simplifies the weak external flow, and keeps the symbol table in a more intuitive order. Also, .sti is no longer in Value, which means that it won't collide with .node_offset if an imported symbol is later exported.

Diffstat:
Msrc/link/Coff.zig | 146++++++++++++++++++++++++++++++++++++-------------------------------------------
Mtest/link/snapshots/static-lib.no-llvm.dmp | 10+++++-----
2 files changed, 71 insertions(+), 85 deletions(-)

diff --git a/src/link/Coff.zig b/src/link/Coff.zig @@ -615,7 +615,8 @@ pub const SymbolTable = struct { ni: MappedFile.Node.Index, strings_ni: MappedFile.Node.Index, strings: std.AutoArrayHashMapUnmanaged(String, StringIndex), - pending: std.AutoArrayHashMapUnmanaged(Symbol.Index, void), + symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, SymbolTable.Index), + pending_symbol_index: u32, // Resizing the symbol table node has the result of accumulating padding // between the last symbol in the symbol table node and the start of the @@ -653,8 +654,8 @@ pub const SymbolTable = struct { none, _, - pub fn wrap(i: ?u32) Index { - return @enumFromInt((i orelse return .none) + 1); + pub fn wrap(i: u32) Index { + return @enumFromInt(i + 1); } pub fn unwrap(sti: Index) ?u32 { @@ -904,13 +905,14 @@ pub const Symbol = struct { }; const ValueTag = enum(u2) { + none, node_offset, weak_alias_si, weak_alias_name, - sti, }; pub const Value = union(ValueTag) { + none, /// 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. @@ -924,9 +926,6 @@ pub const Symbol = struct { /// be generated and resolved if this symbol is not resolved. /// Globals only, images only. weak_alias_name: String, - /// Index of this symbol in the symbol table - /// Only used when outputting objects - sti: SymbolTable.Index, }; const ExtraTag = enum(u2) { @@ -1041,6 +1040,11 @@ pub const Symbol = struct { return ni; } + pub fn sti(si: Symbol.Index, coff: *Coff) SymbolTable.Index { + assert(!coff.isImage()); + return coff.symbol_table.symbols.get(si) orelse .none; + } + pub fn next(si: Symbol.Index) Symbol.Index { return @enumFromInt(@intFromEnum(si) + 1); } @@ -1070,7 +1074,7 @@ pub const Symbol = struct { pub fn flushSymbolTableIndex(si: Symbol.Index, coff: *Coff) void { const sym = si.get(coff); - const index = sym.value.sti.unwrap() orelse return; + const index = si.sti(coff).unwrap().?; var ri = sym.target_relocs; while (ri != .none) { const reloc = ri.get(coff); @@ -1583,7 +1587,8 @@ fn create( .ni = .none, .strings_ni = .none, .strings = .empty, - .pending = .empty, + .symbols = .empty, + .pending_symbol_index = 0, .pending_shrink = false, }, .inputs = .empty, @@ -1667,7 +1672,7 @@ pub fn deinit(coff: *Coff) void { coff.import_table.iat_symbol_indices.deinit(gpa); coff.export_table.entries.deinit(gpa); coff.symbol_table.strings.deinit(gpa); - coff.symbol_table.pending.deinit(gpa); + coff.symbol_table.symbols.deinit(gpa); coff.inputs.deinit(gpa); coff.input_archives.deinit(gpa); coff.input_archive_members.deinit(gpa); @@ -2280,7 +2285,10 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void { }); if (!isImage(coff)) { prog_node.increaseEstimatedTotalItems(2); - coff.symbol_prog_node = prog_node.start("Symbols", coff.symbol_table.pending.count()); + coff.symbol_prog_node = prog_node.start( + "Symbols", + coff.symbol_table.symbols.count() - coff.symbol_table.pending_symbol_index, + ); coff.member_prog_node = prog_node.start("Members", coff.pending_members.count()); } coff.input_prog_node = prog_node.start( @@ -2550,8 +2558,7 @@ 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).value.sti; +pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.coff.SectionDefinition { 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))); @@ -2560,8 +2567,7 @@ pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, si: Symbol.Index) ?*align(2) s } } -pub fn symbolTableWeakExternalAuxEntryPtr(coff: *Coff, si: Symbol.Index) ?*align(2) std.coff.WeakExternalDefinition { - const sti = si.get(coff).value.sti; +pub fn symbolTableWeakExternalAuxEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.coff.WeakExternalDefinition { if (symbolTableEntryPtr(coff, sti)) |entry| { assert(entry.storage_class == .WEAK_EXTERNAL and entry.number_of_aux_symbols == 1); return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1))); @@ -2604,10 +2610,10 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index { defer coff.symbols.addOneAssumeCapacity().* = .{ .ni = .none, .rva = 0, - .value = .{ .sti = .none }, + .value = .{ .none = {} }, .extra = .{ .size = 0 }, .flags = .{ - .value_tag = .sti, + .value_tag = .none, .extra_tag = .size, .type = .unknown, .dll_storage_class = .default, @@ -2761,14 +2767,14 @@ pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index { pub fn pendingSymbolTableEntry(coff: *Coff, si: Symbol.Index) !void { assert(!coff.isImage()); const sym = si.get(coff); - if (sym.flags.value_tag == .sti and sym.value.sti != .none) - return; assert(sym.ni != .none or sym.gmi != .none); const gpa = coff.base.comp.gpa; - const pending_gop = try coff.symbol_table.pending.getOrPut(gpa, si); - if (!pending_gop.found_existing) + const gop = try coff.symbol_table.symbols.getOrPut(gpa, si); + if (!gop.found_existing) { coff.symbol_prog_node.increaseEstimatedTotalItems(1); + gop.value_ptr.* = .none; + } } fn navSection( @@ -3047,19 +3053,17 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void { coff.member_prog_node.increaseEstimatedTotalItems(1); } -fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void { +fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void { assert(!coff.isImage()); const gpa = coff.base.comp.gpa; + const si = coff.symbol_table.symbols.keys()[index]; + const sti = &coff.symbol_table.symbols.values()[index]; + const sym = si.get(coff); assert(sym.ni != .none or sym.gmi != .none); - const existing_sti = switch (sym.flags.value_tag) { - .sti => sym.value.sti, - .weak_alias_si => .none, - else => unreachable, - }; - const entry = coff.symbolTableEntryPtr(existing_sti) orelse entry: { + const entry = coff.symbolTableEntryPtr(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: { @@ -3124,11 +3128,10 @@ 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()); - const old_value = sym.value; - sym.setValue(.{ .sti = .wrap(old_num_symbols) }); + sti.* = .wrap(old_num_symbols); si.flushSymbolTableIndex(coff); - const entry = coff.symbolTableEntryPtr(sym.value.sti).?; + const entry = coff.symbolTableEntryPtr(sti.*).?; symbol_name.store(coff, &entry.name); entry.section_number = @enumFromInt(@intFromEnum(sym.section_number)); @@ -3137,34 +3140,19 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void .base_type = .NULL, }; - entry.storage_class = if (sym.flags.extra_tag == .next_alias_si) storage: { - // TODO: Could avoid this ordering issue by flushing these in fifo order, instead of lifo - // TODO: Instead keep a map of si -> sti (remove it from sym.value) and walk in forwards order - // Update any existing aux symbols for weak externals that reference this symbol - var any_weak_external = false; + entry.storage_class = if (sym.gmi != .none) + .EXTERNAL + else if (sym.flags.extra_tag == .next_alias_si) storage: { var alias_sym = sym; - while (alias_sym.flags.extra_tag == .next_alias_si) { + const weak_external = 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); - - if (alias_sym.flags.weak_external_strat != .none) { - switch (alias_sym.flags.value_tag) { - .sti => if (coff.symbolTableWeakExternalAuxEntryPtr(alias_si)) |aux_ptr| - coff.targetStore(&aux_ptr.tag_index, sym.value.sti.unwrap().?), - .weak_alias_si => {}, - else => unreachable, - } - - any_weak_external = true; - } - } - - break :storage if (any_weak_external or sym.gmi != .none) .EXTERNAL else .STATIC; - } else if (sym.gmi == .none) - .STATIC - else - .EXTERNAL; + if (alias_sym.flags.weak_external_strat != .none) + break true; + } else false; + break :storage if (weak_external) .EXTERNAL else .STATIC; + } else .STATIC; entry.number_of_aux_symbols = num_aux_symbols; if (coff.targetEndian() != native_endian) @@ -3175,15 +3163,8 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void entry.section_number = .UNDEFINED; entry.storage_class = .WEAK_EXTERNAL; - const alias_si = old_value.weak_alias_si; - const alias_sym = alias_si.get(coff); - const tag_index = alias_sym.value.sti.unwrap() orelse tag_index: { - // The alias will update `tag_index` when it is flushed - assert(coff.symbol_table.pending.contains(alias_si)); - break :tag_index 0; - }; - - const aux_ptr = coff.symbolTableWeakExternalAuxEntryPtr(si).?; + const tag_index = sym.value.weak_alias_si.sti(coff).unwrap().?; + const aux_ptr = coff.symbolTableWeakExternalAuxEntryPtr(sti.*).?; aux_ptr.* = .{ .tag_index = tag_index, .flag = switch (sym.flags.weak_external_strat) { @@ -3203,7 +3184,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void .image_section => |sec_si| { assert(si == sec_si); const header = sym.section_number.header(coff); - const aux_ptr = coff.symbolTableSectionAuxEntryPtr(si).?; + const aux_ptr = coff.symbolTableSectionAuxEntryPtr(sti.*).?; aux_ptr.* = .{ .length = @intCast(sym.ni.location(&coff.mf).resolve(&coff.mf)[1]), .number_of_relocations = header.number_of_relocations, @@ -3238,7 +3219,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void }, }); - log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.value.sti }); + log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sti.* }); } fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void { @@ -3300,7 +3281,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.section_table.ensureUnusedCapacity(gpa, 1); try coff.symbols.ensureUnusedCapacity(gpa, 1); - if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1); + if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); const coff_header = coff.headerPtr(); const section_index = coff.targetLoad(&coff_header.number_of_sections); @@ -3643,8 +3624,9 @@ 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.value.sti != .none) - target.value.sti + const existing_sti = target_si.sti(coff); + const sti: SymbolTable.Index = if (existing_sti != .none) + existing_sti else if (target.ni != .none) sti: { try coff.pendingSymbolTableEntry(target_si); break :sti .none; @@ -3669,7 +3651,7 @@ pub fn addReloc( } coff.targetStore(&header.number_of_relocations, new_num_relocations); - if (coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff))) |aux_ptr| + if (coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff).sti(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?) @@ -5306,7 +5288,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde .none => { const sec_si = try coff.navSection(zcu, nav.resolved.?); try coff.nodes.ensureUnusedCapacity(gpa, 1); - if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1); + if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .alignment = zcu.navAlignment(nav_index).toStdMem(), .moved = true, @@ -5428,7 +5410,7 @@ fn updateFuncInner( .none => { const sec_si = try coff.navSection(zcu, nav.resolved.?); try coff.nodes.ensureUnusedCapacity(gpa, 1); - if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1); + if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); const mod = zcu.navFileScope(func.owner_nav).mod.?; const target = &mod.resolved_target.result; const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ @@ -5902,19 +5884,21 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool { }; break :task; }; - while (coff.symbol_table.pending.pop()) |pending_si| { - const sym = pending_si.key.get(coff); + if (coff.symbol_table.pending_symbol_index < coff.symbol_table.symbols.count()) { + defer coff.symbol_table.pending_symbol_index += 1; + const si = coff.symbol_table.symbols.keys()[coff.symbol_table.pending_symbol_index]; + const sym = si.get(coff); const sub_prog_node = coff.idleProgNode( tid, coff.symbol_prog_node, if (sym.ni != .none) coff.getNode(sym.ni) else - .{ .import_thunk = pending_si.key.get(coff).gmi }, + .{ .import_thunk = sym.gmi }, ); defer sub_prog_node.end(); coff.flushSymbolTableEntry( - pending_si.key, + coff.symbol_table.pending_symbol_index, .{ .zcu = comp.zcu.?, .tid = tid }, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -5935,7 +5919,7 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool { 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_symbol_index < coff.symbol_table.symbols.count()) return true; return false; } @@ -6066,7 +6050,7 @@ fn flushUav( .{ .read = true, .initialized = true }, )).symbol(coff); try coff.nodes.ensureUnusedCapacity(gpa, 1); - if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1); + if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1); const sym = si.get(coff); const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .alignment = uav_align.toStdMem(), @@ -7012,7 +6996,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { } if (!coff.isImage()) { - if (coff.symbolTableSectionAuxEntryPtr(si)) |aux_ptr| + if (coff.symbolTableSectionAuxEntryPtr(si.sti(coff))) |aux_ptr| coff.targetStore(&aux_ptr.length, @intCast(size)); } }, @@ -7290,6 +7274,8 @@ fn updateExportsInner( export_sym.rva = exported_sym.rva; export_sym.section_number = exported_sym.section_number; if (@"export".opts.linkage == .weak and !coff.isImage()) { + // exported_si needs to be ahead of export_si in the symbol table, + // so that its sti is known when creating the aux entry try coff.pendingSymbolTableEntry(exported_si); export_sym.flags.weak_external_strat = .alias; export_sym.setValue(.{ .weak_alias_si = exported_si }); @@ -7466,10 +7452,10 @@ fn printSymbol( else 0, switch (sym.flags.value_tag) { + .none => "xx", .weak_alias_name => "an", .weak_alias_si => "as", .node_offset => "no", - .sti => "st", }, switch (sym.flags.extra_tag) { .size => "sz", diff --git a/test/link/snapshots/static-lib.no-llvm.dmp b/test/link/snapshots/static-lib.no-llvm.dmp @@ -4,9 +4,9 @@ xxxx 00000000 1 NULL() EXTERNAL | fooBar xxxx 00000000 2 NULL EXTERNAL | foo1 xxxx 00000004 2 NULL EXTERNAL | foo2 lib.lib(this_is_a_long_name.obj): COFF object -xxxx 00000000 UNDEF NULL() WEAK_EXTERNAL | fooWeak - | Weak External [falls back to 00000012 via SEARCH_ALIAS] -xxxx 00000010 2 NULL EXTERNAL | foo_array -xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias -xxxx 00000000 2 NULL EXTERNAL | foo_strong xxxx 00000000 4 NULL() EXTERNAL | this_is_a_long_name.fooWeak +xxxx 00000000 2 NULL EXTERNAL | foo_strong +xxxx 00000000 2 NULL EXTERNAL | foo_strong_alias +xxxx 00000010 2 NULL EXTERNAL | foo_array +xxxx 00000000 UNDEF NULL() WEAK_EXTERNAL | fooWeak + | Weak External [falls back to 0000000d via SEARCH_ALIAS]