From adfbd8a98b2525dbd5fa9891968794f7ace31cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 01:04:20 +0200 Subject: [PATCH 1/6] std.os.linux.start_pie: Add mips and mips64 support. --- lib/std/os/linux/start_pie.zig | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index 5c35262bc8..4f0d88196e 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -12,6 +12,7 @@ const R_CSKY_RELATIVE = 9; const R_HEXAGON_RELATIVE = 35; const R_LARCH_RELATIVE = 3; const R_68K_RELATIVE = 22; +const R_MIPS_RELATIVE = 128; const R_RISCV_RELATIVE = 3; const R_390_RELATIVE = 12; const R_SPARC_RELATIVE = 22; @@ -26,6 +27,7 @@ const R_RELATIVE = switch (builtin.cpu.arch) { .hexagon => R_HEXAGON_RELATIVE, .loongarch32, .loongarch64 => R_LARCH_RELATIVE, .m68k => R_68K_RELATIVE, + .mips, .mipsel, .mips64, .mips64el => R_MIPS_RELATIVE, .riscv32, .riscv64 => R_RISCV_RELATIVE, .s390x => R_390_RELATIVE, else => @compileError("Missing R_RELATIVE definition for this target"), @@ -111,6 +113,31 @@ fn getDynamicSymbol() [*]elf.Dyn { \\ lea (%[ret], %%pc), %[ret] : [ret] "=r" (-> [*]elf.Dyn), ), + .mips, .mipsel => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ bal 1f + \\ .gpword _DYNAMIC + \\ 1: + \\ lw %[ret], 0($ra) + \\ addu %[ret], %[ret], $gp + : [ret] "=r" (-> [*]elf.Dyn), + : + : "lr" + ), + .mips64, .mips64el => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ .balign 8 + \\ bal 1f + \\ .gpdword _DYNAMIC + \\ 1: + \\ ld %[ret], 0($ra) + \\ daddu %[ret], %[ret], $gp + : [ret] "=r" (-> [*]elf.Dyn), + : + : "lr" + ), .riscv32, .riscv64 => asm volatile ( \\ .weak _DYNAMIC \\ .hidden _DYNAMIC From 5633767b20b1e2c9a650bdf6045c260ec4ce42ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 01:31:42 +0200 Subject: [PATCH 2/6] std.os.linux.start_pie: Add powerpc and powerpc64 support. Closes #20305. --- lib/std/os/linux/start_pie.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index 4f0d88196e..174d5640ad 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -13,6 +13,7 @@ const R_HEXAGON_RELATIVE = 35; const R_LARCH_RELATIVE = 3; const R_68K_RELATIVE = 22; const R_MIPS_RELATIVE = 128; +const R_PPC_RELATIVE = 22; const R_RISCV_RELATIVE = 3; const R_390_RELATIVE = 12; const R_SPARC_RELATIVE = 22; @@ -28,6 +29,7 @@ const R_RELATIVE = switch (builtin.cpu.arch) { .loongarch32, .loongarch64 => R_LARCH_RELATIVE, .m68k => R_68K_RELATIVE, .mips, .mipsel, .mips64, .mips64el => R_MIPS_RELATIVE, + .powerpc, .powerpcle, .powerpc64, .powerpc64le => R_PPC_RELATIVE, .riscv32, .riscv64 => R_RISCV_RELATIVE, .s390x => R_390_RELATIVE, else => @compileError("Missing R_RELATIVE definition for this target"), @@ -138,6 +140,32 @@ fn getDynamicSymbol() [*]elf.Dyn { : : "lr" ), + .powerpc, .powerpcle => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ bl 1f + \\ .long _DYNAMIC - . + \\ 1: + \\ mflr %[ret] + \\ lwz 4, 0(%[ret]) + \\ add %[ret], 4, %[ret] + : [ret] "=r" (-> [*]elf.Dyn), + : + : "lr", "r4" + ), + .powerpc64, .powerpc64le => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ bl 1f + \\ .quad _DYNAMIC - . + \\ 1: + \\ mflr %[ret] + \\ ld 4, 0(%[ret]) + \\ add %[ret], 4, %[ret] + : [ret] "=r" (-> [*]elf.Dyn), + : + : "lr", "r4" + ), .riscv32, .riscv64 => asm volatile ( \\ .weak _DYNAMIC \\ .hidden _DYNAMIC From 68cebde186eb8507509c9418c1ab3b843c6f24ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 19:02:26 +0200 Subject: [PATCH 3/6] std.os.linux.start_pie: Inline the getDynamicSymbol() function. On MIPS, this call will require a relocation, which we can't do until after PIE relocations have been applied. --- lib/std/os/linux/start_pie.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index 174d5640ad..ab44abd898 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -38,7 +38,7 @@ const R_RELATIVE = switch (builtin.cpu.arch) { // Obtain a pointer to the _DYNAMIC array. // We have to compute its address as a PC-relative quantity not to require a // relocation that, at this point, is not yet applied. -fn getDynamicSymbol() [*]elf.Dyn { +inline fn getDynamicSymbol() [*]elf.Dyn { return switch (builtin.cpu.arch) { .x86 => asm volatile ( \\ .weak _DYNAMIC From 2386bfe854826e0726b7002c04cd9fc4c08d68f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 23:55:37 +0200 Subject: [PATCH 4/6] std.os.linux.start_pie: Rewrite relocate() to avoid jump tables and libcalls. The code would cause LLVM to emit a jump table for the switch in the loop over the dynamic tags. That jump table was far enough away that the compiler decided to go through the GOT, which would of course break at this early stage as we haven't applied MIPS's local GOT relocations yet, nor can we until we've walked through the _DYNAMIC array. The first attempt at rewriting this used code like this: var sorted_dynv = [_]elf.Addr{0} ** elf.DT_NUM; But this is also problematic as it results in a memcpy() call. Instead, we explicitly initialize it to undefined and use a loop of volatile stores to clear it. --- lib/std/os/linux/start_pie.zig | 48 +++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index ab44abd898..b5cc06f429 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -193,6 +193,7 @@ pub fn relocate(phdrs: []elf.Phdr) void { @disableInstrumentation(); const dynv = getDynamicSymbol(); + // Recover the delta applied by the loader by comparing the effective and // the theoretical load addresses for the `_DYNAMIC` symbol. const base_addr = base: { @@ -204,34 +205,45 @@ pub fn relocate(phdrs: []elf.Phdr) void { @trap(); }; - var rel_addr: usize = 0; - var rela_addr: usize = 0; - var rel_size: usize = 0; - var rela_size: usize = 0; + var sorted_dynv: [elf.DT_NUM]elf.Addr = undefined; + + // Zero-initialized this way to prevent the compiler from turning this into + // `memcpy` or `memset` calls (which can require relocations). + for (&sorted_dynv) |*dyn| { + const pdyn: *volatile elf.Addr = @ptrCast(dyn); + pdyn.* = 0; + } + { + // `dynv` has no defined order. Fix that. var i: usize = 0; while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) { - switch (dynv[i].d_tag) { - elf.DT_REL => rel_addr = base_addr + dynv[i].d_val, - elf.DT_RELA => rela_addr = base_addr + dynv[i].d_val, - elf.DT_RELSZ => rel_size = dynv[i].d_val, - elf.DT_RELASZ => rela_size = dynv[i].d_val, - else => {}, - } + if (dynv[i].d_tag < elf.DT_NUM) sorted_dynv[@bitCast(dynv[i].d_tag)] = dynv[i].d_val; } } - // Apply the relocations. - if (rel_addr != 0) { - const rel = std.mem.bytesAsSlice(elf.Rel, @as([*]u8, @ptrFromInt(rel_addr))[0..rel_size]); - for (rel) |r| { + + // Apply normal relocations. + + const rel = sorted_dynv[elf.DT_REL]; + if (rel != 0) { + const rels = @call(.always_inline, std.mem.bytesAsSlice, .{ + elf.Rel, + @as([*]u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]], + }); + for (rels) |r| { if (r.r_type() != R_RELATIVE) continue; @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr; } } - if (rela_addr != 0) { - const rela = std.mem.bytesAsSlice(elf.Rela, @as([*]u8, @ptrFromInt(rela_addr))[0..rela_size]); - for (rela) |r| { + + const rela = sorted_dynv[elf.DT_RELA]; + if (rela != 0) { + const relas = @call(.always_inline, std.mem.bytesAsSlice, .{ + elf.Rela, + @as([*]u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]], + }); + for (relas) |r| { if (r.r_type() != R_RELATIVE) continue; @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend)); } From 52519f79e0b3659e43eb8e765a9e2b584fc8f0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 23:55:42 +0200 Subject: [PATCH 5/6] std.os.linux.start_pie: Apply MIPS local GOT relocations. --- lib/std/os/linux/start_pie.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index b5cc06f429..ab4e2f4fe9 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -222,6 +222,24 @@ pub fn relocate(phdrs: []elf.Phdr) void { } } + // Deal with the GOT relocations that MIPS uses first. + if (builtin.cpu.arch.isMIPS()) { + const count: elf.Addr = blk: { + // This is an architecture-specific tag, so not part of `sorted_dynv`. + var i: usize = 0; + while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) { + if (dynv[i].d_tag == elf.DT_MIPS_LOCAL_GOTNO) break :blk dynv[i].d_val; + } + + break :blk 0; + }; + + const got: [*]usize = @ptrFromInt(base_addr + sorted_dynv[elf.DT_PLTGOT]); + + for (0..count) |i| { + got[i] += base_addr; + } + } // Apply normal relocations. From 2e719f32397c1ec87a9f5a5b6947dbc553471707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 27 Jul 2024 10:21:28 +0200 Subject: [PATCH 6/6] std.os.linux.start_pie: Use a 64-bit displacement for s390x. Not likely to be necessary ever, but might as well be 100% correct. --- lib/std/os/linux/start_pie.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index ab4e2f4fe9..7b086250d6 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -176,9 +176,9 @@ inline fn getDynamicSymbol() [*]elf.Dyn { \\ .weak _DYNAMIC \\ .hidden _DYNAMIC \\ larl %[ret], 1f - \\ agf %[ret], 0(%[ret]) + \\ ag %[ret], 0(%[ret]) \\ b 2f - \\ 1: .long _DYNAMIC - . + \\ 1: .quad _DYNAMIC - . \\ 2: : [ret] "=r" (-> [*]elf.Dyn), ),