| ... | @@ -24,6 +24,7 @@ base: link.File, | ... | @@ -24,6 +24,7 @@ base: link.File, |
| 24 | options: link.File.OpenOptions, | 24 | options: link.File.OpenOptions, |
| 25 | mf: MappedFile, | 25 | mf: MappedFile, |
| 26 | ni: Node.Known, | 26 | ni: Node.Known, |
| | 27 | archive: ?Archive, |
| 27 | nodes: std.MultiArrayList(Node), | 28 | nodes: std.MultiArrayList(Node), |
| 28 | /// Does not contain an item for `SHN_UNDEF`. | 29 | /// Does not contain an item for `SHN_UNDEF`. |
| 29 | shdrs: std.ArrayList(Section), | 30 | shdrs: std.ArrayList(Section), |
| ... | @@ -200,19 +201,39 @@ input_prog_node: std.Progress.Node, | ... | @@ -200,19 +201,39 @@ input_prog_node: std.Progress.Node, |
| 200 | const Error = link.Error || error{MappedFileIo}; | 201 | const Error = link.Error || error{MappedFileIo}; |
| 201 | | 202 | |
| 202 | const Node = union(enum) { | 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 | archive, | 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 | archive_header, | 220 | archive_header, |
| 206 | /// This is a footer of the `.elf` node, and contains the next archive entry's file header. | 221 | /// Only used when emitting a static library. |
| 207 | archive_elf_footer, | 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 | elf, | 231 | elf, |
| 209 | ehdr, | 232 | ehdr, |
| 210 | shdr, | 233 | shdr, |
| 211 | segment: u32, | 234 | segment: u32, |
| 212 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. | 235 | /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. |
| 213 | section: Section.Index, | 236 | section: Section.Index, |
| 214 | /// Only valid for static libraries, represents one non-zcu archive member. | | |
| 215 | input_member: InputIndex, | | |
| 216 | /// May contain relocations. | 237 | /// May contain relocations. |
| 217 | input_section: InputSection.Index, | 238 | input_section: InputSection.Index, |
| 218 | /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for | 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,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 | const Section = struct { | 437 | const Section = struct { |
| 408 | /// The node corresponding to this section. | 438 | /// The node corresponding to this section. |
| 409 | ni: MappedFile.Node.Index, | 439 | ni: MappedFile.Node.Index, |
| ... | @@ -2954,13 +2984,13 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { | ... | @@ -2954,13 +2984,13 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| 2954 | const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { | 2984 | const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { |
| 2955 | .archive, | 2985 | .archive, |
| 2956 | .archive_header, | 2986 | .archive_header, |
| 2957 | .archive_elf_footer, | 2987 | .archive_input_member, |
| | 2988 | .archive_elf_member_header, |
| 2958 | .elf, | 2989 | .elf, |
| 2959 | .ehdr, | 2990 | .ehdr, |
| 2960 | .shdr, | 2991 | .shdr, |
| 2961 | .segment, | 2992 | .segment, |
| 2962 | .section, | 2993 | .section, |
| 2963 | .input_member, | | |
| 2964 | .input_section, | 2994 | .input_section, |
| 2965 | .copied_global, | 2995 | .copied_global, |
| 2966 | => unreachable, | 2996 | => unreachable, |
| ... | @@ -3364,6 +3394,7 @@ fn create( | ... | @@ -3364,6 +3394,7 @@ fn create( |
| 3364 | .data_rel_ro = undefined, | 3394 | .data_rel_ro = undefined, |
| 3365 | .tls = .none, | 3395 | .tls = .none, |
| 3366 | }, | 3396 | }, |
| | 3397 | .archive = null, |
| 3367 | .nodes = .empty, | 3398 | .nodes = .empty, |
| 3368 | .shdrs = .empty, | 3399 | .shdrs = .empty, |
| 3369 | .phdrs = .empty, | 3400 | .phdrs = .empty, |
| ... | @@ -3558,51 +3589,58 @@ fn initHeaders( | ... | @@ -3558,51 +3589,58 @@ fn initHeaders( |
| 3558 | .EXEC, .DYN => {}, | 3589 | .EXEC, .DYN => {}, |
| 3559 | } | 3590 | } |
| 3560 | var phnum: u32 = 0; | 3591 | var phnum: u32 = 0; |
| 3561 | break :ph .{ .{ | 3592 | break :ph .{ |
| 3562 | .phdr = phndx: { | 3593 | .{ |
| 3563 | defer phnum += 1; | 3594 | .phdr = phndx: { |
| 3564 | break :phndx phnum; | 3595 | defer phnum += 1; |
| 3565 | }, | 3596 | break :phndx phnum; |
| 3566 | .interp = if (maybe_interp) |_| phndx: { | 3597 | }, |
| 3567 | defer phnum += 1; | 3598 | .interp = if (maybe_interp) |_| phndx: { |
| 3568 | break :phndx phnum; | 3599 | defer phnum += 1; |
| 3569 | } else undefined, | 3600 | break :phndx phnum; |
| 3570 | .rodata = phndx: { | 3601 | } else undefined, |
| 3571 | defer phnum += 1; | 3602 | .rodata = phndx: { |
| 3572 | break :phndx phnum; | 3603 | defer phnum += 1; |
| 3573 | }, | 3604 | break :phndx phnum; |
| 3574 | .text = phndx: { | 3605 | }, |
| 3575 | defer phnum += 1; | 3606 | .text = phndx: { |
| 3576 | break :phndx phnum; | 3607 | defer phnum += 1; |
| 3577 | }, | 3608 | break :phndx phnum; |
| 3578 | .data = phndx: { | 3609 | }, |
| 3579 | defer phnum += 1; | 3610 | .plt = if (plt.got_plt == null) phndx: { |
| 3580 | break :phndx phnum; | 3611 | defer phnum += 1; |
| 3581 | }, | 3612 | break :phndx phnum; |
| 3582 | .plt = if (plt.got_plt == null) phndx: { | 3613 | } else undefined, |
| 3583 | defer phnum += 1; | 3614 | // `data` must be assigned after all other loadable segments so that it has the greatest |
| 3584 | break :phndx phnum; | 3615 | // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions` |
| 3585 | } else undefined, | 3616 | // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC). |
| 3586 | .tls = if (comp.config.any_non_single_threaded) phndx: { | 3617 | .data = phndx: { |
| 3587 | defer phnum += 1; | 3618 | defer phnum += 1; |
| 3588 | break :phndx phnum; | 3619 | break :phndx phnum; |
| 3589 | } else undefined, | 3620 | }, |
| 3590 | .dynamic = if (have_dynamic_section) phndx: { | 3621 | .tls = if (comp.config.any_non_single_threaded) phndx: { |
| 3591 | defer phnum += 1; | 3622 | defer phnum += 1; |
| 3592 | break :phndx phnum; | 3623 | break :phndx phnum; |
| 3593 | } else undefined, | 3624 | } else undefined, |
| 3594 | .relro = phndx: { | 3625 | .dynamic = if (have_dynamic_section) phndx: { |
| 3595 | defer phnum += 1; | 3626 | defer phnum += 1; |
| 3596 | break :phndx phnum; | 3627 | break :phndx phnum; |
| 3597 | }, | 3628 | } else undefined, |
| 3598 | .gnu_stack = phndx: { | 3629 | .relro = phndx: { |
| 3599 | defer phnum += 1; | 3630 | defer phnum += 1; |
| 3600 | break :phndx phnum; | 3631 | break :phndx phnum; |
| | 3632 | }, |
| | 3633 | .gnu_stack = phndx: { |
| | 3634 | defer phnum += 1; |
| | 3635 | break :phndx phnum; |
| | 3636 | }, |
| 3601 | }, | 3637 | }, |
| 3602 | }, phnum }; | 3638 | // (I don't actually want the trailing comma below, but a `zig fmt` bug forces it.) |
| | 3639 | phnum, |
| | 3640 | }; |
| 3603 | }; | 3641 | }; |
| 3604 | | 3642 | |
| 3605 | const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_footer | 3643 | const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header |
| 3606 | 3 + // `.elf`, `.ehdr`, and `.shdr` nodes | 3644 | 3 + // `.elf`, `.ehdr`, and `.shdr` nodes |
| 3607 | (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node | 3645 | (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node |
| 3608 | (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node | 3646 | (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node |
| ... | @@ -3619,11 +3657,14 @@ fn initHeaders( | ... | @@ -3619,11 +3657,14 @@ fn initHeaders( |
| 3619 | const archive_ni: MappedFile.Node.Index = .root; | 3657 | const archive_ni: MappedFile.Node.Index = .root; |
| 3620 | | 3658 | |
| 3621 | const archive_header_ni = try archive_ni.addOnlyHeaderChild(&elf.mf, gpa, .{ | 3659 | const archive_header_ni = try archive_ni.addOnlyHeaderChild(&elf.mf, gpa, .{ |
| 3622 | .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr) * 2, | 3660 | // We intentionally do not set `.alignment = .@"2"` here, because the string table data |
| 3623 | .alignment = .@"2", | 3661 | // in this node does not need to have an aligned length. (This node's offset is aligned |
| 3624 | .next_moved = true, | 3662 | // regardless by virtue of it being a header.) |
| 3625 | .bubbles_moved = false, | 3663 | .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), |
| | 3664 | // The archive header uses 'next_moved' events to resize the "//" member, so that it |
| | 3665 | // absorbs all padding between `archive_header_ni` and the actual object file members. |
| 3626 | .enable_next_moved = true, | 3666 | .enable_next_moved = true, |
| | 3667 | .next_moved = true, |
| 3627 | }); | 3668 | }); |
| 3628 | elf.nodes.appendAssumeCapacity(.archive_header); | 3669 | elf.nodes.appendAssumeCapacity(.archive_header); |
| 3629 | const archive_header_slice = archive_header_ni.slice(&elf.mf); | 3670 | const archive_header_slice = archive_header_ni.slice(&elf.mf); |
| ... | @@ -3635,23 +3676,47 @@ fn initHeaders( | ... | @@ -3635,23 +3676,47 @@ fn initHeaders( |
| 3635 | .ar_uid = @splat(' '), | 3676 | .ar_uid = @splat(' '), |
| 3636 | .ar_gid = @splat(' '), | 3677 | .ar_gid = @splat(' '), |
| 3637 | .ar_mode = @splat(' '), | 3678 | .ar_mode = @splat(' '), |
| 3638 | .ar_size = @splat(' '), | 3679 | .ar_size = undefined, // populated by `flushNextMoved` for `archive_header_ni` |
| 3639 | .ar_fmag = std.elf.ARFMAG.*, | 3680 | .ar_fmag = std.elf.ARFMAG.*, |
| 3640 | }; | 3681 | }; |
| 3641 | | 3682 | |
| 3642 | elf.ni.elf = try archive_ni.addFloatingChild(&elf.mf, gpa, .{ | 3683 | elf.ni.elf = try archive_ni.addOnlyFooterChild(&elf.mf, gpa, .{ |
| 3643 | .alignment = node_block_align.max(.@"2"), | 3684 | .alignment = node_block_align.max(.@"2"), |
| 3644 | .next_moved = true, | | |
| 3645 | .bubbles_moved = false, | 3685 | .bubbles_moved = false, |
| 3646 | .enable_next_moved = true, | 3686 | .resized = true, // ensure that this node's `ar_hdr.ar_size` is updated at least once |
| 3647 | }); | 3687 | }); |
| 3648 | elf.nodes.appendAssumeCapacity(.elf); | 3688 | elf.nodes.appendAssumeCapacity(.elf); |
| 3649 | | 3689 | |
| 3650 | _ = try elf.ni.elf.addOnlyFooterChild(&elf.mf, gpa, .{ | 3690 | const elf_ar_hdr_ni = try archive_ni.addFooterChildBefore(&elf.mf, gpa, .wrap(elf.ni.elf), .{ |
| 3651 | .alignment = .@"2", | 3691 | .alignment = .@"2", |
| 3652 | .size = @sizeOf(std.elf.ar_hdr), | 3692 | .size = @sizeOf(std.elf.ar_hdr), |
| 3653 | }); | 3693 | }); |
| 3654 | elf.nodes.appendAssumeCapacity(.archive_elf_footer); | 3694 | elf.nodes.appendAssumeCapacity(.archive_elf_member_header); |
| | 3695 | |
| | 3696 | // Must be populated before we call `populateArchiveMemberName` below. |
| | 3697 | elf.archive = .{ |
| | 3698 | .ni = archive_ni, |
| | 3699 | .header_ni = archive_header_ni, |
| | 3700 | .elf_member_header_ni = elf_ar_hdr_ni, |
| | 3701 | |
| | 3702 | .elf_member_too_big = false, |
| | 3703 | .strtab_member_too_big = false, |
| | 3704 | }; |
| | 3705 | |
| | 3706 | const elf_ar_hdr: *std.elf.ar_hdr = @ptrCast(elf_ar_hdr_ni.slice(&elf.mf)); |
| | 3707 | elf_ar_hdr.* = .{ |
| | 3708 | .ar_name = undefined, // populated below |
| | 3709 | .ar_date = "0 ".*, |
| | 3710 | .ar_uid = "0 ".*, |
| | 3711 | .ar_gid = "0 ".*, |
| | 3712 | .ar_mode = "644 ".*, |
| | 3713 | .ar_size = undefined, // populated by `flushResized` for the `.elf` node |
| | 3714 | .ar_fmag = std.elf.ARFMAG.*, |
| | 3715 | }; |
| | 3716 | const zcu_member_name = try std.fmt.allocPrint(gpa, "{s}_zcu.o", .{comp.root_name}); |
| | 3717 | defer gpa.free(zcu_member_name); |
| | 3718 | // After this call returns, `elf_ar_hdr` is invalidated. |
| | 3719 | try elf.populateArchiveMemberName(elf_ar_hdr, zcu_member_name); |
| 3655 | } else { | 3720 | } else { |
| 3656 | elf.ni.elf = .root; | 3721 | elf.ni.elf = .root; |
| 3657 | elf.nodes.appendAssumeCapacity(.elf); | 3722 | elf.nodes.appendAssumeCapacity(.elf); |
| ... | @@ -4092,33 +4157,44 @@ fn initHeaders( | ... | @@ -4092,33 +4157,44 @@ fn initHeaders( |
| 4092 | .addralign = addr_align, | 4157 | .addralign = addr_align, |
| 4093 | .entsize = @intCast(addr_align.toByteUnits()), | 4158 | .entsize = @intCast(addr_align.toByteUnits()), |
| 4094 | }); | 4159 | }); |
| 4095 | if (plt.got_plt) |got_plt| { | 4160 | { |
| 4096 | const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data; | 4161 | const init_plt_size = plt.entry_size * plt.header_entries; |
| 4097 | elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{ | 4162 | if (plt.got_plt) |got_plt| { |
| 4098 | .name = ".got.plt", | 4163 | const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data; |
| 4099 | .type = .PROGBITS, | 4164 | elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{ |
| 4100 | .flags = .{ .WRITE = true, .ALLOC = true }, | 4165 | .name = ".got.plt", |
| 4101 | .size = got_plt.header_entries * elf.targetPtrSize(), | 4166 | .type = .PROGBITS, |
| 4102 | .addralign = addr_align, | 4167 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 4103 | .entsize = @intCast(addr_align.toByteUnits()), | 4168 | .size = got_plt.header_entries * elf.targetPtrSize(), |
| 4104 | }); | 4169 | .addralign = addr_align, |
| 4105 | elf.shndx.plt = try elf.addSection(elf.ni.text, .{ | 4170 | .entsize = @intCast(addr_align.toByteUnits()), |
| 4106 | .name = ".plt", | 4171 | }); |
| 4107 | .type = .PROGBITS, | 4172 | elf.shndx.plt = try elf.addSection(elf.ni.text, .{ |
| 4108 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, | 4173 | .name = ".plt", |
| 4109 | .size = plt.entry_size * plt.header_entries, | 4174 | .type = .PROGBITS, |
| 4110 | .addralign = plt.@"align", | 4175 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 4111 | .node_align = node_block_align, | 4176 | .size = plt.@"align".forward(init_plt_size), |
| 4112 | }); | 4177 | .addralign = plt.@"align", |
| 4113 | } else { | 4178 | .node_align = node_block_align, |
| 4114 | elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{ | 4179 | }); |
| 4115 | .name = ".plt", | 4180 | } else { |
| 4116 | .type = .PROGBITS, | 4181 | elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{ |
| 4117 | .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true }, | 4182 | .name = ".plt", |
| 4118 | .size = plt.entry_size * plt.header_entries, | 4183 | .type = .PROGBITS, |
| 4119 | .addralign = plt.@"align", | 4184 | .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true }, |
| 4120 | .node_align = node_block_align, | 4185 | .size = plt.@"align".forward(init_plt_size), |
| 4121 | }); | 4186 | .addralign = plt.@"align", |
| | 4187 | .node_align = node_block_align, |
| | 4188 | }); |
| | 4189 | } |
| | 4190 | // And the award for most annoying PLT requirement goes to SPARC, which decided that the |
| | 4191 | // whole table should have a greater alignment than the size of the individual entries, |
| | 4192 | // hence this bullshit: |
| | 4193 | if (plt.@"align".forward(init_plt_size) != init_plt_size) { |
| | 4194 | switch (elf.shdrPtr(elf.shndx.plt)) { |
| | 4195 | inline else => |shdr| elf.targetStore(&shdr.size, init_plt_size), |
| | 4196 | } |
| | 4197 | } |
| 4122 | } | 4198 | } |
| 4123 | if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{ | 4199 | if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{ |
| 4124 | .name = ".plt.sec", | 4200 | .name = ".plt.sec", |
| ... | @@ -4358,6 +4434,12 @@ fn initHeaders( | ... | @@ -4358,6 +4434,12 @@ fn initHeaders( |
| 4358 | assert(elf.targetLoad(&shdr.size) == elf.got.count() * @sizeOf(Addr)); | 4434 | assert(elf.targetLoad(&shdr.size) == elf.got.count() * @sizeOf(Addr)); |
| 4359 | }, | 4435 | }, |
| 4360 | } | 4436 | } |
| | 4437 | if (elf.shndx.dynamic != .UNDEF) { |
| | 4438 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, elf.got.count()); |
| | 4439 | } |
| | 4440 | for (0..elf.got.count()) |got_index| { |
| | 4441 | elf.updateGotEntry(got_index); |
| | 4442 | } |
| 4361 | | 4443 | |
| 4362 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ | 4444 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ |
| 4363 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` | 4445 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` |
| ... | @@ -4539,6 +4621,22 @@ fn initHeaders( | ... | @@ -4539,6 +4621,22 @@ fn initHeaders( |
| 4539 | break :str try elf.string(.dynstr, slice); | 4621 | break :str try elf.string(.dynstr, slice); |
| 4540 | }, | 4622 | }, |
| 4541 | }; | 4623 | }; |
| | 4624 | |
| | 4625 | if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) { |
| | 4626 | .none => {}, |
| | 4627 | .data_last => switch (elf.phdrSlice()) { |
| | 4628 | inline else => |phdr| { |
| | 4629 | // Ensure that the segment after `.data` (if any) is not a loadable segment. |
| | 4630 | const next_phndx = phndx.data + 1; |
| | 4631 | if (next_phndx < phdr.len) { |
| | 4632 | switch (elf.targetLoad(&phdr[next_phndx].type)) { |
| | 4633 | .NULL, .LOAD => unreachable, // data segment should be the last loadable segment |
| | 4634 | else => {}, |
| | 4635 | } |
| | 4636 | } |
| | 4637 | }, |
| | 4638 | }, |
| | 4639 | }; |
| 4542 | } | 4640 | } |
| 4543 | | 4641 | |
| 4544 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { | 4642 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { |
| ... | @@ -4573,12 +4671,12 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { | ... | @@ -4573,12 +4671,12 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { |
| 4573 | return switch (elf.getNode(ni)) { | 4671 | return switch (elf.getNode(ni)) { |
| 4574 | .archive, | 4672 | .archive, |
| 4575 | .archive_header, | 4673 | .archive_header, |
| 4576 | .archive_elf_footer, | 4674 | .archive_input_member, |
| | 4675 | .archive_elf_member_header, |
| 4577 | .elf, | 4676 | .elf, |
| 4578 | .ehdr, | 4677 | .ehdr, |
| 4579 | .shdr, | 4678 | .shdr, |
| 4580 | .segment, | 4679 | .segment, |
| 4581 | .input_member, | | |
| 4582 | => unreachable, | 4680 | => unreachable, |
| 4583 | .section => |shndx| shndx, | 4681 | .section => |shndx| shndx, |
| 4584 | .input_section, | 4682 | .input_section, |
| ... | @@ -4594,12 +4692,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { | ... | @@ -4594,12 +4692,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4594 | return switch (elf.getNode(ni)) { | 4692 | return switch (elf.getNode(ni)) { |
| 4595 | .archive, | 4693 | .archive, |
| 4596 | .archive_header, | 4694 | .archive_header, |
| 4597 | .archive_elf_footer, | 4695 | .archive_input_member, |
| | 4696 | .archive_elf_member_header, |
| 4598 | .elf, | 4697 | .elf, |
| 4599 | .ehdr, | 4698 | .ehdr, |
| 4600 | .shdr, | 4699 | .shdr, |
| 4601 | .segment, | 4700 | .segment, |
| 4602 | .input_member, | | |
| 4603 | .copied_global, | 4701 | .copied_global, |
| 4604 | => unreachable, | 4702 | => unreachable, |
| 4605 | .section => |shndx| shndx.vaddr(elf), | 4703 | .section => |shndx| shndx.vaddr(elf), |
| ... | @@ -4613,14 +4711,18 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { | ... | @@ -4613,14 +4711,18 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4613 | } | 4711 | } |
| 4614 | fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { | 4712 | fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4615 | const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { | 4713 | const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { |
| 4616 | .archive, .archive_header, .archive_elf_footer => unreachable, | 4714 | .archive, |
| | 4715 | .archive_header, |
| | 4716 | .archive_input_member, |
| | 4717 | .archive_elf_member_header, |
| | 4718 | => unreachable, |
| 4617 | .elf => return 0, | 4719 | .elf => return 0, |
| 4618 | .ehdr, .shdr => unreachable, | 4720 | .ehdr, .shdr => unreachable, |
| 4619 | .segment => |phndx| switch (elf.phdrSlice()) { | 4721 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 4620 | inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), | 4722 | inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), |
| 4621 | }, | 4723 | }, |
| 4622 | .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), | 4724 | .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), |
| 4623 | .input_member, .input_section, .copied_global => unreachable, | 4725 | .input_section, .copied_global => unreachable, |
| 4624 | inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), | 4726 | inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 4625 | }; | 4727 | }; |
| 4626 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); | 4728 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| ... | @@ -4639,12 +4741,12 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { | ... | @@ -4639,12 +4741,12 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 4639 | const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { | 4741 | const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { |
| 4640 | .archive, | 4742 | .archive, |
| 4641 | .archive_header, | 4743 | .archive_header, |
| 4642 | .archive_elf_footer, | 4744 | .archive_input_member, |
| | 4745 | .archive_elf_member_header, |
| 4643 | .elf, | 4746 | .elf, |
| 4644 | .ehdr, | 4747 | .ehdr, |
| 4645 | .shdr, | 4748 | .shdr, |
| 4646 | .segment, | 4749 | .segment, |
| 4647 | .input_member, | | |
| 4648 | .copied_global, | 4750 | .copied_global, |
| 4649 | => unreachable, // cannot contain relocs | 4751 | => unreachable, // cannot contain relocs |
| 4650 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) | 4752 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| ... | @@ -4705,7 +4807,12 @@ fn flushMovedNodeRelocs( | ... | @@ -4705,7 +4807,12 @@ fn flushMovedNodeRelocs( |
| 4705 | // changed, so update the `offset` field of the `ElfN.Rela` entry. | 4807 | // changed, so update the `offset` field of the `ElfN.Rela` entry. |
| 4706 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); | 4808 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 4707 | } | 4809 | } |
| 4708 | reloc.apply(elf); | 4810 | // This is not just the inverse of the above condition, because if `reloc` is relative |
| | 4811 | // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we |
| | 4812 | // still need to call `SymbolReloc.apply` to update that relocation's addend. |
| | 4813 | if (elf.ehdrType() != .REL) { |
| | 4814 | reloc.apply(elf); |
| | 4815 | } |
| 4709 | } | 4816 | } |
| 4710 | } | 4817 | } |
| 4711 | | 4818 | |
| ... | @@ -4888,7 +4995,31 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { | ... | @@ -4888,7 +4995,31 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { |
| 4888 | // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`. | 4995 | // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`. |
| 4889 | }; | 4996 | }; |
| 4890 | } | 4997 | } |
| 4891 | pub fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { | 4998 | /// Specifies any restrictions the current target has regarding how segments are ordered in the |
| | 4999 | /// virtual address space. Most targets do not have any such restrictions. |
| | 5000 | fn targetSegmentLoadAddressRestrictions(elf: *const Elf) enum { |
| | 5001 | none, |
| | 5002 | /// The "mutable data" segment must be the last loadable segment in the virtual address space. |
| | 5003 | data_last, |
| | 5004 | } { |
| | 5005 | return switch (elf.ehdrMachine()) { |
| | 5006 | .AARCH64, |
| | 5007 | .PPC64, |
| | 5008 | .RISCV, |
| | 5009 | .X86_64, |
| | 5010 | .LOONGARCH, |
| | 5011 | => .none, |
| | 5012 | |
| | 5013 | // SPARC uses `R_SPARC_PC{10,22}` relocations to construct pointers to the GOT, but these |
| | 5014 | // relocations write an *unsigned* PC-relative offset. This cannot even be worked around by |
| | 5015 | // using a larger code model, because the crt `_start` assembly always uses these specific |
| | 5016 | // relocations. Therefore, to avoid relocation errors, all code must appear before the GOT |
| | 5017 | // in the virtual address space. The easiest way for us to do that is to ensure that the |
| | 5018 | // "mutable data" segment, containing the GOT, is the last segment in the address space. |
| | 5019 | .SPARCV9 => .data_last, |
| | 5020 | }; |
| | 5021 | } |
| | 5022 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4892 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; | 5023 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 4893 | const Child = pointer_ty.child; | 5024 | const Child = pointer_ty.child; |
| 4894 | const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child); | 5025 | const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child); |
| ... | @@ -4971,16 +5102,6 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr { | ... | @@ -4971,16 +5102,6 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr { |
| 4971 | } | 5102 | } |
| 4972 | } | 5103 | } |
| 4973 | | 5104 | |
| 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 | const SymPtr = union(std.elf.CLASS) { | 5105 | const SymPtr = union(std.elf.CLASS) { |
| 4985 | NONE: noreturn, | 5106 | NONE: noreturn, |
| 4986 | @"32": *std.elf.Elf32.Sym, | 5107 | @"32": *std.elf.Elf32.Sym, |
| ... | @@ -5480,19 +5601,61 @@ fn loadObject( | ... | @@ -5480,19 +5601,61 @@ fn loadObject( |
| 5480 | .member = if (member) |m| try gpa.dupe(u8, m) else null, | 5601 | .member = if (member) |m| try gpa.dupe(u8, m) else null, |
| 5481 | .extra = undefined, | 5602 | .extra = undefined, |
| 5482 | }; | 5603 | }; |
| 5483 | if (elf.ni.elf != .root) { | 5604 | if (elf.archive) |*archive| { |
| 5484 | const archive_ni: MappedFile.Node.Index = .root; | 5605 | // We're creating a static library, so just add this input as an archive member. |
| | 5606 | assert(member == null); // don't try to put static library members into other static libraries |
| | 5607 | |
| | 5608 | const first_member_oni = archive.header_ni.next(&elf.mf); |
| | 5609 | |
| | 5610 | if (first_member_oni.unwrap()) |first_member_ni| switch (elf.getNode(first_member_ni)) { |
| | 5611 | .archive_input_member, .archive_elf_member_header => {}, |
| | 5612 | .elf => unreachable, // always preceded by `.archive_elf_member_header` |
| | 5613 | else => unreachable, // never a child of `.archive` |
| | 5614 | }; |
| | 5615 | |
| 5485 | try elf.nodes.ensureUnusedCapacity(gpa, 1); | 5616 | try elf.nodes.ensureUnusedCapacity(gpa, 1); |
| 5486 | input.extra = .{ .node = try archive_ni.addFloatingChild(&elf.mf, gpa, .{ | 5617 | const new_member_ni = try archive.ni.addFooterChildBefore(&elf.mf, gpa, first_member_oni, .{ |
| 5487 | .size = Alignment.@"2".forward(fl.size + @sizeOf(std.elf.ar_hdr)), | 5618 | .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size), |
| 5488 | .alignment = .@"2", | 5619 | .alignment = .@"2", |
| 5489 | .next_moved = true, | 5620 | }); |
| 5490 | .bubbles_moved = false, | 5621 | elf.nodes.appendAssumeCapacity(.{ .archive_input_member = input_index }); |
| 5491 | .enable_next_moved = true, | 5622 | input.extra = .{ .node = new_member_ni }; |
| 5492 | }) }; | | |
| 5493 | elf.nodes.appendAssumeCapacity(.{ .input_member = input_index }); | | |
| 5494 | elf.input_prog_node.increaseEstimatedTotalItems(1); | 5623 | elf.input_prog_node.increaseEstimatedTotalItems(1); |
| 5495 | | 5624 | |
| | 5625 | // The contents of the input will be written to the file by an idle task (`flushInput`), but |
| | 5626 | // we do need to write the input's archive member header (`ar_hdr`) now, for two reasons: |
| | 5627 | // |
| | 5628 | // * If the input file has a long name, we need to add it to the archive member name string |
| | 5629 | // table, which must happen deterministically (i.e. not in an idle task). |
| | 5630 | // |
| | 5631 | // * `flushInput` needs to know the actual file size (before padding to the alignment). |
| | 5632 | const member_ar_hdr: *std.elf.ar_hdr = @ptrCast( |
| | 5633 | new_member_ni.slice(&elf.mf)[0..@sizeOf(std.elf.ar_hdr)], |
| | 5634 | ); |
| | 5635 | member_ar_hdr.* = .{ |
| | 5636 | .ar_name = undefined, // populated below |
| | 5637 | .ar_date = "0 ".*, |
| | 5638 | .ar_uid = "0 ".*, |
| | 5639 | .ar_gid = "0 ".*, |
| | 5640 | .ar_mode = "644 ".*, |
| | 5641 | .ar_size = undefined, // populated below |
| | 5642 | .ar_fmag = std.elf.ARFMAG.*, |
| | 5643 | }; |
| | 5644 | |
| | 5645 | if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{fl.size})) |size_str| { |
| | 5646 | @memset(member_ar_hdr.ar_size[size_str.len..], ' '); |
| | 5647 | } else |err| switch (err) { |
| | 5648 | error.NoSpaceLeft => return diags.failParse( |
| | 5649 | path, |
| | 5650 | "file size of {Bi} exceeds maximum size of archive member", |
| | 5651 | .{fl.size}, |
| | 5652 | ), |
| | 5653 | } |
| | 5654 | |
| | 5655 | const member_name = std.fs.path.basename(path.sub_path); |
| | 5656 | // After this call returns, `member_ar_hdr` is invalidated. |
| | 5657 | try elf.populateArchiveMemberName(member_ar_hdr, member_name); |
| | 5658 | |
| 5496 | // Since we are not emitting the archive symbol table (yet?) we do not need to parse | 5659 | // Since we are not emitting the archive symbol table (yet?) we do not need to parse |
| 5497 | // the symbols in this input. | 5660 | // the symbols in this input. |
| 5498 | return; | 5661 | return; |
| ... | @@ -5905,6 +6068,46 @@ fn loadObject( | ... | @@ -5905,6 +6068,46 @@ fn loadObject( |
| 5905 | }, | 6068 | }, |
| 5906 | } | 6069 | } |
| 5907 | } | 6070 | } |
| | 6071 | /// This function may resize the archive header, so therefore invalidates `member_ar_hdr`. |
| | 6072 | fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_name: []const u8) Error!void { |
| | 6073 | if (std.mem.print(&member_ar_hdr.ar_name, "{s}/", .{member_name})) |name_str| { |
| | 6074 | @memset(member_ar_hdr.ar_name[name_str.len..], ' '); |
| | 6075 | return; |
| | 6076 | } else |err| switch (err) { |
| | 6077 | error.NoSpaceLeft => {}, // handled below |
| | 6078 | } |
| | 6079 | |
| | 6080 | const gpa = elf.base.comp.gpa; |
| | 6081 | const archive_header_ni = elf.archive.?.header_ni; |
| | 6082 | |
| | 6083 | // The member's name is too big to put directly in the `ar_name` field, so it needs to go in the |
| | 6084 | // "long name" string table instead (in the special member named "//"). |
| | 6085 | |
| | 6086 | _, const old_archive_header_size = archive_header_ni.location(&elf.mf).resolve(&elf.mf); |
| | 6087 | |
| | 6088 | // We're going to add a new string at the end of the table. Update `member_ar_hdr` first, |
| | 6089 | // because resizing the string table will invalidate it. |
| | 6090 | const string_table_offset = old_archive_header_size - (std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr)); |
| | 6091 | if (std.mem.print(&member_ar_hdr.ar_name, "/{d}", .{string_table_offset})) |name_str| { |
| | 6092 | @memset(member_ar_hdr.ar_name[name_str.len..], ' '); |
| | 6093 | } else |inner_err| switch (inner_err) { |
| | 6094 | error.NoSpaceLeft => { |
| | 6095 | // The string table offset is itself too big to represent. This means the string table's |
| | 6096 | // *size* is definitely too big to represent (we only get 10 bytes for that whereas we |
| | 6097 | // get 16 here!), so as long as we still add the string, we're guaranteed to get a link |
| | 6098 | // error for that reason. Therefore, we can just ignore this error and carry on. |
| | 6099 | }, |
| | 6100 | } |
| | 6101 | |
| | 6102 | // We set the size of the archive header node exactly, because we want padding bytes to go into |
| | 6103 | // the root `.archive` node. That way, those bytes could still be used to grow the string table |
| | 6104 | // if necessary, but they could also be used for new archive members. |
| | 6105 | try archive_header_ni.resizeLeaf(&elf.mf, gpa, old_archive_header_size + member_name.len + 2); |
| | 6106 | |
| | 6107 | const dest_slice = archive_header_ni.slice(&elf.mf)[@intCast(old_archive_header_size)..]; |
| | 6108 | @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name); |
| | 6109 | @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird |
| | 6110 | } |
| 5908 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { | 6111 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { |
| 5909 | const comp = elf.base.comp; | 6112 | const comp = elf.base.comp; |
| 5910 | const gpa = comp.gpa; | 6113 | const gpa = comp.gpa; |
| ... | @@ -7072,12 +7275,12 @@ fn addGotRelocAssumeCapacity( | ... | @@ -7072,12 +7275,12 @@ fn addGotRelocAssumeCapacity( |
| 7072 | switch (elf.getNode(node)) { | 7275 | switch (elf.getNode(node)) { |
| 7073 | .archive, | 7276 | .archive, |
| 7074 | .archive_header, | 7277 | .archive_header, |
| 7075 | .archive_elf_footer, | 7278 | .archive_input_member, |
| | 7279 | .archive_elf_member_header, |
| 7076 | .elf, | 7280 | .elf, |
| 7077 | .ehdr, | 7281 | .ehdr, |
| 7078 | .shdr, | 7282 | .shdr, |
| 7079 | .segment, | 7283 | .segment, |
| 7080 | .input_member, | | |
| 7081 | .copied_global, | 7284 | .copied_global, |
| 7082 | => unreachable, // cannot contain relocs, | 7285 | => unreachable, // cannot contain relocs, |
| 7083 | .section, | 7286 | .section, |
| ... | @@ -7129,6 +7332,7 @@ fn addGotRelocAssumeCapacity( | ... | @@ -7129,6 +7332,7 @@ fn addGotRelocAssumeCapacity( |
| 7129 | }); | 7332 | }); |
| 7130 | } | 7333 | } |
| 7131 | fn updateGotEntry(elf: *Elf, got_index: usize) void { | 7334 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| | 7335 | assert(elf.ehdrType() != .REL); |
| 7132 | const entry_value: union(enum) { | 7336 | const entry_value: union(enum) { |
| 7133 | unsigned: u64, | 7337 | unsigned: u64, |
| 7134 | signed: i64, | 7338 | signed: i64, |
| ... | @@ -7514,6 +7718,17 @@ fn flushInner( | ... | @@ -7514,6 +7718,17 @@ fn flushInner( |
| 7514 | diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count}); | 7718 | diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count}); |
| 7515 | } | 7719 | } |
| 7516 | | 7720 | |
| | 7721 | if (elf.archive) |*archive| { |
| | 7722 | if (archive.elf_member_too_big) diags.addError( |
| | 7723 | "file size of {Bi} exceeds maximum size of archive member", |
| | 7724 | .{elf.ni.elf.location(&elf.mf).resolve(&elf.mf)[1]}, |
| | 7725 | ); |
| | 7726 | if (archive.strtab_member_too_big) diags.addError( |
| | 7727 | "archive file name string table exceeds maximum size", |
| | 7728 | .{}, |
| | 7729 | ); |
| | 7730 | } |
| | 7731 | |
| 7517 | elf.flushDynamic(); | 7732 | elf.flushDynamic(); |
| 7518 | | 7733 | |
| 7519 | const entry_addr: u64 = entry: { | 7734 | const entry_addr: u64 = entry: { |
| ... | @@ -7545,6 +7760,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | ... | @@ -7545,6 +7760,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7545 | const comp = elf.base.comp; | 7760 | const comp = elf.base.comp; |
| 7546 | const diags = &comp.link_diags; | 7761 | const diags = &comp.link_diags; |
| 7547 | | 7762 | |
| | 7763 | elf.mf.nodes_lock.lock(); |
| | 7764 | defer elf.mf.nodes_lock.unlock(); |
| | 7765 | |
| 7548 | assert(elf.pending_uavs.items.len == 0); | 7766 | assert(elf.pending_uavs.items.len == 0); |
| 7549 | for (&elf.lazy.values) |*lazy| { | 7767 | for (&elf.lazy.values) |*lazy| { |
| 7550 | assert(lazy.pending_index == lazy.map.count()); | 7768 | assert(lazy.pending_index == lazy.map.count()); |
| ... | @@ -7698,7 +7916,7 @@ fn idleProgNode( | ... | @@ -7698,7 +7916,7 @@ fn idleProgNode( |
| 7698 | return prog_node.start(name: switch (node) { | 7916 | return prog_node.start(name: switch (node) { |
| 7699 | else => |tag| @tagName(tag), | 7917 | else => |tag| @tagName(tag), |
| 7700 | .section => |shndx| shndx.name(elf).slice(elf), | 7918 | .section => |shndx| shndx.name(elf).slice(elf), |
| 7701 | .input_member => |ii| std.fmt.bufPrint(&name, "{f}{f}", .{ | 7919 | .archive_input_member => |ii| std.fmt.bufPrint(&name, "{f}{f}", .{ |
| 7702 | ii.path(elf).fmtEscapeString(), | 7920 | ii.path(elf).fmtEscapeString(), |
| 7703 | fmtMemberString(ii.member(elf)), | 7921 | fmtMemberString(ii.member(elf)), |
| 7704 | }) catch &name, | 7922 | }) catch &name, |
| ... | @@ -7825,7 +8043,6 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { | ... | @@ -7825,7 +8043,6 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { |
| 7825 | fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void { | 8043 | fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void { |
| 7826 | const comp = elf.base.comp; | 8044 | const comp = elf.base.comp; |
| 7827 | const io = comp.io; | 8045 | const io = comp.io; |
| 7828 | const gpa = comp.gpa; | | |
| 7829 | const diags = &comp.link_diags; | 8046 | const diags = &comp.link_diags; |
| 7830 | const path = ii.path(elf); | 8047 | const path = ii.path(elf); |
| 7831 | const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) { | 8048 | const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) { |
| ... | @@ -7833,23 +8050,40 @@ fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void { | ... | @@ -7833,23 +8050,40 @@ fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void { |
| 7833 | else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }), | 8050 | else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }), |
| 7834 | }; | 8051 | }; |
| 7835 | defer file.close(io); | 8052 | defer file.close(io); |
| | 8053 | |
| | 8054 | const slice = ii.node(elf).slice(&elf.mf); |
| | 8055 | |
| | 8056 | const member_ar_hdr: *const std.elf.ar_hdr = @ptrCast(slice[0..@sizeOf(std.elf.ar_hdr)]); |
| | 8057 | const input_size: u32 = member_ar_hdr.size() catch |err| switch (err) { |
| | 8058 | // We wrote the `ar_hdr` ourselves (in `loadObject`), so it is definitely valid. |
| | 8059 | error.Overflow, error.InvalidCharacter => unreachable, |
| | 8060 | }; |
| | 8061 | |
| | 8062 | switch (slice.len - @sizeOf(std.elf.ar_hdr) - input_size) { |
| | 8063 | 0 => {}, |
| | 8064 | 1 => { |
| | 8065 | // Alignment added one padding byte, which the format requires to have value '\n'. |
| | 8066 | slice[slice.len - 1] = '\n'; |
| | 8067 | }, |
| | 8068 | else => unreachable, // node size should agree with the value we wrote into `ar_hdr.ar_size` |
| | 8069 | } |
| | 8070 | |
| 7836 | var fr = file.reader(io, &.{}); | 8071 | var fr = file.reader(io, &.{}); |
| 7837 | var nw: MappedFile.Node.Writer = undefined; | 8072 | var w: Io.Writer = .fixed(slice[@sizeOf(std.elf.ar_hdr)..]); |
| 7838 | ii.node(elf).writer(&elf.mf, gpa, &nw); | 8073 | const n_bytes_read = w.sendFileAll(&fr, .limited(input_size)) catch |err| switch (err) { |
| 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) { | | |
| 7842 | error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{ | 8074 | error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{ |
| 7843 | path.fmtEscapeString(), | 8075 | path.fmtEscapeString(), |
| 7844 | fmtMemberString(ii.member(elf)), | 8076 | fmtMemberString(ii.member(elf)), |
| 7845 | fr.err orelse (fr.seek_err orelse fr.size_err.?), | 8077 | fr.err orelse (fr.seek_err orelse fr.size_err.?), |
| 7846 | }), | 8078 | }), |
| 7847 | error.WriteFailed => return nw.err.?, | 8079 | error.WriteFailed => unreachable, // `.limited(input_size)` prevents us writing too many bytes |
| 7848 | }; | 8080 | }; |
| 7849 | if (n_bytes + 1 < size) return diags.fail("failed to read input \"{f}{f}\": unexpected eof", .{ | 8081 | if (n_bytes_read != input_size) { |
| 7850 | path.fmtEscapeString(), | 8082 | return diags.fail("failed to load input \"{f}{f}\": file truncated during compilation", .{ |
| 7851 | fmtMemberString(ii.member(elf)), | 8083 | path.fmtEscapeString(), |
| 7852 | }); | 8084 | fmtMemberString(ii.member(elf)), |
| | 8085 | }); |
| | 8086 | } |
| 7853 | } | 8087 | } |
| 7854 | | 8088 | |
| 7855 | fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void { | 8089 | fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void { |
| ... | @@ -7931,12 +8165,18 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void | ... | @@ -7931,12 +8165,18 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 7931 | const trace = tracy.trace(@src()); | 8165 | const trace = tracy.trace(@src()); |
| 7932 | defer trace.end(); | 8166 | defer trace.end(); |
| 7933 | | 8167 | |
| 7934 | elf.mf.nodes_lock.lock(); | | |
| 7935 | defer elf.mf.nodes_lock.unlock(); | | |
| 7936 | | | |
| 7937 | switch (elf.getNode(ni)) { | 8168 | switch (elf.getNode(ni)) { |
| 7938 | .archive, .archive_header => unreachable, | 8169 | .archive => unreachable, |
| 7939 | .archive_elf_footer, .elf => {}, | 8170 | .archive_header => unreachable, |
| | 8171 | |
| | 8172 | .archive_input_member, |
| | 8173 | .archive_elf_member_header, |
| | 8174 | .elf, |
| | 8175 | => { |
| | 8176 | assert(elf.archive != null); |
| | 8177 | return; |
| | 8178 | }, |
| | 8179 | |
| 7940 | .ehdr, .shdr => elf.flushElfOffset(ni), | 8180 | .ehdr, .shdr => elf.flushElfOffset(ni), |
| 7941 | .segment => |phndx| { | 8181 | .segment => |phndx| { |
| 7942 | elf.flushElfOffset(ni); | 8182 | elf.flushElfOffset(ni); |
| ... | @@ -8014,7 +8254,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void | ... | @@ -8014,7 +8254,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 8014 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); | 8254 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 8015 | } | 8255 | } |
| 8016 | }, | 8256 | }, |
| 8017 | .input_member => {}, | | |
| 8018 | .input_section => |isi| { | 8257 | .input_section => |isi| { |
| 8019 | const old_section_addr = isi.ptr(elf).vaddr; | 8258 | const old_section_addr = isi.ptr(elf).vaddr; |
| 8020 | const new_section_addr = elf.computeNodeVAddr(ni); | 8259 | const new_section_addr = elf.computeNodeVAddr(ni); |
| ... | @@ -8114,6 +8353,18 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro | ... | @@ -8114,6 +8353,18 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8114 | const page_align = elf.targetPageAlign(); | 8353 | const page_align = elf.targetPageAlign(); |
| 8115 | const node_align = segment_ni.alignment(&elf.mf); | 8354 | const node_align = segment_ni.alignment(&elf.mf); |
| 8116 | const ph_align = page_align.max(node_align); | 8355 | const ph_align = page_align.max(node_align); |
| | 8356 | |
| | 8357 | // If we determine that the segment's virtual address needs to move, then it's a good idea to |
| | 8358 | // make it less likely that it needs to move *again* in the future, because it is expensive to |
| | 8359 | // change a segment's load address (a lot of re-flushing is necessary). To do that, we reserve |
| | 8360 | // more virtual address space than we need (multiplying the actual size by this value). That |
| | 8361 | // way, there will usually be padding between segments which they can grow into. |
| | 8362 | // |
| | 8363 | // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases where |
| | 8364 | // virtual address space is constrained. For instance, 32-bit targets, or targets where short |
| | 8365 | // PC-relative relocations between segments are common. |
| | 8366 | const reserve_size_multiplier = 4; |
| | 8367 | |
| 8117 | switch (elf.phdrSlice()) { | 8368 | switch (elf.phdrSlice()) { |
| 8118 | inline else => |phdr| { | 8369 | inline else => |phdr| { |
| 8119 | const offset = elf.targetLoad(&phdr[orig_phndx].offset); | 8370 | const offset = elf.targetLoad(&phdr[orig_phndx].offset); |
| ... | @@ -8172,15 +8423,46 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro | ... | @@ -8172,15 +8423,46 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8172 | // backwards to the start of the page. | 8423 | // backwards to the start of the page. |
| 8173 | const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits()); | 8424 | const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits()); |
| 8174 | | 8425 | |
| 8175 | // If we're at the same vaddr we started at, then all we're worried about is the | 8426 | // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if |
| 8176 | // segment fitting here. However, if we've already changed our virtual address, then | 8427 | // the segment is already known to be moving---making it easier to grow in-place is |
| 8177 | // we might as well try to reserve a bit *more* virtual address space while we're at | 8428 | // the whole point of the multiplier! |
| 8178 | // it, because changing virtual address is quite disruptive (we need to re-flush a | 8429 | { |
| 8179 | // lot of stuff!) and giving ourselves more space will make it less likely to happen | 8430 | const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier; |
| 8180 | // again. | 8431 | if (vaddr + target_size <= next_page_vaddr) { |
| 8181 | const target_size = if (vaddr == orig_vaddr) size else size * 4; | 8432 | break; // hooray, we fit here! |
| 8182 | if (vaddr + target_size <= next_page_vaddr) { | 8433 | } |
| 8183 | break; // hooray, we fit here! | 8434 | } |
| | 8435 | |
| | 8436 | const next_ni = elf.phdrs.items[next_phndx].unwrap().?; |
| | 8437 | |
| | 8438 | // This segment don't fit here, but before deciding how to proceed, we need to |
| | 8439 | // consider any target-specific restrictions we are subject to. |
| | 8440 | switch (elf.targetSegmentLoadAddressRestrictions()) { |
| | 8441 | .none => {}, |
| | 8442 | .data_last => if (next_ni == elf.ni.data) { |
| | 8443 | // We can't leapfrog over the data segment. Instead, that segment just needs |
| | 8444 | // to be shifted forwards to make space for us, and we'll then `break` with |
| | 8445 | // our current vaddr. |
| | 8446 | |
| | 8447 | if (next_phndx + 1 < phdr.len) switch (elf.targetLoad(&phdr[next_phndx + 1].type)) { |
| | 8448 | .NULL, .LOAD => unreachable, // data segment should be the last loadable segment |
| | 8449 | else => {}, |
| | 8450 | }; |
| | 8451 | |
| | 8452 | const free_vaddr = vaddr + size * reserve_size_multiplier; |
| | 8453 | |
| | 8454 | const next_align = page_align.max(next_ni.alignment(&elf.mf)); |
| | 8455 | const next_offset = elf.targetLoad(&next_ph.offset); |
| | 8456 | const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits(); |
| | 8457 | |
| | 8458 | // This logic for updating the data segment's vaddr is identical to how we |
| | 8459 | // will update the vaddr of `phndx` when we break from the loop. |
| | 8460 | elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr)); |
| | 8461 | elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr)); |
| | 8462 | try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| | 8463 | |
| | 8464 | break; |
| | 8465 | }, |
| 8184 | } | 8466 | } |
| 8185 | | 8467 | |
| 8186 | // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But | 8468 | // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But |
| ... | @@ -8192,8 +8474,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro | ... | @@ -8192,8 +8474,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8192 | | 8474 | |
| 8193 | // Now just swap the phdrs and update our `phndx`. | 8475 | // Now just swap the phdrs and update our `phndx`. |
| 8194 | std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph); | 8476 | std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph); |
| 8195 | const next_ni = elf.phdrs.items[next_phndx]; | 8477 | elf.phdrs.items[phndx] = .wrap(next_ni); |
| 8196 | elf.phdrs.items[phndx] = next_ni; | | |
| 8197 | elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx }; | 8478 | elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx }; |
| 8198 | elf.phdrs.items[next_phndx] = .wrap(segment_ni); | 8479 | elf.phdrs.items[next_phndx] = .wrap(segment_ni); |
| 8199 | elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) }; | 8480 | elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) }; |
| ... | @@ -8213,24 +8494,23 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo | ... | @@ -8213,24 +8494,23 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 8213 | const trace = tracy.trace(@src()); | 8494 | const trace = tracy.trace(@src()); |
| 8214 | defer trace.end(); | 8495 | defer trace.end(); |
| 8215 | | 8496 | |
| 8216 | elf.mf.nodes_lock.lock(); | | |
| 8217 | defer elf.mf.nodes_lock.unlock(); | | |
| 8218 | | | |
| 8219 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); | 8497 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); |
| 8220 | switch (elf.getNode(ni)) { | 8498 | switch (elf.getNode(ni)) { |
| 8221 | .archive => { | 8499 | .archive, .archive_header => {}, |
| 8222 | if (ni.last(&elf.mf).unwrap()) |last_ni| { | 8500 | .archive_input_member => unreachable, |
| 8223 | if (last_ni.prev(&elf.mf).unwrap()) |prev_ni| { | 8501 | .archive_elf_member_header => unreachable, |
| 8224 | if (prev_ni.hasNextMoved(&elf.mf)) return; | 8502 | .elf => if (elf.archive) |*archive| { |
| 8225 | } | 8503 | const member_ar_hdr: *std.elf.ar_hdr = @ptrCast( |
| 8226 | const offset, _ = last_ni.location(&elf.mf).resolve(&elf.mf); | 8504 | archive.elf_member_header_ni.slice(&elf.mf), |
| 8227 | _ = std.mem.print(&elf.arHdrPtr(last_ni).ar_size, "{d:<10}", .{ | 8505 | ); |
| 8228 | size - offset, | 8506 | if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{size})) |size_str| { |
| 8229 | }) catch @panic("archive member too large"); | 8507 | @memset(member_ar_hdr.ar_size[size_str.len..], ' '); |
| | 8508 | archive.elf_member_too_big = false; |
| | 8509 | } else |err| switch (err) { |
| | 8510 | error.NoSpaceLeft => archive.elf_member_too_big = true, |
| 8230 | } | 8511 | } |
| 8231 | }, | 8512 | }, |
| 8232 | .archive_header, .elf => {}, | 8513 | .ehdr => unreachable, |
| 8233 | .ehdr, .archive_elf_footer => unreachable, | | |
| 8234 | .shdr => {}, | 8514 | .shdr => {}, |
| 8235 | .segment => |phndx| switch (elf.phdrSlice()) { | 8515 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 8236 | inline else => |phdr| { | 8516 | inline else => |phdr| { |
| ... | @@ -8302,7 +8582,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo | ... | @@ -8302,7 +8582,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 8302 | } | 8582 | } |
| 8303 | }, | 8583 | }, |
| 8304 | }, | 8584 | }, |
| 8305 | .input_member, .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {}, | 8585 | .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {}, |
| 8306 | } | 8586 | } |
| 8307 | } | 8587 | } |
| 8308 | | 8588 | |
| ... | @@ -8310,12 +8590,11 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! | ... | @@ -8310,12 +8590,11 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! |
| 8310 | const trace = tracy.trace(@src()); | 8590 | const trace = tracy.trace(@src()); |
| 8311 | defer trace.end(); | 8591 | defer trace.end(); |
| 8312 | | 8592 | |
| 8313 | elf.mf.nodes_lock.lock(); | | |
| 8314 | defer elf.mf.nodes_lock.unlock(); | | |
| 8315 | | | |
| 8316 | switch (elf.getNode(ni)) { | 8593 | switch (elf.getNode(ni)) { |
| 8317 | .archive, | 8594 | .archive, |
| 8318 | .archive_elf_footer, | 8595 | .archive_input_member, |
| | 8596 | .archive_elf_member_header, |
| | 8597 | .elf, |
| 8319 | .ehdr, | 8598 | .ehdr, |
| 8320 | .shdr, | 8599 | .shdr, |
| 8321 | .segment, | 8600 | .segment, |
| ... | @@ -8327,54 +8606,32 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! | ... | @@ -8327,54 +8606,32 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! |
| 8327 | .lazy_code, | 8606 | .lazy_code, |
| 8328 | .lazy_const_data, | 8607 | .lazy_const_data, |
| 8329 | => unreachable, | 8608 | => unreachable, |
| 8330 | .archive_header, .elf, .input_member => |_, tag| { | 8609 | |
| 8331 | const member_offset, const update_size = member_offset: { | 8610 | .archive_header => { |
| 8332 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); | 8611 | const archive = &elf.archive.?; |
| 8333 | break :member_offset switch (tag) { | 8612 | |
| 8334 | else => unreachable, | 8613 | // Because we can't just throw padding bytes in the middle of an archive file, we need |
| 8335 | .archive_header => .{ offset + std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), true }, | 8614 | // the member name string table (the "//" member) to absorb all the padding bytes |
| 8336 | .elf, .input_member => .{ offset, !ni.prev(&elf.mf).unwrap().?.hasNextMoved(&elf.mf) }, | 8615 | // between it (in the `.archive_header` node) and the first actual member. |
| 8337 | }; | 8616 | const next_member_ni = ni.next(&elf.mf).unwrap() orelse { |
| 8338 | }; | 8617 | // I guess there are no link inputs yet? But there will be eventually! |
| 8339 | const member_size = if (ni.next(&elf.mf).unwrap()) |next_ni| member_size: { | 8618 | return; |
| 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; | | |
| 8374 | }; | 8619 | }; |
| 8375 | if (update_size) _ = std.mem.print(&elf.arHdrPtr(ni).ar_size, "{d:<10}", .{ | 8620 | const next_member_offset: u64, _ = next_member_ni.location(&elf.mf).resolve(&elf.mf); |
| 8376 | member_size, | 8621 | const strtab_member_offset = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr); |
| 8377 | }) catch @panic("archive member too large"); | 8622 | assert(Alignment.@"2".check(next_member_offset)); |
| | 8623 | assert(Alignment.@"2".check(strtab_member_offset)); |
| | 8624 | const strtab_size = next_member_offset - strtab_member_offset; |
| | 8625 | |
| | 8626 | const member_ar_hdr: *std.elf.ar_hdr = @ptrCast( |
| | 8627 | archive.header_ni.slice(&elf.mf)[std.elf.ARMAG.len..][0..@sizeOf(std.elf.ar_hdr)], |
| | 8628 | ); |
| | 8629 | if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{strtab_size})) |size_str| { |
| | 8630 | @memset(member_ar_hdr.ar_size[size_str.len..], ' '); |
| | 8631 | archive.strtab_member_too_big = false; |
| | 8632 | } else |err| switch (err) { |
| | 8633 | error.NoSpaceLeft => archive.strtab_member_too_big = true, |
| | 8634 | } |
| 8378 | }, | 8635 | }, |
| 8379 | } | 8636 | } |
| 8380 | } | 8637 | } |