/* **********************************************************
 * Copyright (c) 2014-2022 Google, Inc. All rights reserved.
 * Copyright (c) 2016 ARM Limited. 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 ARM Limited 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 ARM LIMITED 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.
 */

/***************************************************************************
 * Assembly and trampoline code shared between between ARM and AArch64.
 */

#include "../asm_defines.asm"
START_FILE

#ifdef UNIX
# if !defined(STANDALONE_UNIT_TEST) && !defined(STATIC_LIBRARY)
        DECLARE_FUNC(_start)
GLOBAL_LABEL(_start:)
        /* i#38: Attaching in middle of blocking syscall requires padded null bytes. */
        nop
        mov      FP, #0   /* clear frame ptr for stack trace bottom */
        /* i#1676, i#1708: relocate dynamorio if it is not loaded to preferred address.
         * We call this here to ensure it's safe to access globals once in C code
         * (xref i#1865).
         */
        CALLC3(GLOBAL_REF(relocate_dynamorio), #0, #0, sp)

        /* Clear 2nd & 3rd args to distinguish from xfer_to_new_libdr */
        mov      ARG2, #0
        mov      ARG3, #0

        /* Entry from xfer_to_new_libdr is here.  It has set up 2nd & 3rd args already. */
.L_start_invoke_C:
        mov      FP, #0   /* clear frame ptr for stack trace bottom */
        mov      ARG1, sp  /* 1st arg to privload_early_inject */
        bl       GLOBAL_REF(privload_early_inject)
        /* shouldn't return */
        bl       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:)
        mov      REG_PRESERVED_1, ARG1
        /* Restore sp */
        mov      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.
         */
        adr      ARG1, .L_start_invoke_C
        adr      ARG2, GLOBAL_REF(_start)
        sub      ARG1, ARG1, ARG2
        add      REG_PRESERVED_1, REG_PRESERVED_1, ARG1
        /* _start expects these as 2nd & 3rd args */
        mov      ARG2, ARG3
        mov      ARG3, ARG4
        INDJMP   REG_PRESERVED_1
        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 r0 and the post-syscall jump target in r1.
 */
        DECLARE_FUNC(dynamorio_condvar_wake_and_jmp)
GLOBAL_LABEL(dynamorio_condvar_wake_and_jmp:)
        mov      REG_R12, REG_R1 /* save across syscall */
        mov      REG_R5, #0 /* arg6 */
        mov      REG_R4, #0 /* arg5 */
        mov      REG_R3, #0 /* arg4 */
        mov      REG_R2, #0x7fffffff /* arg3 = INT_MAX */
        mov      REG_R1, #1 /* arg2 = FUTEX_WAKE */
        /* arg1 = &futex, already in r0 */
#ifdef AARCH64
        mov      w8, #98 /* SYS_futex */
#else
        mov      r7, #240 /* SYS_futex */
#endif
        svc      #0
        INDJMP   REG_R12
        END_FUNC(dynamorio_condvar_wake_and_jmp)

END_FILE