[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:
Ties Stuij 2021-12-08 09:44:45 +00:00
parent 96b92d5b3e
commit e6d0b851f8
9 changed files with 112 additions and 7 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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

View File

@ -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)

View File

@ -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)