authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-13 21:08:02+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-16 10:37:19+01:00
log99e54fc4209af72eb34b7d32bc5efd28a2a7b1ba
treec6db30640d3df6812bca14391b7b89fada61f518
parentbd805e82b9984bc388217415d2a814550d4033a8
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: many alignment fixes

Notably, this ensures that loadable segments are well-aligned in all cases, and does so *without* requiring segments to have well-aligned file offsets (which would result in needless bloat in the ELF file). It also fixes a bunch of small alignment bugs across the backend, and adds correct handling for the case of an input section / NAV / UAV forcing a section, and possibly even segment, to increase its alignment. Resolves: https://codeberg.org/ziglang/zig/issues/32135

1 files changed, 353 insertions(+), 197 deletions(-)

src/link/Elf2.zig+353-197
...@@ -539,6 +539,26 @@ const Section = struct {...@@ -539,6 +539,26 @@ const Section = struct {
539 }539 }
540 }540 }
541541
542 fn ensureAligned(shndx: Index, elf: *Elf, min_align: std.mem.Alignment) Error!void {
543 switch (elf.shdrPtr(shndx)) {
544 inline else => |shdr| {
545 if (elf.targetLoad(&shdr.addralign) >= min_align.toByteUnits()) {
546 return; // already aligned
547 }
548 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));
549 },
550 }
551 const ni = shndx.get(elf).ni;
552 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
553 try ni.realign(&elf.mf, elf.base.comp.gpa, min_align, .{});
554 }
555 switch (elf.getNode(ni.parent(&elf.mf))) {
556 .elf => {},
557 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
558 else => unreachable,
559 }
560 }
561
542 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough562 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
543 /// unused space to hold `n` additional `ElfN.Rela` entries.563 /// unused space to hold `n` additional `ElfN.Rela` entries.
544 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {564 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
...@@ -3455,6 +3475,16 @@ fn initHeaders(...@@ -3455,6 +3475,16 @@ fn initHeaders(
3455 .@"64" => .@"8",3475 .@"64" => .@"8",
3456 };3476 };
34573477
3478 // Minimum alignment for an arbitrarily-chosen set of "large" nodes in the file (e.g. common
3479 // sections), to allow `MappedFile` to perform operations more efficiently. The downside to
3480 // using `elf.mf.flags.block_size` is that it causes outputs to be potentially unreproducible
3481 // across host filesystems, so in the future we may want to set this to `.@"1"` when using a
3482 // build mode that requires reproducibility.
3483 //
3484 // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it
3485 // prevents alignment bugs from being hidden by your filesystem's block alignment.
3486 const node_block_align: std.mem.Alignment = elf.mf.flags.block_size;
3487
3458 const plt: PltInfo = .fromMachine(machine);3488 const plt: PltInfo = .fromMachine(machine);
34593489
3460 const shnum: u32 = shnum: {3490 const shnum: u32 = shnum: {
...@@ -3577,7 +3607,7 @@ fn initHeaders(...@@ -3577,7 +3607,7 @@ fn initHeaders(
35773607
3578 elf.nodes.appendAssumeCapacity(.archive_header);3608 elf.nodes.appendAssumeCapacity(.archive_header);
3579 elf.ni.elf = try elf.mf.addLastChildNode(gpa, elf.ni.archive, .{3609 elf.ni.elf = try elf.mf.addLastChildNode(gpa, elf.ni.archive, .{
3580 .alignment = elf.mf.flags.block_size.max(.@"2"),3610 .alignment = node_block_align.max(.@"2"),
3581 .next_moved = true,3611 .next_moved = true,
3582 .bubbles_moved = false,3612 .bubbles_moved = false,
3583 .enable_next_moved = true,3613 .enable_next_moved = true,
...@@ -3648,36 +3678,16 @@ fn initHeaders(...@@ -3648,36 +3678,16 @@ fn initHeaders(
36483678
3649 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3679 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3650 .size = 1 * entsize.sh, // as above, only the null shdr initially3680 .size = 1 * entsize.sh, // as above, only the null shdr initially
3651 .alignment = elf.mf.flags.block_size,3681 .alignment = addr_align.max(node_block_align),
3652 .moved = true,3682 .moved = true,
3653 .resized = true,3683 .resized = true,
3654 });3684 });
3655 elf.nodes.appendAssumeCapacity(.shdr);3685 elf.nodes.appendAssumeCapacity(.shdr);
36563686
3657 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {3687 if (@"type" != .REL) {
3658 .AARCH64 => 0x10000,
3659 .LOONGARCH => 0x4000,
3660 .PPC64 => 0x10000,
3661 .RISCV => 0x1000,
3662 .SPARCV9 => 0x100000,
3663 .X86_64 => 0x1000,
3664
3665 //.@"68K" => 0x2000,
3666 //.AMDGPU => 0x10000,
3667 //.ARC_COMPACT2 => 0x2000,
3668 //.AVR => 0x1,
3669 //.BPF => 0x100000,
3670 //.MIPS => 0x10000,
3671 //.MSP430 => 0x4,
3672 //.PPC => 0x10000,
3673 //.QDSP6 => 0x10000,
3674 //.SPARC => 0x10000,
3675 //.SPARC32PLUS => 0x10000,
3676 });
3677
3678 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {
3679 elf.ni.rodata = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3688 elf.ni.rodata = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3680 .alignment = elf.mf.flags.block_size,3689 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
3690 .alignment = node_block_align.max(addr_align),
3681 .moved = true,3691 .moved = true,
3682 .bubbles_moved = false,3692 .bubbles_moved = false,
3683 });3693 });
...@@ -3686,7 +3696,7 @@ fn initHeaders(...@@ -3686,7 +3696,7 @@ fn initHeaders(
36863696
3687 elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{3697 elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3688 .size = @as(u64, phnum) * entsize.ph,3698 .size = @as(u64, phnum) * entsize.ph,
3689 .alignment = addr_align,3699 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
3690 .moved = true,3700 .moved = true,
3691 .resized = true,3701 .resized = true,
3692 .bubbles_moved = false,3702 .bubbles_moved = false,
...@@ -3695,7 +3705,7 @@ fn initHeaders(...@@ -3695,7 +3705,7 @@ fn initHeaders(
3695 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;3705 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
36963706
3697 elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3707 elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3698 .alignment = elf.mf.flags.block_size,3708 .alignment = node_block_align,
3699 .moved = true,3709 .moved = true,
3700 .bubbles_moved = false,3710 .bubbles_moved = false,
3701 });3711 });
...@@ -3703,7 +3713,8 @@ fn initHeaders(...@@ -3703,7 +3713,8 @@ fn initHeaders(
3703 elf.phdrs.items[phndx.text] = elf.ni.text;3713 elf.phdrs.items[phndx.text] = elf.ni.text;
37043714
3705 elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3715 elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3706 .alignment = elf.mf.flags.block_size,3716 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node
3717 .alignment = node_block_align.max(addr_align),
3707 .moved = true,3718 .moved = true,
3708 .bubbles_moved = false,3719 .bubbles_moved = false,
3709 });3720 });
...@@ -3711,36 +3722,66 @@ fn initHeaders(...@@ -3711,36 +3722,66 @@ fn initHeaders(
3711 elf.phdrs.items[phndx.data] = elf.ni.data;3722 elf.phdrs.items[phndx.data] = elf.ni.data;
37123723
3713 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{3724 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3714 .alignment = elf.mf.flags.block_size,3725 // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one
3726 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
3727 .alignment = node_block_align.max(addr_align),
3715 .moved = true,3728 .moved = true,
3716 .bubbles_moved = false,3729 .bubbles_moved = false,
3717 });3730 });
3718 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });3731 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3719 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;3732 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
37203733
3721 elf.phdrs.items[phndx.gnu_stack] = .none;3734 if (comp.config.any_non_single_threaded) {
3735 elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
3736 .alignment = node_block_align,
3737 .moved = true,
3738 .bubbles_moved = false,
3739 });
3740 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3741 elf.phdrs.items[phndx.tls] = elf.ni.tls;
3742 }
37223743
3723 break :ph_vaddr switch (elf.ehdrType()) {3744 elf.phdrs.items[phndx.gnu_stack] = .none;
3724 .REL, .DYN => 0,3745 }
3725 .EXEC => switch (machine) {
3726 .AARCH64,
3727 => 0x200000,
3728 .LOONGARCH => 0x10000,
3729 .PPC64 => 0x10000000,
3730 .RISCV => 0x10000,
3731 .SPARCV9 => 0x100000,
3732 .X86_64 => 0x200000,
3733 },
3734 };
3735 } else undefined;
3736 switch (class) {3746 switch (class) {
3737 .NONE, _ => unreachable,3747 .NONE, _ => unreachable,
3738 inline else => |ct_class| {3748 inline else => |ct_class| {
3739 const ElfN = ct_class.ElfN();3749 const ElfN = ct_class.ElfN();
3740 const target_endian = elf.targetEndian();3750 const target_endian = elf.targetEndian();
37413751
3742 if (@"type" != .REL) {3752 populate_phdrs: {
3753 // Initially we will give every `PT_LOAD` segment this address. When we re-allocate
3754 // segments in the virtual address space in `flushMoved` and `flushResized`, we will
3755 // move some segments to higher addresses to prevent overlap. This address therefore
3756 // becomes the image's "base address"; i.e. the first `PT_LOAD` segment will start
3757 // at this address. The base address could eventually end up higher than this due to
3758 // how we re-allocate the address space, but never lower.
3759 const base_vaddr: u64 = switch (@"type") {
3760 .REL => break :populate_phdrs,
3761 .DYN => 0,
3762 .EXEC => switch (machine) {
3763 .AARCH64 => 0x200000,
3764 .LOONGARCH => 0x10000,
3765 .PPC64 => 0x10000000,
3766 .RISCV => 0x10000,
3767 .SPARCV9 => 0x100000,
3768 .X86_64 => 0x200000,
3769 },
3770 };
3771
3772 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the
3773 // binary, their *nodes* are not aligned to this boundary---ELF only requires that
3774 // ecah segment's address equals its file offset modulo this alignment, not that its
3775 // file offset is actually aligned to this boundary. This property is maintained by
3776 // the segment virtual address space allocation logic.
3777 const page_align = elf.targetPageAlign();
3778
3779 // We will populate elements in this slice (by index). The `PT_LOAD` segments are
3780 // actually `PT_NULL` for now, because we initialize `filesz` and `memsz` to zero.
3781 // Any which end up non-empty will have their size populated (and their type set to
3782 // `PT_LOAD`) by the segment virtual address space allocation logic.
3743 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));3783 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));
3784
3744 const ph_phdr = &phdr[phndx.phdr];3785 const ph_phdr = &phdr[phndx.phdr];
3745 ph_phdr.* = .{3786 ph_phdr.* = .{
3746 .type = .PHDR,3787 .type = .PHDR,
...@@ -3752,7 +3793,6 @@ fn initHeaders(...@@ -3752,7 +3793,6 @@ fn initHeaders(
3752 .flags = .{ .R = true },3793 .flags = .{ .R = true },
3753 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),3794 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),
3754 };3795 };
3755 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
37563796
3757 if (maybe_interp) |_| {3797 if (maybe_interp) |_| {
3758 const ph_interp = &phdr[phndx.interp];3798 const ph_interp = &phdr[phndx.interp];
...@@ -3766,53 +3806,43 @@ fn initHeaders(...@@ -3766,53 +3806,43 @@ fn initHeaders(
3766 .flags = .{ .R = true },3806 .flags = .{ .R = true },
3767 .@"align" = 1,3807 .@"align" = 1,
3768 };3808 };
3769 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
3770 }3809 }
37713810
3772 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);
3773 const ph_rodata = &phdr[phndx.rodata];3811 const ph_rodata = &phdr[phndx.rodata];
3774 ph_rodata.* = .{3812 ph_rodata.* = .{
3775 .type = if (rodata_size == 0) .NULL else .LOAD,3813 .type = .NULL,
3776 .offset = 0,3814 .offset = 0,
3777 .vaddr = ph_vaddr,3815 .vaddr = @intCast(base_vaddr),
3778 .paddr = ph_vaddr,3816 .paddr = @intCast(base_vaddr),
3779 .filesz = @intCast(rodata_size),3817 .filesz = 0,
3780 .memsz = @intCast(rodata_size),3818 .memsz = 0,
3781 .flags = .{ .R = true },3819 .flags = .{ .R = true },
3782 .@"align" = @intCast(elf.ni.rodata.alignment(&elf.mf).max(page_align).toByteUnits()),3820 .@"align" = @intCast(page_align.toByteUnits()),
3783 };3821 };
3784 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
3785 ph_vaddr += @intCast(rodata_size);
37863822
3787 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);
3788 const ph_text = &phdr[phndx.text];3823 const ph_text = &phdr[phndx.text];
3789 ph_text.* = .{3824 ph_text.* = .{
3790 .type = if (text_size == 0) .NULL else .LOAD,3825 .type = .NULL,
3791 .offset = 0,3826 .offset = 0,
3792 .vaddr = ph_vaddr,3827 .vaddr = @intCast(base_vaddr),
3793 .paddr = ph_vaddr,3828 .paddr = @intCast(base_vaddr),
3794 .filesz = @intCast(text_size),3829 .filesz = 0,
3795 .memsz = @intCast(text_size),3830 .memsz = 0,
3796 .flags = .{ .R = true, .X = true },3831 .flags = .{ .R = true, .X = true },
3797 .@"align" = @intCast(elf.ni.text.alignment(&elf.mf).max(page_align).toByteUnits()),3832 .@"align" = @intCast(page_align.toByteUnits()),
3798 };3833 };
3799 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
3800 ph_vaddr += @intCast(text_size);
38013834
3802 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);
3803 const ph_data = &phdr[phndx.data];3835 const ph_data = &phdr[phndx.data];
3804 ph_data.* = .{3836 ph_data.* = .{
3805 .type = if (data_size == 0) .NULL else .LOAD,3837 .type = .NULL,
3806 .offset = 0,3838 .offset = 0,
3807 .vaddr = ph_vaddr,3839 .vaddr = @intCast(base_vaddr),
3808 .paddr = ph_vaddr,3840 .paddr = @intCast(base_vaddr),
3809 .filesz = @intCast(data_size),3841 .filesz = 0,
3810 .memsz = @intCast(data_size),3842 .memsz = 0,
3811 .flags = .{ .R = true, .W = true },3843 .flags = .{ .R = true, .W = true },
3812 .@"align" = @intCast(elf.ni.data.alignment(&elf.mf).max(page_align).toByteUnits()),3844 .@"align" = @intCast(page_align.toByteUnits()),
3813 };3845 };
3814 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
3815 ph_vaddr += @intCast(data_size);
38163846
3817 if (comp.config.any_non_single_threaded) {3847 if (comp.config.any_non_single_threaded) {
3818 const ph_tls = &phdr[phndx.tls];3848 const ph_tls = &phdr[phndx.tls];
...@@ -3824,9 +3854,8 @@ fn initHeaders(...@@ -3824,9 +3854,8 @@ fn initHeaders(
3824 .filesz = 0,3854 .filesz = 0,
3825 .memsz = 0,3855 .memsz = 0,
3826 .flags = .{ .R = true },3856 .flags = .{ .R = true },
3827 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),3857 .@"align" = @intCast(elf.ni.tls.alignment(&elf.mf).toByteUnits()),
3828 };3858 };
3829 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
3830 }3859 }
38313860
3832 if (have_dynamic_section) {3861 if (have_dynamic_section) {
...@@ -3841,7 +3870,6 @@ fn initHeaders(...@@ -3841,7 +3870,6 @@ fn initHeaders(
3841 .flags = .{ .R = true, .W = true },3870 .flags = .{ .R = true, .W = true },
3842 .@"align" = @intCast(addr_align.toByteUnits()),3871 .@"align" = @intCast(addr_align.toByteUnits()),
3843 };3872 };
3844 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
3845 }3873 }
38463874
3847 const ph_relro = &phdr[phndx.relro];3875 const ph_relro = &phdr[phndx.relro];
...@@ -3853,9 +3881,8 @@ fn initHeaders(...@@ -3853,9 +3881,8 @@ fn initHeaders(
3853 .filesz = 0,3881 .filesz = 0,
3854 .memsz = 0,3882 .memsz = 0,
3855 .flags = .{ .R = true },3883 .flags = .{ .R = true },
3856 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),3884 .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()),
3857 };3885 };
3858 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro);
38593886
3860 const ph_gnu_stack = &phdr[phndx.gnu_stack];3887 const ph_gnu_stack = &phdr[phndx.gnu_stack];
3861 ph_gnu_stack.* = .{3888 ph_gnu_stack.* = .{
...@@ -3868,7 +3895,10 @@ fn initHeaders(...@@ -3868,7 +3895,10 @@ fn initHeaders(
3868 .flags = .{ .R = true, .W = true },3895 .flags = .{ .R = true, .W = true },
3869 .@"align" = 1,3896 .@"align" = 1,
3870 };3897 };
3871 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_gnu_stack);3898
3899 if (target_endian != std.lang.Endian.native) {
3900 std.mem.byteSwapAllElements(ElfN.Phdr, phdr);
3901 }
3872 }3902 }
38733903
3874 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));3904 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
...@@ -3896,7 +3926,7 @@ fn initHeaders(...@@ -3896,7 +3926,7 @@ fn initHeaders(
3896 .size = @sizeOf(ElfN.Sym) * 1,3926 .size = @sizeOf(ElfN.Sym) * 1,
3897 .addralign = addr_align,3927 .addralign = addr_align,
3898 .entsize = @sizeOf(ElfN.Sym),3928 .entsize = @sizeOf(ElfN.Sym),
3899 .node_align = elf.mf.flags.block_size,3929 .node_align = node_block_align,
3900 .info = 1, // index of first non-local symbol3930 .info = 1, // index of first non-local symbol
3901 }));3931 }));
3902 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));3932 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
...@@ -3918,7 +3948,7 @@ fn initHeaders(...@@ -3918,7 +3948,7 @@ fn initHeaders(
3918 .type = .STRTAB,3948 .type = .STRTAB,
3919 .size = 1,3949 .size = 1,
3920 .entsize = 1,3950 .entsize = 1,
3921 .node_align = elf.mf.flags.block_size,3951 .node_align = node_block_align,
3922 }));3952 }));
3923 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;3953 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;
39243954
...@@ -3930,7 +3960,7 @@ fn initHeaders(...@@ -3930,7 +3960,7 @@ fn initHeaders(
3930 .type = .STRTAB,3960 .type = .STRTAB,
3931 .size = 1,3961 .size = 1,
3932 .entsize = 1,3962 .entsize = 1,
3933 .node_align = elf.mf.flags.block_size,3963 .node_align = node_block_align,
3934 }));3964 }));
3935 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;3965 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;
3936 switch (elf.shdrPtr(.symtab)) {3966 switch (elf.shdrPtr(.symtab)) {
...@@ -3940,22 +3970,22 @@ fn initHeaders(...@@ -3940,22 +3970,22 @@ fn initHeaders(
3940 assert(.rodata == try elf.addSection(elf.ni.rodata, .{3970 assert(.rodata == try elf.addSection(elf.ni.rodata, .{
3941 .name = ".rodata",3971 .name = ".rodata",
3942 .flags = .{ .ALLOC = true },3972 .flags = .{ .ALLOC = true },
3943 .addralign = elf.mf.flags.block_size,3973 .node_align = node_block_align,
3944 }));3974 }));
3945 assert(.text == try elf.addSection(elf.ni.text, .{3975 assert(.text == try elf.addSection(elf.ni.text, .{
3946 .name = ".text",3976 .name = ".text",
3947 .flags = .{ .ALLOC = true, .EXECINSTR = true },3977 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3948 .addralign = elf.mf.flags.block_size,3978 .node_align = node_block_align,
3949 }));3979 }));
3950 assert(.data == try elf.addSection(elf.ni.data, .{3980 assert(.data == try elf.addSection(elf.ni.data, .{
3951 .name = ".data",3981 .name = ".data",
3952 .flags = .{ .WRITE = true, .ALLOC = true },3982 .flags = .{ .WRITE = true, .ALLOC = true },
3953 .addralign = elf.mf.flags.block_size,3983 .node_align = node_block_align,
3954 }));3984 }));
3955 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{3985 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
3956 .name = ".data.rel.ro",3986 .name = ".data.rel.ro",
3957 .flags = .{ .WRITE = true, .ALLOC = true },3987 .flags = .{ .WRITE = true, .ALLOC = true },
3958 .addralign = elf.mf.flags.block_size,3988 .node_align = node_block_align,
3959 }));3989 }));
3960 if (@"type" != .REL) {3990 if (@"type" != .REL) {
3961 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{3991 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
...@@ -3992,13 +4022,13 @@ fn initHeaders(...@@ -3992,13 +4022,13 @@ fn initHeaders(
3992 },4022 },
3993 .size = plt.entry_size * plt.header_entries,4023 .size = plt.entry_size * plt.header_entries,
3994 .addralign = plt.@"align",4024 .addralign = plt.@"align",
3995 .node_align = elf.mf.flags.block_size,4025 .node_align = node_block_align,
3996 });4026 });
3997 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{4027 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
3998 .name = ".plt.sec",4028 .name = ".plt.sec",
3999 .flags = .{ .ALLOC = true, .EXECINSTR = true },4029 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4000 .addralign = plt.@"align",4030 .addralign = plt.@"align",
4001 .node_align = elf.mf.flags.block_size,4031 .node_align = node_block_align,
4002 });4032 });
4003 if (maybe_interp) |interp| {4033 if (maybe_interp) |interp| {
4004 const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{4034 const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
...@@ -4021,6 +4051,7 @@ fn initHeaders(...@@ -4021,6 +4051,7 @@ fn initHeaders(
4021 sec_interp[interp.len] = 0;4051 sec_interp[interp.len] = 0;
4022 }4052 }
4023 if (have_dynamic_section) {4053 if (have_dynamic_section) {
4054 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));
4024 const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{4055 const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{
4025 .alignment = addr_align,4056 .alignment = addr_align,
4026 .moved = true,4057 .moved = true,
...@@ -4035,7 +4066,7 @@ fn initHeaders(...@@ -4035,7 +4066,7 @@ fn initHeaders(
4035 .flags = .{ .ALLOC = true },4066 .flags = .{ .ALLOC = true },
4036 .size = 1,4067 .size = 1,
4037 .entsize = 1,4068 .entsize = 1,
4038 .node_align = elf.mf.flags.block_size,4069 .node_align = node_block_align,
4039 });4070 });
4040 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;4071 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;
4041 elf.shndx.dynstr = dynstr_shndx;4072 elf.shndx.dynstr = dynstr_shndx;
...@@ -4053,7 +4084,7 @@ fn initHeaders(...@@ -4053,7 +4084,7 @@ fn initHeaders(
4053 .info = 1,4084 .info = 1,
4054 .addralign = addr_align,4085 .addralign = addr_align,
4055 .entsize = @sizeOf(Sym),4086 .entsize = @sizeOf(Sym),
4056 .node_align = elf.mf.flags.block_size,4087 .node_align = node_block_align,
4057 });4088 });
4058 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));4089 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));
4059 dynsym_null.* = .{4090 dynsym_null.* = .{
...@@ -4081,7 +4112,7 @@ fn initHeaders(...@@ -4081,7 +4112,7 @@ fn initHeaders(
4081 .link = elf.shndx.dynsym.toSection().?,4112 .link = elf.shndx.dynsym.toSection().?,
4082 .addralign = addr_align,4113 .addralign = addr_align,
4083 .entsize = rela_size,4114 .entsize = rela_size,
4084 .node_align = elf.mf.flags.block_size,4115 .node_align = node_block_align,
4085 });4116 });
4086 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{4117 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
4087 .name = ".rela.plt",4118 .name = ".rela.plt",
...@@ -4091,7 +4122,7 @@ fn initHeaders(...@@ -4091,7 +4122,7 @@ fn initHeaders(
4091 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,4122 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,
4092 .addralign = addr_align,4123 .addralign = addr_align,
4093 .entsize = rela_size,4124 .entsize = rela_size,
4094 .node_align = elf.mf.flags.block_size,4125 .node_align = node_block_align,
4095 });4126 });
4096 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{4127 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{
4097 .name = ".dynamic",4128 .name = ".dynamic",
...@@ -4198,15 +4229,6 @@ fn initHeaders(...@@ -4198,15 +4229,6 @@ fn initHeaders(
4198 .SPARCV9 => {},4229 .SPARCV9 => {},
4199 }4230 }
4200 }4231 }
4201 if (comp.config.any_non_single_threaded) {
4202 elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
4203 .alignment = elf.mf.flags.block_size,
4204 .moved = true,
4205 .bubbles_moved = false,
4206 });
4207 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
4208 elf.phdrs.items[phndx.tls] = elf.ni.tls;
4209 }
42104232
4211 // Populate reserved GOT words.4233 // Populate reserved GOT words.
4212 switch (machine) {4234 switch (machine) {
...@@ -4382,7 +4404,7 @@ fn initHeaders(...@@ -4382,7 +4404,7 @@ fn initHeaders(
4382 if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{4404 if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{
4383 .name = ".tdata",4405 .name = ".tdata",
4384 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },4406 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
4385 .addralign = elf.mf.flags.block_size,4407 .node_align = node_block_align,
4386 });4408 });
43874409
4388 assert(elf.nodes.len == expected_nodes_len);4410 assert(elf.nodes.len == expected_nodes_len);
...@@ -4648,6 +4670,28 @@ fn ehdrType(elf: *const Elf) EhdrType {...@@ -4648,6 +4670,28 @@ fn ehdrType(elf: *const Elf) EhdrType {
4648fn targetPtrSize(elf: *const Elf) u8 {4670fn targetPtrSize(elf: *const Elf) u8 {
4649 return elf.identClass().size();4671 return elf.identClass().size();
4650}4672}
4673fn targetPageAlign(elf: *const Elf) std.mem.Alignment {
4674 return .fromByteUnits(switch (elf.ehdrMachine()) {
4675 .AARCH64 => 0x10000,
4676 .LOONGARCH => 0x4000,
4677 .PPC64 => 0x10000,
4678 .RISCV => 0x1000,
4679 .SPARCV9 => 0x100000,
4680 .X86_64 => 0x1000,
4681
4682 //.@"68K" => 0x2000,
4683 //.AMDGPU => 0x10000,
4684 //.ARC_COMPACT2 => 0x2000,
4685 //.AVR => 0x1,
4686 //.BPF => 0x100000,
4687 //.MIPS => 0x10000,
4688 //.MSP430 => 0x4,
4689 //.PPC => 0x10000,
4690 //.QDSP6 => 0x10000,
4691 //.SPARC => 0x10000,
4692 //.SPARC32PLUS => 0x10000,
4693 });
4694}
4651fn targetEndian(elf: *const Elf) std.lang.Endian {4695fn targetEndian(elf: *const Elf) std.lang.Endian {
4652 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);4696 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);
4653 return ident_data.endian();4697 return ident_data.endian();
...@@ -4790,7 +4834,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {...@@ -4790,7 +4834,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
4790 switch (elf.identClass()) {4834 switch (elf.identClass()) {
4791 .NONE, _ => unreachable,4835 .NONE, _ => unreachable,
4792 inline else => |class| {4836 inline else => |class| {
4793 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(raw_slice));4837 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(
4838 raw_slice[0 .. elf.shdrs.items.len * @sizeOf(class.ElfN().Shdr)],
4839 ));
4794 const shdr_ptr = &shdr_slice[@backingInt(shndx)];4840 const shdr_ptr = &shdr_slice[@backingInt(shndx)];
4795 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);4841 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
4796 },4842 },
...@@ -4845,7 +4891,6 @@ fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {...@@ -4845,7 +4891,6 @@ fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
4845fn mapInputSection(elf: *Elf, opts: struct {4891fn mapInputSection(elf: *Elf, opts: struct {
4846 name: []const u8,4892 name: []const u8,
4847 flags: std.elf.SHF,4893 flags: std.elf.SHF,
4848 addralign: std.elf.Xword,
4849 entsize: std.elf.Xword,4894 entsize: std.elf.Xword,
4850}) (Error || error{4895}) (Error || error{
4851 UnsupportedSectionFlags,4896 UnsupportedSectionFlags,
...@@ -4917,16 +4962,12 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4917,16 +4962,12 @@ fn mapInputSection(elf: *Elf, opts: struct {
4917 flags.COMPRESSED = false;4962 flags.COMPRESSED = false;
4918 break :flags flags;4963 break :flags flags;
4919 },4964 },
4920 .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
4921 usize,
4922 @intCast(@max(opts.addralign, 1)),
4923 )),
4924 .entsize = std.math.lossyCast(u32, opts.entsize),4965 .entsize = std.math.lossyCast(u32, opts.entsize),
4925 });4966 });
4926 };4967 };
4927 // Validate that the input is compatible with this section...
4928 switch (elf.shdrPtr(existing_shndx)) {4968 switch (elf.shdrPtr(existing_shndx)) {
4929 inline else => |shdr| {4969 inline else => |shdr| {
4970 // Validate that the input is compatible with this section
4930 const cur_flags = elf.targetLoad(&shdr.flags).shf;4971 const cur_flags = elf.targetLoad(&shdr.flags).shf;
4931 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or4972 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
4932 cur_flags.WRITE != opts.flags.WRITE or4973 cur_flags.WRITE != opts.flags.WRITE or
...@@ -4939,20 +4980,8 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4939,20 +4980,8 @@ fn mapInputSection(elf: *Elf, opts: struct {
4939 .NULL, .PROGBITS => {},4980 .NULL, .PROGBITS => {},
4940 else => return error.SectionTypeConflict,4981 else => return error.SectionTypeConflict,
4941 }4982 }
4942 },4983
4943 }4984 // All okay, combine the section flags
4944 // ...then realign the section's node if necessary...
4945 if (opts.addralign > existing_shndx.get(elf).ni.alignment(&elf.mf).toByteUnits()) {
4946 const new_alignment: std.mem.Alignment = .fromByteUnits(
4947 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),
4948 );
4949 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment, .{});
4950 }
4951 // ...and update the shdr as needed.
4952 switch (elf.shdrPtr(existing_shndx)) {
4953 inline else => |shdr| {
4954 // Combine the section flags.
4955 const cur_flags = elf.targetLoad(&shdr.flags).shf;
4956 elf.targetStore(&shdr.flags, .{ .shf = .{4985 elf.targetStore(&shdr.flags, .{ .shf = .{
4957 .EXECINSTR = cur_flags.EXECINSTR,4986 .EXECINSTR = cur_flags.EXECINSTR,
4958 .WRITE = cur_flags.WRITE,4987 .WRITE = cur_flags.WRITE,
...@@ -4961,11 +4990,6 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4961,11 +4990,6 @@ fn mapInputSection(elf: *Elf, opts: struct {
4961 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,4990 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
4962 .MERGE = cur_flags.MERGE and opts.flags.MERGE,4991 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
4963 } });4992 } });
4964 // Increase addralign to the maximum of the current value and the new value---the node
4965 // alignment was already increased above.
4966 if (opts.addralign > elf.targetLoad(&shdr.addralign)) {
4967 elf.targetStore(&shdr.addralign, @intCast(opts.addralign));
4968 }
4969 },4993 },
4970 }4994 }
4971 return existing_shndx;4995 return existing_shndx;
...@@ -4993,7 +5017,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -4993,7 +5017,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
4993 .TLS = elf.base.comp.config.any_non_single_threaded and5017 .TLS = elf.base.comp.config.any_non_single_threaded and
4994 nav.resolved.?.@"threadlocal",5018 nav.resolved.?.@"threadlocal",
4995 },5019 },
4996 .addralign = 1,
4997 .entsize = 0,5020 .entsize = 0,
4998 })) |shndx| {5021 })) |shndx| {
4999 break :section shndx;5022 break :section shndx;
...@@ -5039,6 +5062,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -5039,6 +5062,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
5039 else => |a| a,5062 else => |a| a,
5040 },5063 },
5041 };5064 };
5065 try shndx.ensureAligned(elf, alignment.toStdMem());
5042 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{5066 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
5043 .alignment = alignment.toStdMem(),5067 .alignment = alignment.toStdMem(),
5044 });5068 });
...@@ -5082,6 +5106,7 @@ fn uavMapIndex(...@@ -5082,6 +5106,7 @@ fn uavMapIndex(
5082 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));5106 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));
5083 if (!uav_gop.found_existing) {5107 if (!uav_gop.found_existing) {
5084 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs5108 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
5109 try shndx.ensureAligned(elf, resolved_align.toStdMem());
5085 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{5110 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
5086 .moved = true, // see assert at end of `genUav`5111 .moved = true, // see assert at end of `genUav`
5087 .alignment = resolved_align.toStdMem(),5112 .alignment = resolved_align.toStdMem(),
...@@ -5108,6 +5133,8 @@ fn uavMapIndex(...@@ -5108,6 +5133,8 @@ fn uavMapIndex(
5108 elf.pending_uavs.appendAssumeCapacity(umi);5133 elf.pending_uavs.appendAssumeCapacity(umi);
5109 } else {5134 } else {
5110 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;5135 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
5136 const shndx = elf.getNode(node.parent(&elf.mf)).section;
5137 try shndx.ensureAligned(elf, resolved_align.toStdMem());
5111 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {5138 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
5112 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{});5139 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{});
5113 }5140 }
...@@ -5408,7 +5435,6 @@ fn loadObject(...@@ -5408,7 +5435,6 @@ fn loadObject(
5408 const shndx = elf.mapInputSection(.{5435 const shndx = elf.mapInputSection(.{
5409 .name = name,5436 .name = name,
5410 .flags = section.shdr.flags.shf,5437 .flags = section.shdr.flags.shf,
5411 .addralign = section.shdr.addralign,
5412 .entsize = section.shdr.entsize,5438 .entsize = section.shdr.entsize,
5413 }) catch |err| switch (err) {5439 }) catch |err| switch (err) {
5414 error.StripSection => continue,5440 error.StripSection => continue,
...@@ -5507,12 +5533,13 @@ fn loadObject(...@@ -5507,12 +5533,13 @@ fn loadObject(
5507 .node_fixed = true,5533 .node_fixed = true,
5508 },5534 },
5509 };5535 };
5536 const need_align: std.mem.Alignment = .fromByteUnits(
5537 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
5538 );
5539 try opts.shndx.ensureAligned(elf, need_align);
5510 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{5540 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{
5511 .size = section.shdr.size,5541 .size = section.shdr.size,
5512 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(5542 .alignment = need_align,
5513 usize,
5514 @intCast(@max(section.shdr.addralign, 1)),
5515 )),
5516 .moved = true, // see assert at end of `flushInputSection`5543 .moved = true, // see assert at end of `flushInputSection`
5517 .fixed = opts.node_fixed,5544 .fixed = opts.node_fixed,
5518 });5545 });
...@@ -5882,6 +5909,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -5882,6 +5909,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
5882 if (elf.copied_globals.get(name)) |copied_global| {5909 if (elf.copied_globals.get(name)) |copied_global| {
5883 // We have a copy relocation for this global, but the amount of space we5910 // We have a copy relocation for this global, but the amount of space we
5884 // reserved for it could be too small or underaligned!5911 // reserved for it could be too small or underaligned!
5912 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
5885 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);5913 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);
5886 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{});5914 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{});
5887 const global_ptr = elf.globalByName(name).?;5915 const global_ptr = elf.globalByName(name).?;
...@@ -6264,7 +6292,8 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6264,7 +6292,8 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6264 else => {},6292 else => {},
6265 }6293 }
6266 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {6294 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {
6267 assert(elf.getNode(segment_ni) == .segment);6295 const phndx = elf.getNode(segment_ni).segment;
6296 try elf.ensureSegmentAligned(phndx, opts.addralign);
6268 }6297 }
6269 const gpa = elf.base.comp.gpa;6298 const gpa = elf.base.comp.gpa;
6270 try elf.nodes.ensureUnusedCapacity(gpa, 1);6299 try elf.nodes.ensureUnusedCapacity(gpa, 1);
...@@ -7145,6 +7174,8 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -7145,6 +7174,8 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
7145 if (gop.found_existing) return true;7174 if (gop.found_existing) return true;
7146 errdefer assert(elf.copied_globals.pop().?.key == global_name);7175 errdefer assert(elf.copied_globals.pop().?.key == global_name);
71477176
7177 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
7178
7148 try elf.nodes.ensureUnusedCapacity(gpa, 1);7179 try elf.nodes.ensureUnusedCapacity(gpa, 1);
7149 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{7180 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{
7150 .size = dso_global.size,7181 .size = dso_global.size,
...@@ -7777,17 +7808,22 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7777,17 +7808,22 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7777 const ph = &phdr[phndx];7808 const ph = &phdr[phndx];
7778 switch (elf.targetLoad(&ph.type)) {7809 switch (elf.targetLoad(&ph.type)) {
7779 else => unreachable,7810 else => unreachable,
7780 .NULL, .LOAD => return,7811
7812 .NULL, .LOAD => {
7813 try elf.allocateSegmentLoadAddress(phndx);
7814 },
77817815
7782 .DYNAMIC,7816 .DYNAMIC,
7783 .INTERP,7817 .INTERP,
7784 .PHDR,7818 .PHDR,
7785 .TLS,7819 .TLS,
7786 .GNU_RELRO,7820 .GNU_RELRO,
7787 => {},7821 => {
7822 const new_vaddr = elf.computeNodeVAddr(ni);
7823 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
7824 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
7825 },
7788 }7826 }
7789 elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni)));
7790 ph.paddr = ph.vaddr;
7791 },7827 },
7792 }7828 }
7793 },7829 },
...@@ -7928,6 +7964,114 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7928,6 +7964,114 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7928 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);7964 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
7929}7965}
79307966
7967/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
7968/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
7969/// `memsz` fields of the segment, in order to place it at a valid virtual address.
7970///
7971/// TODO: this function is currently a source of non-determinism in the linker, because handling the
7972/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*
7973/// changes to segments.
7974fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {
7975 const segment_ni = elf.phdrs.items[orig_phndx];
7976 assert(elf.getNode(segment_ni).segment == orig_phndx);
7977 const page_align = elf.targetPageAlign();
7978 const node_align = segment_ni.alignment(&elf.mf);
7979 const ph_align = page_align.max(node_align);
7980 switch (elf.phdrSlice()) {
7981 inline else => |phdr| {
7982 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
7983 const size = elf.targetLoad(&phdr[orig_phndx].filesz);
7984
7985 if (size == 0) {
7986 assert(elf.targetLoad(&phdr[orig_phndx].type) == .NULL);
7987 } else {
7988 assert(elf.targetLoad(&phdr[orig_phndx].type) == .LOAD);
7989 }
7990
7991 elf.targetStore(&phdr[orig_phndx].memsz, size);
7992 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
7993
7994 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
7995 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);
7996
7997 var vaddr: u64 = orig_vaddr;
7998
7999 // First, we will shift the virtual address as needed in order to maintain the required
8000 // property that vaddr is congruent to offset modulo the phdr alignment.
8001 {
8002 // Compute the candidate address by undoing the current offset and then re-offsetting
8003 vaddr = std.mem.alignBackward(u64, vaddr, ph_align.toByteUnits()) + offset % ph_align.toByteUnits();
8004 // If `node_align` is greater than `page_align`, the address we just set might be in
8005 // the previous segment. The first page we "own" is the one in which the old vaddr
8006 // resides, so check against that.
8007 const first_good_vaddr = std.mem.alignBackward(u64, orig_vaddr, page_align.toByteUnits());
8008 if (vaddr < first_good_vaddr) {
8009 // Yep, we crossed into the previous segment's pages, so correct for that by
8010 // offsetting our address by another `ph_align`.
8011 vaddr += ph_align.toByteUnits();
8012 assert(vaddr >= first_good_vaddr);
8013 }
8014 }
8015
8016 // If our size has changed, or if the address shift above caused our "end" address to
8017 // cross a page boundary, then we might be overlapping with the next segment's pages. In
8018 // that case, we will jump past that segment and give ourselves a new address after it.
8019 // We'll need to repeat this for every loadable phdr after us, until we're no longer
8020 // overlapping anything.
8021 var phndx = orig_phndx;
8022 for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| {
8023 switch (elf.targetLoad(&next_ph.type)) {
8024 .NULL, .LOAD => {},
8025 else => {
8026 // All loadable segments have contiguous indices, so this indicates we have
8027 // become the last loadable segment, meaning we definitely don't overlap any
8028 // other loadable segment.
8029 break;
8030 },
8031 }
8032
8033 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
8034 // Find the first virtual address which the next phdr "owns" by aligning its vaddr
8035 // backwards to the start of the page.
8036 const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits());
8037
8038 // If we're at the same vaddr we started at, then all we're worried about is the
8039 // segment fitting here. However, if we've already changed our virtual address, then
8040 // we might as well try to reserve a bit *more* virtual address space while we're at
8041 // it, because changing virtual address is quite disruptive (we need to re-flush a
8042 // lot of stuff!) and giving ourselves more space will make it less likely to happen
8043 // again.
8044 const target_size = if (vaddr == orig_vaddr) size else size * 4;
8045 if (vaddr + target_size <= next_page_vaddr) {
8046 break; // hooray, we fit here!
8047 }
8048
8049 // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But
8050 // first we need to adjust `vaddr` to come after it.
8051 const next_size = elf.targetLoad(&next_ph.memsz);
8052 // Instead of putting ourselves right after `next_ph`, we'll go a bit later in the
8053 // address space so that `next_ph` has address space to grow into (like above).
8054 vaddr = ph_align.forward(next_vaddr + next_size * 4) + offset % ph_align.toByteUnits();
8055
8056 // Now just swap the phdrs and update our `phndx`.
8057 std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph);
8058 const next_ni = elf.phdrs.items[next_phndx];
8059 elf.phdrs.items[phndx] = next_ni;
8060 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };
8061 elf.phdrs.items[next_phndx] = segment_ni;
8062 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
8063 phndx = @intCast(next_phndx);
8064 }
8065
8066 if (vaddr != orig_vaddr) {
8067 elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr));
8068 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
8069 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8070 }
8071 },
8072 }
8073}
8074
7931fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {8075fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
7932 const trace = tracy.trace(@src());8076 const trace = tracy.trace(@src());
7933 defer trace.end();8077 defer trace.end();
...@@ -7955,68 +8099,40 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -7955,68 +8099,40 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
7955 assert(elf.phdrs.items[phndx] == ni);8099 assert(elf.phdrs.items[phndx] == ni);
7956 const ph = &phdr[phndx];8100 const ph = &phdr[phndx];
7957 elf.targetStore(&ph.filesz, @intCast(size));8101 elf.targetStore(&ph.filesz, @intCast(size));
7958 if (size > elf.targetLoad(&ph.memsz)) {8102 switch (elf.targetLoad(&ph.type)) {
7959 switch (elf.targetLoad(&ph.type)) {8103 else => unreachable,
7960 else => unreachable,8104 .NULL, .LOAD => {
7961 .NULL => if (size > 0) elf.targetStore(&ph.type, .LOAD),8105 elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL);
7962 .LOAD => if (size == 0) elf.targetStore(&ph.type, .NULL),8106 try elf.allocateSegmentLoadAddress(phndx);
7963 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {8107 },
7964 elf.targetStore(&ph.memsz, @intCast(size));8108 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {
7965 return;8109 elf.targetStore(&ph.memsz, @intCast(size));
7966 },8110 },
7967 .TLS => {8111 .TLS => {
7968 elf.targetStore(&ph.memsz, @intCast(size));8112 elf.targetStore(&ph.memsz, @intCast(size));
7969 // TPOFF relocations care about the size of the TLS segment. Re-apply8113 // TPOFF relocations care about the size of the TLS segment. Re-apply
7970 // those, and also update any GOT entries from GOTTPOFF relocations.8114 // those, and also update any GOT entries from GOTTPOFF relocations.
7971 for (elf.tls_size_symbol_relocs.keys()) |reloc| {8115 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
7972 reloc.get(elf).apply(elf);8116 reloc.get(elf).apply(elf);
7973 }8117 }
7974 for (elf.got.keys(), 0..) |got_key, got_index| {8118 for (elf.got.keys(), 0..) |got_key, got_index| {
7975 switch (got_key) {8119 switch (got_key) {
7976 .reserved,8120 .reserved,
7977 .symbol,8121 .symbol,
7978 .tlsld0,8122 .tlsld0,
7979 .tlsld1,8123 .tlsld1,
7980 .tlsgd0,8124 .tlsgd0,
7981 .tlsgd1,8125 .tlsgd1,
7982 => {8126 => {
7983 @branchHint(.likely);8127 @branchHint(.likely);
7984 continue;8128 continue;
7985 },8129 },
79868130
7987 .tpoff => elf.updateGotEntry(got_index),8131 .tpoff => elf.updateGotEntry(got_index),
7988 }
7989 }8132 }
7990 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
7991 },
7992 }
7993 const memsz = ni.alignment(&elf.mf).forward(@intCast(size * 4));
7994 elf.targetStore(&ph.memsz, @intCast(memsz));
7995 var vaddr = elf.targetLoad(&ph.vaddr);
7996 var new_phndx = phndx;
7997 for (phdr[phndx + 1 ..], phndx + 1..) |*next_ph, next_phndx| {
7998 switch (elf.targetLoad(&next_ph.type)) {
7999 else => unreachable,
8000 .NULL, .LOAD => {},
8001 .DYNAMIC, .INTERP, .PHDR, .TLS, .GNU_RELRO, .GNU_STACK => break,
8002 }8133 }
8003 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
8004 if (vaddr + memsz <= next_vaddr) break;
8005 vaddr = next_vaddr + elf.targetLoad(&next_ph.memsz);
8006 std.mem.swap(@TypeOf(ph.*), &phdr[new_phndx], next_ph);
8007 const next_ni = elf.phdrs.items[next_phndx];
8008 elf.phdrs.items[new_phndx] = next_ni;
8009 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = new_phndx };
8010 new_phndx = @intCast(next_phndx);
8011 }
8012 if (new_phndx != phndx) {
8013 const new_ph = &phdr[new_phndx];
8014 elf.targetStore(&new_ph.vaddr, vaddr);
8015 new_ph.paddr = new_ph.vaddr;
8016 elf.phdrs.items[new_phndx] = ni;
8017 elf.nodes.items(.data)[@backingInt(ni)] = .{ .segment = new_phndx };
8018 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);8134 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8019 }8135 },
8020 }8136 }
8021 },8137 },
8022 },8138 },
...@@ -8693,6 +8809,46 @@ pub fn printNode(...@@ -8693,6 +8809,46 @@ pub fn printNode(
8693 }8809 }
8694}8810}
86958811
8812fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignment) Error!void {
8813 const gpa = elf.base.comp.gpa;
8814 // We need to loop through parent nodes because segments may be nested (e.g. a PT_TLS segment
8815 // inside a PT_LOAD segment).
8816 var phndx = start_phndx;
8817 while (true) {
8818 // Align the actual node
8819 const seg_ni = elf.phdrs.items[phndx];
8820 if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) {
8821 try seg_ni.realign(&elf.mf, gpa, min_align, .{});
8822 }
8823 // Update the phdr `@"align"` field if necessary
8824 switch (elf.phdrSlice()) {
8825 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {
8826 .NULL, .LOAD => {
8827 // The `@"align"` field is managed by `allocateSegmentLoadAddress`.
8828 //
8829 // It's very likely that the node was moved and/or resized when we realigned it
8830 // just above, but it is possible that it was not moved *but* still has an
8831 // unaligned virtual address. In that case, we need to ensure the segment's
8832 // virtual address range will be recomputed.
8833 if (!min_align.check(elf.targetLoad(&phdr[phndx].vaddr))) {
8834 try seg_ni.moved(gpa, &elf.mf);
8835 }
8836 },
8837 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(
8838 elf.targetLoad(&phdr[phndx].@"align"),
8839 min_align.toByteUnits(),
8840 ))),
8841 },
8842 }
8843 // Continue on to the parent segment, if any
8844 switch (elf.getNode(seg_ni.parent(&elf.mf))) {
8845 .segment => |parent_phndx| phndx = parent_phndx,
8846 .elf => return,
8847 else => unreachable,
8848 }
8849 }
8850}
8851
8696/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`8852/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
8697/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.8853/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
8698fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {8854fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {