Skip to content

Commit e6d0b85

Browse files
committed
[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
1 parent 96b92d5 commit e6d0b85

File tree

9 files changed

+112
-7
lines changed

9 files changed

+112
-7
lines changed

clang/lib/Headers/unwind.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ typedef enum {
172172
_UVRSC_CORE = 0, /* integer register */
173173
_UVRSC_VFP = 1, /* vfp */
174174
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
175-
_UVRSC_WMMXC = 4 /* Intel WMMX control register */
175+
_UVRSC_WMMXC = 4, /* Intel WMMX control register */
176+
_UVRSC_PSEUDO = 5 /* Special purpose pseudo register */
176177
} _Unwind_VRS_RegClass;
177178

178179
typedef enum {

libunwind/include/libunwind.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,8 @@ enum {
718718
UNW_ARM_WR14 = 126,
719719
UNW_ARM_WR15 = 127,
720720
// 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC}
721-
// 134-143 -- Reserved
721+
// 134-142 -- Reserved
722+
UNW_ARM_RA_AUTH_CODE = 143,
722723
// 144-150 -- R8_USR-R14_USR
723724
// 151-157 -- R8_FIQ-R14_FIQ
724725
// 158-159 -- R13_IRQ-R14_IRQ

libunwind/include/unwind_arm_ehabi.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ extern void _Unwind_Resume(_Unwind_Exception *exception_object);
8787
extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
8888

8989
typedef enum {
90-
_UVRSC_CORE = 0, /* integer register */
91-
_UVRSC_VFP = 1, /* vfp */
90+
_UVRSC_CORE = 0, /* integer register */
91+
_UVRSC_VFP = 1, /* vfp */
9292
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
93-
_UVRSC_WMMXC = 4 /* Intel WMMX control register */
93+
_UVRSC_WMMXC = 4, /* Intel WMMX control register */
94+
_UVRSC_PSEUDO = 5 /* Special purpose pseudo register */
9495
} _Unwind_VRS_RegClass;
9596

9697
typedef enum {

libunwind/src/DwarfInstructions.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,20 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
242242
}
243243
#endif
244244

245+
#if defined(_LIBUNWIND_IS_NATIVE_ONLY) && defined(_LIBUNWIND_TARGET_ARM) && \
246+
defined(__ARM_FEATURE_PAUTH)
247+
if ((R::getArch() == REGISTERS_ARM) &&
248+
prolog.savedRegisters[UNW_ARM_RA_AUTH_CODE].value) {
249+
pint_t pac =
250+
getSavedRegister(addressSpace, registers, cfa,
251+
prolog.savedRegisters[UNW_ARM_RA_AUTH_CODE]);
252+
__asm__ __volatile__("autg %0, %1, %2"
253+
:
254+
: "r"(pac), "r"(returnAddress), "r"(cfa)
255+
:);
256+
}
257+
#endif
258+
245259
#if defined(_LIBUNWIND_TARGET_SPARC)
246260
if (R::getArch() == REGISTERS_SPARC) {
247261
// Skip call site instruction and delay slot

libunwind/src/Registers.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,6 +2140,10 @@ class _LIBUNWIND_HIDDEN Registers_arm {
21402140
uint32_t __pc; // Program counter r15
21412141
};
21422142

2143+
struct PseudoRegisters {
2144+
uint32_t __pac; // Return Authentication Code (PAC)
2145+
};
2146+
21432147
static void saveVFPWithFSTMD(void*);
21442148
static void saveVFPWithFSTMX(void*);
21452149
static void saveVFPv3(void*);
@@ -2156,6 +2160,7 @@ class _LIBUNWIND_HIDDEN Registers_arm {
21562160

21572161
// ARM registers
21582162
GPRs _registers;
2163+
PseudoRegisters _pseudo_registers;
21592164

21602165
// We save floating point registers lazily because we can't know ahead of
21612166
// time which ones are used. See EHABI #4.7.
@@ -2193,6 +2198,7 @@ inline Registers_arm::Registers_arm(const void *registers)
21932198
"arm registers do not fit into unw_context_t");
21942199
// See __unw_getcontext() note about data.
21952200
memcpy(&_registers, registers, sizeof(_registers));
2201+
memset(&_pseudo_registers, 0, sizeof(_pseudo_registers));
21962202
memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad));
21972203
memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31));
21982204
#if defined(__ARM_WMMX)
@@ -2208,6 +2214,7 @@ inline Registers_arm::Registers_arm()
22082214
_saved_vfp_d0_d15(false),
22092215
_saved_vfp_d16_d31(false) {
22102216
memset(&_registers, 0, sizeof(_registers));
2217+
memset(&_pseudo_registers, 0, sizeof(_pseudo_registers));
22112218
memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad));
22122219
memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31));
22132220
#if defined(__ARM_WMMX)
@@ -2235,6 +2242,11 @@ inline bool Registers_arm::validRegister(int regNum) const {
22352242
return true;
22362243
#endif
22372244

2245+
#ifdef __ARM_FEATURE_PAUTH
2246+
if (regNum == UNW_ARM_RA_AUTH_CODE)
2247+
return true;
2248+
#endif
2249+
22382250
return false;
22392251
}
22402252

@@ -2261,6 +2273,11 @@ inline uint32_t Registers_arm::getRegister(int regNum) const {
22612273
}
22622274
#endif
22632275

2276+
#ifdef __ARM_FEATURE_PAUTH
2277+
if (regNum == UNW_ARM_RA_AUTH_CODE)
2278+
return _pseudo_registers.__pac;
2279+
#endif
2280+
22642281
_LIBUNWIND_ABORT("unsupported arm register");
22652282
}
22662283

@@ -2296,6 +2313,11 @@ inline void Registers_arm::setRegister(int regNum, uint32_t value) {
22962313
}
22972314
#endif
22982315

2316+
if (regNum == UNW_ARM_RA_AUTH_CODE) {
2317+
_pseudo_registers.__pac = value;
2318+
return;
2319+
}
2320+
22992321
_LIBUNWIND_ABORT("unsupported arm register");
23002322
}
23012323

libunwind/src/Unwind-EHABI.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
261261
size_t offset, size_t len) {
262262
bool wrotePC = false;
263263
bool finish = false;
264+
bool hasReturnAddrAuthCode = false;
264265
while (offset < len && !finish) {
265266
uint8_t byte = getByte(data, offset++);
266267
if ((byte & 0x80) == 0) {
@@ -347,6 +348,10 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
347348
break;
348349
}
349350
case 0xb4:
351+
hasReturnAddrAuthCode = true;
352+
_Unwind_VRS_Pop(context, _UVRSC_PSEUDO,
353+
0 /* Return Address Auth Code */, _UVRSD_UINT32);
354+
break;
350355
case 0xb5:
351356
case 0xb6:
352357
case 0xb7:
@@ -422,6 +427,16 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
422427
if (!wrotePC) {
423428
uint32_t lr;
424429
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr);
430+
#ifdef __ARM_FEATURE_PAUTH
431+
if (hasReturnAddrAuthCode) {
432+
uint32_t sp;
433+
uint32_t pac;
434+
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp);
435+
_Unwind_VRS_Get(context, _UVRSC_PSEUDO, UNW_ARM_RA_AUTH_CODE,
436+
_UVRSD_UINT32, &pac);
437+
__asm__ __volatile__("autg %0, %1, %2" : : "r"(pac), "r"(lr), "r"(sp) :);
438+
}
439+
#endif
425440
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr);
426441
}
427442
return _URC_CONTINUE_UNWIND;
@@ -941,6 +956,15 @@ _Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
941956
case _UVRSC_WMMXD:
942957
break;
943958
#endif
959+
case _UVRSC_PSEUDO:
960+
// There's only one pseudo-register, PAC, with regno == 0.
961+
if (representation != _UVRSD_UINT32 || regno != 0)
962+
return _UVRSR_FAILED;
963+
return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_RA_AUTH_CODE),
964+
*(unw_word_t *)valuep) == UNW_ESUCCESS
965+
? _UVRSR_OK
966+
: _UVRSR_FAILED;
967+
break;
944968
}
945969
_LIBUNWIND_ABORT("unsupported register class");
946970
}
@@ -995,6 +1019,15 @@ _Unwind_VRS_Get_Internal(_Unwind_Context *context,
9951019
case _UVRSC_WMMXD:
9961020
break;
9971021
#endif
1022+
case _UVRSC_PSEUDO:
1023+
// There's only one pseudo-register, PAC, with regno == 0.
1024+
if (representation != _UVRSD_UINT32 || regno != 0)
1025+
return _UVRSR_FAILED;
1026+
return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_RA_AUTH_CODE),
1027+
(unw_word_t *)valuep) == UNW_ESUCCESS
1028+
? _UVRSR_OK
1029+
: _UVRSR_FAILED;
1030+
break;
9981031
}
9991032
_LIBUNWIND_ABORT("unsupported register class");
10001033
}
@@ -1092,6 +1125,20 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
10921125
return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
10931126
&sp);
10941127
}
1128+
case _UVRSC_PSEUDO: {
1129+
if (representation != _UVRSD_UINT32 || discriminator != 0)
1130+
return _UVRSR_FAILED;
1131+
// Return Address Authentication code (PAC) - discriminator 0
1132+
uint32_t *sp;
1133+
if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
1134+
&sp) != _UVRSR_OK) {
1135+
return _UVRSR_FAILED;
1136+
}
1137+
uint32_t pac = *sp++;
1138+
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp);
1139+
return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_RA_AUTH_CODE,
1140+
_UVRSD_UINT32, &pac);
1141+
}
10951142
}
10961143
_LIBUNWIND_ABORT("unsupported register class");
10971144
}

libunwind/src/UnwindCursor.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,9 @@ bool UnwindCursor<A, R>::validReg(int regNum) {
655655
#if defined(_LIBUNWIND_TARGET_X86_64)
656656
if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_R15) return true;
657657
#elif defined(_LIBUNWIND_TARGET_ARM)
658-
if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) return true;
658+
if ((regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) ||
659+
regNum == UNW_ARM_RA_AUTH_CODE)
660+
return true;
659661
#elif defined(_LIBUNWIND_TARGET_AARCH64)
660662
if (regNum >= UNW_AARCH64_X0 && regNum <= UNW_ARM64_X30) return true;
661663
#endif

libunwind/src/UnwindRegistersRestore.S

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv)
660660
ldr sp, [lr, #52]
661661
ldr lr, [lr, #60] @ restore pc into lr
662662
#endif
663+
#if defined(__ARM_FEATURE_BTI_DEFAULT) && !defined(__ARM_ARCH_ISA_ARM)
664+
// 'bx' is not BTI setting when used with lr, therefore r12 is used instead
665+
mov r12, lr
666+
JMP(r12)
667+
#else
663668
JMP(lr)
669+
#endif
664670

665671
@
666672
@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values)

libunwind/src/assembly.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
#define PPC64_OPD2
8282
#endif
8383

84-
#if defined(__ARM_FEATURE_BTI_DEFAULT)
84+
#if defined(__aarch64__) && defined(__ARM_FEATURE_BTI_DEFAULT)
8585
.pushsection ".note.gnu.property", "a" SEPARATOR \
8686
.balign 8 SEPARATOR \
8787
.long 4 SEPARATOR \
@@ -99,6 +99,17 @@
9999
#define AARCH64_BTI
100100
#endif
101101

102+
#if !defined(__aarch64__)
103+
#ifdef __ARM_FEATURE_PAC_DEFAULT
104+
.eabi_attribute Tag_PAC_extension, 2
105+
.eabi_attribute Tag_PACRET_use, 1
106+
#endif
107+
#ifdef __ARM_FEATURE_BTI_DEFAULT
108+
.eabi_attribute Tag_BTI_extension, 1
109+
.eabi_attribute Tag_BTI_use, 1
110+
#endif
111+
#endif
112+
102113
#define GLUE2(a, b) a ## b
103114
#define GLUE(a, b) GLUE2(a, b)
104115
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)

0 commit comments

Comments
 (0)