authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 15:53:16+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 17:41:24+01:00
logfbeb341d9689959f222eefa8720c234e8a5ec9c7
treecdad5fcf2677fa1053f7ed55516f81cb1b7ea46f
parent6c1a5a1e5b12e775552e1e3ff52daa6576789487
signature Commit is signed but in an unrecognized format.

Elf2: track bss and tbss globals separately


1 files changed, 53 insertions(+), 26 deletions(-)

src/link/Elf2.zig+53-26
...@@ -137,11 +137,18 @@ node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Globa...@@ -137,11 +137,18 @@ node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Globa
137/// See the definition of `DsoGlobals`.137/// See the definition of `DsoGlobals`.
138dso_globals: DsoGlobals,138dso_globals: DsoGlobals,
139139
140/// Contains all defined global symbols that do not have a node.140/// Contains all global symbols defined relative to `.bss`. These globals are not associated with
141nodeless_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),141/// any `MappedFile.Node`, but they need to be updated when the address of `.bss` changes.
142142bss_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
143// These are needed to find local symbols when a nodeless section moves.143/// Contains all global symbols defined relative to `.tbss`. These globals are not associated with
144/// any `MappedFile.Node`, but they need to be updated when the address of `.tbss` changes.
145tbss_globals: std.array_hash_map.Auto(Symbol.Global.Index, void),
146
147/// Contains all inputs which have added input sections to `.bss`, so that we can update local
148/// symbols when the address of `.bss` changes.
144bss_inputs: std.ArrayList(Node.InputIndex),149bss_inputs: std.ArrayList(Node.InputIndex),
150/// Contains all inputs which have added input sections to `.tbss`, so that we can update local
151/// symbols when the address of `.tbss` changes.
145tbss_inputs: std.ArrayList(Node.InputIndex),152tbss_inputs: std.ArrayList(Node.InputIndex),
146153
147shstrtab: StringTable,154shstrtab: StringTable,
...@@ -2747,7 +2754,6 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{...@@ -2747,7 +2754,6 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2747 try elf.globals.ensureUnusedCapacity(gpa, 1);2754 try elf.globals.ensureUnusedCapacity(gpa, 1);
2748 try elf.globals_by_name.ensureUnusedCapacity(gpa, 1);2755 try elf.globals_by_name.ensureUnusedCapacity(gpa, 1);
2749 try elf.node_global_symbols.ensureUnusedCapacity(gpa, 1);2756 try elf.node_global_symbols.ensureUnusedCapacity(gpa, 1);
2750 try elf.nodeless_globals.ensureUnusedCapacity(gpa, 1);
27512757
2752 try Section.Index.symtab.node(elf).ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {2758 try Section.Index.symtab.node(elf).ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {
2753 inline else => |shdr, class| elf.targetLoad(&shdr.size) + @sizeOf(class.ElfN().Sym),2759 inline else => |shdr, class| elf.targetLoad(&shdr.size) + @sizeOf(class.ElfN().Sym),
...@@ -2915,8 +2921,16 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{...@@ -2915,8 +2921,16 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2915 old_head_ptr.prev_in_node = .wrap(gsi);2921 old_head_ptr.prev_in_node = .wrap(gsi);
2916 }2922 }
29172923
2918 if (opts.node == .none and opts.shndx != .UNDEF) {2924 if (elf.ehdrType() != .REL) {
2919 elf.nodeless_globals.putAssumeCapacity(gsi, {});2925 if (opts.shndx == elf.shndx.bss) {
2926 assert(opts.node == .none);
2927 try elf.bss_globals.put(gpa, gsi, {});
2928 } else if (elf.ni.tls != .none and opts.shndx == elf.shndx.tbss) {
2929 assert(opts.node == .none);
2930 try elf.tbss_globals.put(gpa, gsi, {});
2931 } else if (opts.shndx != .UNDEF and opts.shndx != .ABS) {
2932 assert(opts.node != .none);
2933 }
2920 }2934 }
29212935
2922 if (force_local_bind) {2936 if (force_local_bind) {
...@@ -3356,8 +3370,9 @@ fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {...@@ -3356,8 +3370,9 @@ fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
3356 type: std.elf.STT,3370 type: std.elf.STT,
3357 shndx: Section.Index,3371 shndx: Section.Index,
3358}) Error!void {3372}) Error!void {
3359 assert(new.shndx != .UNDEF);3373 const gpa = elf.base.comp.gpa;
33603374
3375 assert(new.shndx != .UNDEF);
3361 const global_ptr = gsi.ptr(elf);3376 const global_ptr = gsi.ptr(elf);
33623377
3363 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {3378 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
...@@ -3379,11 +3394,21 @@ fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {...@@ -3379,11 +3394,21 @@ fn setGlobalSymbolValue(elf: *Elf, gsi: Symbol.Global.Index, new: struct {
3379 } else {3394 } else {
3380 assert(global_ptr.next_in_node == .none);3395 assert(global_ptr.next_in_node == .none);
3381 assert(global_ptr.prev_in_node == .none);3396 assert(global_ptr.prev_in_node == .none);
3397 // If the symbol didn't have a node, it may have been in `.bss` or `.tbss`.
3398 _ = elf.bss_globals.swapRemove(gsi);
3399 _ = elf.tbss_globals.swapRemove(gsi);
3382 }3400 }
33833401
3384 _ = elf.nodeless_globals.swapRemove(gsi);3402 if (elf.ehdrType() != .REL) {
3385 if (new.node == .none) {3403 if (new.shndx == elf.shndx.bss) {
3386 try elf.nodeless_globals.put(elf.base.comp.gpa, gsi, {});3404 assert(new.node == .none);
3405 try elf.bss_globals.put(gpa, gsi, {});
3406 } else if (elf.ni.tls != .none and new.shndx == elf.shndx.tbss) {
3407 assert(new.node == .none);
3408 try elf.tbss_globals.put(gpa, gsi, {});
3409 } else if (new.shndx != .UNDEF) {
3410 assert(new.node != .none);
3411 }
3387 }3412 }
33883413
3389 global_ptr.symtab_index.ptr(elf).node = new.node;3414 global_ptr.symtab_index.ptr(elf).node = new.node;
...@@ -3667,7 +3692,8 @@ fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Erro...@@ -3667,7 +3692,8 @@ fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Erro
36673692
3668const Symbol = struct {3693const Symbol = struct {
3669 /// The node which this symbol's value is defined relative to. Possible values are:3694 /// The node which this symbol's value is defined relative to. Possible values are:
3670 /// * `.none` for a SHN_ABS or SHN_UNDEF symbol3695 /// * `.none` for a SHN_ABS or SHN_UNDEF symbol, *or* a symbol in `.bss`/`.tbss`
3696 /// (see `Elf.bss_globals` and `Elf.tbss_globals`)
3671 /// * A section (the symbol's value is some vaddr in that section)3697 /// * A section (the symbol's value is some vaddr in that section)
3672 /// * An input section (the symbol's value is some vaddr in that input section)3698 /// * An input section (the symbol's value is some vaddr in that input section)
3673 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)3699 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
...@@ -4783,7 +4809,8 @@ fn create(...@@ -4783,7 +4809,8 @@ fn create(
4783 .symbols = .empty,4809 .symbols = .empty,
4784 .default_sym_vers = .empty,4810 .default_sym_vers = .empty,
4785 },4811 },
4786 .nodeless_globals = .empty,4812 .bss_globals = .empty,
4813 .tbss_globals = .empty,
4787 .bss_inputs = .empty,4814 .bss_inputs = .empty,
4788 .tbss_inputs = .empty,4815 .tbss_inputs = .empty,
4789 .shstrtab = .{ .map = .empty },4816 .shstrtab = .{ .map = .empty },
...@@ -4868,7 +4895,8 @@ pub fn deinit(elf: *Elf) void {...@@ -4868,7 +4895,8 @@ pub fn deinit(elf: *Elf) void {
4868 elf.dso_globals.string_bytes.deinit(gpa);4895 elf.dso_globals.string_bytes.deinit(gpa);
4869 elf.dso_globals.symbols.deinit(gpa);4896 elf.dso_globals.symbols.deinit(gpa);
4870 elf.dso_globals.default_sym_vers.deinit(gpa);4897 elf.dso_globals.default_sym_vers.deinit(gpa);
4871 elf.nodeless_globals.deinit(gpa);4898 elf.bss_globals.deinit(gpa);
4899 elf.tbss_globals.deinit(gpa);
4872 elf.bss_inputs.deinit(gpa);4900 elf.bss_inputs.deinit(gpa);
4873 elf.tbss_inputs.deinit(gpa);4901 elf.tbss_inputs.deinit(gpa);
4874 elf.shstrtab.map.deinit(gpa);4902 elf.shstrtab.map.deinit(gpa);
...@@ -11601,6 +11629,7 @@ fn updateNobitsAddress(...@@ -11601,6 +11629,7 @@ fn updateNobitsAddress(
11601 old_symbol_base: u64,11629 old_symbol_base: u64,
11602 new_symbol_base: u64,11630 new_symbol_base: u64,
11603 inputs: []const Node.InputIndex,11631 inputs: []const Node.InputIndex,
11632 globals: []const Symbol.Global.Index,
11604) void {11633) void {
11605 assert(shndx != .UNDEF);11634 assert(shndx != .UNDEF);
1160611635
...@@ -11611,8 +11640,6 @@ fn updateNobitsAddress(...@@ -11611,8 +11640,6 @@ fn updateNobitsAddress(
11611 },11640 },
11612 }11641 }
1161311642
11614 const expected_shndx = shndx.toSection().?;
11615
11616 // Update local symbols.11643 // Update local symbols.
11617 for (inputs) |ii| {11644 for (inputs) |ii| {
11618 var lsi, const end_lsi = ii.localSymbolRange(elf);11645 var lsi, const end_lsi = ii.localSymbolRange(elf);
...@@ -11620,14 +11647,14 @@ fn updateNobitsAddress(...@@ -11620,14 +11647,14 @@ fn updateNobitsAddress(
11620 while (lsi != end_lsi) : (lsi = @fromBackingInt(11647 while (lsi != end_lsi) : (lsi = @fromBackingInt(
11621 @backingInt(lsi) + 1,11648 @backingInt(lsi) + 1,
11622 )) {11649 )) {
11623 const symbol_shndx, const visibility = switch (elf.symPtr(lsi.index())) {11650 const symbol_shndx: Section.Index, const visibility = switch (elf.symPtr(lsi.index())) {
11624 inline else => |sym| .{11651 inline else => |sym| .{
11625 elf.targetLoad(&sym.shndx),11652 .fromSection(elf.targetLoad(&sym.shndx)),
11626 elf.targetLoad(&sym.other).visibility,11653 elf.targetLoad(&sym.other).visibility,
11627 },11654 },
11628 };11655 };
1162911656
11630 if (symbol_shndx != expected_shndx) continue;11657 if (symbol_shndx != shndx) continue;
1163111658
11632 switch (visibility) {11659 switch (visibility) {
11633 // These are globals demoted to STB_LOCAL and are updated below.11660 // These are globals demoted to STB_LOCAL and are updated below.
...@@ -11644,16 +11671,14 @@ fn updateNobitsAddress(...@@ -11644,16 +11671,14 @@ fn updateNobitsAddress(
11644 }11671 }
11645 }11672 }
1164611673
11647 // Update globals.11674 // Update global symbols.
11648 // Since nobits sections have no nodes, we can't just iterate over `node_global_symbols`,11675 for (globals) |gsi| {
11649 // so instead filter manually through nodeless symbols.
11650 for (elf.nodeless_globals.keys()) |gsi| {
11651 const symbol: Symbol.Id = .global(gsi);11676 const symbol: Symbol.Id = .global(gsi);
1165211677
11653 const symbol_shndx = switch (elf.symPtr(symbol.index(elf))) {11678 const sym_shndx: Section.Index = switch (elf.symPtr(symbol.index(elf))) {
11654 inline else => |sym| elf.targetLoad(&sym.shndx),11679 inline else => |sym| .fromSection(elf.targetLoad(&sym.shndx)),
11655 };11680 };
11656 if (symbol_shndx != expected_shndx) continue;11681 assert(sym_shndx == shndx);
1165711682
11658 symbol.flushMoved(11683 symbol.flushMoved(
11659 elf,11684 elf,
...@@ -11687,6 +11712,7 @@ fn loadSegmentChildrenMoved(...@@ -11687,6 +11712,7 @@ fn loadSegmentChildrenMoved(
11687 old_bss_addr,11712 old_bss_addr,
11688 new_bss_addr,11713 new_bss_addr,
11689 elf.bss_inputs.items,11714 elf.bss_inputs.items,
11715 elf.bss_globals.keys(),
11690 );11716 );
11691 }11717 }
1169211718
...@@ -11920,6 +11946,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -11920,6 +11946,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
11920 old_tbss_base,11946 old_tbss_base,
11921 new_tbss_base,11947 new_tbss_base,
11922 elf.tbss_inputs.items,11948 elf.tbss_inputs.items,
11949 elf.tbss_globals.keys(),
11923 );11950 );
1192411951
11925 // TPOFF relocations care about the size of the TLS segment. Re-apply11952 // TPOFF relocations care about the size of the TLS segment. Re-apply