/* **********************************************************
 * Copyright (c) 2022 Rivos, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of Rivos, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL RIVOS, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/* RISC-V-specific assembly and trampoline code */

#include "../asm_defines.asm"
START_FILE
#include "include/syscall.h"

#ifndef LINUX
# error Non-Linux is not supported
#endif

/* sizeof(priv_mcontext_t) rounded up to a multiple of 16 */
/* The reserved space for SIMD is also included. */
#define PRIV_MCONTEXT_SIZE 0x290

/* offset of priv_mcontext_t in dr_mcontext_t */
#define PRIV_MCONTEXT_OFFSET 16

#if PRIV_MCONTEXT_OFFSET < 16 || PRIV_MCONTEXT_OFFSET % 16 != 0
# error PRIV_MCONTEXT_OFFSET
#endif

/* offsetof(dcontext_t, dstack) */
#define dstack_OFFSET     0x2d8
/* offsetof(dcontext_t, is_exiting) */
#define is_exiting_OFFSET (dstack_OFFSET+1*ARG_SZ)

#ifndef RISCV64
# error RISCV64 must be defined
#endif

/* All CPU ID registers are accessible only in privileged modes. */
        DECLARE_FUNC(cpuid_supported)
GLOBAL_LABEL(cpuid_supported:)
        li      a0, 0
        ret
        END_FUNC(cpuid_supported)

/* void call_switch_stack(void *func_arg,             // REG_A0
 *                        byte *stack,                // REG_A1
 *                        void (*func)(void *arg),    // REG_A2
 *                        void *mutex_to_free,        // REG_A3
 *                        bool return_on_return)      // REG_A4
 */
        DECLARE_FUNC(call_switch_stack)
GLOBAL_LABEL(call_switch_stack:)
        /* Init the stack. */
        addi     sp, sp, -32
        sd       ra, 16(sp)
        /* Use two callee-saved regs to call func. */
        sd       s0, 8 (sp)
        sd       s1, 0 (sp)
        /* Check mutex_to_free. */
        beqz     ARG4, call_dispatch_alt_stack_no_free
        /* Release the mutex. */
        sd       x0, 0(ARG4)
call_dispatch_alt_stack_no_free:
        /* Copy ARG5 (return_on_return) to callee-saved reg. */
        mv       s1, ARG5
        /* Switch the stack. */
        mv       s0, sp
        mv       sp, ARG2
        /* Call func. */
        jalr     ARG3
        /* Switch stack back. */
        mv       sp, s0
        beqz     s1, GLOBAL_LABEL(unexpected_return)
        /* Restore the stack. */
        ld       s1, 0 (sp)
        ld       s0, 8 (sp)
        ld       ra, 16(sp)
        addi     sp, sp, 32
        ret
        END_FUNC(call_switch_stack)

/*
 * Calls the specified function 'func' after switching to the DR stack
 * for the thread corresponding to 'drcontext'.
 * Passes in 8 arguments.  Uses the C calling convention, so 'func' will work
 * just fine even if it takes fewer than 8 args.
 * Swaps the stack back upon return and returns the value returned by 'func'.
 *
 * void * dr_call_on_clean_stack(void *drcontext,
 *                               void *(*func)(arg1...arg8),
 *                               void *arg1,
 *                               void *arg2,
 *                               void *arg3,
 *                               void *arg4,
 *                               void *arg5,
 *                               void *arg6,
 *                               void *arg7,
 *                               void *arg8)
 */
        DECLARE_EXPORTED_FUNC(dr_call_on_clean_stack)
