505 lines
15 KiB
C
505 lines
15 KiB
C
/*
|
|
* Copyright (c) 2009 ARM Ltd
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. 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.
|
|
* 3. The name of the company may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY ARM LTD ``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 LTD 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.
|
|
*/
|
|
|
|
#ifndef ARM_ASM__H
|
|
#define ARM_ASM__H
|
|
|
|
#include "arm-acle-compat.h"
|
|
|
|
#if __ARM_ARCH >= 7 && defined (__ARM_ARCH_ISA_ARM)
|
|
# define _ISA_ARM_7
|
|
#endif
|
|
|
|
#if __ARM_ARCH >= 6 && defined (__ARM_ARCH_ISA_ARM)
|
|
# define _ISA_ARM_6
|
|
#endif
|
|
|
|
#if __ARM_ARCH >= 5
|
|
# define _ISA_ARM_5
|
|
#endif
|
|
|
|
#if __ARM_ARCH >= 4 && __ARM_ARCH_ISA_THUMB >= 1
|
|
# define _ISA_ARM_4T
|
|
#endif
|
|
|
|
#if __ARM_ARCH >= 4 && __ARM_ARCH_ISA_THUMB == 0
|
|
# define _ISA_ARM_4
|
|
#endif
|
|
|
|
|
|
#if __ARM_ARCH_ISA_THUMB >= 2
|
|
# define _ISA_THUMB_2
|
|
#endif
|
|
|
|
#if __ARM_ARCH_ISA_THUMB >= 1
|
|
# define _ISA_THUMB_1
|
|
#endif
|
|
|
|
/* Check whether leaf function PAC signing has been requested in the
|
|
-mbranch-protect compile-time option. */
|
|
#define LEAF_PROTECT_BIT 2
|
|
|
|
#ifdef __ARM_FEATURE_PAC_DEFAULT
|
|
# define HAVE_PAC_LEAF \
|
|
((__ARM_FEATURE_PAC_DEFAULT & (1 << LEAF_PROTECT_BIT)) && 1)
|
|
#else
|
|
# define HAVE_PAC_LEAF 0
|
|
#endif
|
|
|
|
/* Provide default parameters for PAC-code handling in leaf-functions. */
|
|
#if HAVE_PAC_LEAF
|
|
# ifndef PAC_LEAF_PUSH_IP
|
|
# define PAC_LEAF_PUSH_IP 1
|
|
# endif
|
|
#else /* !HAVE_PAC_LEAF */
|
|
# undef PAC_LEAF_PUSH_IP
|
|
# define PAC_LEAF_PUSH_IP 0
|
|
#endif /* HAVE_PAC_LEAF */
|
|
|
|
#define STACK_ALIGN_ENFORCE 0
|
|
|
|
#ifdef __ASSEMBLER__
|
|
|
|
/******************************************************************************
|
|
* Implementation of the prologue and epilogue assembler macros and their
|
|
* associated helper functions.
|
|
*
|
|
* These functions add support for the following:
|
|
*
|
|
* - M-profile branch target identification (BTI) landing-pads when compiled
|
|
* with `-mbranch-protection=bti'.
|
|
* - PAC-signing and verification instructions, depending on hardware support
|
|
* and whether the PAC-signing of leaf functions has been requested via the
|
|
* `-mbranch-protection=pac-ret+leaf' compiler argument.
|
|
* - 8-byte stack alignment preservation at function entry, defaulting to the
|
|
* value of STACK_ALIGN_ENFORCE.
|
|
*
|
|
* Notes:
|
|
* - Prologue stack alignment is implemented by detecting a push with an odd
|
|
* number of registers and prepending a dummy register to the list.
|
|
* - If alignment is attempted on a list containing r0, compilation will result
|
|
* in an error.
|
|
* - If alignment is attempted in a list containing r1, r0 will be prepended to
|
|
* the register list and r0 will be restored prior to function return. for
|
|
* functions with non-void return types, this will result in the corruption of
|
|
* the result register.
|
|
* - Stack alignment is enforced via the following helper macro call-chain:
|
|
*
|
|
* {prologue|epilogue} ->_align8 -> _preprocess_reglist ->
|
|
* _preprocess_reglist1 -> {_prologue|_epilogue}
|
|
*
|
|
* - Debug CFI directives are automatically added to prologues and epilogues,
|
|
* assisted by `cfisavelist' and `cfirestorelist', respectively.
|
|
*
|
|
* Arguments:
|
|
* prologue
|
|
* --------
|
|
* - first - If `last' specified, this serves as start of general-purpose
|
|
* register (GPR) range to push onto stack, otherwise represents
|
|
* single GPR to push onto stack. If omitted, no GPRs pushed
|
|
* onto stack at prologue.
|
|
* - last - If given, specifies inclusive upper-bound of GPR range.
|
|
* - push_ip - Determines whether IP register is to be pushed to stack at
|
|
* prologue. When pac-signing is requested, this holds the
|
|
* the pac-key. Either 1 or 0 to push or not push, respectively.
|
|
* Default behavior: Set to value of PAC_LEAF_PUSH_IP macro.
|
|
* - push_lr - Determines whether to push lr to the stack on function entry.
|
|
* Either 1 or 0 to push or not push, respectively.
|
|
* - align8 - Whether to enforce alignment. Either 1 or 0, with 1 requesting
|
|
* alignment.
|
|
*
|
|
* epilogue
|
|
* --------
|
|
* The epilogue should be called passing the same arguments as those passed to
|
|
* the prologue to ensure the stack is not corrupted on function return.
|
|
*
|
|
* Usage examples:
|
|
*
|
|
* prologue push_ip=1 -> push {ip}
|
|
* epilogue push_ip=1, align8=1 -> pop {r2, ip}
|
|
* prologue push_ip=1, push_lr=1 -> push {ip, lr}
|
|
* epilogue 1 -> pop {r1}
|
|
* prologue 1, align8=1 -> push {r0, r1}
|
|
* epilogue 1, push_ip=1 -> pop {r1, ip}
|
|
* prologue 1, 4 -> push {r1-r4}
|
|
* epilogue 1, 4 push_ip=1 -> pop {r1-r4, ip}
|
|
*
|
|
******************************************************************************/
|
|
|
|
/* Emit .cfi_restore directives for a consecutive sequence of registers. */
|
|
.macro cfirestorelist first, last
|
|
.cfi_restore \last
|
|
.if \last-\first
|
|
cfirestorelist \first, \last-1
|
|
.endif
|
|
.endm
|
|
|
|
/* Emit .cfi_offset directives for a consecutive sequence of registers. */
|
|
.macro cfisavelist first, last, index=1
|
|
.cfi_offset \last, -4*(\index)
|
|
.if \last-\first
|
|
cfisavelist \first, \last-1, \index+1
|
|
.endif
|
|
.endm
|
|
|
|
.macro _prologue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
|
|
.if \push_ip & 1 != \push_ip
|
|
.error "push_ip may be either 0 or 1"
|
|
.endif
|
|
.if \push_lr & 1 != \push_lr
|
|
.error "push_lr may be either 0 or 1"
|
|
.endif
|
|
.if \first != -1
|
|
.if \last == -1
|
|
/* Upper-bound not provided: Set upper = lower. */
|
|
_prologue \first, \first, \push_ip, \push_lr
|
|
.exitm
|
|
.endif
|
|
.endif
|
|
#if HAVE_PAC_LEAF
|
|
#if __ARM_FEATURE_BTI_DEFAULT
|
|
pacbti ip, lr, sp
|
|
#else
|
|
pac ip, lr, sp
|
|
#endif /* __ARM_FEATURE_BTI_DEFAULT */
|
|
.cfi_register 143, 12
|
|
#else
|
|
#if __ARM_FEATURE_BTI_DEFAULT
|
|
bti
|
|
#endif /* __ARM_FEATURE_BTI_DEFAULT */
|
|
#endif /* HAVE_PAC_LEAF */
|
|
.if \first != -1
|
|
.if \last != \first
|
|
.if \last >= 13
|
|
.error "SP cannot be in the save list"
|
|
.endif
|
|
.if \push_ip
|
|
.if \push_lr
|
|
/* Case 1: push register range, ip and lr registers. */
|
|
push {r\first-r\last, ip, lr}
|
|
.cfi_adjust_cfa_offset ((\last-\first)+3)*4
|
|
.cfi_offset 14, -4
|
|
.cfi_offset 143, -8
|
|
cfisavelist \first, \last, 3
|
|
.else // !\push_lr
|
|
/* Case 2: push register range and ip register. */
|
|
push {r\first-r\last, ip}
|
|
.cfi_adjust_cfa_offset ((\last-\first)+2)*4
|
|
.cfi_offset 143, -4
|
|
cfisavelist \first, \last, 2
|
|
.endif
|
|
.else // !\push_ip
|
|
.if \push_lr
|
|
/* Case 3: push register range and lr register. */
|
|
push {r\first-r\last, lr}
|
|
.cfi_adjust_cfa_offset ((\last-\first)+2)*4
|
|
.cfi_offset 14, -4
|
|
cfisavelist \first, \last, 2
|
|
.else // !\push_lr
|
|
/* Case 4: push register range. */
|
|
push {r\first-r\last}
|
|
.cfi_adjust_cfa_offset ((\last-\first)+1)*4
|
|
cfisavelist \first, \last, 1
|
|
.endif
|
|
.endif
|
|
.else // \last == \first
|
|
.if \push_ip
|
|
.if \push_lr
|
|
/* Case 5: push single GP register plus ip and lr registers. */
|
|
push {r\first, ip, lr}
|
|
.cfi_adjust_cfa_offset 12
|
|
.cfi_offset 14, -4
|
|
.cfi_offset 143, -8
|
|
cfisavelist \first, \first, 3
|
|
.else // !\push_lr
|
|
/* Case 6: push single GP register plus ip register. */
|
|
push {r\first, ip}
|
|
.cfi_adjust_cfa_offset 8
|
|
.cfi_offset 143, -4
|
|
cfisavelist \first, \first, 2
|
|
.endif
|
|
.else // !\push_ip
|
|
.if \push_lr
|
|
/* Case 7: push single GP register plus lr register. */
|
|
push {r\first, lr}
|
|
.cfi_adjust_cfa_offset 8
|
|
.cfi_offset 14, -4
|
|
cfisavelist \first, \first, 2
|
|
.else // !\push_lr
|
|
/* Case 8: push single GP register. */
|
|
push {r\first}
|
|
.cfi_adjust_cfa_offset 4
|
|
cfisavelist \first, \first, 1
|
|
.endif
|
|
.endif
|
|
.endif
|
|
.else // \first == -1
|
|
.if \push_ip
|
|
.if \push_lr
|
|
/* Case 9: push ip and lr registers. */
|
|
push {ip, lr}
|
|
.cfi_adjust_cfa_offset 8
|
|
.cfi_offset 14, -4
|
|
.cfi_offset 143, -8
|
|
.else // !\push_lr
|
|
/* Case 10: push ip register. */
|
|
push {ip}
|
|
.cfi_adjust_cfa_offset 4
|
|
.cfi_offset 143, -4
|
|
.endif
|
|
.else // !\push_ip
|
|
.if \push_lr
|
|
/* Case 11: push lr register. */
|
|
push {lr}
|
|
.cfi_adjust_cfa_offset 4
|
|
.cfi_offset 14, -4
|
|
.endif
|
|
.endif
|
|
.endif
|
|
.endm
|
|
|
|
.macro _epilogue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
|
|
.if \push_ip & 1 != \push_ip
|
|
.error "push_ip may be either 0 or 1"
|
|
.endif
|
|
.if \push_lr & 1 != \push_lr
|
|
.error "push_lr may be either 0 or 1"
|
|
.endif
|
|
.if \first != -1
|
|
.if \last == -1
|
|
/* Upper-bound not provided: Set upper = lower. */
|
|
_epilogue \first, \first, \push_ip, \push_lr
|
|
.exitm
|
|
.endif
|
|
.if \last != \first
|
|
.if \last >= 13
|
|
.error "SP cannot be in the save list"
|
|
.endif
|
|
.if \push_ip
|
|
.if \push_lr
|
|
/* Case 1: pop register range, ip and lr registers. */
|
|
pop {r\first-r\last, ip, lr}
|
|
.cfi_restore 14
|
|
.cfi_register 143, 12
|
|
cfirestorelist \first, \last
|
|
.else // !\push_lr
|
|
/* Case 2: pop register range and ip register. */
|
|
pop {r\first-r\last, ip}
|
|
.cfi_register 143, 12
|
|
cfirestorelist \first, \last
|
|
.endif
|
|
.else // !\push_ip
|
|
.if \push_lr
|
|
/* Case 3: pop register range and lr register. */
|
|
pop {r\first-r\last, lr}
|
|
.cfi_restore 14
|
|
cfirestorelist \first, \last
|
|
.else // !\push_lr
|
|
/* Case 4: pop register range. */
|
|
pop {r\first-r\last}
|
|
cfirestorelist \first, \last
|
|
.endif
|
|
.endif
|
|
.else // \last == \first
|
|
.if \push_ip
|
|
.if \push_lr
|
|
/* Case 5: pop single GP register plus ip and lr registers. */
|
|
pop {r\first, ip, lr}
|
|
.cfi_restore 14
|
|
.cfi_register 143, 12
|
|
cfirestorelist \first, \first
|
|
.else // !\push_lr
|
|
/* Case 6: pop single GP register plus ip register. */
|
|
pop {r\first, ip}
|
|
.cfi_register 143, 12
|
|
cfirestorelist \first, \first
|
|
.endif
|
|
.else // !\push_ip
|
|
.if \push_lr
|
|
/* Case 7: pop single GP register plus lr register. */
|
|
pop {r\first, lr}
|
|
.cfi_restore 14
|
|
cfirestorelist \first, \first
|
|
.else // !\push_lr
|
|
/* Case 8: pop single GP register. */
|
|
pop {r\first}
|
|
cfirestorelist \first, \first
|
|
.endif
|
|
.endif
|
|
.endif
|
|
.else // \first == -1
|
|
.if \push_ip
|
|
.if \push_lr
|
|
/* Case 9: pop ip and lr registers. */
|
|
pop {ip, lr}
|
|
.cfi_restore 14
|
|
.cfi_register 143, 12
|
|
.else // !\push_lr
|
|
/* Case 10: pop ip register. */
|
|
pop {ip}
|
|
.cfi_register 143, 12
|
|
.endif
|
|
.else // !\push_ip
|
|
.if \push_lr
|
|
/* Case 11: pop lr register. */
|
|
pop {lr}
|
|
.cfi_restore 14
|
|
.endif
|
|
.endif
|
|
.endif
|
|
#if HAVE_PAC_LEAF
|
|
aut ip, lr, sp
|
|
#endif /* HAVE_PAC_LEAF */
|
|
bx lr
|
|
.endm
|
|
|
|
# clean up expressions in 'last'
|
|
.macro _preprocess_reglist1 first:req, last:req, push_ip:req, push_lr:req, reglist_op:req
|
|
.if \last == 0
|
|
\reglist_op \first, 0, \push_ip, \push_lr
|
|
.elseif \last == 1
|
|
\reglist_op \first, 1, \push_ip, \push_lr
|
|
.elseif \last == 2
|
|
\reglist_op \first, 2, \push_ip, \push_lr
|
|
.elseif \last == 3
|
|
\reglist_op \first, 3, \push_ip, \push_lr
|
|
.elseif \last == 4
|
|
\reglist_op \first, 4, \push_ip, \push_lr
|
|
.elseif \last == 5
|
|
\reglist_op \first, 5, \push_ip, \push_lr
|
|
.elseif \last == 6
|
|
\reglist_op \first, 6, \push_ip, \push_lr
|
|
.elseif \last == 7
|
|
\reglist_op \first, 7, \push_ip, \push_lr
|
|
.elseif \last == 8
|
|
\reglist_op \first, 8, \push_ip, \push_lr
|
|
.elseif \last == 9
|
|
\reglist_op \first, 9, \push_ip, \push_lr
|
|
.elseif \last == 10
|
|
\reglist_op \first, 10, \push_ip, \push_lr
|
|
.elseif \last == 11
|
|
\reglist_op \first, 11, \push_ip, \push_lr
|
|
.else
|
|
.error "last (\last) out of range"
|
|
.endif
|
|
.endm
|
|
|
|
# clean up expressions in 'first'
|
|
.macro _preprocess_reglist first:req, last, push_ip=0, push_lr=0, reglist_op:req
|
|
.ifb \last
|
|
_preprocess_reglist \first \first \push_ip \push_lr
|
|
.else
|
|
.if \first > \last
|
|
.error "last (\last) must be at least as great as first (\first)"
|
|
.endif
|
|
.if \first == 0
|
|
_preprocess_reglist1 0, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 1
|
|
_preprocess_reglist1 1, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 2
|
|
_preprocess_reglist1 2, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 3
|
|
_preprocess_reglist1 3, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 4
|
|
_preprocess_reglist1 4, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 5
|
|
_preprocess_reglist1 5, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 6
|
|
_preprocess_reglist1 6, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 7
|
|
_preprocess_reglist1 7, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 8
|
|
_preprocess_reglist1 8, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 9
|
|
_preprocess_reglist1 9, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 10
|
|
_preprocess_reglist1 10, \last, \push_ip, \push_lr, \reglist_op
|
|
.elseif \first == 11
|
|
_preprocess_reglist1 11, \last, \push_ip, \push_lr, \reglist_op
|
|
.else
|
|
.error "first (\first) out of range"
|
|
.endif
|
|
.endif
|
|
.endm
|
|
|
|
.macro _align8 first, last, push_ip=0, push_lr=0, reglist_op=_prologue
|
|
.ifb \first
|
|
.ifnb \last
|
|
.error "can't have last (\last) without specifying first"
|
|
.else // \last not blank
|
|
.if ((\push_ip + \push_lr) % 2) == 0
|
|
\reglist_op first=-1, last=-1, push_ip=\push_ip, push_lr=\push_lr
|
|
.exitm
|
|
.else // ((\push_ip + \push_lr) % 2) odd
|
|
_align8 2, 2, \push_ip, \push_lr, \reglist_op
|
|
.exitm
|
|
.endif // ((\push_ip + \push_lr) % 2) == 0
|
|
.endif // .ifnb \last
|
|
.endif // .ifb \first
|
|
|
|
.ifb \last
|
|
_align8 \first, \first, \push_ip, \push_lr, \reglist_op
|
|
.else
|
|
.if \push_ip & 1 <> \push_ip
|
|
.error "push_ip may be 0 or 1"
|
|
.endif
|
|
.if \push_lr & 1 <> \push_lr
|
|
.error "push_lr may be 0 or 1"
|
|
.endif
|
|
.ifeq (\last - \first + \push_ip + \push_lr) % 2
|
|
.if \first == 0
|
|
.error "Alignment required and first register is r0"
|
|
.exitm
|
|
.endif
|
|
_preprocess_reglist \first-1, \last, \push_ip, \push_lr, \reglist_op
|
|
.else
|
|
_preprocess_reglist \first \last, \push_ip, \push_lr, \reglist_op
|
|
.endif
|
|
.endif
|
|
.endm
|
|
|
|
.macro prologue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
|
|
.if \align8
|
|
_align8 \first, \last, \push_ip, \push_lr, _prologue
|
|
.else
|
|
_prologue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
|
|
.endif
|
|
.endm
|
|
|
|
.macro epilogue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
|
|
.if \align8
|
|
_align8 \first, \last, \push_ip, \push_lr, reglist_op=_epilogue
|
|
.else
|
|
_epilogue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
|
|
.endif
|
|
.endm
|
|
|
|
#endif /* __ASSEMBLER__ */
|
|
|
|
#endif /* ARM_ASM__H */
|