authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-23 21:38:28+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-24 20:42:40+01:00
logdce4edf4d03c1e2b7b4110ea51c09d1a14db2982
treeb7fa8e50bb84be2053c4a96ce2b55fb740759283
parent7ece5e656da5cdd991f9fd1cb289f71c586a9210
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: slightly simplify archive handling

Eliminate the need to call this `ensureElfNodeSize` function all the time, with the result of failing to do so being introduction of a rare but possible miscompilation bug. Instead, add one of the new "footer" nodes to keep space available at the end of the `.elf` node.

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

src/link/Elf2.zig+27-26
......@@ -203,6 +203,8 @@ const Node = union(enum) {
203203 archive,
204204 /// This includes the archive magic and long file member.
205205 archive_header,
206 /// This is a footer of the `.elf` node, and contains the next archive entry's file header.
207 archive_elf_footer,
206208 elf,
207209 ehdr,
208210 shdr,
......@@ -2952,6 +2954,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
29522954 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
29532955 .archive,
29542956 .archive_header,
2957 .archive_elf_footer,
29552958 .elf,
29562959 .ehdr,
29572960 .shdr,
......@@ -3599,8 +3602,8 @@ fn initHeaders(
35993602 }, phnum };
36003603 };
36013604
3602 const expected_nodes_len = @as(usize, if (is_archive) 2 else 0) + // .archive, .archive_header
3603 3 + // `.file`, `.ehdr`, and `.shdr` nodes
3605 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_footer
3606 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
36043607 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
36053608 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
36063609
......@@ -3643,6 +3646,12 @@ fn initHeaders(
36433646 .enable_next_moved = true,
36443647 });
36453648 elf.nodes.appendAssumeCapacity(.elf);
3649
3650 _ = try elf.ni.elf.addOnlyFooterChild(&elf.mf, gpa, .{
3651 .alignment = .@"2",
3652 .size = @sizeOf(std.elf.ar_hdr),
3653 });
3654 elf.nodes.appendAssumeCapacity(.archive_elf_footer);
36463655 } else {
36473656 elf.ni.elf = .root;
36483657 elf.nodes.appendAssumeCapacity(.elf);
......@@ -3729,6 +3738,14 @@ fn initHeaders(
37293738 }
37303739
37313740 elf.phdrs.items[phndx.gnu_stack] = .none;
3741 } else {
3742 elf.ni.rodata = elf.ni.elf;
3743 elf.ni.text = elf.ni.elf;
3744 elf.ni.data = elf.ni.elf;
3745 elf.ni.data_rel_ro = elf.ni.elf;
3746 if (comp.config.any_non_single_threaded) {
3747 elf.ni.tls = .wrap(elf.ni.elf);
3748 }
37323749 }
37333750
37343751 switch (class) {
......@@ -4522,8 +4539,6 @@ fn initHeaders(
45224539 break :str try elf.string(.dynstr, slice);
45234540 },
45244541 };
4525
4526 try elf.ensureElfNodeSize();
45274542}
45284543
45294544pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
......@@ -4558,6 +4573,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
45584573 return switch (elf.getNode(ni)) {
45594574 .archive,
45604575 .archive_header,
4576 .archive_elf_footer,
45614577 .elf,
45624578 .ehdr,
45634579 .shdr,
......@@ -4578,6 +4594,7 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
45784594 return switch (elf.getNode(ni)) {
45794595 .archive,
45804596 .archive_header,
4597 .archive_elf_footer,
45814598 .elf,
45824599 .ehdr,
45834600 .shdr,
......@@ -4596,7 +4613,7 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
45964613}
45974614fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
45984615 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
4599 .archive, .archive_header => unreachable,
4616 .archive, .archive_header, .archive_elf_footer => unreachable,
46004617 .elf => return 0,
46014618 .ehdr, .shdr => unreachable,
46024619 .segment => |phndx| switch (elf.phdrSlice()) {
......@@ -4622,6 +4639,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
46224639 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
46234640 .archive,
46244641 .archive_header,
4642 .archive_elf_footer,
46254643 .elf,
46264644 .ehdr,
46274645 .shdr,
......@@ -6255,8 +6273,6 @@ fn prelinkInner(elf: *Elf) Error!void {
62556273 };
62566274 elf.input_pending_index += 1;
62576275 }
6258
6259 try elf.ensureElfNodeSize();
62606276}
62616277
62626278fn prepareDynamic(elf: *Elf) Error!void {
......@@ -7056,6 +7072,7 @@ fn addGotRelocAssumeCapacity(
70567072 switch (elf.getNode(node)) {
70577073 .archive,
70587074 .archive_header,
7075 .archive_elf_footer,
70597076 .elf,
70607077 .ehdr,
70617078 .shdr,
......@@ -7485,7 +7502,6 @@ fn flushInner(
74857502
74867503 try elf.prepareDynamic();
74877504
7488 try elf.ensureElfNodeSize();
74897505 while (try elf.idle(tid)) {}
74907506
74917507 // We've done the final `idle` loop, so everything is at its final place in the file. We have a
......@@ -7738,8 +7754,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {
77387754 };
77397755 break;
77407756 }
7741
7742 try elf.ensureElfNodeSize();
77437757}
77447758
77457759fn genUav(
......@@ -7922,7 +7936,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
79227936
79237937 switch (elf.getNode(ni)) {
79247938 .archive, .archive_header => unreachable,
7925 .elf => {},
7939 .archive_elf_footer, .elf => {},
79267940 .ehdr, .shdr => elf.flushElfOffset(ni),
79277941 .segment => |phndx| {
79287942 elf.flushElfOffset(ni);
......@@ -8216,7 +8230,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
82168230 }
82178231 },
82188232 .archive_header, .elf => {},
8219 .ehdr => unreachable,
8233 .ehdr, .archive_elf_footer => unreachable,
82208234 .shdr => {},
82218235 .segment => |phndx| switch (elf.phdrSlice()) {
82228236 inline else => |phdr| {
......@@ -8301,6 +8315,7 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
83018315
83028316 switch (elf.getNode(ni)) {
83038317 .archive,
8318 .archive_elf_footer,
83048319 .ehdr,
83058320 .shdr,
83068321 .segment,
......@@ -8746,8 +8761,6 @@ fn updateExportInner(
87468761 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
87478762 };
87488763
8749 try elf.ensureElfNodeSize();
8750
87518764 // Initialize the global symbol with the same values that the local one currently has. If the
87528765 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,
87538766 // and `flushMoved` will update their values.
......@@ -8970,18 +8983,6 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error
89708983 }
89718984}
89728985
8973/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
8974/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
8975fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {
8976 if (elf.ni.elf == .root) return;
8977 var child_it = elf.ni.elf.reverseChildren(&elf.mf);
8978 const last_end = if (child_it.next()) |last_ni| last_end: {
8979 const last_offset, const last_size = last_ni.location(&elf.mf).resolve(&elf.mf);
8980 break :last_end last_offset + last_size;
8981 } else 0;
8982 try elf.ni.elf.ensureMinimumSize(&elf.mf, elf.base.comp.gpa, last_end + @sizeOf(std.elf.ar_hdr));
8983}
8984
89858986/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
89868987/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.
89878988fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {