| ... | ... | @@ -24,6 +24,7 @@ base: link.File, |
| 24 | 24 | options: link.File.OpenOptions, |
| 25 | 25 | mf: MappedFile, |
| 26 | 26 | ni: Node.Known, |
| 27 | archive: ?Archive, |
| 27 | 28 | nodes: std.MultiArrayList(Node), |
| 28 | 29 | /// Does not contain an item for `SHN_UNDEF`. |
| 29 | 30 | shdrs: std.ArrayList(Section), |
| ... | ... | @@ -200,19 +201,39 @@ input_prog_node: std.Progress.Node, |
| 200 | 201 | const Error = link.Error || error{MappedFileIo}; |
| 201 | 202 | |
| 202 | 203 | const 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). |
| 203 | 215 | 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 ("//"). |
| 205 | 220 | 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 | |
| 208 | 231 | elf, |
| 209 | 232 | ehdr, |
| 210 | 233 | shdr, |
| 211 | 234 | segment: u32, |
| 212 | 235 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. |
| 213 | 236 | section: Section.Index, |
| 214 | | /// Only valid for static libraries, represents one non-zcu archive member. |
| 215 | | input_member: InputIndex, |
| 216 | 237 | /// May contain relocations. |
| 217 | 238 | input_section: InputSection.Index, |
| 218 | 239 | /// 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 { |
| 404 | 425 | }; |
| 405 | 426 | }; |
| 406 | 427 | |
| 428 | const 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 | |
| 407 | 437 | const Section = struct { |
| 408 | 438 | /// The node corresponding to this section. |
| 409 | 439 | ni: MappedFile.Node.Index, |
| ... | ... | @@ -2954,13 +2984,13 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| 2954 | 2984 | const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { |
| 2955 | 2985 | .archive, |
| 2956 | 2986 | .archive_header, |
| 2957 | | .archive_elf_footer, |
| 2987 | .archive_input_member, |
| 2988 | .archive_elf_member_header, |
| 2958 | 2989 | .elf, |
| 2959 | 2990 | .ehdr, |
| 2960 | 2991 | .shdr, |
| 2961 | 2992 | .segment, |
| 2962 | 2993 | .section, |
| 2963 | | .input_member, |
| 2964 | 2994 | .input_section, |
| 2965 | 2995 | .copied_global, |
| 2966 | 2996 | => unreachable, |
| ... | ... | @@ -3364,6 +3394,7 @@ fn create( |
| 3364 | 3394 | .data_rel_ro = undefined, |
| 3365 | 3395 | .tls = .none, |
| 3366 | 3396 | }, |
| 3397 | .archive = null, |
| 3367 | 3398 | .nodes = .empty, |
| 3368 | 3399 | .shdrs = .empty, |
| 3369 | 3400 | .phdrs = .empty, |
| ... | ... | @@ -3602,7 +3633,7 @@ fn initHeaders( |
| 3602 | 3633 | }, phnum }; |
| 3603 | 3634 | }; |
| 3604 | 3635 | |
| 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 |
| 3606 | 3637 | 3 + // `.elf`, `.ehdr`, and `.shdr` nodes |
| 3607 | 3638 | (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node |
| 3608 | 3639 | (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node |
| ... | ... | @@ -3619,11 +3650,14 @@ fn initHeaders( |
| 3619 | 3650 | const archive_ni: MappedFile.Node.Index = .root; |
| 3620 | 3651 | |
| 3621 | 3652 | 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. |
| 3626 | 3659 | .enable_next_moved = true, |
| 3660 | .next_moved = true, |
| 3627 | 3661 | }); |
| 3628 | 3662 | elf.nodes.appendAssumeCapacity(.archive_header); |
| 3629 | 3663 | const archive_header_slice = archive_header_ni.slice(&elf.mf); |
| ... | ... | @@ -3635,23 +3669,47 @@ fn initHeaders( |
| 3635 | 3669 | .ar_uid = @splat(' '), |
| 3636 | 3670 | .ar_gid = @splat(' '), |
| 3637 | 3671 | .ar_mode = @splat(' '), |
| 3638 | | .ar_size = @splat(' '), |
| 3672 | .ar_size = undefined, // populated by `flushNextMoved` for `archive_header_ni` |
| 3639 | 3673 | .ar_fmag = std.elf.ARFMAG.*, |
| 3640 | 3674 | }; |
| 3641 | 3675 | |
| 3642 | | elf.ni.elf = try archive_ni.addFloatingChild(&elf.mf, gpa, .{ |
| 3676 | elf.ni.elf = try archive_ni.addOnlyFooterChild(&elf.mf, gpa, .{ |
| 3643 | 3677 | .alignment = node_block_align.max(.@"2"), |
| 3644 | | .next_moved = true, |
| 3645 | 3678 | .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 |
| 3647 | 3680 | }); |
| 3648 | 3681 | elf.nodes.appendAssumeCapacity(.elf); |
| 3649 | 3682 | |
| 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), .{ |
| 3651 | 3684 | .alignment = .@"2", |
| 3652 | 3685 | .size = @sizeOf(std.elf.ar_hdr), |
| 3653 | 3686 | }); |
| 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); |
| 3655 | 3713 | } else { |
| 3656 | 3714 | elf.ni.elf = .root; |
| 3657 | 3715 | elf.nodes.appendAssumeCapacity(.elf); |
| ... | ... | @@ -4573,12 +4631,12 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { |
| 4573 | 4631 | return switch (elf.getNode(ni)) { |
| 4574 | 4632 | .archive, |
| 4575 | 4633 | .archive_header, |
| 4576 | | .archive_elf_footer, |
| 4634 | .archive_input_member, |
| 4635 | .archive_elf_member_header, |
| 4577 | 4636 | .elf, |
| 4578 | 4637 | .ehdr, |
| 4579 | 4638 | .shdr, |
| 4580 | 4639 | .segment, |
| 4581 | | .input_member, |
| 4582 | 4640 | => unreachable, |
| 4583 | 4641 | .section => |shndx| shndx, |
| 4584 | 4642 | .input_section, |
| ... | ... | @@ -4594,12 +4652,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4594 | 4652 | return switch (elf.getNode(ni)) { |
| 4595 | 4653 | .archive, |
| 4596 | 4654 | .archive_header, |
| 4597 | | .archive_elf_footer, |
| 4655 | .archive_input_member, |
| 4656 | .archive_elf_member_header, |
| 4598 | 4657 | .elf, |
| 4599 | 4658 | .ehdr, |
| 4600 | 4659 | .shdr, |
| 4601 | 4660 | .segment, |
| 4602 | | .input_member, |
| 4603 | 4661 | .copied_global, |
| 4604 | 4662 | => unreachable, |
| 4605 | 4663 | .section => |shndx| shndx.vaddr(elf), |
| ... | ... | @@ -4613,14 +4671,18 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4613 | 4671 | } |
| 4614 | 4672 | fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4615 | 4673 | 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, |
| 4617 | 4679 | .elf => return 0, |
| 4618 | 4680 | .ehdr, .shdr => unreachable, |
| 4619 | 4681 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 4620 | 4682 | inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), |
| 4621 | 4683 | }, |
| 4622 | 4684 | .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, |
| 4624 | 4686 | inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 4625 | 4687 | }; |
| 4626 | 4688 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| ... | ... | @@ -4639,12 +4701,12 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 4639 | 4701 | const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { |
| 4640 | 4702 | .archive, |
| 4641 | 4703 | .archive_header, |
| 4642 | | .archive_elf_footer, |
| 4704 | .archive_input_member, |
| 4705 | .archive_elf_member_header, |
| 4643 | 4706 | .elf, |
| 4644 | 4707 | .ehdr, |
| 4645 | 4708 | .shdr, |
| 4646 | 4709 | .segment, |
| 4647 | | .input_member, |
| 4648 | 4710 | .copied_global, |
| 4649 | 4711 | => unreachable, // cannot contain relocs |
| 4650 | 4712 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| ... | ... | @@ -4705,7 +4767,12 @@ fn flushMovedNodeRelocs( |
| 4705 | 4767 | // changed, so update the `offset` field of the `ElfN.Rela` entry. |
| 4706 | 4768 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 4707 | 4769 | } |
| 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 | } |
| 4709 | 4776 | } |
| 4710 | 4777 | } |
| 4711 | 4778 | |
| ... | ... | @@ -4971,16 +5038,6 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr { |
| 4971 | 5038 | } |
| 4972 | 5039 | } |
| 4973 | 5040 | |
| 4974 | | fn 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 | | |
| 4984 | 5041 | const SymPtr = union(std.elf.CLASS) { |
| 4985 | 5042 | NONE: noreturn, |
| 4986 | 5043 | @"32": *std.elf.Elf32.Sym, |
| ... | ... | @@ -5480,19 +5537,61 @@ fn loadObject( |
| 5480 | 5537 | .member = if (member) |m| try gpa.dupe(u8, m) else null, |
| 5481 | 5538 | .extra = undefined, |
| 5482 | 5539 | }; |
| 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 | |
| 5485 | 5552 | 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), |
| 5488 | 5555 | .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 }; |
| 5494 | 5559 | elf.input_prog_node.increaseEstimatedTotalItems(1); |
| 5495 | 5560 | |
| 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 | |
| 5496 | 5595 | // Since we are not emitting the archive symbol table (yet?) we do not need to parse |
| 5497 | 5596 | // the symbols in this input. |
| 5498 | 5597 | return; |
| ... | ... | @@ -5905,6 +6004,46 @@ fn loadObject( |
| 5905 | 6004 | }, |
| 5906 | 6005 | } |
| 5907 | 6006 | } |
| 6007 | /// This function may resize the archive header, so therefore invalidates `member_ar_hdr`. |
| 6008 | fn 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 | } |
| 5908 | 6047 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { |
| 5909 | 6048 | const comp = elf.base.comp; |
| 5910 | 6049 | const gpa = comp.gpa; |
| ... | ... | @@ -7072,12 +7211,12 @@ fn addGotRelocAssumeCapacity( |
| 7072 | 7211 | switch (elf.getNode(node)) { |
| 7073 | 7212 | .archive, |
| 7074 | 7213 | .archive_header, |
| 7075 | | .archive_elf_footer, |
| 7214 | .archive_input_member, |
| 7215 | .archive_elf_member_header, |
| 7076 | 7216 | .elf, |
| 7077 | 7217 | .ehdr, |
| 7078 | 7218 | .shdr, |
| 7079 | 7219 | .segment, |
| 7080 | | .input_member, |
| 7081 | 7220 | .copied_global, |
| 7082 | 7221 | => unreachable, // cannot contain relocs, |
| 7083 | 7222 | .section, |
| ... | ... | @@ -7514,6 +7653,17 @@ fn flushInner( |
| 7514 | 7653 | diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count}); |
| 7515 | 7654 | } |
| 7516 | 7655 | |
| 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 | |
| 7517 | 7667 | elf.flushDynamic(); |
| 7518 | 7668 | |
| 7519 | 7669 | const entry_addr: u64 = entry: { |
| ... | ... | @@ -7545,6 +7695,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7545 | 7695 | const comp = elf.base.comp; |
| 7546 | 7696 | const diags = &comp.link_diags; |
| 7547 | 7697 | |
| 7698 | elf.mf.nodes_lock.lock(); |
| 7699 | defer elf.mf.nodes_lock.unlock(); |
| 7700 | |
| 7548 | 7701 | assert(elf.pending_uavs.items.len == 0); |
| 7549 | 7702 | for (&elf.lazy.values) |*lazy| { |
| 7550 | 7703 | assert(lazy.pending_index == lazy.map.count()); |
| ... | ... | @@ -7698,7 +7851,7 @@ fn idleProgNode( |
| 7698 | 7851 | return prog_node.start(name: switch (node) { |
| 7699 | 7852 | else => |tag| @tagName(tag), |
| 7700 | 7853 | .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}", .{ |
| 7702 | 7855 | ii.path(elf).fmtEscapeString(), |
| 7703 | 7856 | fmtMemberString(ii.member(elf)), |
| 7704 | 7857 | }) catch &name, |
| ... | ... | @@ -7825,7 +7978,6 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { |
| 7825 | 7978 | fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void { |
| 7826 | 7979 | const comp = elf.base.comp; |
| 7827 | 7980 | const io = comp.io; |
| 7828 | | const gpa = comp.gpa; |
| 7829 | 7981 | const diags = &comp.link_diags; |
| 7830 | 7982 | const path = ii.path(elf); |
| 7831 | 7983 | 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 { |
| 7833 | 7985 | else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }), |
| 7834 | 7986 | }; |
| 7835 | 7987 | 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 | |
| 7836 | 8006 | 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) { |
| 7842 | 8009 | error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{ |
| 7843 | 8010 | path.fmtEscapeString(), |
| 7844 | 8011 | fmtMemberString(ii.member(elf)), |
| 7845 | 8012 | fr.err orelse (fr.seek_err orelse fr.size_err.?), |
| 7846 | 8013 | }), |
| 7847 | | error.WriteFailed => return nw.err.?, |
| 8014 | error.WriteFailed => unreachable, // `.limited(input_size)` prevents us writing too many bytes |
| 7848 | 8015 | }; |
| 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 | } |
| 7853 | 8022 | } |
| 7854 | 8023 | |
| 7855 | 8024 | fn 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 |
| 7931 | 8100 | const trace = tracy.trace(@src()); |
| 7932 | 8101 | defer trace.end(); |
| 7933 | 8102 | |
| 7934 | | elf.mf.nodes_lock.lock(); |
| 7935 | | defer elf.mf.nodes_lock.unlock(); |
| 7936 | | |
| 7937 | 8103 | 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 | |
| 7940 | 8115 | .ehdr, .shdr => elf.flushElfOffset(ni), |
| 7941 | 8116 | .segment => |phndx| { |
| 7942 | 8117 | elf.flushElfOffset(ni); |
| ... | ... | @@ -8014,7 +8189,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 8014 | 8189 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 8015 | 8190 | } |
| 8016 | 8191 | }, |
| 8017 | | .input_member => {}, |
| 8018 | 8192 | .input_section => |isi| { |
| 8019 | 8193 | const old_section_addr = isi.ptr(elf).vaddr; |
| 8020 | 8194 | const new_section_addr = elf.computeNodeVAddr(ni); |
| ... | ... | @@ -8213,24 +8387,23 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 8213 | 8387 | const trace = tracy.trace(@src()); |
| 8214 | 8388 | defer trace.end(); |
| 8215 | 8389 | |
| 8216 | | elf.mf.nodes_lock.lock(); |
| 8217 | | defer elf.mf.nodes_lock.unlock(); |
| 8218 | | |
| 8219 | 8390 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); |
| 8220 | 8391 | 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, |
| 8230 | 8404 | } |
| 8231 | 8405 | }, |
| 8232 | | .archive_header, .elf => {}, |
| 8233 | | .ehdr, .archive_elf_footer => unreachable, |
| 8406 | .ehdr => unreachable, |
| 8234 | 8407 | .shdr => {}, |
| 8235 | 8408 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 8236 | 8409 | inline else => |phdr| { |
| ... | ... | @@ -8302,7 +8475,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 8302 | 8475 | } |
| 8303 | 8476 | }, |
| 8304 | 8477 | }, |
| 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 => {}, |
| 8306 | 8479 | } |
| 8307 | 8480 | } |
| 8308 | 8481 | |
| ... | ... | @@ -8310,12 +8483,11 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! |
| 8310 | 8483 | const trace = tracy.trace(@src()); |
| 8311 | 8484 | defer trace.end(); |
| 8312 | 8485 | |
| 8313 | | elf.mf.nodes_lock.lock(); |
| 8314 | | defer elf.mf.nodes_lock.unlock(); |
| 8315 | | |
| 8316 | 8486 | switch (elf.getNode(ni)) { |
| 8317 | 8487 | .archive, |
| 8318 | | .archive_elf_footer, |
| 8488 | .archive_input_member, |
| 8489 | .archive_elf_member_header, |
| 8490 | .elf, |
| 8319 | 8491 | .ehdr, |
| 8320 | 8492 | .shdr, |
| 8321 | 8493 | .segment, |
| ... | ... | @@ -8327,54 +8499,32 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! |
| 8327 | 8499 | .lazy_code, |
| 8328 | 8500 | .lazy_const_data, |
| 8329 | 8501 | => 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; |
| 8374 | 8512 | }; |
| 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 | } |
| 8378 | 8528 | }, |
| 8379 | 8529 | } |
| 8380 | 8530 | } |