| ... | ... | @@ -3558,48 +3558,55 @@ fn initHeaders( |
| 3558 | 3558 | .EXEC, .DYN => {}, |
| 3559 | 3559 | } |
| 3560 | 3560 | 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; |
| 3561 | break :ph .{ |
| 3562 | .{ |
| 3563 | .phdr = phndx: { |
| 3564 | defer phnum += 1; |
| 3565 | break :phndx phnum; |
| 3566 | }, |
| 3567 | .interp = if (maybe_interp) |_| phndx: { |
| 3568 | defer phnum += 1; |
| 3569 | break :phndx phnum; |
| 3570 | } else undefined, |
| 3571 | .rodata = phndx: { |
| 3572 | defer phnum += 1; |
| 3573 | break :phndx phnum; |
| 3574 | }, |
| 3575 | .text = phndx: { |
| 3576 | defer phnum += 1; |
| 3577 | break :phndx phnum; |
| 3578 | }, |
| 3579 | .plt = if (plt.got_plt == null) phndx: { |
| 3580 | defer phnum += 1; |
| 3581 | break :phndx phnum; |
| 3582 | } else undefined, |
| 3583 | // `data` must be assigned after all other loadable segments so that it has the greatest |
| 3584 | // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions` |
| 3585 | // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC). |
| 3586 | .data = phndx: { |
| 3587 | defer phnum += 1; |
| 3588 | break :phndx phnum; |
| 3589 | }, |
| 3590 | .tls = if (comp.config.any_non_single_threaded) phndx: { |
| 3591 | defer phnum += 1; |
| 3592 | break :phndx phnum; |
| 3593 | } else undefined, |
| 3594 | .dynamic = if (have_dynamic_section) phndx: { |
| 3595 | defer phnum += 1; |
| 3596 | break :phndx phnum; |
| 3597 | } else undefined, |
| 3598 | .relro = phndx: { |
| 3599 | defer phnum += 1; |
| 3600 | break :phndx phnum; |
| 3601 | }, |
| 3602 | .gnu_stack = phndx: { |
| 3603 | defer phnum += 1; |
| 3604 | break :phndx phnum; |
| 3605 | }, |
| 3601 | 3606 | }, |
| 3602 | | }, phnum }; |
| 3607 | // (I don't actually want the trailing comma below, but a `zig fmt` bug forces it.) |
| 3608 | phnum, |
| 3609 | }; |
| 3603 | 3610 | }; |
| 3604 | 3611 | |
| 3605 | 3612 | const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_footer |
| ... | ... | @@ -4539,6 +4546,22 @@ fn initHeaders( |
| 4539 | 4546 | break :str try elf.string(.dynstr, slice); |
| 4540 | 4547 | }, |
| 4541 | 4548 | }; |
| 4549 | |
| 4550 | if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) { |
| 4551 | .none => {}, |
| 4552 | .data_last => switch (elf.phdrSlice()) { |
| 4553 | inline else => |phdr| { |
| 4554 | // Ensure that the segment after `.data` (if any) is not a loadable segment. |
| 4555 | const next_phndx = phndx.data + 1; |
| 4556 | if (next_phndx < phdr.len) { |
| 4557 | switch (elf.targetLoad(&phdr[next_phndx].type)) { |
| 4558 | .NULL, .LOAD => unreachable, // data segment should be the last loadable segment |
| 4559 | else => {}, |
| 4560 | } |
| 4561 | } |
| 4562 | }, |
| 4563 | }, |
| 4564 | }; |
| 4542 | 4565 | } |
| 4543 | 4566 | |
| 4544 | 4567 | pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void { |
| ... | ... | @@ -4888,7 +4911,31 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { |
| 4888 | 4911 | // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`. |
| 4889 | 4912 | }; |
| 4890 | 4913 | } |
| 4891 | | pub fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4914 | /// Specifies any restrictions the current target has regarding how segments are ordered in the |
| 4915 | /// virtual address space. Most targets do not have any such restrictions. |
| 4916 | fn targetSegmentLoadAddressRestrictions(elf: *const Elf) enum { |
| 4917 | none, |
| 4918 | /// The "mutable data" segment must be the last loadable segment in the virtual address space. |
| 4919 | data_last, |
| 4920 | } { |
| 4921 | return switch (elf.ehdrMachine()) { |
| 4922 | .AARCH64, |
| 4923 | .PPC64, |
| 4924 | .RISCV, |
| 4925 | .X86_64, |
| 4926 | .LOONGARCH, |
| 4927 | => .none, |
| 4928 | |
| 4929 | // SPARC uses `R_SPARC_PC{10,22}` relocations to construct pointers to the GOT, but these |
| 4930 | // relocations write an *unsigned* PC-relative offset. This cannot even be worked around by |
| 4931 | // using a larger code model, because the crt `_start` assembly always uses these specific |
| 4932 | // relocations. Therefore, to avoid relocation errors, all code must appear before the GOT |
| 4933 | // in the virtual address space. The easiest way for us to do that is to ensure that the |
| 4934 | // "mutable data" segment, containing the GOT, is the last segment in the address space. |
| 4935 | .SPARCV9 => .data_last, |
| 4936 | }; |
| 4937 | } |
| 4938 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4892 | 4939 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 4893 | 4940 | const Child = pointer_ty.child; |
| 4894 | 4941 | const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child); |
| ... | ... | @@ -8114,6 +8161,18 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8114 | 8161 | const page_align = elf.targetPageAlign(); |
| 8115 | 8162 | const node_align = segment_ni.alignment(&elf.mf); |
| 8116 | 8163 | const ph_align = page_align.max(node_align); |
| 8164 | |
| 8165 | // If we determine that the segment's virtual address needs to move, then it's a good idea to |
| 8166 | // make it less likely that it needs to move *again* in the future, because it is expensive to |
| 8167 | // change a segment's load address (a lot of re-flushing is necessary). To do that, we reserve |
| 8168 | // more virtual address space than we need (multiplying the actual size by this value). That |
| 8169 | // way, there will usually be padding between segments which they can grow into. |
| 8170 | // |
| 8171 | // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases where |
| 8172 | // virtual address space is constrained. For instance, 32-bit targets, or targets where short |
| 8173 | // PC-relative relocations between segments are common. |
| 8174 | const reserve_size_multiplier = 4; |
| 8175 | |
| 8117 | 8176 | switch (elf.phdrSlice()) { |
| 8118 | 8177 | inline else => |phdr| { |
| 8119 | 8178 | const offset = elf.targetLoad(&phdr[orig_phndx].offset); |
| ... | ... | @@ -8172,15 +8231,46 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8172 | 8231 | // backwards to the start of the page. |
| 8173 | 8232 | const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits()); |
| 8174 | 8233 | |
| 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! |
| 8234 | // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if |
| 8235 | // the segment is already known to be moving---making it easier to grow in-place is |
| 8236 | // the whole point of the multiplier! |
| 8237 | { |
| 8238 | const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier; |
| 8239 | if (vaddr + target_size <= next_page_vaddr) { |
| 8240 | break; // hooray, we fit here! |
| 8241 | } |
| 8242 | } |
| 8243 | |
| 8244 | const next_ni = elf.phdrs.items[next_phndx].unwrap().?; |
| 8245 | |
| 8246 | // This segment don't fit here, but before deciding how to proceed, we need to |
| 8247 | // consider any target-specific restrictions we are subject to. |
| 8248 | switch (elf.targetSegmentLoadAddressRestrictions()) { |
| 8249 | .none => {}, |
| 8250 | .data_last => if (next_ni == elf.ni.data) { |
| 8251 | // We can't leapfrog over the data segment. Instead, that segment just needs |
| 8252 | // to be shifted forwards to make space for us, and we'll then `break` with |
| 8253 | // our current vaddr. |
| 8254 | |
| 8255 | if (next_phndx + 1 < phdr.len) switch (elf.targetLoad(&phdr[next_phndx + 1].type)) { |
| 8256 | .NULL, .LOAD => unreachable, // data segment should be the last loadable segment |
| 8257 | else => {}, |
| 8258 | }; |
| 8259 | |
| 8260 | const free_vaddr = vaddr + size * reserve_size_multiplier; |
| 8261 | |
| 8262 | const next_align = page_align.max(next_ni.alignment(&elf.mf)); |
| 8263 | const next_offset = elf.targetLoad(&next_ph.offset); |
| 8264 | const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits(); |
| 8265 | |
| 8266 | // This logic for updating the data segment's vaddr is identical to how we |
| 8267 | // will update the vaddr of `phndx` when we break from the loop. |
| 8268 | elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr)); |
| 8269 | elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr)); |
| 8270 | try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 8271 | |
| 8272 | break; |
| 8273 | }, |
| 8184 | 8274 | } |
| 8185 | 8275 | |
| 8186 | 8276 | // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But |
| ... | ... | @@ -8192,8 +8282,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 8192 | 8282 | |
| 8193 | 8283 | // Now just swap the phdrs and update our `phndx`. |
| 8194 | 8284 | 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; |
| 8285 | elf.phdrs.items[phndx] = .wrap(next_ni); |
| 8197 | 8286 | elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx }; |
| 8198 | 8287 | elf.phdrs.items[next_phndx] = .wrap(segment_ni); |
| 8199 | 8288 | elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) }; |