forked from OSchip/llvm-project
[ARM][libunwind] add PACBTI-M support for libunwind
This patch implements the following: - Emit PACBTI-M build attributes in libunwind asm files - Authenticate LR in DWARF32 using PACBTI Use Armv8.1-M.Main PACBTI extension to authenticate the return address (stored in the LR register) before moving it to the PC (IP) register. The AUTG instruction is used with the candidate return address, the CFA, and the authentication code that is retrieved from the saved pseudo-register RA_AUTH_CODE. - Authenticate LR in EHABI using PACBTI Authenticate the contents of the LR register using Armv8.1-M.Main PACBTI extension. A new frame unwinding instruction is introduced (0xb4). This instruction pops out of the stack the return address authentication code, which is then used in conjunction with the SP and the next-to-be instruction pointer to perform authentication. This authentication code is popped into a new register, UNW_ARM_PSEUDO_PAC, which is a pseudo-register. This patch is part of a series that adds support for the PACBTI-M extension of the Armv8.1-M architecture, as detailed here: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension The PACBTI-M specification can be found in the Armv8-M Architecture Reference Manual: https://developer.arm.com/documentation/ddi0553/latest The following people contributed to this patch: - Momchil Velikov - Victor Campos - Ties Stuij Reviewed By: #libunwind, danielkiss, mstorsjo Differential Revision: https://reviews.llvm.org/D112430
This commit is contained in:
parent
96b92d5b3e
commit
e6d0b851f8
|
@ -172,7 +172,8 @@ typedef enum {
|
|||
_UVRSC_CORE = 0, /* integer register */
|
||||
_UVRSC_VFP = 1, /* vfp */
|
||||
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
|
||||
_UVRSC_WMMXC = 4 /* Intel WMMX control register */
|
||||
_UVRSC_WMMXC = 4, /* Intel WMMX control register */
|
||||
_UVRSC_PSEUDO = 5 /* Special purpose pseudo register */
|
||||
} _Unwind_VRS_RegClass;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -718,7 +718,8 @@ enum {
|
|||
UNW_ARM_WR14 = 126,
|
||||
UNW_ARM_WR15 = 127,
|
||||
// 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC}
|
||||
// 134-143 -- Reserved
|
||||
// 134-142 -- Reserved
|
||||
UNW_ARM_RA_AUTH_CODE = 143,
|
||||
// 144-150 -- R8_USR-R14_USR
|
||||
// 151-157 -- R8_FIQ-R14_FIQ
|
||||
// 158-159 -- R13_IRQ-R14_IRQ
|
||||
|
|
|
@ -87,10 +87,11 @@ extern void _Unwind_Resume(_Unwind_Exception *exception_object);
|
|||
extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
|
||||
|
||||
typedef enum {
|
||||
_UVRSC_CORE = 0, /* integer register */
|
||||
_UVRSC_VFP = 1, /* vfp */
|
||||
_UVRSC_CORE = 0, /* integer register */
|
||||
_UVRSC_VFP = 1, /* vfp */
|
||||
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
|
||||
_UVRSC_WMMXC = 4 /* Intel WMMX control register */
|
||||
_UVRSC_WMMXC = 4, /* Intel WMMX control register */
|
||||
_UVRSC_PSEUDO = 5 /* Special purpose pseudo register */
|
||||
} _Unwind_VRS_RegClass;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -242,6 +242,20 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_IS_NATIVE_ONLY) && defined(_LIBUNWIND_TARGET_ARM) && \
|
||||
defined(__ARM_FEATURE_PAUTH)
|
||||
if ((R::getArch() == REGISTERS_ARM) &&
|
||||
prolog.savedRegisters[UNW_ARM_RA_AUTH_CODE].value) {
|
||||
pint_t pac =
|
||||
getSavedRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[UNW_ARM_RA_AUTH_CODE]);
|
||||
__asm__ __volatile__("autg %0, %1, %2"
|
||||
:
|
||||
: "r"(pac), "r"(returnAddress), "r"(cfa)
|
||||
:);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_SPARC)
|
||||
if (R::getArch() == REGISTERS_SPARC) {
|
||||
// Skip call site instruction and delay slot
|
||||
|
|
|
@ -2140,6 +2140,10 @@ private:
|
|||
uint32_t __pc; // Program counter r15
|
||||
};
|
||||
|
||||
struct PseudoRegisters {
|
||||
uint32_t __pac; // Return Authentication Code (PAC)
|
||||
};
|
||||
|
||||
static void saveVFPWithFSTMD(void*);
|
||||
static void saveVFPWithFSTMX(void*);
|
||||
static void saveVFPv3(void*);
|
||||
|
@ -2156,6 +2160,7 @@ private:
|
|||
|
||||
// ARM registers
|
||||
GPRs _registers;
|
||||
PseudoRegisters _pseudo_registers;
|
||||
|
||||
// We save floating point registers lazily because we can't know ahead of
|
||||
// time which ones are used. See EHABI #4.7.
|
||||
|
@ -2193,6 +2198,7 @@ inline Registers_arm::Registers_arm(const void *registers)
|
|||
"arm registers do not fit into unw_context_t");
|
||||
// See __unw_getcontext() note about data.
|
||||
memcpy(&_registers, registers, sizeof(_registers));
|
||||
memset(&_pseudo_registers, 0, sizeof(_pseudo_registers));
|
||||
memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad));
|
||||
memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31));
|
||||
#if defined(__ARM_WMMX)
|
||||
|
@ -2208,6 +2214,7 @@ inline Registers_arm::Registers_arm()
|
|||
_saved_vfp_d0_d15(false),
|
||||
_saved_vfp_d16_d31(false) {
|
||||
memset(&_registers, 0, sizeof(_registers));
|
||||
memset(&_pseudo_registers, 0, sizeof(_pseudo_registers));
|
||||
memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad));
|
||||
memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31));
|
||||
#if defined(__ARM_WMMX)
|
||||
|
@ -2235,6 +2242,11 @@ inline bool Registers_arm::validRegister(int regNum) const {
|
|||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef __ARM_FEATURE_PAUTH
|
||||
if (regNum == UNW_ARM_RA_AUTH_CODE)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2261,6 +2273,11 @@ inline uint32_t Registers_arm::getRegister(int regNum) const {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ARM_FEATURE_PAUTH
|
||||
if (regNum == UNW_ARM_RA_AUTH_CODE)
|
||||
return _pseudo_registers.__pac;
|
||||
#endif
|
||||
|
||||
_LIBUNWIND_ABORT("unsupported arm register");
|
||||
}
|
||||
|
||||
|
@ -2296,6 +2313,11 @@ inline void Registers_arm::setRegister(int regNum, uint32_t value) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (regNum == UNW_ARM_RA_AUTH_CODE) {
|
||||
_pseudo_registers.__pac = value;
|
||||
return;
|
||||
}
|
||||
|
||||
_LIBUNWIND_ABORT("unsupported arm register");
|
||||
}
|
||||
|
||||
|
|
|
@ -261,6 +261,7 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
|
|||
size_t offset, size_t len) {
|
||||
bool wrotePC = false;
|
||||
bool finish = false;
|
||||
bool hasReturnAddrAuthCode = false;
|
||||
while (offset < len && !finish) {
|
||||
uint8_t byte = getByte(data, offset++);
|
||||
if ((byte & 0x80) == 0) {
|
||||
|
@ -347,6 +348,10 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
|
|||
break;
|
||||
}
|
||||
case 0xb4:
|
||||
hasReturnAddrAuthCode = true;
|
||||
_Unwind_VRS_Pop(context, _UVRSC_PSEUDO,
|
||||
0 /* Return Address Auth Code */, _UVRSD_UINT32);
|
||||
break;
|
||||
case 0xb5:
|
||||
case 0xb6:
|
||||
case 0xb7:
|
||||
|
@ -422,6 +427,16 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
|
|||
if (!wrotePC) {
|
||||
uint32_t lr;
|
||||
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr);
|
||||
#ifdef __ARM_FEATURE_PAUTH
|
||||
if (hasReturnAddrAuthCode) {
|
||||
uint32_t sp;
|
||||
uint32_t pac;
|
||||
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp);
|
||||
_Unwind_VRS_Get(context, _UVRSC_PSEUDO, UNW_ARM_RA_AUTH_CODE,
|
||||
_UVRSD_UINT32, &pac);
|
||||
__asm__ __volatile__("autg %0, %1, %2" : : "r"(pac), "r"(lr), "r"(sp) :);
|
||||
}
|
||||
#endif
|
||||
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr);
|
||||
}
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
|
@ -941,6 +956,15 @@ _Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
|||
case _UVRSC_WMMXD:
|
||||
break;
|
||||
#endif
|
||||
case _UVRSC_PSEUDO:
|
||||
// There's only one pseudo-register, PAC, with regno == 0.
|
||||
if (representation != _UVRSD_UINT32 || regno != 0)
|
||||
return _UVRSR_FAILED;
|
||||
return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_RA_AUTH_CODE),
|
||||
*(unw_word_t *)valuep) == UNW_ESUCCESS
|
||||
? _UVRSR_OK
|
||||
: _UVRSR_FAILED;
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported register class");
|
||||
}
|
||||
|
@ -995,6 +1019,15 @@ _Unwind_VRS_Get_Internal(_Unwind_Context *context,
|
|||
case _UVRSC_WMMXD:
|
||||
break;
|
||||
#endif
|
||||
case _UVRSC_PSEUDO:
|
||||
// There's only one pseudo-register, PAC, with regno == 0.
|
||||
if (representation != _UVRSD_UINT32 || regno != 0)
|
||||
return _UVRSR_FAILED;
|
||||
return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_RA_AUTH_CODE),
|
||||
(unw_word_t *)valuep) == UNW_ESUCCESS
|
||||
? _UVRSR_OK
|
||||
: _UVRSR_FAILED;
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported register class");
|
||||
}
|
||||
|
@ -1092,6 +1125,20 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
|||
return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
|
||||
&sp);
|
||||
}
|
||||
case _UVRSC_PSEUDO: {
|
||||
if (representation != _UVRSD_UINT32 || discriminator != 0)
|
||||
return _UVRSR_FAILED;
|
||||
// Return Address Authentication code (PAC) - discriminator 0
|
||||
uint32_t *sp;
|
||||
if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
|
||||
&sp) != _UVRSR_OK) {
|
||||
return _UVRSR_FAILED;
|
||||
}
|
||||
uint32_t pac = *sp++;
|
||||
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp);
|
||||
return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_RA_AUTH_CODE,
|
||||
_UVRSD_UINT32, &pac);
|
||||
}
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported register class");
|
||||
}
|
||||
|
|
|
@ -655,7 +655,9 @@ bool UnwindCursor<A, R>::validReg(int regNum) {
|
|||
#if defined(_LIBUNWIND_TARGET_X86_64)
|
||||
if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_R15) return true;
|
||||
#elif defined(_LIBUNWIND_TARGET_ARM)
|
||||
if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) return true;
|
||||
if ((regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) ||
|
||||
regNum == UNW_ARM_RA_AUTH_CODE)
|
||||
return true;
|
||||
#elif defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
if (regNum >= UNW_AARCH64_X0 && regNum <= UNW_ARM64_X30) return true;
|
||||
#endif
|
||||
|
|
|
@ -660,7 +660,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv)
|
|||
ldr sp, [lr, #52]
|
||||
ldr lr, [lr, #60] @ restore pc into lr
|
||||
#endif
|
||||
#if defined(__ARM_FEATURE_BTI_DEFAULT) && !defined(__ARM_ARCH_ISA_ARM)
|
||||
// 'bx' is not BTI setting when used with lr, therefore r12 is used instead
|
||||
mov r12, lr
|
||||
JMP(r12)
|
||||
#else
|
||||
JMP(lr)
|
||||
#endif
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values)
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
#define PPC64_OPD2
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_FEATURE_BTI_DEFAULT)
|
||||
#if defined(__aarch64__) && defined(__ARM_FEATURE_BTI_DEFAULT)
|
||||
.pushsection ".note.gnu.property", "a" SEPARATOR \
|
||||
.balign 8 SEPARATOR \
|
||||
.long 4 SEPARATOR \
|
||||
|
@ -99,6 +99,17 @@
|
|||
#define AARCH64_BTI
|
||||
#endif
|
||||
|
||||
#if !defined(__aarch64__)
|
||||
#ifdef __ARM_FEATURE_PAC_DEFAULT
|
||||
.eabi_attribute Tag_PAC_extension, 2
|
||||
.eabi_attribute Tag_PACRET_use, 1
|
||||
#endif
|
||||
#ifdef __ARM_FEATURE_BTI_DEFAULT
|
||||
.eabi_attribute Tag_BTI_extension, 1
|
||||
.eabi_attribute Tag_BTI_use, 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define GLUE2(a, b) a ## b
|
||||
#define GLUE(a, b) GLUE2(a, b)
|
||||
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)
|
||||
|
|
Loading…
Reference in New Issue