https://github.com/libffi/libffi/commit/45d284f2d066cc3a080c5be88e51b4d934349797

From 45d284f2d066cc3a080c5be88e51b4d934349797 Mon Sep 17 00:00:00 2001
From: Bill Roberts <152999275+billatarm@users.noreply.github.com>
Date: Sat, 1 Jun 2024 12:34:53 -0500
Subject: [PATCH] aarch64: support pointer authentication (#834)

* aarch64: fix callstack in ffi_call_SYSV

The debug stack gets corrupted between the frame and stack pivots, update
the CFI directives so the call stack stays correct in the debugger.

str     x9, [x1, #32] // stack is ffi_call_SYSV() -> ffi_call_int() -> ffi_call_int() -> main() (good)
mov     x29, x1       // stack is ffi_call_SYSV() -> ffi_call_int() -> ffi_call_int() -> ffi_call() -> main() (bad)
mov     sp, x0        // stack is ffi_call_SYSV() -> ffi_call_int() -> ffi_call_int() -> main() (good)

The CFA data needs to be updated around the pivots, after this patch the
callstack stays correct.

Signed-off-by: Bill Roberts <bill.roberts@arm.com>

* aarch64: remove uneeded CFI directive

This directive doesn't actually set the CFA to anything valid, and
during unwinding this isn't even used. Note that the PAC/Darwin usage
is quite suspect as well, as the CFA is either x1 or x29 after the frame
pivot, and the CFA address is what's used as the modifier when verifying
the PAC. At least this is the behavior on Linux with PAC, I need to
verify ARME ABI unwinding. So for now leave Darwin as is.

Signed-off-by: Bill Roberts <bill.roberts@arm.com>

* ptrauth: rename define for clarity

Rename the HAVE_PTRAUTH define for clarity that its associated with the
ARM64E ABI and not the ARM64 ABI that can be supported on Linux and
enabled with -mbranch-protection=standard.

Signed-off-by: Bill Roberts <bill.roberts@arm.com>

* aarch64: add PAC support to ffi_call_SYSV

Support AARCH64 Pointer Authentication Codes (PAC) within ffi_call_SYSV
and support exception unwinding.

The Linux ABI for PAC is to use paciasp/autiasp instructions which also
have hint space equivelent instructions. They sign the LR (x30) with the
A key and the current stack pointer as the salt. Note that this can also be
configured to use the B key and will use pacibsp/autibsp hint instructions.

The Linux ABI for exception frame data when PAC is enabled assumes that the
Connonical Frame Address, or CFA is equal to the stack pointer. I.E sp is
equal to x29 (fp). When the unwinder is invoked the cfa will point to
the frame which will include the *signed* return address from the LR.
This will then be passed to __builtin_aarch64_autia1716 where the CFA
will be used as the salt and stored to register x16 and register x17
will contain the signed address to demangle. This can be noted in:
  - https://github.com/gcc-mirror/gcc/blob/d6d7afcdbc04adb0ec42a44b2d7e05600945af42/libgcc/config/aarch64/aarch64-unwind.h#L56

The other required portion of this is to indicate to the unwinder that
this is a signed address that needs to go the special demangle route in
the unwinder. This is accomplished by using CFI directive "cfi_window_save"
which marks that frame as being signed.

Putting all of this together is a bit tricky, as the internals of
ffi_call_SYSV the callee allocates its stack and frame and passes it in
arg1 (x0) and arg2 (x1) to the called function, where that function
pivots its stack, so care must be taken to get the sp == fp before
paciasp is called and also restore that state before autiasp is called.

Signed-off-by: Bill Roberts <bill.roberts@arm.com>

---------

Signed-off-by: Bill Roberts <bill.roberts@arm.com>
---
 configure.ac           |  6 ++--
 include/ffi_cfi.h      |  2 ++
 src/aarch64/ffi.c      |  4 +--
 src/aarch64/internal.h | 76 ++++++++++++++++++++++++++++++++----------
 src/aarch64/sysv.S     | 20 ++++++-----
 src/closures.c         |  6 ++--
 6 files changed, 81 insertions(+), 33 deletions(-)

diff --git a/configure.ac b/configure.ac
index 816bfd666..b35a999fb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -189,17 +189,17 @@ AC_CACHE_CHECK([whether compiler supports pointer authentication],
    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
 #ifdef __clang__
 # if __has_feature(ptrauth_calls)
-#  define HAVE_PTRAUTH 1
+#  define HAVE_ARM64E_PTRAUTH 1
 # endif
 #endif
 
-#ifndef HAVE_PTRAUTH
+#ifndef HAVE_ARM64E_PTRAUTH
 # error Pointer authentication not supported
 #endif
 		   ]])],[libffi_cv_as_ptrauth=yes],[libffi_cv_as_ptrauth=no])
 ])
 if test "x$libffi_cv_as_ptrauth" = xyes; then
