Repository: JuliaLang/julia
File: src/signals-unix.c (C)

Code before:
static int thread0_exit_count = 0;
static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size);

int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT;
static void jl_longjmp_in_ctx(int sig, void *_ctx, jl_jmp_buf jmpbuf);

// create a fake function that describes the variable manipulations in jl_call_in_ctx/jl_call_in_state
__attribute__((naked)) static void fake_stack_pop(void)
{
#if (defined(_OS_LINUX_) || defined(_OS_DARWIN_)) && defined(_CPU_X86_64_)
    __asm__ volatile (
        "  .cfi_signal_frame\n"
        "  .cfi_def_cfa %rsp, 0\n" // CFA here uses %rsp directly
        "  .cfi_offset %rip, 0\n" // previous value of %rip at CFA
        "  .cfi_offset %rsp, 8\n" // previous value of %rsp at CFA
        "  nop\n"
    );
#elif defined(_OS_LINUX_) && defined(_CPU_X86_)
    __asm__ volatile (
        "  .cfi_signal_frame\n"
        "  .cfi_def_cfa %esp, 0\n" // CFA here uses %esp directly
        "  .cfi_offset %eip, 0\n" // previous value of %eip at CFA
        "  .cfi_offset %esp, 4\n" // previous value of %esp at CFA
        "  nop\n"
    );
#elif (defined(_OS_LINUX_) || defined(_OS_DARWIN_)) && defined(_CPU_AARCH64_)
    __asm__ volatile (
        "  .cfi_signal_frame\n"
        "  .cfi_def_cfa sp, 0\n" // use sp as fp here
        "  .cfi_offset lr, 0\n"
        "  .cfi_offset sp, 8\n"
        // Anything else got smashed, since we didn't explicitly copy all of the
        // state object to the stack (to build a real sigreturn frame).
        // This is also not quite valid, since the AArch64 DWARF spec lacks the ability to define how to restore the LR register correctly,
        // so normally libunwind implementations on linux detect this function specially and hack around the invalid info:
        // https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958
        " nop\n"
    );
#else
CFI_NORETURN
#endif
}

#if !defined(_OS_DARWIN_)
static inline uintptr_t jl_get_rsp_from_ctx(const void *_ctx)
{
#if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
    const ucontext_t *ctx = (const ucontext_t*)_ctx;
    return ctx->uc_mcontext.gregs[REG_RSP];
#elif defined(_OS_LINUX_) && defined(_CPU_X86_)
    const ucontext_t *ctx = (const ucontext_t*)_ctx;

Review comment (bug):
```suggestion
#elif defined(_OS_DARWIN_) && defined(_CPU_AARCH64_)
```

GCC implements this function wrong (crashes the compiler)

Code after:
static int thread0_exit_count = 0;
static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size);

int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT;
static void jl_longjmp_in_ctx(int sig, void *_ctx, jl_jmp_buf jmpbuf);

#if !defined(_OS_DARWIN_)
extern void jl_fake_signal_return(void);
// Create a trampoline function that does the stack manipulations for jl_call_in_ctx/jl_call_in_state
// The callee-saved registers still may get smashed (by the cdecl fptr), since we didn't explicitly copy all of the
// state to the stack (to build a real sigreturn frame).
#if (defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || defined(_OS_OPENBSD_)) && defined(_CPU_X86_64_)
__asm__(
    "  .type jl_fake_signal_return, @function\n"
    "jl_fake_signal_return:\n"
    "  .cfi_startproc\n"
    "  .cfi_signal_frame\n"
    // Mark as end of stack until frame is set up
    "  .cfi_undefined %rip\n"
    "  .cfi_undefined %rsp\n"
    // rdi points to signal_ctx_pc in ptls (followed by signal_ctx_sp, signal_ctx_fptr, signal_ctx_arg)
    "  pushq (%rdi)\n"        // push pc (signal_ctx_pc)
    "  pushq 8(%rdi)\n"       // push sp (signal_ctx_sp)
    // stack layout: [sp, pc] (pc at higher address, like return address after call)
    "  .cfi_def_cfa %rsp, 8\n"
    "  .cfi_offset %rip, 0\n"  // previous %rip at CFA+0 (pc slot at rsp+8)
    "  .cfi_offset %rsp, -8\n" // previous %rsp at CFA-8 (sp slot at rsp+0)
    "  pushq 16(%rdi)\n"      // push fptr (signal_ctx_fptr)
    "  .cfi_def_cfa %rsp, 16\n"
    "  movq 24(%rdi), %rdi\n" // restore original rdi from signal_ctx_arg
    "  subq $8, %rsp\n"       // align stack to 16 bytes
    "  .cfi_def_cfa %rsp, 24\n"
    "  callq *8(%rsp)\n"      // call fptr
    "  ud2\n"                 // unreachable
    "  .cfi_endproc\n"
    "  .size jl_fake_signal_return, .-jl_fake_signal_return\n"
);

#elif (defined(_OS_LINUX_) || defined(_OS_FREEBSD_)) && defined(_CPU_X86_)
__asm__(
    "  .type jl_fake_signal_return, @function\n"
    "jl_fake_signal_return:\n"
    "  .cfi_startproc\n"
    "  .cfi_signal_frame\n"
    // Mark as end of stack until frame is set up
    "  .cfi_undefined 1\n"
    // eax points to signal_ctx_pc in ptls (followed by signal_ctx_sp, signal_ctx_fptr, signal_ctx_arg)
    "  pushl (%eax)\n"        // push pc (signal_ctx_pc)
    "  pushl 4(%eax)\n"       // push sp (signal_ctx_sp)
    // stack layout: [sp, pc] (pc at higher address, like return address after call)
    "  .cfi_def_cfa %esp, 4\n"