GLOBAL_LABEL(dr_call_on_clean_stack:)
        addi     sp, sp, -16
        sd       ra, 8(sp)   /* Save link register. */
        sd       s0, 0(sp)
        mv       s0, sp      /* Save sp across the call. */
        /* Swap stacks. */
        ld       sp, dstack_OFFSET(ARG1)
        /* Set up args. */
        mv       ra,   ARG2 /* void *(*func)(arg1...arg8) */
        mv       ARG1, ARG3          /* void *arg1 */
        mv       ARG2, ARG4          /* void *arg2 */
        mv       ARG3, ARG5          /* void *arg3 */
        mv       ARG4, ARG6          /* void *arg4 */
        mv       ARG5, ARG7          /* void *arg5 */
        mv       ARG6, ARG8          /* void *arg6 */
        ld       ARG7, 2*ARG_SZ(s0)  /* void *arg7, retrived from the stack */
        ld       ARG8, 3*ARG_SZ(s0)  /* void *arg8, retrived from the stack */
        jalr     ra
        /* Swap stacks. */
        mv       sp, s0
        ld       s0, 0 (sp)
        ld       ra, 8 (sp)
        addi     sp, sp, 16
        ret
        END_FUNC(dr_call_on_clean_stack)

#ifndef NOT_DYNAMORIO_CORE_PROPER

#ifdef DR_APP_EXPORTS

/* Save priv_mcontext_t, except for x0(zero), x1(ra), x2(sp), x3(scratch),
 * x10(arg1) and pc, to the address in ARG1.
 * Typically the caller will save those five registers itself before calling this.
 */
save_priv_mcontext_helper:
        sd       x4,   4*ARG_SZ(ARG1)
        sd       x5,   5*ARG_SZ(ARG1)
        sd       x6,   6*ARG_SZ(ARG1)
        sd       x7,   7*ARG_SZ(ARG1)
        sd       x8,   8*ARG_SZ(ARG1)
        sd       x9,   9*ARG_SZ(ARG1)
        /* a0 (10*ARG_SZ) is already saved. */
        sd       x11, 11*ARG_SZ(ARG1)
        sd       x12, 12*ARG_SZ(ARG1)
        sd       x13, 13*ARG_SZ(ARG1)
        sd       x14, 14*ARG_SZ(ARG1)
        sd       x15, 15*ARG_SZ(ARG1)
        sd       x16, 16*ARG_SZ(ARG1)
        sd       x17, 17*ARG_SZ(ARG1)
        sd       x18, 18*ARG_SZ(ARG1)
        sd       x19, 19*ARG_SZ(ARG1)
        sd       x20, 20*ARG_SZ(ARG1)
        sd       x21, 21*ARG_SZ(ARG1)
        sd       x22, 22*ARG_SZ(ARG1)
        sd       x23, 23*ARG_SZ(ARG1)
        sd       x24, 24*ARG_SZ(ARG1)
        sd       x25, 25*ARG_SZ(ARG1)
        sd       x26, 26*ARG_SZ(ARG1)
        sd       x27, 27*ARG_SZ(ARG1)
        sd       x28, 28*ARG_SZ(ARG1)
        sd       x29, 29*ARG_SZ(ARG1)
        sd       x30, 30*ARG_SZ(ARG1)
        sd       x31, 31*ARG_SZ(ARG1)
        /* pc (32*ARG_SZ) is already saved. */
        fsd      f0,  33*ARG_SZ(ARG1)
        fsd      f1,  34*ARG_SZ(ARG1)
        fsd      f2,  35*ARG_SZ(ARG1)
        fsd      f3,  36*ARG_SZ(ARG1)
        fsd      f4,  37*ARG_SZ(ARG1)
        fsd      f5,  38*ARG_SZ(ARG1)
        fsd      f6,  39*ARG_SZ(ARG1)
        fsd      f7,  40*ARG_SZ(ARG1)
        fsd      f8,  41*ARG_SZ(ARG1)
        fsd      f9,  42*ARG_SZ(ARG1)
        fsd      f10, 43*ARG_SZ(ARG1)
        fsd      f11, 44*ARG_SZ(ARG1)
        fsd      f12, 45*ARG_SZ(ARG1)
        fsd      f13, 46*ARG_SZ(ARG1)
        fsd      f14, 47*ARG_SZ(ARG1)
        fsd      f15, 48*ARG_SZ(ARG1)
        fsd      f16, 49*ARG_SZ(ARG1)
        fsd      f17, 50*ARG_SZ(ARG1)
        fsd      f18, 51*ARG_SZ(ARG1)
        fsd      f19, 52*ARG_SZ(ARG1)
        fsd      f20, 53*ARG_SZ(ARG1)
        fsd      f21, 54*ARG_SZ(ARG1)
        fsd      f22, 55*ARG_SZ(ARG1)
        fsd      f23, 56*ARG_SZ(ARG1)
        fsd      f24, 57*ARG_SZ(ARG1)
        fsd      f25, 58*ARG_SZ(ARG1)
        fsd      f26, 59*ARG_SZ(ARG1)
        fsd      f27, 60*ARG_SZ(ARG1)
        fsd      f28, 61*ARG_SZ(ARG1)
        fsd      f29, 62*ARG_SZ(ARG1)
        fsd      f30, 63*ARG_SZ(ARG1)
        fsd      f31, 64*ARG_SZ(ARG1)
        frcsr    x3
        sd       x3,  65*ARG_SZ(ARG1)
        /* No need to save simd registers, at least for now. */
        ret

        DECLARE_EXPORTED_FUNC(dr_app_start)
