diff --git a/lib/std/start.zig b/lib/std/start.zig index aeefbaffc056..45c224d39288 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -289,17 +289,42 @@ fn _start() callconv(.Naked) noreturn { , .mips, .mipsel => \\ move $fp, $0 + \\ bal 1f + \\ .gpword . + \\ .gpword %[posixCallMainAndExit] + \\ 1: + \\ lw $gp, 0($ra) + \\ subu $gp, $ra, $gp + \\ 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 + \\ 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. @@ -389,7 +414,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: { @@ -407,12 +431,17 @@ 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 + // 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