From 77fc3b88fbdf805e0c2c1fc2647513cee56813a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 01:08:03 +0200 Subject: [PATCH 1/4] start: Set up the gp register on mips and mips64. --- lib/std/start.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/std/start.zig b/lib/std/start.zig index 326857d9c0953b57a6afa9e27f3b91aacaa58a4b..78845904d628ff6530954cb65ca7138d09bc14a4 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -329,6 +329,11 @@ fn _start() callconv(.Naked) noreturn { \\ jsr (%%pc, %%a0) , .mips, .mipsel => + \\ bal 1f + \\ .gpword . + \\ 1: + \\ lw $gp, 0($ra) + \\ subu $gp, $ra, $gp \\ move $fp, $0 \\ move $ra, $0 \\ move $a0, $sp @@ -336,6 +341,14 @@ fn _start() callconv(.Naked) noreturn { \\ j %[posixCallMainAndExit] , .mips64, .mips64el => + \\ bal 1f + \\ .gpdword . + \\ 1: + // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64. + // We need to set it up in order for dynamically-linked / position-independent code to + // work. + \\ ld $gp, 0($ra) + \\ dsubu $gp, $ra, $gp \\ move $fp, $0 \\ move $ra, $0 \\ move $a0, $sp -- 2.54.0 From e33af8e9021199696e413057b57fb86e750a9de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 19:06:38 +0200 Subject: [PATCH 2/4] start: Perform the posixCallMainAndExit() call with jalr on mips. It's actually important for the ABI that r25 (t9) contains the address of the called function, so that this standard prologue sequence works: lui $2, %hi(_gp_disp) addiu $2, $2, %lo(_gp_disp) addu $gp, $2, $t9 (This is a bit similar to the ToC situation on powerpc that was fixed in 7bc78967b400322a0fc5651f37a1b0428c37fb9d.) --- lib/std/start.zig | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 78845904d628ff6530954cb65ca7138d09bc14a4..fcc7fcd10021bb08f632d49586c6c56b281be426 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -329,31 +329,43 @@ fn _start() callconv(.Naked) noreturn { \\ jsr (%%pc, %%a0) , .mips, .mipsel => + \\ move $fp, $0 \\ bal 1f \\ .gpword . + \\ .gpword %[posixCallMainAndExit] \\ 1: \\ lw $gp, 0($ra) \\ subu $gp, $ra, $gp - \\ move $fp, $0 + \\ lw $25, 4($ra) + \\ addu $25, $25, $gp \\ move $ra, $0 \\ move $a0, $sp \\ and $sp, -8 - \\ j %[posixCallMainAndExit] + \\ subu $sp, $sp, 16 + \\ jalr $25 , .mips64, .mips64el => + \\ move $fp, $0 + // This is needed because early MIPS versions don't support misaligned loads. Without + // this directive, the hidden `nop` inserted to fill the delay slot after `bal` would + // cause the two doublewords to be aligned to 4 bytes instead of 8. + \\ .balign 8 \\ bal 1f \\ .gpdword . + \\ .gpdword %[posixCallMainAndExit] \\ 1: // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64. // We need to set it up in order for dynamically-linked / position-independent code to // work. \\ ld $gp, 0($ra) \\ dsubu $gp, $ra, $gp - \\ move $fp, $0 + \\ ld $25, 8($ra) + \\ daddu $25, $25, $gp \\ move $ra, $0 \\ move $a0, $sp \\ and $sp, -16 - \\ j %[posixCallMainAndExit] + \\ dsubu $sp, $sp, 16 + \\ jalr $25 , .powerpc, .powerpcle => // Set up the initial stack frame, and clear the back chain pointer. -- 2.54.0 From d6c637c36b399ae9d876592826f3e62d7636dc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 01:15:46 +0200 Subject: [PATCH 3/4] start: Set std.os.linux.elf_aux_maybe after PIE relocations. Accesses to this global variable can require relocations on some platforms (e.g. MIPS). If we do it before PIE relocations have been applied, we'll crash. --- lib/std/start.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index fcc7fcd10021bb08f632d49586c6c56b281be426..5bbb4dadce768158cb73a2b2a0083b31dbf02df5 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -455,7 +455,6 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn { if (native_os == .linux) { // Find the beginning of the auxiliary vector const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1)); - std.os.linux.elf_aux_maybe = auxv; var at_hwcap: usize = 0; const phdrs = init: { @@ -479,6 +478,10 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn { std.os.linux.pie.relocate(phdrs); } + // This must be done after PIE relocations have been applied or we may crash + // while trying to access the global variable (happens on MIPS at least). + std.os.linux.elf_aux_maybe = auxv; + if (!builtin.single_threaded) { // ARMv6 targets (and earlier) have no support for TLS in hardware. // FIXME: Elide the check for targets >= ARMv7 when the target feature API -- 2.54.0 From d633b35f35ddb9dd4db8529346bbb414cf8fbfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 25 Jul 2024 18:59:16 +0200 Subject: [PATCH 4/4] start: Always inline the call to std.os.linux.pie.relocate(). At this stage, we're not ready to make calls yet on some architectures (e.g. MIPS). --- lib/std/start.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 5bbb4dadce768158cb73a2b2a0083b31dbf02df5..d40765e4cbe369fe5ebf40db9a2e9c50078f6e47 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -472,10 +472,11 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn { break :init @as([*]elf.Phdr, @ptrFromInt(at_phdr))[0..at_phnum]; }; - // Apply the initial relocations as early as possible in the startup - // process. + // Apply the initial relocations as early as possible in the startup process. We cannot + // make calls yet on some architectures (e.g. MIPS) *because* they haven't been applied yet, + // so this must be fully inlined. if (builtin.position_independent_executable) { - std.os.linux.pie.relocate(phdrs); + @call(.always_inline, std.os.linux.pie.relocate, .{phdrs}); } // This must be done after PIE relocations have been applied or we may crash -- 2.54.0