GLOBAL_LABEL(dr_app_start:)
        /* Save link register for the case that DR is not taking over. */
        addi     sp, sp, -16
        sd       ra, 0(sp)

        /* Preverse stack space. */
        addi     sp, sp, -PRIV_MCONTEXT_SIZE
        /* Push x3 on to stack to use it as a scratch. */
        sd       x3, 3*ARG_SZ(sp)
        /* Compute original sp. */
        addi     x3, sp, PRIV_MCONTEXT_SIZE+16
        /* Save original sp on to stack. */
        sd       x3, 2*ARG_SZ(sp)
        /* Save link register on to stack. */
        sd       ra, 1*ARG_SZ(sp)
        /* Save ra as pc */
        sd       ra, 32*ARG_SZ(sp)
        /* Save arg1 */
        sd       ARG1, 10*ARG_SZ(sp)
        CALLC1(save_priv_mcontext_helper, sp)
        CALLC1(GLOBAL_REF(dr_app_start_helper), sp)
        /* If we get here, DR is not taking over. */
        addi     sp, sp, PRIV_MCONTEXT_SIZE
        ld       ra, 0(sp)
        addi     sp, sp, 16
        ret
        END_FUNC(dr_app_start)

        DECLARE_EXPORTED_FUNC(dr_app_take_over)
GLOBAL_LABEL(dr_app_take_over:)
        j        GLOBAL_REF(dynamorio_app_take_over)
        END_FUNC(dr_app_take_over)

        DECLARE_EXPORTED_FUNC(dr_app_running_under_dynamorio)
GLOBAL_LABEL(dr_app_running_under_dynamorio:)
        li       ARG1, 0 /* This instruction is manged by mangle_pre_client. */
        ret
        END_FUNC(dr_app_running_under_dynamorio)

#endif /* DR_APP_EXPORTS */

        DECLARE_EXPORTED_FUNC(dynamorio_app_take_over)
GLOBAL_LABEL(dynamorio_app_take_over:)
        /* Save link register for the case that DR is not taking over. */
        addi     sp, sp, -16
        sd       ra, 0(sp)

        /* Preverse stack space. */
        addi     sp, sp, -PRIV_MCONTEXT_SIZE
        /* Push x3 on to stack to use it as a scratch. */
        sd       x3, 3*ARG_SZ(sp)
        /* Compute original sp. */
        addi     x3, sp, PRIV_MCONTEXT_SIZE+16
        /* Save original sp on to stack. */
        sd       x3, 2*ARG_SZ(sp)
        /* Save link register on to stack. */
        sd       ra, 1*ARG_SZ(sp)
        /* Save ra as pc */
        sd       ra, 32*ARG_SZ(sp)
        /* Save arg1 */
        sd       ARG1, 10*ARG_SZ(sp)
        CALLC1(save_priv_mcontext_helper, sp)
        CALLC1(GLOBAL_REF(dynamorio_app_take_over_helper), sp)
        /* If we get here, DR is not taking over. */
        addi     sp, sp, PRIV_MCONTEXT_SIZE
        ld       ra, 0(sp)
        addi     sp, sp, 16
        ret
        END_FUNC(dynamorio_app_take_over)

