| ... | ... | @@ -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, |
| ... | ... | @@ -3558,51 +3589,58 @@ fn initHeaders( |
| 3558 | 3589 | .EXEC, .DYN => {}, |
| 3559 | 3590 | } |
| 3560 | 3591 | var phnum: u32 = 0; |
| 3561 | | break :ph .{ .{ |
| 3562 | | .phdr = phndx: { |
| 3563 | | defer phnum += 1; |
| 3564 | | break :phndx phnum; |
| 3565 | | }, |
| 3566 | | .interp = if (maybe_interp) |_| phndx: { |
| 3567 | | defer phnum += 1; |
| 3568 | | break :phndx phnum; |
| 3569 | | } else undefined, |
| 3570 | | .rodata = phndx: { |
| 3571 | | defer phnum += 1; |
| 3572 | | break :phndx phnum; |
| 3573 | | }, |
| 3574 | | .text = phndx: { |
| 3575 | | defer phnum += 1; |
| 3576 | | break :phndx phnum; |
| 3577 | | }, |
| 3578 | | .data = phndx: { |
| 3579 | | defer phnum += 1; |
| 3580 | | break :phndx phnum; |
| 3581 | | }, |
| 3582 | | .plt = if (plt.got_plt == null) phndx: { |
| 3583 | | defer phnum += 1; |
| 3584 | | break :phndx phnum; |
| 3585 | | } else undefined, |
| 3586 | | .tls = if (comp.config.any_non_single_threaded) phndx: { |
| 3587 | | defer phnum += 1; |
| 3588 | | break :phndx phnum; |
| 3589 | | } else undefined, |
| 3590 | | .dynamic = if (have_dynamic_section) phndx: { |
| 3591 | | defer phnum += 1; |
| 3592 | | break :phndx phnum; |
| 3593 | | } else undefined, |
| 3594 | | .relro = phndx: { |
| 3595 | | defer phnum += 1; |
| 3596 | | break :phndx phnum; |
| 3597 | | }, |
| 3598 | | .gnu_stack = phndx: { |
| 3599 | | defer phnum += 1; |
| 3600 | | break :phndx phnum; |
| 3592 | break :ph .{ |
| 3593 | .{ |
| 3594 | .phdr = phndx: { |
| 3595 | defer phnum += 1; |
| 3596 | break :phndx phnum; |
| 3597 | }, |
| 3598 | .interp = if (maybe_interp) |_| phndx: { |
| 3599 | defer phnum += 1; |
| 3600 | break :phndx phnum; |
| 3601 | } else undefined, |
| 3602 | .rodata = phndx: { |
| 3603 | defer phnum += 1; |
| 3604 | break :phndx phnum; |
| 3605 | }, |
| 3606 | .text = phndx: { |
| 3607 | defer phnum += 1; |
| 3608 | break :phndx phnum; |
| 3609 | }, |
| 3610 | .plt = if (plt.got_plt == null) phndx: { |
| 3611 | defer phnum += 1; |
| 3612 | break :phndx phnum; |
| 3613 | } else undefined, |
| 3614 | // `data` must be assigned after all other loadable segments so that it has the greatest |
| 3615 | // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions` |
| 3616 | // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC). |
| 3617 | .data = phndx: { |
| 3618 | defer phnum += 1; |
| 3619 | break :phndx phnum; |
| 3620 | }, |
| 3621 | .tls = if (comp.config.any_non_single_threaded) phndx: { |
| 3622 | defer phnum += 1; |
| 3623 | break :phndx phnum; |
| 3624 | } else undefined, |
| 3625 | .dynamic = if (have_dynamic_section) phndx: { |
| 3626 | defer phnum += 1; |
| 3627 | break :phndx phnum; |
| 3628 | } else undefined, |
| 3629 | .relro = phndx: { |
| 3630 | defer phnum += 1; |
| 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 | 3644 | 3 + // `.elf`, `.ehdr`, and `.shdr` nodes |
| 3607 | 3645 | (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node |
| 3608 | 3646 | (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node |
| ... | ... | @@ -3619,11 +3657,14 @@ fn initHeaders( |
| 3619 | 3657 | const archive_ni: MappedFile.Node.Index = .root; |
| 3620 | 3658 | |
| 3621 | 3659 | 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, |
| 3660 | // We intentionally do not set `.alignment = .@"2"` here, because the string table data |
| 3661 | // in this node does not need to have an aligned length. (This node's offset is aligned |
| 3662 | // regardless by virtue of it being a header.) |
| 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 | 3666 | .enable_next_moved = true, |
| 3667 | .next_moved = true, |
| 3627 | 3668 | }); |
| 3628 | 3669 | elf.nodes.appendAssumeCapacity(.archive_header); |
| 3629 | 3670 | const archive_header_slice = archive_header_ni.slice(&elf.mf); |
| ... | ... | @@ -3635,23 +3676,47 @@ fn initHeaders( |
| 3635 | 3676 | .ar_uid = @splat(' '), |
| 3636 | 3677 | .ar_gid = @splat(' '), |
| 3637 | 3678 | .ar_mode = @splat(' '), |
| 3638 | | .ar_size = @splat(' '), |
| 3679 | .ar_size = undefined, // populated by `flushNextMoved` for `archive_header_ni` |
| 3639 | 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 | 3684 | .alignment = node_block_align.max(.@"2"), |
| 3644 | | .next_moved = true, |
| 3645 | 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 | 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 | 3691 | .alignment = .@"2", |
| 3652 | 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 | 3720 | } else { |
| 3656 | 3721 | elf.ni.elf = .root; |
| 3657 | 3722 | elf.nodes.appendAssumeCapacity(.elf); |
| ... | ... | @@ -4092,33 +4157,44 @@ fn initHeaders( |
| 4092 | 4157 | .addralign = addr_align, |
| 4093 | 4158 | .entsize = @intCast(addr_align.toByteUnits()), |
| 4094 | 4159 | }); |
| 4095 | | if (plt.got_plt) |got_plt| { |
| 4096 | | const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data; |
| 4097 | | elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{ |
| 4098 | | .name = ".got.plt", |
| 4099 | | .type = .PROGBITS, |
| 4100 | | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 4101 | | .size = got_plt.header_entries * elf.targetPtrSize(), |
| 4102 | | .addralign = addr_align, |
| 4103 | | .entsize = @intCast(addr_align.toByteUnits()), |
| 4104 | | }); |
| 4105 | | elf.shndx.plt = try elf.addSection(elf.ni.text, .{ |
| 4106 | | .name = ".plt", |
| 4107 | | .type = .PROGBITS, |
| 4108 | | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 4109 | | .size = plt.entry_size * plt.header_entries, |
| 4110 | | .addralign = plt.@"align", |
| 4111 | | .node_align = node_block_align, |
| 4112 | | }); |
| 4113 | | } else { |
| 4114 | | elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{ |
| 4115 | | .name = ".plt", |
| 4116 | | .type = .PROGBITS, |
| 4117 | | .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true }, |
| 4118 | | .size = plt.entry_size * plt.header_entries, |
| 4119 | | .addralign = plt.@"align", |
| 4120 | | .node_align = node_block_align, |
| 4121 | | }); |
| 4160 | { |
| 4161 | const init_plt_size = plt.entry_size * plt.header_entries; |
| 4162 | if (plt.got_plt) |got_plt| { |
| 4163 | const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data; |
| 4164 | elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{ |
| 4165 | .name = ".got.plt", |
| 4166 | .type = .PROGBITS, |
| 4167 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 4168 | .size = got_plt.header_entries * elf.targetPtrSize(), |
| 4169 | .addralign = addr_align, |
| 4170 | .entsize = @intCast(addr_align.toByteUnits()), |
| 4171 | }); |
| 4172 | elf.shndx.plt = try elf.addSection(elf.ni.text, .{ |
| 4173 | .name = ".plt", |
| 4174 | .type = .PROGBITS, |
| 4175 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 4176 | .size = plt.@"align".forward(init_plt_size), |
| 4177 | .addralign = plt.@"align", |
| 4178 | .node_align = node_block_align, |
| 4179 | }); |
| 4180 | } else { |
| 4181 | elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{ |
| 4182 | .name = ".plt", |
| 4183 | .type = .PROGBITS, |
| 4184 | .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true }, |
| 4185 | .size = plt.@"align".forward(init_plt_size), |
| 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 | 4199 | if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{ |
| 4124 | 4200 | .name = ".plt.sec", |
| ... | ... | @@ -4358,6 +4434,12 @@ fn initHeaders( |
| 4358 | 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 | 4444 | // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/ |
| 4363 | 4445 | // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection` |
| ... | ... | @@ -4539,6 +4621,22 @@ fn initHeaders( |
| 4539 | 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 | 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 | 4671 | return switch (elf.getNode(ni)) { |
| 4574 | 4672 | .archive, |
| 4575 | 4673 | .archive_header, |
| 4576 | | .archive_elf_footer, |
| 4674 | .archive_input_member, |
| 4675 | .archive_elf_member_header, |
| 4577 | 4676 | .elf, |
| 4578 | 4677 | .ehdr, |
| 4579 | 4678 | .shdr, |
| 4580 | 4679 | .segment, |
| 4581 | | .input_member, |
| 4582 | 4680 | => unreachable, |
| 4583 | 4681 | .section => |shndx| shndx, |
| 4584 | 4682 | .input_section, |
| ... | ... | @@ -4594,12 +4692,12 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4594 | 4692 | return switch (elf.getNode(ni)) { |
| 4595 | 4693 | .archive, |
| 4596 | 4694 | .archive_header, |
| 4597 | | .archive_elf_footer, |
| 4695 | .archive_input_member, |
| 4696 | .archive_elf_member_header, |
| 4598 | 4697 | .elf, |
| 4599 | 4698 | .ehdr, |
| 4600 | 4699 | .shdr, |
| 4601 | 4700 | .segment, |
| 4602 | | .input_member, |
| 4603 | 4701 | .copied_global, |
| 4604 | 4702 | => unreachable, |
| 4605 | 4703 | .section => |shndx| shndx.vaddr(elf), |
| ... | ... | @@ -4613,14 +4711,18 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4613 | 4711 | } |
| 4614 | 4712 | fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 4615 | 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 | 4719 | .elf => return 0, |
| 4618 | 4720 | .ehdr, .shdr => unreachable, |
| 4619 | 4721 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 4620 | 4722 | inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), |
| 4621 | 4723 | }, |
| 4622 | 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 | 4726 | inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| 4625 | 4727 | }; |
| 4626 | 4728 | const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); |
| ... | ... | @@ -4639,12 +4741,12 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 4639 | 4741 | const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { |
| 4640 | 4742 | .archive, |
| 4641 | 4743 | .archive_header, |
| 4642 | | .archive_elf_footer, |
| 4744 | .archive_input_member, |
| 4745 | .archive_elf_member_header, |
| 4643 | 4746 | .elf, |
| 4644 | 4747 | .ehdr, |
| 4645 | 4748 | .shdr, |
| 4646 | 4749 | .segment, |
| 4647 | | .input_member, |
| 4648 | 4750 | .copied_global, |
| 4649 | 4751 | => unreachable, // cannot contain relocs |
| 4650 | 4752 | .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) |
| ... | ... | @@ -4705,7 +4807,12 @@ fn flushMovedNodeRelocs( |
| 4705 | 4807 | // changed, so update the `offset` field of the `ElfN.Rela` entry. |
| 4706 | 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 | 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 | 5023 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 4893 | 5024 | const Child = pointer_ty.child; |
| 4894 | 5025 | const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child); |
| ... | ... | @@ -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 | 5105 | const SymPtr = union(std.elf.CLASS) { |
| 4985 | 5106 | NONE: noreturn, |
| 4986 | 5107 | @"32": *std.elf.Elf32.Sym, |
| ... | ... | @@ -5480,19 +5601,61 @@ fn loadObject( |
| 5480 | 5601 | .member = if (member) |m| try gpa.dupe(u8, m) else null, |
| 5481 | 5602 | .extra = undefined, |
| 5482 | 5603 | }; |
| 5483 | | if (elf.ni.elf != .root) { |
| 5484 | | const archive_ni: MappedFile.Node.Index = .root; |
| 5604 | if (elf.archive) |*archive| { |
| 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 | 5616 | 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)), |
| 5617 | const new_member_ni = try archive.ni.addFooterChildBefore(&elf.mf, gpa, first_member_oni, .{ |
| 5618 | .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size), |
| 5488 | 5619 | .alignment = .@"2", |
| 5489 | | .next_moved = true, |
| 5490 | | .bubbles_moved = false, |
| 5491 | | .enable_next_moved = true, |
| 5492 | | }) }; |
| 5493 | | elf.nodes.appendAssumeCapacity(.{ .input_member = input_index }); |
| 5620 | }); |
| 5621 | elf.nodes.appendAssumeCapacity(.{ .archive_input_member = input_index }); |
| 5622 | input.extra = .{ .node = new_member_ni }; |
| 5494 | 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 | 5659 | // Since we are not emitting the archive symbol table (yet?) we do not need to parse |
| 5497 | 5660 | // the symbols in this input. |
| 5498 | 5661 | return; |
| ... | ... | @@ -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 | 6111 | fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { |
| 5909 | 6112 | const comp = elf.base.comp; |
| 5910 | 6113 | const gpa = comp.gpa; |
| ... | ... | @@ -7072,12 +7275,12 @@ fn addGotRelocAssumeCapacity( |
| 7072 | 7275 | switch (elf.getNode(node)) { |
| 7073 | 7276 | .archive, |
| 7074 | 7277 | .archive_header, |
| 7075 | | .archive_elf_footer, |
| 7278 | .archive_input_member, |
| 7279 | .archive_elf_member_header, |
| 7076 | 7280 | .elf, |
| 7077 | 7281 | .ehdr, |
| 7078 | 7282 | .shdr, |
| 7079 | 7283 | .segment, |
| 7080 | | .input_member, |
| 7081 | 7284 | .copied_global, |
| 7082 | 7285 | => unreachable, // cannot contain relocs, |
| 7083 | 7286 | .section, |
| ... | ... | @@ -7129,6 +7332,7 @@ fn addGotRelocAssumeCapacity( |
| 7129 | 7332 | }); |
| 7130 | 7333 | } |
| 7131 | 7334 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 7335 | assert(elf.ehdrType() != .REL); |
| 7132 | 7336 | const entry_value: union(enum) { |
| 7133 | 7337 | unsigned: u64, |
| 7134 | 7338 | signed: i64, |
| ... | ... | @@ -7514,6 +7718,17 @@ fn flushInner( |
| 7514 | 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 | 7732 | elf.flushDynamic(); |
| 7518 | 7733 | |
| 7519 | 7734 | const entry_addr: u64 = entry: { |
| ... | ... | @@ -7545,6 +7760,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7545 | 7760 | const comp = elf.base.comp; |
| 7546 | 7761 | const diags = &comp.link_diags; |
| 7547 | 7762 | |
| 7763 | elf.mf.nodes_lock.lock(); |
| 7764 | defer elf.mf.nodes_lock.unlock(); |
| 7765 | |
| 7548 | 7766 | assert(elf.pending_uavs.items.len == 0); |
| 7549 | 7767 | for (&elf.lazy.values) |*lazy| { |
| 7550 | 7768 | assert(lazy.pending_index == lazy.map.count()); |
| ... | ... | @@ -7698,7 +7916,7 @@ fn idleProgNode( |
| 7698 | 7916 | return prog_node.start(name: switch (node) { |
| 7699 | 7917 | else => |tag| @tagName(tag), |
| 7700 | 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 | 7920 | ii.path(elf).fmtEscapeString(), |
| 7703 | 7921 | fmtMemberString(ii.member(elf)), |
| 7704 | 7922 | }) catch &name, |
| ... | ... | @@ -7825,7 +8043,6 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { |
| 7825 | 8043 | fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void { |
| 7826 | 8044 | const comp = elf.base.comp; |
| 7827 | 8045 | const io = comp.io; |
| 7828 | | const gpa = comp.gpa; |
| 7829 | 8046 | const diags = &comp.link_diags; |
| 7830 | 8047 | const path = ii.path(elf); |
| 7831 | 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 | 8050 | else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }), |
| 7834 | 8051 | }; |
| 7835 | 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 | 8071 | 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) { |
| 8072 | var w: Io.Writer = .fixed(slice[@sizeOf(std.elf.ar_hdr)..]); |
| 8073 | const n_bytes_read = w.sendFileAll(&fr, .limited(input_size)) catch |err| switch (err) { |
| 7842 | 8074 | error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{ |
| 7843 | 8075 | path.fmtEscapeString(), |
| 7844 | 8076 | fmtMemberString(ii.member(elf)), |
| 7845 | 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", .{ |
| 7850 | | path.fmtEscapeString(), |
| 7851 | | fmtMemberString(ii.member(elf)), |
| 7852 | | }); |
| 8081 | if (n_bytes_read != input_size) { |
| 8082 | return diags.fail("failed to load input \"{f}{f}\": file truncated during compilation", .{ |
| 8083 | path.fmtEscapeString(), |
| 8084 | fmtMemberString(ii.member(elf)), |
| 8085 | }); |
| 8086 | } |
| 7853 | 8087 | } |
| 7854 | 8088 | |
| 7855 | 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 | 8165 | const trace = tracy.trace(@src()); |
| 7932 | 8166 | defer trace.end(); |
| 7933 | 8167 | |
| 7934 | | elf.mf.nodes_lock.lock(); |
| 7935 | | defer elf.mf.nodes_lock.unlock(); |
| 7936 | | |
| 7937 | 8168 | switch (elf.getNode(ni)) { |
| 7938 | | .archive, .archive_header => unreachable, |
| 7939 | | .archive_elf_footer, .elf => {}, |
| 8169 | .archive => unreachable, |
| 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 | 8180 | .ehdr, .shdr => elf.flushElfOffset(ni), |
| 7941 | 8181 | .segment => |phndx| { |
| 7942 | 8182 | elf.flushElfOffset(ni); |
| ... | ... | @@ -8014,7 +8254,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 8014 | 8254 | elf.flushMovedPltSection(.plt_sec, old_addr, addr); |
| 8015 | 8255 | } |
| 8016 | 8256 | }, |
| 8017 | | .input_member => {}, |
| 8018 | 8257 | .input_section => |isi| { |
| 8019 | 8258 | const old_section_addr = isi.ptr(elf).vaddr; |
| 8020 | 8259 | const new_section_addr = elf.computeNodeVAddr(ni); |
| ... | ... | @@ -8114,6 +8353,18 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8114 | 8353 | const page_align = elf.targetPageAlign(); |
| 8115 | 8354 | const node_align = segment_ni.alignment(&elf.mf); |
| 8116 | 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 | 8368 | switch (elf.phdrSlice()) { |
| 8118 | 8369 | inline else => |phdr| { |
| 8119 | 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 | 8423 | // backwards to the start of the page. |
| 8173 | 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 |
| 8176 | | // segment fitting here. However, if we've already changed our virtual address, then |
| 8177 | | // we might as well try to reserve a bit *more* virtual address space while we're at |
| 8178 | | // it, because changing virtual address is quite disruptive (we need to re-flush a |
| 8179 | | // lot of stuff!) and giving ourselves more space will make it less likely to happen |
| 8180 | | // again. |
| 8181 | | const target_size = if (vaddr == orig_vaddr) size else size * 4; |
| 8182 | | if (vaddr + target_size <= next_page_vaddr) { |
| 8183 | | break; // hooray, we fit here! |
| 8426 | // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if |
| 8427 | // the segment is already known to be moving---making it easier to grow in-place is |
| 8428 | // the whole point of the multiplier! |
| 8429 | { |
| 8430 | const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier; |
| 8431 | if (vaddr + target_size <= next_page_vaddr) { |
| 8432 | break; // hooray, we fit here! |
| 8433 | } |
| 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 | 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 | 8474 | |
| 8193 | 8475 | // Now just swap the phdrs and update our `phndx`. |
| 8194 | 8476 | std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph); |
| 8195 | | const next_ni = elf.phdrs.items[next_phndx]; |
| 8196 | | elf.phdrs.items[phndx] = next_ni; |
| 8477 | elf.phdrs.items[phndx] = .wrap(next_ni); |
| 8197 | 8478 | elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx }; |
| 8198 | 8479 | elf.phdrs.items[next_phndx] = .wrap(segment_ni); |
| 8199 | 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 | 8494 | const trace = tracy.trace(@src()); |
| 8214 | 8495 | defer trace.end(); |
| 8215 | 8496 | |
| 8216 | | elf.mf.nodes_lock.lock(); |
| 8217 | | defer elf.mf.nodes_lock.unlock(); |
| 8218 | | |
| 8219 | 8497 | _, const size = ni.location(&elf.mf).resolve(&elf.mf); |
| 8220 | 8498 | 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"); |
| 8499 | .archive, .archive_header => {}, |
| 8500 | .archive_input_member => unreachable, |
| 8501 | .archive_elf_member_header => unreachable, |
| 8502 | .elf => if (elf.archive) |*archive| { |
| 8503 | const member_ar_hdr: *std.elf.ar_hdr = @ptrCast( |
| 8504 | archive.elf_member_header_ni.slice(&elf.mf), |
| 8505 | ); |
| 8506 | if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{size})) |size_str| { |
| 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 => {}, |
| 8233 | | .ehdr, .archive_elf_footer => unreachable, |
| 8513 | .ehdr => unreachable, |
| 8234 | 8514 | .shdr => {}, |
| 8235 | 8515 | .segment => |phndx| switch (elf.phdrSlice()) { |
| 8236 | 8516 | inline else => |phdr| { |
| ... | ... | @@ -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 | 8590 | const trace = tracy.trace(@src()); |
| 8311 | 8591 | defer trace.end(); |
| 8312 | 8592 | |
| 8313 | | elf.mf.nodes_lock.lock(); |
| 8314 | | defer elf.mf.nodes_lock.unlock(); |
| 8315 | | |
| 8316 | 8593 | switch (elf.getNode(ni)) { |
| 8317 | 8594 | .archive, |
| 8318 | | .archive_elf_footer, |
| 8595 | .archive_input_member, |
| 8596 | .archive_elf_member_header, |
| 8597 | .elf, |
| 8319 | 8598 | .ehdr, |
| 8320 | 8599 | .shdr, |
| 8321 | 8600 | .segment, |
| ... | ... | @@ -8327,54 +8606,32 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! |
| 8327 | 8606 | .lazy_code, |
| 8328 | 8607 | .lazy_const_data, |
| 8329 | 8608 | => 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; |
| 8609 | |
| 8610 | .archive_header => { |
| 8611 | const archive = &elf.archive.?; |
| 8612 | |
| 8613 | // Because we can't just throw padding bytes in the middle of an archive file, we need |
| 8614 | // the member name string table (the "//" member) to absorb all the padding bytes |
| 8615 | // between it (in the `.archive_header` node) and the first actual member. |
| 8616 | const next_member_ni = ni.next(&elf.mf).unwrap() orelse { |
| 8617 | // I guess there are no link inputs yet? But there will be eventually! |
| 8618 | return; |
| 8374 | 8619 | }; |
| 8375 | | if (update_size) _ = std.mem.print(&elf.arHdrPtr(ni).ar_size, "{d:<10}", .{ |
| 8376 | | member_size, |
| 8377 | | }) catch @panic("archive member too large"); |
| 8620 | const next_member_offset: u64, _ = next_member_ni.location(&elf.mf).resolve(&elf.mf); |
| 8621 | const strtab_member_offset = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr); |
| 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 | } |