blob: 502791c2524b60d56aee1674e09ab6cb88cb9c7d [file] [log] [blame] [edit]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/arch/asm.h>
#include "../asm-linkage.h"
// See startup-trampoline.h for more details on what this code is doing.
#if defined(__aarch64__)
#define TP_DWARF_REGNO 36 // TPIDR_EL0
#define SCSP_DWARF_REGNO shadow_call_sp
.macro call target
bl \target
.endm
// Save the 3rd and 4th arguments in call-saved registers.
.macro save_arguments
mov x19, x2
.cfi_undefined x19
mov x20, x3
.cfi_undefined x20
.endm
.macro prepare_for_jump
// Until the tail call the original CFA will be found in x16. This frame's
// CFI will only ever be used when stepping through startup. The tail call
// will clobber x16, but unwinding will never reach here to want its value.
mov x16, sp
.cfi_def_cfa_register x16
// x0 is both first return value register and first argument register. The
// argument pointer for start_main is already there. x1 is the second return
// value register, where StartCompilerAbi returned the new starting SP.
//
// Pretend to be the _start prologue and chain on the empty frame for a zero
// return address and zero entering FP that _start did on the original stack.
// In case this is actually the old stack, store zeroes so as not to presume
// that the _start code did exactly as expected (though now pretending that
// it did). (Note this doesn't use .prologue.fp and has no CFI updates
// because the new stack is still unrelated to the current CFA.)
stp xzr, xzr, [x1, #-16]
sub fp, x1, #16
mov sp, fp
// Move saved arguments into place.
mov x1, x19
mov x2, x20
// Clear those registers just to keep tidy outermost call-saved state.
mov x19, xzr
mov x20, xzr
.endm
.macro tail target
b \target
.endm
#elif defined(__riscv)
#define TP_DWARF_REGNO tp
#define SCSP_DWARF_REGNO shadow_call_sp
// Save the 3rd and 4th arguments in call-saved registers.
.macro save_arguments
mv s1, a2
.cfi_undefined s1
mv s2, a3
.cfi_undefined s2
.endm
.macro prepare_for_jump
// Until the tail call the original CFA will be found in t0. This frame's
// CFI will only ever be used when stepping through startup. The tail call
// will clobber t0, but unwinding will never reach here to want its value.
mv t0, sp
.cfi_def_cfa_register t0
// a0 is both first return value register and first argument register. The
// argument pointer for start_main is already there. a1 is the second return
// value register, where StartCompilerAbi returned the new starting SP.
//
// Pretend to be the _start prologue and chain on the empty frame for a zero
// return address and zero entering FP that _start did on the original stack.
// In case this is actually the old stack, store zeroes so as not to presume
// that the _start code did exactly as expected (though now pretending that
// it did). (Note this doesn't use .prologue.fp and has no CFI updates
// because the new stack is still unrelated to the current CFA.)
sd zero, -16(a1)
sd zero, -8(a1)
mv fp, a1
add sp, a1, -16
// Move saved arguments into place.
mv a1, s1
mv a2, s2
// Clear those registers just to keep tidy outermost call-saved state.
mv s1, zero
mv s2, zero
.endm
#elif defined(__x86_64__)
#define TP_DWARF_REGNO %fs.base
// Save the 3rd and 4th arguments in call-saved registers.
.macro save_arguments
mov %rdx, %r12
.cfi_undefined %r12
mov %rcx, %r13
.cfi_undefined %r13
.endm
.macro prepare_for_jump
// Install the new stack pointer. It's already aligned to 16 bytes. Until
// the tail call the original CFA will be found in %rcx. This frame's CFI
// will only ever be used when stepping through startup. The tail call will
// clobber %rcx, but unwinding will never reach here to want its value.
mov %rsp, %rcx
mov %rdx, %rsp
.cfi_def_cfa_register %rcx
// Move all the arguments into place.
mov %rax, %rdi
mov %r12, %rsi
mov %r13, %rdx
// After .epilogue.fp (%rsp) points to the original return address,
// but that %rsp value is now in %rcx instead.
mov (%rcx), %rax
// Clear scratch registers just to keep tidy outermost call-saved state.
xor %r12, %r12
xor %r12, %r12
xor %rcx, %rcx
// First pretend to push the zero return address that _start would have seen
// on the original stack. Then pretend to be the _start prologue and chain
// on the empty frame for a zero return address and zero entering FP. (Note
// this doesn't use .prologue.fp and has no CFI updates because the new stack
// is still unrelated to the current CFA.) Finally, pretend to be _start's
// call instruction. This all simulates exactly what _start is presumed to
// have done, but on the new stack.
mov %rcx, -2 * 8(%rsp)
mov %rcx, -1 * 8(%rsp)
// At the end of these three instructions, it's as if _start had done these:
sub $3 * 8, %rsp // _start: push %rbp
lea 8(%rsp), %rbp // mov %rsp, %rbp
mov %rax, (%rsp) // call __libc_start_main
xor %rax, %rax
.endm
.macro tail target
jmp \target
.endm
#endif
.llvm_libc_function __libc_start_main
.llvm_libc_public __libc_start_main
.cfi.all_integer .cfi_same_value
.cfi.all_vectorfp .cfi_same_value
.prologue.fp
// This moves the additional arguments (from the dynamic linker) into
// call-saved registers to be recovered in the final tail call.
save_arguments
call LIBC_ASM_LINKAGE(StartCompilerAbi)
.cfi.all_call_used .cfi_undefined
#ifdef SCSP_DWARF_REGNO
// The shadow_call_sp register now points to the base of the shadow call
// stack. The caller's value is not saved.
.cfi_undefined SCSP_DWARF_REGNO
#endif
// The thread pointer register has been reset and the caller's value is gone.
.cfi_undefined TP_DWARF_REGNO
.epilogue.fp
// This moves the SP and argument register into place for the tail call.
prepare_for_jump
// Now this frame ceases to exist and is replaced by the C++ function
// using the full Fuchsia Compiler ABI on its new stacks.
tail LIBC_ASM_LINKAGE(start_main)
.end_function