[clang] e6d0b85 - [ARM][libunwind] add PACBTI-M support for libunwind

Ties Stuij via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 8 02:05:06 PST 2021


Author: Ties Stuij
Date: 2021-12-08T09:44:45Z
New Revision: e6d0b851f854849240bc1f02901b0dbb3be84388

URL: https://github.com/llvm/llvm-project/commit/e6d0b851f854849240bc1f02901b0dbb3be84388
DIFF: https://github.com/llvm/llvm-project/commit/e6d0b851f854849240bc1f02901b0dbb3be84388.diff

LOG: [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

Added: 
    

Modified: 
    clang/lib/Headers/unwind.h
    libunwind/include/libunwind.h
    libunwind/include/unwind_arm_ehabi.h
    libunwind/src/DwarfInstructions.hpp
    libunwind/src/Registers.hpp
    libunwind/src/Unwind-EHABI.cpp
    libunwind/src/UnwindCursor.hpp
    libunwind/src/UnwindRegistersRestore.S
    libunwind/src/assembly.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/Headers/unwind.h b/clang/lib/Headers/unwind.h
index 029524b7bc84c..6e069798f02d6 100644
--- a/clang/lib/Headers/unwind.h
+++ b/clang/lib/Headers/unwind.h
@@ -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 {

diff  --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index e187ee27b0dba..9a74faa48d6ff 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -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

diff  --git a/libunwind/include/unwind_arm_ehabi.h b/libunwind/include/unwind_arm_ehabi.h
index dc9d403e264cf..6277a1457f896 100644
--- a/libunwind/include/unwind_arm_ehabi.h
+++ b/libunwind/include/unwind_arm_ehabi.h
@@ -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 {

diff  --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp
index 19835aad668f9..1c50941680b33 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -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

diff  --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c2d5327eade31..373d9e49018e0 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -2140,6 +2140,10 @@ class _LIBUNWIND_HIDDEN Registers_arm {
     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 @@ class _LIBUNWIND_HIDDEN Registers_arm {
 
   // 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");
 }
 

diff  --git a/libunwind/src/Unwind-EHABI.cpp b/libunwind/src/Unwind-EHABI.cpp
index 5959d2a25fead..257db724c2679 100644
--- a/libunwind/src/Unwind-EHABI.cpp
+++ b/libunwind/src/Unwind-EHABI.cpp
@@ -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");
 }

diff  --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 3931df0b33513..c400fdf33d8a9 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -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

diff  --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 497bf46dbdd43..694738b5155aa 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -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)

diff  --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index e38d32336929b..b17f2ade590b4 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -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)


        


More information about the cfe-commits mailing list