-    AC_DEFINE(HAVE_PTRAUTH, 1,
+    AC_DEFINE(HAVE_ARM64E_PTRAUTH, 1,
 	      [Define if your compiler supports pointer authentication.])
 fi
 
diff --git a/include/ffi_cfi.h b/include/ffi_cfi.h
index f4c292d00..856566324 100644
--- a/include/ffi_cfi.h
+++ b/include/ffi_cfi.h
@@ -49,6 +49,7 @@
 # define cfi_personality(enc, exp)	.cfi_personality enc, exp
 # define cfi_lsda(enc, exp)		.cfi_lsda enc, exp
 # define cfi_escape(...)		.cfi_escape __VA_ARGS__
+# define cfi_window_save		.cfi_window_save
 
 #else
 
@@ -71,6 +72,7 @@
 # define cfi_personality(enc, exp)
 # define cfi_lsda(enc, exp)
 # define cfi_escape(...)
+# define cfi_window_save
 
 #endif /* HAVE_AS_CFI_PSEUDO_OP */
 #endif /* FFI_CFI_H */
diff --git a/src/aarch64/ffi.c b/src/aarch64/ffi.c
index b13738e38..964934dfb 100644
--- a/src/aarch64/ffi.c
+++ b/src/aarch64/ffi.c
@@ -63,7 +63,7 @@ struct call_context
 #if FFI_EXEC_TRAMPOLINE_TABLE
 
 #ifdef __MACH__
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
 #include <ptrauth.h>
 #endif
 #include <mach/vm_param.h>
@@ -877,7 +877,7 @@ ffi_prep_closure_loc (ffi_closure *closure,
 
 #if FFI_EXEC_TRAMPOLINE_TABLE
 # ifdef __MACH__
-#  ifdef HAVE_PTRAUTH
+#  ifdef HAVE_ARM64E_PTRAUTH
   codeloc = ptrauth_auth_data(codeloc, ptrauth_key_function_pointer, 0);
 #  endif
   void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
diff --git a/src/aarch64/internal.h b/src/aarch64/internal.h
index b5d102b4a..c39f9cb22 100644
--- a/src/aarch64/internal.h
+++ b/src/aarch64/internal.h
@@ -81,20 +81,62 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 /* Helpers for writing assembly compatible with arm ptr auth */
 #ifdef LIBFFI_ASM
 
-#ifdef HAVE_PTRAUTH
-#define SIGN_LR pacibsp
-#define SIGN_LR_WITH_REG(x) pacib lr, x
-#define AUTH_LR_AND_RET retab
-#define AUTH_LR_WITH_REG(x) autib lr, x
-#define BRANCH_AND_LINK_TO_REG blraaz
-#define BRANCH_TO_REG braaz
-#else
-#define SIGN_LR
-#define SIGN_LR_WITH_REG(x)
-#define AUTH_LR_AND_RET ret
-#define AUTH_LR_WITH_REG(x)
-#define BRANCH_AND_LINK_TO_REG blr
-#define BRANCH_TO_REG br
-#endif
-
-#endif
+  #if defined(HAVE_ARM64E_PTRAUTH)
+  /* ARM64E ABI For Darwin */
+  #define SIGN_LR pacibsp
+  #define SIGN_LR_WITH_REG(x) pacib lr, x
+  #define AUTH_LR_AND_RET retab
+  #define AUTH_LR_WITH_REG(x) autib lr, x
+  #define BRANCH_AND_LINK_TO_REG blraaz
+  #define BRANCH_TO_REG braaz
+  #define PAC_CFI_WINDOW_SAVE
+  /* Linux PAC Support */
+  #elif defined(__ARM_FEATURE_PAC_DEFAULT)
+    #define GNU_PROPERTY_AARCH64_POINTER_AUTH (1 << 1)
+    #define PAC_CFI_WINDOW_SAVE cfi_window_save
+    #define TMP_REG x9
+    #define BRANCH_TO_REG br
+    #define BRANCH_AND_LINK_TO_REG blr
+	#define SIGN_LR_LINUX_ONLY SIGN_LR
+    /* Which key to sign with? */
+    #if (__ARM_FEATURE_PAC_DEFAULT & 1) == 1
+      /* Signed with A-key */
+      #define SIGN_LR            hint #25  /* paciasp */
+      #define AUTH_LR            hint #29  /* autiasp */
+    #else
+      /* Signed with B-key */
+      #define SIGN_LR            hint #27  /* pacibsp */
+      #define AUTH_LR            hint #31  /* autibsp */
+    #endif /* __ARM_FEATURE_PAC_DEFAULT */
+    #define AUTH_LR_WITH_REG(x) _auth_lr_with_reg x
+.macro _auth_lr_with_reg modifier
+    mov TMP_REG, sp
+    mov sp, \modifier
+    AUTH_LR
+    mov sp, TMP_REG
+.endm
+  #define SIGN_LR_WITH_REG(x) _sign_lr_with_reg x
+.macro _sign_lr_with_reg modifier
+    mov TMP_REG, sp
+    mov sp, \modifier
+    SIGN_LR
+    mov sp, TMP_REG
+.endm
+  #define AUTH_LR_AND_RET _auth_lr_and_ret modifier
+.macro _auth_lr_and_ret modifier
+    AUTH_LR
+    ret
+.endm
+  #undef TMP_REG
+
+  /* No Pointer Auth */
+  #else
+    #define SIGN_LR
+    #define SIGN_LR_WITH_REG(x)
+    #define AUTH_LR_AND_RET ret
+    #define AUTH_LR_WITH_REG(x)
+    #define BRANCH_AND_LINK_TO_REG blr
+    #define BRANCH_TO_REG br
+    #define PAC_CFI_WINDOW_SAVE
+  #endif /* HAVE_ARM64E_PTRAUTH */
+#endif /* LIBFFI_ASM */
diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S
index 60cfa505b..6a9a5611f 100644
--- a/src/aarch64/sysv.S
+++ b/src/aarch64/sysv.S
@@ -92,27 +92,27 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 	cfi_startproc
 CNAME(ffi_call_SYSV):
 	BTI_C
-	/* Sign the lr with x1 since that is where it will be stored */
+	PAC_CFI_WINDOW_SAVE
+	/* Sign the lr with x1 since that is the CFA which is the modifer used in auth instructions */
 	SIGN_LR_WITH_REG(x1)
 
-	/* Use a stack frame allocated by our caller.  */
-#if defined(HAVE_PTRAUTH) && defined(__APPLE__)
+#if defined(HAVE_ARM64E_PTRAUTH) && defined(__APPLE__)
 	/* darwin's libunwind assumes that the cfa is the sp and that's the data
 	 * used to sign the lr.  In order to allow unwinding through this
 	 * function it is necessary to point the cfa at the signing register.
 	 */
 	cfi_def_cfa(x1, 0);
-#else
-	cfi_def_cfa(x1, 40);
 #endif
+	/* Use a stack frame allocated by our caller.  */
 	stp	x29, x30, [x1]
+	cfi_def_cfa_register(x1)
+	cfi_rel_offset (x29, 0)
+	cfi_rel_offset (x30, 8)
 	mov	x9, sp
 	str	x9, [x1, #32]
 	mov	x29, x1
-	mov	sp, x0
 	cfi_def_cfa_register(x29)
-	cfi_rel_offset (x29, 0)
-	cfi_rel_offset (x30, 8)
+	mov	sp, x0
 
 	mov	x9, x2			/* save fn */
 	mov	x8, x3			/* install structure return */
@@ -326,6 +326,7 @@ CNAME(ffi_closure_SYSV_V):
 	cfi_startproc
 	BTI_C
 	SIGN_LR
+	PAC_CFI_WINDOW_SAVE
 	stp     x29, x30, [sp, #-ffi_closure_SYSV_FS]!
 	cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
 	cfi_rel_offset (x29, 0)
@@ -351,6 +352,7 @@ CNAME(ffi_closure_SYSV_V):
 CNAME(ffi_closure_SYSV):
 	BTI_C
 	SIGN_LR
+	PAC_CFI_WINDOW_SAVE
 	stp     x29, x30, [sp, #-ffi_closure_SYSV_FS]!
 	cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
 	cfi_rel_offset (x29, 0)
@@ -648,6 +650,8 @@ CNAME(ffi_go_closure_SYSV_V):
 	cfi_startproc
 CNAME(ffi_go_closure_SYSV):
 	BTI_C
+	SIGN_LR_LINUX_ONLY
+	PAC_CFI_WINDOW_SAVE
 	stp     x29, x30, [sp, #-ffi_closure_SYSV_FS]!
 	cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
 	cfi_rel_offset (x29, 0)
diff --git a/src/closures.c b/src/closures.c
index 67a94a822..02cf78fa2 100644
--- a/src/closures.c
+++ b/src/closures.c
@@ -164,7 +164,7 @@ ffi_tramp_is_present (__attribute__((unused)) void *ptr)
 
 #include <mach/mach.h>
 #include <pthread.h>
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
 #include <ptrauth.h>
 #endif
 #include <stdio.h>
@@ -223,7 +223,7 @@ ffi_trampoline_table_alloc (void)
   /* Remap the trampoline table on top of the placeholder page */
   trampoline_page = config_page + PAGE_MAX_SIZE;
 
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
   trampoline_page_template = (vm_address_t)(uintptr_t)ptrauth_auth_data((void *)&ffi_closure_trampoline_table_page, ptrauth_key_function_pointer, 0);
 #else
   trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
@@ -268,7 +268,7 @@ ffi_trampoline_table_alloc (void)
       ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
       entry->trampoline =
 	(void *) (trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
       entry->trampoline = ptrauth_sign_unauthenticated(entry->trampoline, ptrauth_key_function_pointer, 0);
 #endif
 

