authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 17:39:50+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-16 18:44:29+01:00
log716b778d5564a671148cc524d845f016364d1cec
tree025b5ec28b8dca7d52bf18a95e0da0494a5b1fb8
parenta32fb5b5276ba6dca4bedcf3b225499c70dd53d4
signature Commit is signed but in an unrecognized format.

Elf2: refactor updates of .[t]bss addresses


1 files changed, 116 insertions(+), 110 deletions(-)

src/link/Elf2.zig+116-110
......@@ -151,6 +151,11 @@ bss_inputs: std.ArrayList(Node.InputIndex),
151151/// symbols when the address of `.tbss` changes.
152152tbss_inputs: std.ArrayList(Node.InputIndex),
153153
154/// Indicates that the address of the `.bss` section needs to be updated.
155///
156/// Equivalent to a `PROGBITS` section's `MappedFile.Node` being marked as "moved".
157bss_moved: bool,
158
154159shstrtab: StringTable,
155160strtab: StringTable,
156161dynstr: StringTable,
......@@ -4815,6 +4820,7 @@ fn create(
48154820 .tbss_globals = .empty,
48164821 .bss_inputs = .empty,
48174822 .tbss_inputs = .empty,
4823 .bss_moved = false,
48184824 .shstrtab = .{ .map = .empty },
48194825 .strtab = .{ .map = .empty },
48204826 .dynstr = .{ .map = .empty },
......@@ -6426,20 +6432,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
64266432 };
64276433}
64286434
6429fn tbssBaseOffset(elf: *Elf) u64 {
6430 const tbss_addr = elf.shndx.tbss.vaddr(elf);
6431
6432 // Hasn't been laid out yet.
6433 if (tbss_addr == 0 or elf.ehdrType() == .REL) return 0;
6434
6435 const tls_ni = elf.ni.tls.unwrap().?;
6436 const tls_phndx = elf.getNode(tls_ni).segment;
6437
6438 const tls_vaddr = switch (elf.phdrSlice()) {
6439 inline else => |phdr| elf.targetLoad(&phdr[tls_phndx].vaddr),
6435fn tbssOffset(elf: *Elf) u64 {
6436 // The `.tbss` section is placed exactly at the end of the TLS segment.
6437 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
6438 return switch (elf.phdrSlice()) {
6439 inline else => |phdr| elf.targetLoad(&phdr[tls_phndx].filesz),
64406440 };
6441
6442 return tbss_addr - tls_vaddr;
64436441}
64446442
64456443fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
......@@ -7921,7 +7919,7 @@ fn loadObject(
79217919 if (elf.ni.tls != .none and
79227920 nobits.shndx == elf.shndx.tbss)
79237921 {
7924 break :base elf.tbssBaseOffset();
7922 break :base elf.tbssOffset();
79257923 }
79267924 assert(nobits.shndx == elf.shndx.bss);
79277925 break :base nobits.shndx.vaddr(elf);
......@@ -10871,12 +10869,46 @@ pub fn idle(elf: *Elf) link.Error!bool {
1087110869 if (clean_moved or clean_resized or clean_next_moved) try elf.flushPadding(ni);
1087210870 break :task;
1087310871 }
10872 if (elf.bss_moved) {
10873 elf.bss_moved = false;
10874
10875 const data_phndx = elf.getNode(elf.ni.data).segment;
10876 const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) {
10877 inline else => |phdr| .{
10878 elf.targetLoad(&phdr[data_phndx].offset),
10879 elf.targetLoad(&phdr[data_phndx].vaddr),
10880 elf.targetLoad(&phdr[data_phndx].filesz),
10881 },
10882 };
10883
10884 const old_bss_addr = elf.shndx.bss.vaddr(elf);
10885 const new_bss_addr = data_vaddr + data_filesz;
10886
10887 const bss_section_sym: Symbol.Id = .local(elf.shndx.bss.get(elf).lsi);
10888 bss_section_sym.flushMoved(elf, new_bss_addr);
10889 switch (elf.shdrPtr(elf.shndx.bss)) {
10890 inline else => |shdr| {
10891 elf.targetStore(&shdr.offset, @intCast(data_offset + data_filesz));
10892 elf.targetStore(&shdr.addr, @intCast(new_bss_addr));
10893 },
10894 }
10895 elf.flushMovedNobitsSymbols(
10896 elf.shndx.bss,
10897 old_bss_addr,
10898 new_bss_addr,
10899 elf.bss_inputs.items,
10900 elf.bss_globals.keys(),
10901 );
10902
10903 break :task;
10904 }
1087410905 }
1087510906 if (elf.inputs.items.len - elf.input_pending_index > 0) return true;
1087610907 if (elf.input_sections.items.len - elf.input_section_pending_index > 0) return true;
1087710908 if (elf.one_shot_fixups.items.len > 0) return true;
1087810909 if (elf.changed_symtab_index.count() > 0) return true;
1087910910 if (elf.mf.updates.items.len > 0) return true;
10911 if (elf.bss_moved) return true;
1088010912 return false;
1088110913}
1088210914
......@@ -11231,8 +11263,23 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
1123111263 inline else => |phdr, class| {
1123211264 const ph = &phdr[phndx];
1123311265 elf.targetStore(&ph.offset, @intCast(elf_offset));
11234 if (elf.targetLoad(&ph.type) == .PHDR) {
11235 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;
11266 switch (elf.targetLoad(&ph.type)) {
11267 .PHDR => {
11268 const ehdr = @field(elf.ehdrPtr(), @tagName(class));
11269 elf.targetStore(&ehdr.phoff, @intCast(elf_offset));
11270 },
11271 .TLS => {
11272 // The `.tbss` section's offset should be at the end of the TLS segment.
11273 const tls_filesz = elf.targetLoad(&ph.filesz);
11274 const tbss_shdr = @field(elf.shdrPtr(elf.shndx.tbss), @tagName(class));
11275 elf.targetStore(&tbss_shdr.offset, @intCast(elf_offset + tls_filesz));
11276 },
11277 else => if (ni == elf.ni.data) {
11278 // The `.bss` section's offset should be at the end of the data segment.
11279 const data_filesz = elf.targetLoad(&ph.filesz);
11280 const bss_shdr = @field(elf.shdrPtr(elf.shndx.bss), @tagName(class));
11281 elf.targetStore(&bss_shdr.offset, @intCast(elf_offset + data_filesz));
11282 },
1123611283 }
1123711284 },
1123811285 }
......@@ -11283,23 +11330,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1128311330 },
1128411331
1128511332 .TLS => {
11286 const tbss_base = elf.tbssBaseOffset();
1128711333 const new_vaddr = elf.computeNodeVAddr(ni);
11288 const tls_offset = elf.targetLoad(&ph.offset);
11289 const tls_filesz = elf.targetLoad(&ph.filesz);
11290
1129111334 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
1129211335 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
1129311336
11294 const tbss_shndx = elf.shndx.tbss;
11295 const tbss_addr = new_vaddr + tbss_base;
11296 switch (elf.shdrPtr(tbss_shndx)) {
11297 inline else => |shdr| {
11298 elf.targetStore(&shdr.addr, @intCast(tbss_addr));
11299 elf.targetStore(&shdr.offset, @intCast(tls_offset + tls_filesz));
11300 },
11337 // Update the address of the `.tbss` section at the end of this segment:
11338 const tbss_addr = new_vaddr + elf.tbssOffset();
11339 switch (elf.shdrPtr(elf.shndx.tbss)) {
11340 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(tbss_addr)),
1130111341 }
11302 Symbol.Id.local(tbss_shndx.get(elf).lsi).flushMoved(
11342 Symbol.Id.local(elf.shndx.tbss.get(elf).lsi).flushMoved(
1130311343 elf,
1130411344 tbss_addr,
1130511345 );
......@@ -11634,13 +11674,15 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1163411674 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1163511675}
1163611676
11637/// Update the position of a `NOBITS` section.
11638/// BSS needs an absolute symbol base, and TBSS needs TLS-segment-relative base.
11639fn updateNobitsAddress(
11677/// Update the value of all symbols in a `NOBITS` section. All symbol values are updated to
11678/// `old_value - old_symbol_base + new_symbol_base`.
11679///
11680/// For `.bss`, the symbol base is the section's virtual address.
11681///
11682/// For `.tbss`, the symbol base is the offset into the TLS segment.
11683fn flushMovedNobitsSymbols(
1164011684 elf: *Elf,
1164111685 shndx: Section.Index,
11642 new_section_addr: u64,
11643 new_file_offset: u64,
1164411686 old_symbol_base: u64,
1164511687 new_symbol_base: u64,
1164611688 inputs: []const Node.InputIndex,
......@@ -11648,13 +11690,6 @@ fn updateNobitsAddress(
1164811690) void {
1164911691 assert(shndx != .UNDEF);
1165011692
11651 switch (elf.shdrPtr(shndx)) {
11652 inline else => |shdr| {
11653 elf.targetStore(&shdr.addr, @intCast(new_section_addr));
11654 elf.targetStore(&shdr.offset, @intCast(new_file_offset));
11655 },
11656 }
11657
1165811693 // Update local symbols.
1165911694 for (inputs) |ii| {
1166011695 var lsi, const end_lsi = ii.localSymbolRange(elf);
......@@ -11662,14 +11697,14 @@ fn updateNobitsAddress(
1166211697 while (lsi != end_lsi) : (lsi = @fromBackingInt(
1166311698 @backingInt(lsi) + 1,
1166411699 )) {
11665 const symbol_shndx: Section.Index, const visibility = switch (elf.symPtr(lsi.index())) {
11700 const sym_shndx: Section.Index, const visibility = switch (elf.symPtr(lsi.index())) {
1166611701 inline else => |sym| .{
1166711702 .fromSection(elf.targetLoad(&sym.shndx)),
1166811703 elf.targetLoad(&sym.other).visibility,
1166911704 },
1167011705 };
1167111706
11672 if (symbol_shndx != shndx) continue;
11707 if (sym_shndx != shndx) continue;
1167311708
1167411709 switch (visibility) {
1167511710 // These are globals demoted to STB_LOCAL and are updated below.
......@@ -11700,40 +11735,26 @@ fn updateNobitsAddress(
1170011735 symbol.value(elf) - old_symbol_base + new_symbol_base,
1170111736 );
1170211737 }
11703
11704 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(
11705 elf,
11706 new_section_addr,
11707 );
1170811738}
1170911739
11710fn loadSegmentChildrenMoved(
11740/// Updates `vaddr` and `paddr` of the segment header, and marks everything in the segment as moved.
11741fn updateSegmentLoadAddress(
1171111742 elf: *Elf,
1171211743 segment_ni: MappedFile.Node.Index,
11713 old_vaddr: u64,
1171411744 new_vaddr: u64,
11715 offset: u64,
11716 filesz: u64,
1171711745) std.mem.Allocator.Error!void {
11718 if (segment_ni == elf.ni.data) {
11719 const bss_shndx = elf.shndx.bss;
11720 const old_bss_addr = bss_shndx.vaddr(elf);
11721 const new_bss_addr = new_vaddr + filesz;
11722
11723 elf.updateNobitsAddress(
11724 bss_shndx,
11725 new_bss_addr,
11726 offset + filesz,
11727 old_bss_addr,
11728 new_bss_addr,
11729 elf.bss_inputs.items,
11730 elf.bss_globals.keys(),
11731 );
11732 }
11746 const phndx = elf.getNode(segment_ni).segment;
1173311747
11734 if (old_vaddr != new_vaddr) {
11735 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
11748 switch (elf.phdrSlice()) {
11749 inline else => |phdr| {
11750 assert(new_vaddr != elf.targetLoad(&phdr[phndx].vaddr));
11751 elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr));
11752 elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr));
11753 },
1173611754 }
11755
11756 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
11757 if (segment_ni == elf.ni.data) elf.bss_moved = true;
1173711758}
1173811759
1173911760/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
......@@ -11852,19 +11873,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1185211873 const next_offset = elf.targetLoad(&next_ph.offset);
1185311874 const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits();
1185411875
11855 // This logic for updating the data segment's vaddr is identical to how we
11856 // will update the vaddr of `phndx` when we break from the loop.
11857 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));
11858 elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr));
11859
11860 const next_filesz = elf.targetLoad(&next_ph.filesz);
11861 try elf.loadSegmentChildrenMoved(
11862 next_ni,
11863 next_vaddr,
11864 next_new_vaddr,
11865 next_offset,
11866 next_filesz,
11867 );
11876 try elf.updateSegmentLoadAddress(next_ni, next_new_vaddr);
1186811877
1186911878 break;
1187011879 },
......@@ -11887,17 +11896,8 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1188711896 }
1188811897
1188911898 if (vaddr != orig_vaddr) {
11890 elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr));
11891 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
11899 try elf.updateSegmentLoadAddress(segment_ni, vaddr);
1189211900 }
11893
11894 try elf.loadSegmentChildrenMoved(
11895 segment_ni,
11896 orig_vaddr,
11897 vaddr,
11898 offset,
11899 filesz,
11900 );
1190111901 },
1190211902 }
1190311903}
......@@ -11926,40 +11926,46 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1192611926 .ehdr => unreachable,
1192711927 .shdr => {},
1192811928 .segment => |phndx| switch (elf.phdrSlice()) {
11929 inline else => |phdr| {
11929 inline else => |phdr, class| {
1193011930 assert(elf.phdrs.items[phndx].unwrap().? == ni);
1193111931 const ph = &phdr[phndx];
11932 elf.targetStore(&ph.filesz, @intCast(size));
1193311932 switch (elf.targetLoad(&ph.type)) {
1193411933 else => unreachable,
1193511934 .NULL, .LOAD => {
11935 elf.targetStore(&ph.filesz, @intCast(size));
1193611936 try elf.allocateSegmentLoadAddress(phndx);
11937 if (ni == elf.ni.data) elf.bss_moved = true;
1193711938 },
1193811939 .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => {
11940 elf.targetStore(&ph.filesz, @intCast(size));
1193911941 elf.targetStore(&ph.memsz, @intCast(size));
1194011942 },
1194111943 .TLS => {
11942 const tls_filesz: u64 = size;
11944 // We must fetch this value before modifying our `filesz`.
11945 const old_tbss_offset = elf.tbssOffset();
1194311946
11944 const old_tbss_base = elf.tbssBaseOffset();
11945 const new_tbss_base = tls_filesz;
11946 const tbss_size = elf.shndx.tbss.size(elf);
11947 const tls_vaddr = elf.targetLoad(&ph.vaddr);
11948 const tls_offset = elf.targetLoad(&ph.offset);
11947 elf.targetStore(&ph.filesz, @intCast(size));
1194911948
11950 const tls_memsz = std.mem.alignForward(
11951 u64,
11952 tls_filesz + tbss_size,
11953 elf.targetLoad(&ph.@"align"),
11954 );
11955 elf.targetStore(&ph.memsz, @intCast(tls_memsz));
11949 // Our memsz must include the `.tbss` section.
11950 const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align"));
11951 elf.targetStore(&ph.memsz, @intCast(
11952 tls_align.forward(size + elf.shndx.tbss.size(elf)),
11953 ));
1195611954
11957 elf.updateNobitsAddress(
11955 // `.tbss` section goes at the end of this segment. Update its offset:
11956 const tls_offset = elf.targetLoad(&ph.offset);
11957 const tbss_shdr = @field(elf.shdrPtr(elf.shndx.tbss), @tagName(class));
11958 elf.targetStore(&tbss_shdr.offset, @intCast(tls_offset + size));
11959 // and its address:
11960 const tls_vaddr = elf.targetLoad(&ph.vaddr);
11961 const tbss_section_sym: Symbol.Id = .local(elf.shndx.tbss.get(elf).lsi);
11962 tbss_section_sym.flushMoved(elf, tls_vaddr + size);
11963 elf.targetStore(&tbss_shdr.addr, @intCast(tls_vaddr + size));
11964 // and update the values of symbols in that section:
11965 elf.flushMovedNobitsSymbols(
1195811966 elf.shndx.tbss,
11959 tls_vaddr + new_tbss_base,
11960 tls_offset + new_tbss_base,
11961 old_tbss_base,
11962 new_tbss_base,
11967 old_tbss_offset,
11968 size,
1196311969 elf.tbss_inputs.items,
1196411970 elf.tbss_globals.keys(),
1196511971 );