/*
 * cleanup_and_terminate(dcontext_t *dcontext,            // A0
 *                       int sysnum,                      // A1
 *                       ptr_uint_t sys_arg1/param_base,  // A2
 *                       ptr_uint_t sys_arg2,             // A3
 *                       bool exitproc)                   // A4
 *
 * See decl in arch_exports.h for description.
 */
        DECLARE_FUNC(cleanup_and_terminate)
GLOBAL_LABEL(cleanup_and_terminate:)
        /* Move argument registers to callee saved registers. */
        mv       s0, ARG1  /* dcontext ptr size */
        mv       s1, ARG2  /* sysnum */
        mv       s2, ARG3  /* sys_arg1 */
        mv       s3, ARG4  /* sys_arg2 */
        mv       s4, ARG5  /* exitproc */
                          /* s5 reserved for dstack ptr */
                          /* s6 reserved for syscall ptr */

        li       a1, 1
        /* Inc exiting_thread_count to avoid being killed once off all_threads list. */
        la       a0, GLOBAL_REF(exiting_thread_count)
        amoadd.w zero, a1, (a0)

        /* Save dcontext->dstack for freeing later and set dcontext->is_exiting. */
        sw       a1, is_exiting_OFFSET(s0) /* dcontext->is_exiting = 1 */
        CALLC1(GLOBAL_REF(is_currently_on_dstack), s0)
        bnez     a0, cat_save_dstack
        li       s5, 0
        j        cat_done_saving_dstack
cat_save_dstack:
        ld       s5, dstack_OFFSET(s0)
cat_done_saving_dstack:
        CALLC0(GLOBAL_REF(get_cleanup_and_terminate_global_do_syscall_entry))
        mv       s6, a0
        beqz     s4, cat_thread_only
        CALLC0(GLOBAL_REF(dynamo_process_exit))
        j        cat_no_thread
cat_thread_only:
        CALLC0(GLOBAL_REF(dynamo_thread_exit))
cat_no_thread:
        /* Switch to d_r_initstack for cleanup of dstack. */
        la       s7, GLOBAL_REF(initstack_mutex)
cat_spin:
        /* We don't have a YIELD-like hint instruction like what's available on
         * Aarch64, so we use a plain spin lock here.
         */
        li       a0, 1
        amoswap.w a0, a0, (s7)
        bnez     a0, cat_spin

        /* We have the spin lock. */

        /* Switch stack. */
        la       a0, GLOBAL_REF(d_r_initstack)
        ld       sp, 0(a0)

        /* Free dstack and call the EXIT_DR_HOOK. */
        CALLC1(GLOBAL_REF(dynamo_thread_stack_free_and_exit), s5) /* pass dstack */

        /* Give up initstack_mutex. */
        la       a0, GLOBAL_REF(initstack_mutex)
        sd       zero, 0(a0)

        /* Dec exiting_thread_count (allows another thread to kill us). */
        la       a0, GLOBAL_REF(exiting_thread_count)
        li       a1, -1
        amoadd.w zero, a1, (a0)

        /* Put system call number in a7. */
        mv       a0, s2 /* sys_arg1 */
        mv       a1, s3 /* sys_arg2 */
        mv       SYSNUM_REG, s1 /* sys_call */

        jr       s6  /* Go do the syscall! */
        jal      GLOBAL_REF(unexpected_return)
        END_FUNC(cleanup_and_terminate)

#endif /* NOT_DYNAMORIO_CORE_PROPER */

        DECLARE_FUNC(global_do_syscall_int)
GLOBAL_LABEL(global_do_syscall_int:)
        ecall
        END_FUNC(global_do_syscall_int)

DECLARE_GLOBAL(safe_read_asm_pre)
DECLARE_GLOBAL(safe_read_asm_mid)
DECLARE_GLOBAL(safe_read_asm_post)
DECLARE_GLOBAL(safe_read_asm_recover)

/*
 * void *safe_read_asm(void *dst, const void *src, size_t n);
 */
        DECLARE_FUNC(safe_read_asm)
GLOBAL_LABEL(safe_read_asm:)
1:      beqz    ARG3, safe_read_asm_recover
ADDRTAKEN_LABEL(safe_read_asm_pre:)
        lbu     a3, 0(ARG2)
