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) {...@@ -203,6 +203,8 @@ const Node = union(enum) {
203 archive,203 archive,
204 /// This includes the archive magic and long file member.204 /// This includes the archive magic and long file member.
205 archive_header,205 archive_header,
206 /// This is a footer of the `.elf` node, and contains the next archive entry's file header.
207 archive_elf_footer,
206 elf,208 elf,
207 ehdr,209 ehdr,
208 shdr,210 shdr,
...@@ -2952,6 +2954,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {...@@ -2952,6 +2954,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
2952 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {2954 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
2953 .archive,2955 .archive,
2954 .archive_header,2956 .archive_header,
2957 .archive_elf_footer,
2955 .elf,2958 .elf,
2956 .ehdr,2959 .ehdr,
2957 .shdr,2960 .shdr,
...@@ -3599,8 +3602,8 @@ fn initHeaders(...@@ -3599,8 +3602,8 @@ fn initHeaders(
3599 }, phnum };3602 }, phnum };
3600 };3603 };
36013604
3602 const expected_nodes_len = @as(usize, if (is_archive) 2 else 0) + // .archive, .archive_header3605 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_footer
3603 3 + // `.file`, `.ehdr`, and `.shdr` nodes3606 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
3604 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node3607 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
3605 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node3608 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
36063609
...@@ -3643,6 +3646,12 @@ fn initHeaders(...@@ -3643,6 +3646,12 @@ fn initHeaders(
3643 .enable_next_moved = true,3646 .enable_next_moved = true,
3644 });3647 });
3645 elf.nodes.appendAssumeCapacity(.elf);3648 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);
3646 } else {3655 } else {
3647 elf.ni.elf = .root;3656 elf.ni.elf = .root;
3648 elf.nodes.appendAssumeCapacity(.elf);3657 elf.nodes.appendAssumeCapacity(.elf);
...@@ -3729,6 +3738,14 @@ fn initHeaders(...@@ -3729,6 +3738,14 @@ fn initHeaders(
3729 }3738 }
37303739
3731 elf.phdrs.items[phndx.gnu_stack] = .none;3740 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 }
3732 }3749 }
37333750
3734 switch (class) {3751 switch (class) {
...@@ -4522,8 +4539,6 @@ fn initHeaders(...@@ -4522,8 +4539,6 @@ fn initHeaders(
4522 break :str try elf.string(.dynstr, slice);4539 break :str try elf.string(.dynstr, slice);
4523 },4540 },
4524 };4541 };
4525
4526 try elf.ensureElfNodeSize();
4527}4542}
45284543
4529pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {4544pub 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 {...@@ -4558,6 +4573,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
4558 return switch (elf.getNode(ni)) {4573 return switch (elf.getNode(ni)) {
4559 .archive,4574 .archive,
4560 .archive_header,4575 .archive_header,
4576 .archive_elf_footer,
4561 .elf,4577 .elf,
4562 .ehdr,4578 .ehdr,
4563 .shdr,4579 .shdr,
...@@ -4578,6 +4594,7 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -4578,6 +4594,7 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4578 return switch (elf.getNode(ni)) {4594 return switch (elf.getNode(ni)) {
4579 .archive,4595 .archive,
4580 .archive_header,4596 .archive_header,
4597 .archive_elf_footer,
4581 .elf,4598 .elf,
4582 .ehdr,4599 .ehdr,
4583 .shdr,4600 .shdr,
...@@ -4596,7 +4613,7 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -4596,7 +4613,7 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4596}4613}
4597fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {4614fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4598 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {4615 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
4599 .archive, .archive_header => unreachable,4616 .archive, .archive_header, .archive_elf_footer => unreachable,
4600 .elf => return 0,4617 .elf => return 0,
4601 .ehdr, .shdr => unreachable,4618 .ehdr, .shdr => unreachable,
4602 .segment => |phndx| switch (elf.phdrSlice()) {4619 .segment => |phndx| switch (elf.phdrSlice()) {
...@@ -4622,6 +4639,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -4622,6 +4639,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
4622 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {4639 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
4623 .archive,4640 .archive,
4624 .archive_header,4641 .archive_header,
4642 .archive_elf_footer,
4625 .elf,4643 .elf,
4626 .ehdr,4644 .ehdr,
4627 .shdr,4645 .shdr,
...@@ -6255,8 +6273,6 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -6255,8 +6273,6 @@ fn prelinkInner(elf: *Elf) Error!void {
6255 };6273 };
6256 elf.input_pending_index += 1;6274 elf.input_pending_index += 1;
6257 }6275 }
6258
6259 try elf.ensureElfNodeSize();
6260}6276}
62616277
6262fn prepareDynamic(elf: *Elf) Error!void {6278fn prepareDynamic(elf: *Elf) Error!void {
...@@ -7056,6 +7072,7 @@ fn addGotRelocAssumeCapacity(...@@ -7056,6 +7072,7 @@ fn addGotRelocAssumeCapacity(
7056 switch (elf.getNode(node)) {7072 switch (elf.getNode(node)) {
7057 .archive,7073 .archive,
7058 .archive_header,7074 .archive_header,
7075 .archive_elf_footer,
7059 .elf,7076 .elf,
7060 .ehdr,7077 .ehdr,
7061 .shdr,7078 .shdr,
...@@ -7485,7 +7502,6 @@ fn flushInner(...@@ -7485,7 +7502,6 @@ fn flushInner(
74857502
7486 try elf.prepareDynamic();7503 try elf.prepareDynamic();
74877504
7488 try elf.ensureElfNodeSize();
7489 while (try elf.idle(tid)) {}7505 while (try elf.idle(tid)) {}
74907506
7491 // We've done the final `idle` loop, so everything is at its final place in the file. We have a7507 // 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 {...@@ -7738,8 +7754,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {
7738 };7754 };
7739 break;7755 break;
7740 }7756 }
7741
7742 try elf.ensureElfNodeSize();
7743}7757}
77447758
7745fn genUav(7759fn genUav(
...@@ -7922,7 +7936,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7922,7 +7936,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
79227936
7923 switch (elf.getNode(ni)) {7937 switch (elf.getNode(ni)) {
7924 .archive, .archive_header => unreachable,7938 .archive, .archive_header => unreachable,
7925 .elf => {},7939 .archive_elf_footer, .elf => {},
7926 .ehdr, .shdr => elf.flushElfOffset(ni),7940 .ehdr, .shdr => elf.flushElfOffset(ni),
7927 .segment => |phndx| {7941 .segment => |phndx| {
7928 elf.flushElfOffset(ni);7942 elf.flushElfOffset(ni);
...@@ -8216,7 +8230,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -8216,7 +8230,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
8216 }8230 }
8217 },8231 },
8218 .archive_header, .elf => {},8232 .archive_header, .elf => {},
8219 .ehdr => unreachable,8233 .ehdr, .archive_elf_footer => unreachable,
8220 .shdr => {},8234 .shdr => {},
8221 .segment => |phndx| switch (elf.phdrSlice()) {8235 .segment => |phndx| switch (elf.phdrSlice()) {
8222 inline else => |phdr| {8236 inline else => |phdr| {
...@@ -8301,6 +8315,7 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!...@@ -8301,6 +8315,7 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
83018315
8302 switch (elf.getNode(ni)) {8316 switch (elf.getNode(ni)) {
8303 .archive,8317 .archive,
8318 .archive_elf_footer,
8304 .ehdr,8319 .ehdr,
8305 .shdr,8320 .shdr,
8306 .segment,8321 .segment,
...@@ -8746,8 +8761,6 @@ fn updateExportInner(...@@ -8746,8 +8761,6 @@ fn updateExportInner(
8746 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),8761 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
8747 };8762 };
87488763
8749 try elf.ensureElfNodeSize();
8750
8751 // Initialize the global symbol with the same values that the local one currently has. If the8764 // Initialize the global symbol with the same values that the local one currently has. If the
8752 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,8765 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,
8753 // and `flushMoved` will update their values.8766 // and `flushMoved` will update their values.
...@@ -8970,18 +8983,6 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error...@@ -8970,18 +8983,6 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error
8970 }8983 }
8971}8984}
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
8985/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a8986/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
8986/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.8987/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.
8987fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {8988fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {