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 5c35262bc8d72c96052fe7dc8bb1dda8277b06de..4f0d88196eba7238c04a77a752ab004ab4feddfa 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 -- 2.54.0 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 4f0d88196eba7238c04a77a752ab004ab4feddfa..174d5640ad20d66bdd11464d5161ae8da83633e3 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 -- 2.54.0 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 174d5640ad20d66bdd11464d5161ae8da83633e3..ab44abd898a34b5bc3c323718f5a5c46b11f96aa 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 -- 2.54.0 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 ab44abd898a34b5bc3c323718f5a5c46b11f96aa..b5cc06f4291f01b29992f5e9c71222739f79f68a 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)); } -- 2.54.0 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 b5cc06f4291f01b29992f5e9c71222739f79f68a..ab4e2f4fe9b13a37ac86c6359ed48f15db55a087 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. -- 2.54.0 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 ab4e2f4fe9b13a37ac86c6359ed48f15db55a087..7b086250d612b6bab9620be3abfc3191db5a7dba 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), ), -- 2.54.0