ADDRTAKEN_LABEL(safe_read_asm_mid:)
ADDRTAKEN_LABEL(safe_read_asm_post:)
        sb      a3, 0(ARG1)
        addi    ARG3, ARG3, -1
        addi    ARG2, ARG2, 1
        addi    ARG1, ARG1, 1
        j       1b
ADDRTAKEN_LABEL(safe_read_asm_recover:)
        mv      a0, ARG2
        ret
        END_FUNC(safe_read_asm)

/*
 * int dr_try_start(try_except_context_t *cxt) ;
 */
        DECLARE_EXPORTED_FUNC(dr_try_start)
GLOBAL_LABEL(dr_try_start:)
        addi     ARG1, ARG1, TRY_CXT_SETJMP_OFFS
        j        GLOBAL_REF(dr_setjmp)
        END_FUNC(dr_try_start)

/* We save only callee-saved registers and ra: ra, SP, x8/fp, x9, x18-x27, f8-9, f18-27:
 * a total of 26 reg_t (64-bit) slots. See definition of dr_jmp_buf_t.
 *
 * int dr_setjmp(dr_jmp_buf_t *buf);
 */
        DECLARE_FUNC(dr_setjmp)
GLOBAL_LABEL(dr_setjmp:)
        sd       ra, 0 (ARG1)
        sd       sp, ARG_SZ (ARG1)
        sd       s0, 2*ARG_SZ (ARG1)
        sd       s1, 3*ARG_SZ (ARG1)
        sd       s2, 4*ARG_SZ (ARG1)
        sd       s3, 5*ARG_SZ (ARG1)
        sd       s4, 6*ARG_SZ (ARG1)
        sd       s5, 7*ARG_SZ (ARG1)
        sd       s6, 8*ARG_SZ (ARG1)
        sd       s7, 9*ARG_SZ (ARG1)
        sd       s8, 10*ARG_SZ (ARG1)
        sd       s9, 11*ARG_SZ (ARG1)
        sd       s10, 12*ARG_SZ (ARG1)
        sd       s11, 13*ARG_SZ (ARG1)
        fsd      fs0, 14*ARG_SZ (ARG1)
        fsd      fs1, 15*ARG_SZ (ARG1)
        fsd      fs2, 16*ARG_SZ (ARG1)
        fsd      fs3, 17*ARG_SZ (ARG1)
        fsd      fs4, 18*ARG_SZ (ARG1)
        fsd      fs5, 19*ARG_SZ (ARG1)
        fsd      fs6, 20*ARG_SZ (ARG1)
        fsd      fs7, 21*ARG_SZ (ARG1)
        fsd      fs8, 22*ARG_SZ (ARG1)
        fsd      fs9, 23*ARG_SZ (ARG1)
        fsd      fs10, 24*ARG_SZ (ARG1)
        fsd      fs11, 25*ARG_SZ (ARG1)
# ifdef UNIX
        addi     sp, sp, -16
        sd       ra, 0 (sp)
        jal      GLOBAL_REF(dr_setjmp_sigmask)
        ld       ra, 0 (sp)
        add      sp, sp, 16
# endif
        li       a0, 0
        ret
        END_FUNC(dr_setjmp)

/*
 * int dr_longjmp(dr_jmp_buf_t *buf,  int val);
 */
        DECLARE_FUNC(dr_longjmp)
