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

Elf2: add segment for PLT on SPARC

On SPARC, because JUMP_SLOT relocations write directly to the PLT, the PLT is required to be RWX. Conventionally, this is achieved by placing it in the "data" segment and making that RWX instead of RW, but that just seems unnecessarily dangerous. Instead, let's try making a segment specifically for the PLT.

1 files changed, 54 insertions(+), 17 deletions(-)

src/link/Elf2.zig+54-17
...@@ -3521,6 +3521,10 @@ fn initHeaders(...@@ -3521,6 +3521,10 @@ fn initHeaders(
3521 rodata: u32,3521 rodata: u32,
3522 text: u32,3522 text: u32,
3523 data: u32,3523 data: u32,
3524 /// On most targets this is `undefined`, but on machines where JUMP_SLOT relocations write
3525 /// directly to the PLT, we place the PLT in its own segment in order to avoid making the
3526 /// general data segment RWX.
3527 plt: u32,
3524 tls: u32,3528 tls: u32,
3525 dynamic: u32,3529 dynamic: u32,
3526 relro: u32,3530 relro: u32,
...@@ -3552,6 +3556,10 @@ fn initHeaders(...@@ -3552,6 +3556,10 @@ fn initHeaders(
3552 defer phnum += 1;3556 defer phnum += 1;
3553 break :phndx phnum;3557 break :phndx phnum;
3554 },3558 },
3559 .plt = if (plt.got_plt == null) phndx: {
3560 defer phnum += 1;
3561 break :phndx phnum;
3562 } else undefined,
3555 .tls = if (comp.config.any_non_single_threaded) phndx: {3563 .tls = if (comp.config.any_non_single_threaded) phndx: {
3556 defer phnum += 1;3564 defer phnum += 1;
3557 break :phndx phnum;3565 break :phndx phnum;
...@@ -3721,6 +3729,16 @@ fn initHeaders(...@@ -3721,6 +3729,16 @@ fn initHeaders(
3721 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });3729 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3722 elf.phdrs.items[phndx.data] = elf.ni.data;3730 elf.phdrs.items[phndx.data] = elf.ni.data;
37233731
3732 if (plt.got_plt == null) {
3733 const plt_ni = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3734 .alignment = node_block_align,
3735 .moved = true,
3736 .bubbles_moved = false,
3737 });
3738 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt });
3739 elf.phdrs.items[phndx.plt] = plt_ni;
3740 }
3741
3724 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{3742 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3725 // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one3743 // 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.3744 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
...@@ -3844,6 +3862,20 @@ fn initHeaders(...@@ -3844,6 +3862,20 @@ fn initHeaders(
3844 .@"align" = @intCast(page_align.toByteUnits()),3862 .@"align" = @intCast(page_align.toByteUnits()),
3845 };3863 };
38463864
3865 if (plt.got_plt == null) {
3866 const ph_plt = &phdr[phndx.plt];
3867 ph_plt.* = .{
3868 .type = .NULL,
3869 .offset = 0,
3870 .vaddr = @intCast(base_vaddr),
3871 .paddr = @intCast(base_vaddr),
3872 .filesz = 0,
3873 .memsz = 0,
3874 .flags = .{ .R = true, .W = true, .X = true },
3875 .@"align" = @intCast(page_align.toByteUnits()),
3876 };
3877 }
3878
3847 if (comp.config.any_non_single_threaded) {3879 if (comp.config.any_non_single_threaded) {
3848 const ph_tls = &phdr[phndx.tls];3880 const ph_tls = &phdr[phndx.tls];
3849 ph_tls.* = .{3881 ph_tls.* = .{
...@@ -4001,29 +4033,34 @@ fn initHeaders(...@@ -4001,29 +4033,34 @@ fn initHeaders(
4001 .addralign = addr_align,4033 .addralign = addr_align,
4002 .entsize = @intCast(addr_align.toByteUnits()),4034 .entsize = @intCast(addr_align.toByteUnits()),
4003 });4035 });
4004 if (plt.got_plt) |got_plt| elf.shndx.got_plt = try elf.addSection(4036 if (plt.got_plt) |got_plt| {
4005 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,4037 const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data;
4006 .{4038 elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{
4007 .name = ".got.plt",4039 .name = ".got.plt",
4008 .type = .PROGBITS,4040 .type = .PROGBITS,
4009 .flags = .{ .WRITE = true, .ALLOC = true },4041 .flags = .{ .WRITE = true, .ALLOC = true },
4010 .size = got_plt.header_entries * elf.targetPtrSize(),4042 .size = got_plt.header_entries * elf.targetPtrSize(),
4011 .addralign = addr_align,4043 .addralign = addr_align,
4012 .entsize = @intCast(addr_align.toByteUnits()),4044 .entsize = @intCast(addr_align.toByteUnits()),
4013 },4045 });
4014 );4046 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
4015 elf.shndx.plt = try elf.addSection(elf.ni.text, .{4047 .name = ".plt",
4016 .name = ".plt",4048 .type = .PROGBITS,
4017 .type = .PROGBITS,4049 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4018 .flags = .{4050 .size = plt.entry_size * plt.header_entries,
4019 .ALLOC = true,4051 .addralign = plt.@"align",
4020 .EXECINSTR = true,4052 .node_align = node_block_align,
4021 .WRITE = plt.got_plt == null,4053 });
4022 },4054 } else {
4023 .size = plt.entry_size * plt.header_entries,4055 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt], .{
4024 .addralign = plt.@"align",4056 .name = ".plt",
4025 .node_align = node_block_align,4057 .type = .PROGBITS,
4026 });4058 .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true },
4059 .size = plt.entry_size * plt.header_entries,
4060 .addralign = plt.@"align",
4061 .node_align = node_block_align,
4062 });
4063 }
4027 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{4064 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
4028 .name = ".plt.sec",4065 .name = ".plt.sec",
4029 .flags = .{ .ALLOC = true, .EXECINSTR = true },4066 .flags = .{ .ALLOC = true, .EXECINSTR = true },