authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-25 13:08:02+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-25 22:59:25+01:00
log6f8eb736856054b8a02ae2fe734340e2f8ce0a50
treef28812ba3b12bc753ad3d34cfc0702fd87352f42
parent18e38104ce60a02f27b565097cff66c3429aa8d4
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: rework archives

Resolves: https://codeberg.org/ziglang/zig/issues/36496

1 files changed, 282 insertions(+), 132 deletions(-)

src/link/Elf2.zig+282-132
......@@ -24,6 +24,7 @@ base: link.File,
2424options: link.File.OpenOptions,
2525mf: MappedFile,
2626ni: Node.Known,
27archive: ?Archive,
2728nodes: std.MultiArrayList(Node),
2829/// Does not contain an item for `SHN_UNDEF`.
2930shdrs: std.ArrayList(Section),
......@@ -200,19 +201,39 @@ input_prog_node: std.Progress.Node,
200201const Error = link.Error || error{MappedFileIo};
201202
202203const Node = union(enum) {
204 /// Only used when emitting a static library.
205 ///
206 /// Contains a header node which is an `.archive_header`.
207 ///
208 /// Contains the following footer nodes:
209 /// * One `.archive_input_member` for each external input in the archive
210 /// * One `.archive_elf_member_header` containing the `ar_hdr` for the ZCU
211 /// * One `.elf` containing the ZCU's actual ELF object
212 ///
213 /// Padding between the headers and footers is absorbed into the "//" member (whose actual
214 /// content is in the `.archive_header` node).
203215 archive,
204 /// This includes the archive magic and long file member.
216 /// Only used when emitting a static library.
217 ///
218 /// Contains the archive magic (`ARMAG`), as well as the `ar_hdr` and content for the long file
219 /// name string table member ("//").
205220 archive_header,
206 /// This is a footer of the `.elf` node, and contains the next archive entry's file header.
207 archive_elf_footer,
221 /// Only used when emitting a static library.
222 ///
223 /// Contains the `ar_hdr` and content for one non-ZCU archive member (external link input). Also
224 /// includes the single byte '\n' padding at the end of this archive member, if necessary.
225 archive_input_member: InputIndex,
226 /// Only used when emitting a static library.
227 ///
228 /// Contains the `ar_hdr` for the `.elf` node.
229 archive_elf_member_header,
230
208231 elf,
209232 ehdr,
210233 shdr,
211234 segment: u32,
212235 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
213236 section: Section.Index,
214 /// Only valid for static libraries, represents one non-zcu archive member.
215 input_member: InputIndex,
216237 /// May contain relocations.
217238 input_section: InputSection.Index,
218239 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
......@@ -404,6 +425,15 @@ const InputSection = struct {
404425 };
405426};
406427
428const Archive = struct {
429 ni: MappedFile.Node.Index,
430 header_ni: MappedFile.Node.Index,
431 elf_member_header_ni: MappedFile.Node.Index,
432
433 elf_member_too_big: bool,
434 strtab_member_too_big: bool,
435};
436
407437const Section = struct {
408438 /// The node corresponding to this section.
409439 ni: MappedFile.Node.Index,
......@@ -2954,13 +2984,13 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
29542984 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
29552985 .archive,
29562986 .archive_header,
2957 .archive_elf_footer,
2987 .archive_input_member,
2988 .archive_elf_member_header,
29582989 .elf,
29592990 .ehdr,
29602991 .shdr,
29612992 .segment,
29622993 .section,
2963 .input_member,
29642994 .input_section,
29652995 .copied_global,
29662996 => unreachable,
......@@ -3364,6 +3394,7 @@ fn create(
33643394 .data_rel_ro = undefined,
33653395 .tls = .none,
33663396 },
3397 .archive = null,
33673398 .nodes = .empty,
33683399 .shdrs = .empty,
33693400 .phdrs = .empty,
......@@ -3602,7 +3633,7 @@ fn initHeaders(
36023633 }, phnum };
36033634 };
36043635
3605 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_footer
3636 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header
36063637 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
36073638 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
36083639 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
......@@ -3619,11 +3650,14 @@ fn initHeaders(
36193650 const archive_ni: MappedFile.Node.Index = .root;
36203651
36213652 const archive_header_ni = try archive_ni.addOnlyHeaderChild(&elf.mf, gpa, .{
3622 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr) * 2,
3623 .alignment = .@"2",
3624 .next_moved = true,
3625 .bubbles_moved = false,
3653 // We intentionally do not set `.alignment = .@"2"` here, because the string table data
3654 // in this node does not need to have an aligned length. (This node's offset is aligned
3655 // regardless by virtue of it being a header.)
3656 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr),
3657 // The archive header uses 'next_moved' events to resize the "//" member, so that it
3658 // absorbs all padding between `archive_header_ni` and the actual object file members.
36263659 .enable_next_moved = true,
3660 .next_moved = true,
36273661 });
36283662 elf.nodes.appendAssumeCapacity(.archive_header);
36293663 const archive_header_slice = archive_header_ni.slice(&elf.mf);
......@@ -3635,23 +3669,47 @@ fn initHeaders(
36353669 .ar_uid = @splat(' '),
36363670 .ar_gid = @splat(' '),
36373671 .ar_mode = @splat(' '),
3638 .ar_size = @splat(' '),
3672 .ar_size = undefined, // populated by `flushNextMoved` for `archive_header_ni`
36393673 .ar_fmag = std.elf.ARFMAG.*,
36403674 };
36413675
3642 elf.ni.elf = try archive_ni.addFloatingChild(&elf.mf, gpa, .{
3676 elf.ni.elf = try archive_ni.addOnlyFooterChild(&elf.mf, gpa, .{
36433677 .alignment = node_block_align.max(.@"2"),
3644 .next_moved = true,
36453678 .bubbles_moved = false,
3646 .enable_next_moved = true,
3679 .resized = true, // ensure that this node's `ar_hdr.ar_size` is updated at least once
36473680 });
36483681 elf.nodes.appendAssumeCapacity(.elf);
36493682
3650 _ = try elf.ni.elf.addOnlyFooterChild(&elf.mf, gpa, .{
3683 const elf_ar_hdr_ni = try archive_ni.addFooterChildBefore(&elf.mf, gpa, .wrap(elf.ni.elf), .{
36513684 .alignment = .@"2",
36523685 .size = @sizeOf(std.elf.ar_hdr),
36533686 });
3654 elf.nodes.appendAssumeCapacity(.archive_elf_footer);
3687 elf.nodes.appendAssumeCapacity(.archive_elf_member_header);
3688
3689 // Must be populated before we call `populateArchiveMemberName` below.
3690 elf.archive = .{
3691 .ni = archive_ni,
3692 .header_ni = archive_header_ni,
3693 .elf_member_header_ni = elf_ar_hdr_ni,
3694
3695 .elf_member_too_big = false,
3696 .strtab_member_too_big = false,
3697 };
3698
3699 const elf_ar_hdr: *std.elf.ar_hdr = @ptrCast(elf_ar_hdr_ni.slice(&elf.mf));
3700 elf_ar_hdr.* = .{
3701 .ar_name = undefined, // populated below
3702 .ar_date = "0 ".*,
3703 .ar_uid = "0 ".*,
3704 .ar_gid = "0 ".*,
3705 .ar_mode = "644 ".*,
3706 .ar_size = undefined, // populated by `flushResized` for the `.elf` node
3707 .ar_fmag = std.elf.ARFMAG.*,
3708 };
3709 const zcu_member_name = try std.fmt.allocPrint(gpa, "{s}_zcu.o", .{comp.root_name});
3710 defer gpa.free(zcu_member_name);
3711 // After this call returns, `elf_ar_hdr` is invalidated.
3712 try elf.populateArchiveMemberName(elf_ar_hdr, zcu_member_name);
36553713 } else {
36563714 elf.ni.elf = .root;
36573715 elf.nodes.appendAssumeCapacity(.elf);
......@@ -4573,12 +4631,12 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
45734631 return switch (elf.getNode(ni)) {
45744632 .archive,
45754633 .archive_header,
4576 .archive_elf_footer,
4634 .archive_input_member,
4635 .archive_elf_member_header,
45774636 .elf,
45784637 .ehdr,
45794638 .shdr,
45804639 .segment,
4581 .input_member,
45824640 => unreachable,
45834641 .section => |shndx| shndx,
45844642 .input_section,
......@@ -4594,12 +4652,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
45944652 return switch (elf.getNode(ni)) {
45954653 .archive,
45964654 .archive_header,
4597 .archive_elf_footer,
4655 .archive_input_member,
4656 .archive_elf_member_header,
45984657 .elf,
45994658 .ehdr,
46004659 .shdr,
46014660 .segment,
4602 .input_member,
46034661 .copied_global,
46044662 => unreachable,
46054663 .section => |shndx| shndx.vaddr(elf),
......@@ -4613,14 +4671,18 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
46134671}
46144672fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
46154673 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
4616 .archive, .archive_header, .archive_elf_footer => unreachable,
4674 .archive,
4675 .archive_header,
4676 .archive_input_member,
4677 .archive_elf_member_header,
4678 => unreachable,
46174679 .elf => return 0,
46184680 .ehdr, .shdr => unreachable,
46194681 .segment => |phndx| switch (elf.phdrSlice()) {
46204682 inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
46214683 },
46224684 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
4623 .input_member, .input_section, .copied_global => unreachable,
4685 .input_section, .copied_global => unreachable,
46244686 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
46254687 };
46264688 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
......@@ -4639,12 +4701,12 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
46394701 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
46404702 .archive,
46414703 .archive_header,
4642 .archive_elf_footer,
4704 .archive_input_member,
4705 .archive_elf_member_header,
46434706 .elf,
46444707 .ehdr,
46454708 .shdr,
46464709 .segment,
4647 .input_member,
46484710 .copied_global,
46494711 => unreachable, // cannot contain relocs
46504712 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
......@@ -4705,7 +4767,12 @@ fn flushMovedNodeRelocs(
47054767 // changed, so update the `offset` field of the `ElfN.Rela` entry.
47064768 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
47074769 }
4708 reloc.apply(elf);
4770 // This is not just the inverse of the above condition, because if `reloc` is relative
4771 // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we
4772 // still need to call `SymbolReloc.apply` to update that relocation's addend.
4773 if (elf.ehdrType() != .REL) {
4774 reloc.apply(elf);
4775 }
47094776 }
47104777 }
47114778
......@@ -4971,16 +5038,6 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
49715038 }
49725039}
49735040
4974fn arHdrPtr(elf: *Elf, ni: MappedFile.Node.Index) *align(2) std.elf.ar_hdr {
4975 assert(elf.ni.elf != .root);
4976 const file_offset = ni.fileLocation(&elf.mf, false).offset;
4977 return @ptrCast(@alignCast(elf.mf.memory_map.memory[@intCast(switch (elf.getNode(ni)) {
4978 else => unreachable,
4979 .archive_header => file_offset + std.elf.ARMAG.len,
4980 .elf, .input_member => file_offset - @sizeOf(std.elf.ar_hdr),
4981 })..][0..@sizeOf(std.elf.ar_hdr)]));
4982}
4983
49845041const SymPtr = union(std.elf.CLASS) {
49855042 NONE: noreturn,
49865043 @"32": *std.elf.Elf32.Sym,
......@@ -5480,19 +5537,61 @@ fn loadObject(
54805537 .member = if (member) |m| try gpa.dupe(u8, m) else null,
54815538 .extra = undefined,
54825539 };
5483 if (elf.ni.elf != .root) {
5484 const archive_ni: MappedFile.Node.Index = .root;
5540 if (elf.archive) |*archive| {
5541 // We're creating a static library, so just add this input as an archive member.
5542 assert(member == null); // don't try to put static library members into other static libraries
5543
5544 const first_member_oni = archive.header_ni.next(&elf.mf);
5545
5546 if (first_member_oni.unwrap()) |first_member_ni| switch (elf.getNode(first_member_ni)) {
5547 .archive_input_member, .archive_elf_member_header => {},
5548 .elf => unreachable, // always preceded by `.archive_elf_member_header`
5549 else => unreachable, // never a child of `.archive`
5550 };
5551
54855552 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5486 input.extra = .{ .node = try archive_ni.addFloatingChild(&elf.mf, gpa, .{
5487 .size = Alignment.@"2".forward(fl.size + @sizeOf(std.elf.ar_hdr)),
5553 const new_member_ni = try archive.ni.addFooterChildBefore(&elf.mf, gpa, first_member_oni, .{
5554 .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size),
54885555 .alignment = .@"2",
5489 .next_moved = true,
5490 .bubbles_moved = false,
5491 .enable_next_moved = true,
5492 }) };
5493 elf.nodes.appendAssumeCapacity(.{ .input_member = input_index });
5556 });
5557 elf.nodes.appendAssumeCapacity(.{ .archive_input_member = input_index });
5558 input.extra = .{ .node = new_member_ni };
54945559 elf.input_prog_node.increaseEstimatedTotalItems(1);
54955560
5561 // The contents of the input will be written to the file by an idle task (`flushInput`), but
5562 // we do need to write the input's archive member header (`ar_hdr`) now, for two reasons:
5563 //
5564 // * If the input file has a long name, we need to add it to the archive member name string
5565 // table, which must happen deterministically (i.e. not in an idle task).
5566 //
5567 // * `flushInput` needs to know the actual file size (before padding to the alignment).
5568 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
5569 new_member_ni.slice(&elf.mf)[0..@sizeOf(std.elf.ar_hdr)],
5570 );
5571 member_ar_hdr.* = .{
5572 .ar_name = undefined, // populated below
5573 .ar_date = "0 ".*,
5574 .ar_uid = "0 ".*,
5575 .ar_gid = "0 ".*,
5576 .ar_mode = "644 ".*,
5577 .ar_size = undefined, // populated below
5578 .ar_fmag = std.elf.ARFMAG.*,
5579 };
5580
5581 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{fl.size})) |size_str| {
5582 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
5583 } else |err| switch (err) {
5584 error.NoSpaceLeft => return diags.failParse(
5585 path,
5586 "file size of {Bi} exceeds maximum size of archive member",
5587 .{fl.size},
5588 ),
5589 }
5590
5591 const member_name = std.fs.path.basename(path.sub_path);
5592 // After this call returns, `member_ar_hdr` is invalidated.
5593 try elf.populateArchiveMemberName(member_ar_hdr, member_name);
5594
54965595 // Since we are not emitting the archive symbol table (yet?) we do not need to parse
54975596 // the symbols in this input.
54985597 return;
......@@ -5905,6 +6004,46 @@ fn loadObject(
59056004 },
59066005 }
59076006}
6007/// This function may resize the archive header, so therefore invalidates `member_ar_hdr`.
6008fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_name: []const u8) Error!void {
6009 if (std.mem.print(&member_ar_hdr.ar_name, "{s}/", .{member_name})) |name_str| {
6010 @memset(member_ar_hdr.ar_name[name_str.len..], ' ');
6011 return;
6012 } else |err| switch (err) {
6013 error.NoSpaceLeft => {}, // handled below
6014 }
6015
6016 const gpa = elf.base.comp.gpa;
6017 const archive_header_ni = elf.archive.?.header_ni;
6018
6019 // The member's name is too big to put directly in the `ar_name` field, so it needs to go in the
6020 // "long name" string table instead (in the special member named "//").
6021
6022 _, const old_archive_header_size = archive_header_ni.location(&elf.mf).resolve(&elf.mf);
6023
6024 // We're going to add a new string at the end of the table. Update `member_ar_hdr` first,
6025 // because resizing the string table will invalidate it.
6026 const string_table_offset = old_archive_header_size - (std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr));
6027 if (std.mem.print(&member_ar_hdr.ar_name, "/{d}", .{string_table_offset})) |name_str| {
6028 @memset(member_ar_hdr.ar_name[name_str.len..], ' ');
6029 } else |inner_err| switch (inner_err) {
6030 error.NoSpaceLeft => {
6031 // The string table offset is itself too big to represent. This means the string table's
6032 // *size* is definitely too big to represent (we only get 10 bytes for that whereas we
6033 // get 16 here!), so as long as we still add the string, we're guaranteed to get a link
6034 // error for that reason. Therefore, we can just ignore this error and carry on.
6035 },
6036 }
6037
6038 // We set the size of the archive header node exactly, because we want padding bytes to go into
6039 // the root `.archive` node. That way, those bytes could still be used to grow the string table
6040 // if necessary, but they could also be used for new archive members.
6041 try archive_header_ni.resizeLeaf(&elf.mf, gpa, old_archive_header_size + member_name.len + 2);
6042
6043 const dest_slice = archive_header_ni.slice(&elf.mf)[old_archive_header_size..];
6044 @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name);
6045 @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird
6046}
59086047fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
59096048 const comp = elf.base.comp;
59106049 const gpa = comp.gpa;
......@@ -7072,12 +7211,12 @@ fn addGotRelocAssumeCapacity(
70727211 switch (elf.getNode(node)) {
70737212 .archive,
70747213 .archive_header,
7075 .archive_elf_footer,
7214 .archive_input_member,
7215 .archive_elf_member_header,
70767216 .elf,
70777217 .ehdr,
70787218 .shdr,
70797219 .segment,
7080 .input_member,
70817220 .copied_global,
70827221 => unreachable, // cannot contain relocs,
70837222 .section,
......@@ -7514,6 +7653,17 @@ fn flushInner(
75147653 diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count});
75157654 }
75167655
7656 if (elf.archive) |*archive| {
7657 if (archive.elf_member_too_big) diags.addError(
7658 "file size of {Bi} exceeds maximum size of archive member",
7659 .{elf.ni.elf.location(&elf.mf).resolve(&elf.mf)[1]},
7660 );
7661 if (archive.strtab_member_too_big) diags.addError(
7662 "archive file name string table exceeds maximum size",
7663 .{},
7664 );
7665 }
7666
75177667 elf.flushDynamic();
75187668
75197669 const entry_addr: u64 = entry: {
......@@ -7545,6 +7695,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
75457695 const comp = elf.base.comp;
75467696 const diags = &comp.link_diags;
75477697
7698 elf.mf.nodes_lock.lock();
7699 defer elf.mf.nodes_lock.unlock();
7700
75487701 assert(elf.pending_uavs.items.len == 0);
75497702 for (&elf.lazy.values) |*lazy| {
75507703 assert(lazy.pending_index == lazy.map.count());
......@@ -7698,7 +7851,7 @@ fn idleProgNode(
76987851 return prog_node.start(name: switch (node) {
76997852 else => |tag| @tagName(tag),
77007853 .section => |shndx| shndx.name(elf).slice(elf),
7701 .input_member => |ii| std.fmt.bufPrint(&name, "{f}{f}", .{
7854 .archive_input_member => |ii| std.fmt.bufPrint(&name, "{f}{f}", .{
77027855 ii.path(elf).fmtEscapeString(),
77037856 fmtMemberString(ii.member(elf)),
77047857 }) catch &name,
......@@ -7825,7 +7978,6 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {
78257978fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void {
78267979 const comp = elf.base.comp;
78277980 const io = comp.io;
7828 const gpa = comp.gpa;
78297981 const diags = &comp.link_diags;
78307982 const path = ii.path(elf);
78317983 const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) {
......@@ -7833,23 +7985,40 @@ fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void {
78337985 else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }),
78347986 };
78357987 defer file.close(io);
7988
7989 const slice = ii.node(elf).slice(&elf.mf);
7990
7991 const member_ar_hdr: *const std.elf.ar_hdr = @ptrCast(slice[0..@sizeOf(std.elf.ar_hdr)]);
7992 const input_size: u32 = member_ar_hdr.size() catch |err| switch (err) {
7993 // We wrote the `ar_hdr` ourselves (in `loadObject`), so it is definitely valid.
7994 error.Overflow, error.InvalidCharacter => unreachable,
7995 };
7996
7997 switch (slice.len - @sizeOf(std.elf.ar_hdr) - input_size) {
7998 0 => {},
7999 1 => {
8000 // Alignment added one padding byte, which the format requires to have value '\n'.
8001 slice[slice.len - 1] = '\n';
8002 },
8003 else => unreachable, // node size should agree with the value we wrote into `ar_hdr.ar_size`
8004 }
8005
78368006 var fr = file.reader(io, &.{});
7837 var nw: MappedFile.Node.Writer = undefined;
7838 ii.node(elf).writer(&elf.mf, gpa, &nw);
7839 defer nw.deinit();
7840 const size = nw.interface.buffer.len - @sizeOf(std.elf.ar_hdr);
7841 const n_bytes = nw.interface.sendFileAll(&fr, .limited(size)) catch |err| switch (err) {
8007 var w: Io.Writer = .fixed(slice[@sizeOf(std.elf.ar_hdr)..]);
8008 const n_bytes_read = w.sendFileAll(&fr, .limited(input_size)) catch |err| switch (err) {
78428009 error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{
78438010 path.fmtEscapeString(),
78448011 fmtMemberString(ii.member(elf)),
78458012 fr.err orelse (fr.seek_err orelse fr.size_err.?),
78468013 }),
7847 error.WriteFailed => return nw.err.?,
8014 error.WriteFailed => unreachable, // `.limited(input_size)` prevents us writing too many bytes
78488015 };
7849 if (n_bytes + 1 < size) return diags.fail("failed to read input \"{f}{f}\": unexpected eof", .{
7850 path.fmtEscapeString(),
7851 fmtMemberString(ii.member(elf)),
7852 });
8016 if (n_bytes_read != input_size) {
8017 return diags.fail("failed to load input \"{f}{f}\": file truncated during compilation", .{
8018 path.fmtEscapeString(),
8019 fmtMemberString(ii.member(elf)),
8020 });
8021 }
78538022}
78548023
78558024fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
......@@ -7931,12 +8100,18 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
79318100 const trace = tracy.trace(@src());
79328101 defer trace.end();
79338102
7934 elf.mf.nodes_lock.lock();
7935 defer elf.mf.nodes_lock.unlock();
7936
79378103 switch (elf.getNode(ni)) {
7938 .archive, .archive_header => unreachable,
7939 .archive_elf_footer, .elf => {},
8104 .archive => unreachable,
8105 .archive_header => unreachable,
8106
8107 .archive_input_member,
8108 .archive_elf_member_header,
8109 .elf,
8110 => {
8111 assert(elf.archive != null);
8112 return;
8113 },
8114
79408115 .ehdr, .shdr => elf.flushElfOffset(ni),
79418116 .segment => |phndx| {
79428117 elf.flushElfOffset(ni);
......@@ -8014,7 +8189,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
80148189 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
80158190 }
80168191 },
8017 .input_member => {},
80188192 .input_section => |isi| {
80198193 const old_section_addr = isi.ptr(elf).vaddr;
80208194 const new_section_addr = elf.computeNodeVAddr(ni);
......@@ -8213,24 +8387,23 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
82138387 const trace = tracy.trace(@src());
82148388 defer trace.end();
82158389
8216 elf.mf.nodes_lock.lock();
8217 defer elf.mf.nodes_lock.unlock();
8218
82198390 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
82208391 switch (elf.getNode(ni)) {
8221 .archive => {
8222 if (ni.last(&elf.mf).unwrap()) |last_ni| {
8223 if (last_ni.prev(&elf.mf).unwrap()) |prev_ni| {
8224 if (prev_ni.hasNextMoved(&elf.mf)) return;
8225 }
8226 const offset, _ = last_ni.location(&elf.mf).resolve(&elf.mf);
8227 _ = std.mem.print(&elf.arHdrPtr(last_ni).ar_size, "{d:<10}", .{
8228 size - offset,
8229 }) catch @panic("archive member too large");
8392 .archive, .archive_header => {},
8393 .archive_input_member => unreachable,
8394 .archive_elf_member_header => unreachable,
8395 .elf => if (elf.archive) |*archive| {
8396 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
8397 archive.elf_member_header_ni.slice(&elf.mf),
8398 );
8399 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{size})) |size_str| {
8400 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
8401 archive.elf_member_too_big = false;
8402 } else |err| switch (err) {
8403 error.NoSpaceLeft => archive.elf_member_too_big = true,
82308404 }
82318405 },
8232 .archive_header, .elf => {},
8233 .ehdr, .archive_elf_footer => unreachable,
8406 .ehdr => unreachable,
82348407 .shdr => {},
82358408 .segment => |phndx| switch (elf.phdrSlice()) {
82368409 inline else => |phdr| {
......@@ -8302,7 +8475,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
83028475 }
83038476 },
83048477 },
8305 .input_member, .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {},
8478 .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {},
83068479 }
83078480}
83088481
......@@ -8310,12 +8483,11 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
83108483 const trace = tracy.trace(@src());
83118484 defer trace.end();
83128485
8313 elf.mf.nodes_lock.lock();
8314 defer elf.mf.nodes_lock.unlock();
8315
83168486 switch (elf.getNode(ni)) {
83178487 .archive,
8318 .archive_elf_footer,
8488 .archive_input_member,
8489 .archive_elf_member_header,
8490 .elf,
83198491 .ehdr,
83208492 .shdr,
83218493 .segment,
......@@ -8327,54 +8499,32 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
83278499 .lazy_code,
83288500 .lazy_const_data,
83298501 => unreachable,
8330 .archive_header, .elf, .input_member => |_, tag| {
8331 const member_offset, const update_size = member_offset: {
8332 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
8333 break :member_offset switch (tag) {
8334 else => unreachable,
8335 .archive_header => .{ offset + std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), true },
8336 .elf, .input_member => .{ offset, !ni.prev(&elf.mf).unwrap().?.hasNextMoved(&elf.mf) },
8337 };
8338 };
8339 const member_size = if (ni.next(&elf.mf).unwrap()) |next_ni| member_size: {
8340 const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf);
8341 const next_member_size = if (next_ni.next(&elf.mf).unwrap()) |next_next_ni| next_member_size: {
8342 const next_next_offset, _ = next_next_ni.location(&elf.mf).resolve(&elf.mf);
8343 const next_member_end = next_next_offset - @sizeOf(std.elf.ar_hdr);
8344 break :next_member_size next_member_end - next_offset;
8345 } else next_member_size: {
8346 _, const parent_size = ni.parent(&elf.mf).unwrap().?.location(&elf.mf).resolve(&elf.mf);
8347 const next_member_end = parent_size;
8348 break :next_member_size next_member_end - next_offset;
8349 };
8350 const ar_hdr = elf.arHdrPtr(next_ni);
8351 var name_buf: [16]u8 = undefined;
8352 _ = std.mem.print(&ar_hdr.ar_name, "{s:<16}", .{
8353 switch (elf.getNode(next_ni)) {
8354 else => unreachable,
8355 .elf => std.mem.print(&name_buf, "{s}_zcu.o/", .{elf.base.comp.root_name}),
8356 .input_member => |ii| std.mem.print(&name_buf, "{s}/", .{
8357 std.fs.path.basename(ii.path(elf).sub_path),
8358 }),
8359 } catch @panic("TODO: long archive member names"),
8360 }) catch @panic("TODO: long archive member names");
8361 ar_hdr.ar_date = "0 ".*;
8362 ar_hdr.ar_uid = "0 ".*;
8363 ar_hdr.ar_gid = "0 ".*;
8364 ar_hdr.ar_mode = "644 ".*;
8365 _ = std.mem.print(&ar_hdr.ar_size, "{d:<10}", .{next_member_size}) catch
8366 @panic("archive member too large");
8367 ar_hdr.ar_fmag = std.elf.ARFMAG.*;
8368 const member_end = next_offset - @sizeOf(std.elf.ar_hdr);
8369 break :member_size member_end - member_offset;
8370 } else member_size: {
8371 _, const parent_size = ni.parent(&elf.mf).unwrap().?.location(&elf.mf).resolve(&elf.mf);
8372 const member_end = parent_size;
8373 break :member_size member_end - member_offset;
8502
8503 .archive_header => {
8504 const archive = &elf.archive.?;
8505
8506 // Because we can't just throw padding bytes in the middle of an archive file, we need
8507 // the member name string table (the "//" member) to absorb all the padding bytes
8508 // between it (in the `.archive_header` node) and the first actual member.
8509 const next_member_ni = ni.next(&elf.mf).unwrap() orelse {
8510 // I guess there are no link inputs yet? But there will be eventually!
8511 return;
83748512 };
8375 if (update_size) _ = std.mem.print(&elf.arHdrPtr(ni).ar_size, "{d:<10}", .{
8376 member_size,
8377 }) catch @panic("archive member too large");
8513 const next_member_offset: u64, _ = next_member_ni.location(&elf.mf).resolve(&elf.mf);
8514 const strtab_member_offset = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr);
8515 assert(Alignment.@"2".check(next_member_offset));
8516 assert(Alignment.@"2".check(strtab_member_offset));
8517 const strtab_size = next_member_offset - strtab_member_offset;
8518
8519 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
8520 archive.header_ni.slice(&elf.mf)[std.elf.ARMAG.len..][0..@sizeOf(std.elf.ar_hdr)],
8521 );
8522 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{strtab_size})) |size_str| {
8523 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
8524 archive.strtab_member_too_big = false;
8525 } else |err| switch (err) {
8526 error.NoSpaceLeft => archive.strtab_member_too_big = true,
8527 }
83788528 },
83798529 }
83808530}