GLOBAL_LABEL(dr_longjmp:)
        ld       ra, 0 (ARG1) /* Restore return address from buf */
        ld       sp, ARG_SZ (ARG1)
        ld       s0, 2*ARG_SZ (ARG1)
        ld       s1, 3*ARG_SZ (ARG1)
        ld       s2, 4*ARG_SZ (ARG1)
        ld       s3, 5*ARG_SZ (ARG1)
        ld       s4, 6*ARG_SZ (ARG1)
        ld       s5, 7*ARG_SZ (ARG1)
        ld       s6, 8*ARG_SZ (ARG1)
        ld       s7, 9*ARG_SZ (ARG1)
        ld       s8, 10*ARG_SZ (ARG1)
        ld       s9, 11*ARG_SZ (ARG1)
        ld       s10, 12*ARG_SZ (ARG1)
        ld       s11, 13*ARG_SZ (ARG1)
        fld      fs0, 14*ARG_SZ (ARG1)
        fld      fs1, 15*ARG_SZ (ARG1)
        fld      fs2, 16*ARG_SZ (ARG1)
        fld      fs3, 17*ARG_SZ (ARG1)
        fld      fs4, 18*ARG_SZ (ARG1)
        fld      fs5, 19*ARG_SZ (ARG1)
        fld      fs6, 20*ARG_SZ (ARG1)
        fld      fs7, 21*ARG_SZ (ARG1)
        fld      fs8, 22*ARG_SZ (ARG1)
        fld      fs9, 23*ARG_SZ (ARG1)
        fld      fs10, 24*ARG_SZ (ARG1)
        fld      fs11, 25*ARG_SZ (ARG1)
        seqz     ARG1, ARG2
        add      ARG1, ARG1, ARG2 /* ARG1 = ( ARG2 == 0 ) ? 1 : ARG2 */
        ret
        END_FUNC(dr_longjmp)

        /* int atomic_swap(int *adr, int val) */
        DECLARE_FUNC(atomic_swap)
GLOBAL_LABEL(atomic_swap:)
        amoswap.w       ARG1, ARG2, (ARG1)
        ret
        END_FUNC(atomic_swap)

#ifdef UNIX
        DECLARE_FUNC(client_int_syscall)
GLOBAL_LABEL(client_int_syscall:)
/* FIXME i#3544: Not implemented */
        jal       GLOBAL_REF(unexpected_return)
        END_FUNC(client_int_syscall)

        DECLARE_FUNC(native_plt_call)
GLOBAL_LABEL(native_plt_call:)
/* FIXME i#3544: Not implemented */
        jal       GLOBAL_REF(unexpected_return)
        END_FUNC(native_plt_call)

        DECLARE_FUNC(_dynamorio_runtime_resolve)
GLOBAL_LABEL(_dynamorio_runtime_resolve:)
/* FIXME i#3544: Not implemented */
        jal       GLOBAL_REF(unexpected_return)
        END_FUNC(_dynamorio_runtime_resolve)
#endif /* UNIX */

#ifdef LINUX
/*
 * thread_id_t dynamorio_clone(uint flags, byte *newsp, void *ptid, void *tls,
 *                             void *ctid, void (*func)(void))
 */
        DECLARE_FUNC(dynamorio_clone)
GLOBAL_LABEL(dynamorio_clone:)
        addi     ARG2, ARG2, -16 /* Description: newsp = newsp - 16. */
        sd       ARG6, 0 (ARG2) /* The func is now on TOS of newsp. */
        li       SYSNUM_REG, SYS_clone /* All args are already in syscall registers.*/
        ecall
        bnez     ARG1, dynamorio_clone_parent
        ld       ARG1, 0 (sp)
        addi     sp, sp, 16
        jalr     ARG1
        jal      GLOBAL_REF(unexpected_return)
dynamorio_clone_parent:
        ret
        END_FUNC(dynamorio_clone)

        DECLARE_FUNC(dynamorio_sigreturn)
GLOBAL_LABEL(dynamorio_sigreturn:)
        li       SYSNUM_REG, SYS_rt_sigreturn
        ecall
        jal      GLOBAL_REF(unexpected_return)
        END_FUNC(dynamorio_sigreturn)

        DECLARE_FUNC(dynamorio_sys_exit)
GLOBAL_LABEL(dynamorio_sys_exit:)
        li       a0, 0 /* exit code */
        li       SYSNUM_REG, SYS_exit
        ecall
        jal      GLOBAL_REF(unexpected_return)
        END_FUNC(dynamorio_sys_exit)

# ifndef NOT_DYNAMORIO_CORE_PROPER

#  ifndef HAVE_SIGALTSTACK
#   error NYI
#  endif
        DECLARE_FUNC(main_signal_handler)
GLOBAL_LABEL(main_signal_handler:)
        mv       ARG4, sp /* pass as extra arg */
        j        GLOBAL_REF(main_signal_handler_C) /* chain call */
        END_FUNC(main_signal_handler)

# endif /* NOT_DYNAMORIO_CORE_PROPER */

#endif /* LINUX */

        DECLARE_FUNC(hashlookup_null_handler)
GLOBAL_LABEL(hashlookup_null_handler:)
/* FIXME i#3544: Not implemented */
        jal       GLOBAL_REF(unexpected_return)
        END_FUNC(hashlookup_null_handler)

        DECLARE_FUNC(back_from_native_retstubs)
GLOBAL_LABEL(back_from_native_retstubs:)
DECLARE_GLOBAL(back_from_native_retstubs_end)
ADDRTAKEN_LABEL(back_from_native_retstubs_end:)
/* FIXME i#3544: Not implemented */
        jal       GLOBAL_REF(unexpected_return)
        END_FUNC(back_from_native_retstubs)

        DECLARE_FUNC(back_from_native)
GLOBAL_LABEL(back_from_native:)
/* FIXME i#3544: Not implemented */
        jal       GLOBAL_REF(unexpected_return)
        END_FUNC(back_from_native)

#ifdef UNIX
# if !defined(STANDALONE_UNIT_TEST) && !defined(STATIC_LIBRARY)
        DECLARE_FUNC(_start)
GLOBAL_LABEL(_start:)
        nop
        mv       fp, x0  /* Clear frame ptr for stack trace bottom. */

        CALLC3(GLOBAL_REF(relocate_dynamorio), 0, 0, sp)

        /* Clear 2nd & 3rd args to distinguish from xfer_to_new_libdr. */
        mv       ARG2, x0
        mv       ARG3, x0

        /* Entry from xfer_to_new_libdr is here.  It has set up 2nd & 3rd args already. */
GLOBAL_LABEL(.L_start_invoke_C:)
        mv       fp, x0 /* Clear frame ptr for stack trace bottom. */
        mv       ARG1, sp /* 1st arg to privload_early_inject. */
        jal      GLOBAL_REF(privload_early_inject)
        /* Shouldn't return. */
        jal     GLOBAL_REF(unexpected_return)
        END_FUNC(_start)

/* i#1227: on a conflict with the app we reload ourselves.
 * xfer_to_new_libdr(entry, init_sp, cur_dr_map, cur_dr_size)
 * =>
 * Invokes entry after setting sp to init_sp and placing the current (old)
 * libdr bounds in registers for the new libdr to unmap.
 */
        DECLARE_FUNC(xfer_to_new_libdr)
GLOBAL_LABEL(xfer_to_new_libdr:)
        mv       s0, ARG1
        /* Restore sp */
        mv       sp, ARG2
        /* Skip prologue that calls relocate_dynamorio() and clears args 2+3 by
         * adjusting the _start in the reloaded DR by the same distance as in
         * the current DR, but w/o clobbering ARG3 or ARG4.
         */
        la       ARG1, GLOBAL_REF(.L_start_invoke_C)
        la       ARG2, GLOBAL_REF(_start)
        sub      ARG1, ARG1, ARG2
        add      s0, s0, ARG1
        /* _start expects these as 2nd & 3rd args */
        mv       ARG2, ARG3
        mv       ARG3, ARG4
        jr       s0
        ret
        END_FUNC(xfer_to_new_libdr)
# endif /* !STANDALONE_UNIT_TEST && !STATIC_LIBRARY */
#endif /* UNIX */
/* We need to call futex_wakeall without using any stack.
 * Takes KSYNCH_TYPE* in a0 and the post-syscall jump target in a1
 */
        DECLARE_FUNC(dynamorio_condvar_wake_and_jmp)
GLOBAL_LABEL(dynamorio_condvar_wake_and_jmp:)
        mv       REG_R9, ARG2 /* save across syscall */
        li       ARG6, 0
        li       ARG5, 0
        li       ARG4, 0
        li       ARG3, 0x7fffffff /* arg3 = INT_MAX */
        li       ARG2, 1 /* arg2 = FUTEX_WAKE */
        li       SYSNUM_REG, 98 /* SYS_futex */
        ecall
        jr REG_R9
        END_FUNC(dynamorio_condvar_wake_and_jmp)

END_FILE