stuij created this revision.
Herald added subscribers: libcxx-commits, kristof.beyls.
Herald added a project: libunwind.
Herald added a reviewer: libunwind.
stuij requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112430

Files:
  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

Index: libunwind/src/assembly.h
===================================================================
--- libunwind/src/assembly.h
+++ 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)
Index: libunwind/src/UnwindRegistersRestore.S
===================================================================
--- libunwind/src/UnwindRegistersRestore.S
+++ libunwind/src/UnwindRegistersRestore.S
@@ -660,7 +660,13 @@
   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)
Index: libunwind/src/UnwindCursor.hpp
===================================================================
--- libunwind/src/UnwindCursor.hpp
+++ libunwind/src/UnwindCursor.hpp
@@ -655,7 +655,9 @@
 #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
Index: libunwind/src/Unwind-EHABI.cpp
===================================================================
--- libunwind/src/Unwind-EHABI.cpp
+++ libunwind/src/Unwind-EHABI.cpp
@@ -256,6 +256,7 @@
                       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) {
@@ -308,7 +309,7 @@
               if (offset >= len)
                 return _URC_FAILURE;
               uint8_t registers = getByte(data, offset++);
-              if (registers & 0xf0 || !registers)
+              if (registers & 0xf0)
                 return _URC_FAILURE;
               _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32);
               break;
@@ -342,6 +343,10 @@
               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:
@@ -417,6 +422,16 @@
   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;
@@ -927,6 +942,15 @@
     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");
 }
@@ -981,6 +1005,15 @@
     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");
 }
@@ -1078,6 +1111,20 @@
       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");
 }
Index: libunwind/src/Registers.hpp
===================================================================
--- libunwind/src/Registers.hpp
+++ libunwind/src/Registers.hpp
@@ -2140,6 +2140,10 @@
     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 @@
 
   // 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 @@
                 "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 @@
     _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,9 @@
     return true;
 #endif
 
+  if (regNum == UNW_ARM_RA_AUTH_CODE)
+    return true;
+
   return false;
 }
 
@@ -2261,6 +2271,9 @@
   }
 #endif
 
+  if (regNum == UNW_ARM_RA_AUTH_CODE)
+    return _pseudo_registers.__pac;
+
   _LIBUNWIND_ABORT("unsupported arm register");
 }
 
@@ -2296,6 +2309,11 @@
   }
 #endif
 
+  if (regNum == UNW_ARM_RA_AUTH_CODE) {
+    _pseudo_registers.__pac = value;
+    return;
+  }
+
   _LIBUNWIND_ABORT("unsupported arm register");
 }
 
Index: libunwind/src/DwarfInstructions.hpp
===================================================================
--- libunwind/src/DwarfInstructions.hpp
+++ libunwind/src/DwarfInstructions.hpp
@@ -242,6 +242,20 @@
       }
 #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
Index: libunwind/include/unwind_arm_ehabi.h
===================================================================
--- libunwind/include/unwind_arm_ehabi.h
+++ libunwind/include/unwind_arm_ehabi.h
@@ -87,10 +87,11 @@
 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 {
Index: libunwind/include/libunwind.h
===================================================================
--- libunwind/include/libunwind.h
+++ libunwind/include/libunwind.h
@@ -718,7 +718,8 @@
   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
Index: clang/lib/Headers/unwind.h
===================================================================
--- clang/lib/Headers/unwind.h
+++ clang/lib/Headers/unwind.h
@@ -172,7 +172,8 @@
   _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 {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to