From a6ef415fa95661b67017f1bdc32d8f98db7d9c44 Mon Sep 17 00:00:00 2001
From: Daniel Holth <dholth@anaconda.com>
Date: Fri, 27 Aug 2021 15:55:36 -0400
Subject: [PATCH] arm64 changes merged from v3.3 arm64-changes patch

---
 src/aarch64/ffi.c | 29 +++++++++++++++++---
 src/closures.c    | 67 ++++++++++++++++++++++++++++++++++++++++-------
 src/x86/ffi64.c   |  6 +++++
 3 files changed, 89 insertions(+), 13 deletions(-)

diff --git a/src/aarch64/ffi.c b/src/aarch64/ffi.c
index 6544ac0..3f936d3 100644
--- a/src/aarch64/ffi.c
+++ b/src/aarch64/ffi.c
@@ -715,12 +715,13 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *orig_rvalue,
 		state.ngrn = N_X_ARG_REG;
 		/* Note that the default abi extends each argument
 		   to a full 64-bit slot, while the iOS abi allocates
-		   only enough space. */
+		   only enough space, except for variadic arguments. */
 #ifdef __APPLE__
-		memcpy(d, a, s);
-#else
-		*(ffi_arg *)d = ext;
+		if (!state.allocating_variadic)
+		  memcpy(d, a, s);
+		else
 #endif
+		  *(ffi_arg *)d = ext;
 	      }
 	  }
 	  break;
@@ -867,7 +868,11 @@ ffi_prep_closure_loc (ffi_closure *closure,
 #  ifdef HAVE_PTRAUTH
   codeloc = ptrauth_auth_data(codeloc, ptrauth_key_function_pointer, 0);
 #  endif
+#  ifdef FFI_TRAMPOLINE_WHOLE_DYLIB
+  void **config = (void **)((uint8_t *)codeloc - 2*PAGE_MAX_SIZE);
+#  else
   void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
+#  endif
   config[0] = closure;
   config[1] = start;
 # endif
@@ -920,6 +925,22 @@ out:
   return FFI_OK;
 }
 
+ffi_closure *
+ffi_find_closure_for_code(void *codeloc)
+{
+#if FFI_EXEC_TRAMPOLINE_TABLE
+#  ifdef FFI_TRAMPOLINE_WHOLE_DYLIB
+    void **config = (void **)((uint8_t *)codeloc - 2*PAGE_MAX_SIZE);
+#  else
+    void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
+#  endif
+    return config[0];
+#else
+    return (ffi_closure*)codeloc;
+#endif
+}
+ffi_closure *ffi_find_closure_for_code_np(void *codeloc) { return ffi_find_closure_for_code(codeloc); }  /* Apple renamed this entry ... */
+
 #ifdef FFI_GO_CLOSURES
 extern void ffi_go_closure_SYSV (void) FFI_HIDDEN;
 extern void ffi_go_closure_SYSV_V (void) FFI_HIDDEN;
diff --git a/src/closures.c b/src/closures.c
index 9aafbec..af1c3b8 100644
--- a/src/closures.c
+++ b/src/closures.c
@@ -168,7 +168,13 @@ ffi_tramp_is_present (__attribute__((unused)) void *ptr)
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef FFI_TRAMPOLINE_WHOLE_DYLIB
+#include <assert.h>
+#include <dispatch/dispatch.h>
+#include <dlfcn.h>
+#else
 extern void *ffi_closure_trampoline_table_page;
+#endif
 
 typedef struct ffi_trampoline_table ffi_trampoline_table;
 typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
@@ -196,6 +202,21 @@ struct ffi_trampoline_table_entry
 /* Total number of trampolines that fit in one trampoline table */
 #define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
 
+/* The trampoline dylib has one page for the MACHO_HEADER and one page for the
+ * trampolines. iOS 12.0 and later, and macOS on Apple Silicon require that
+ * the entire dylib needs to be remapped as a unit.
+ *
+ * arm (legacy): Allocate two pages -- a config page and a placeholder for the trampolines
+ * arm64 (modern): Allocate three pages -- a config page and two placeholders for the trampoline dylib
+ */
+#ifdef FFI_TRAMPOLINE_WHOLE_DYLIB
+#define FFI_TRAMPOLINE_ALLOCATION_PAGE_COUNT 3
+#define FFI_TRAMPOLINE_PAGE_SEGMENT_OFFSET PAGE_MAX_SIZE
+#else
+#define FFI_TRAMPOLINE_ALLOCATION_PAGE_COUNT 2
+#define FFI_TRAMPOLINE_PAGE_SEGMENT_OFFSET 0
+#endif
+
 static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
 static ffi_trampoline_table *ffi_trampoline_tables = NULL;
 
@@ -211,35 +232,63 @@ ffi_trampoline_table_alloc (void)
   kern_return_t kt;
   uint16_t i;
 
-  /* Allocate two pages -- a config page and a placeholder page */
   config_page = 0x0;
-  kt = vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
+  /* The entire allocation is:
+   *    config_page
+   *    trampoline_segment
+   *
+   * trampoline_segment is:
+   *    trampoline dylib mach-o header (if FFI_TRAMPOLINE_WHOLE_DYLIB)
+   *    trampoline page
+   */
+  kt = vm_allocate (mach_task_self (), &config_page, FFI_TRAMPOLINE_ALLOCATION_PAGE_COUNT * PAGE_MAX_SIZE,
 		    VM_FLAGS_ANYWHERE);
   if (kt != KERN_SUCCESS)
     return NULL;
 
-  /* Remap the trampoline table on top of the placeholder page */
-  trampoline_page = config_page + PAGE_MAX_SIZE;
+  static void *trampoline_table_page;
+
+#ifdef FFI_TRAMPOLINE_WHOLE_DYLIB
+  static dispatch_once_t trampoline_template_init_once;
+
+  dispatch_once(&trampoline_template_init_once, ^{
+    void * const trampoline_handle = dlopen("/usr/lib/libffi-trampolines.dylib", RTLD_NOW | RTLD_LOCAL | RTLD_FIRST);
+    assert(trampoline_handle);
+
+    trampoline_table_page = dlsym(trampoline_handle, "ffi_closure_trampoline_table_page");
+    assert(trampoline_table_page);
+  });
+#else
+  trampoline_table_page = &ffi_closure_trampoline_table_page;
+#endif
 
 #ifdef HAVE_PTRAUTH
-  trampoline_page_template = (vm_address_t)(uintptr_t)ptrauth_auth_data((void *)&ffi_closure_trampoline_table_page, ptrauth_key_function_pointer, 0);
+  trampoline_page_template = (uintptr_t)ptrauth_auth_data(trampoline_table_page, ptrauth_key_function_pointer, 0);
 #else
-  trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
+  trampoline_page_template = (uintptr_t)trampoline_table_page;
 #endif
 
 #ifdef __arm__
   /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
   trampoline_page_template &= ~1UL;
 #endif
-  kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0,
-		 VM_FLAGS_OVERWRITE, mach_task_self (), trampoline_page_template,
+
+  vm_address_t trampoline_segment_template = trampoline_page_template - FFI_TRAMPOLINE_PAGE_SEGMENT_OFFSET;
+  vm_size_t trampoline_segment_size = (FFI_TRAMPOLINE_ALLOCATION_PAGE_COUNT - 1) * PAGE_MAX_SIZE;
+
+  /* Remap the trampoline table on top of the placeholder page */
+  vm_address_t trampoline_segment = config_page + PAGE_MAX_SIZE;
+  kt = vm_remap (mach_task_self(), &trampoline_segment, trampoline_segment_size, 0x0,
+		 VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, mach_task_self(), trampoline_segment_template,
 		 FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
   if (kt != KERN_SUCCESS)
     {
-      vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
+      vm_deallocate (mach_task_self (), config_page, FFI_TRAMPOLINE_ALLOCATION_PAGE_COUNT * PAGE_MAX_SIZE);
       return NULL;
     }
 
+  trampoline_page = trampoline_segment + FFI_TRAMPOLINE_PAGE_SEGMENT_OFFSET;
+
   if (!(cur_prot & VM_PROT_EXECUTE))
     {
       /* If VM_PROT_EXECUTE isn't set on the remapped trampoline page, set it */
       kt = vm_protect (mach_task_self (), trampoline_page, PAGE_MAX_SIZE,
          FALSE, cur_prot | VM_PROT_EXECUTE);
       if (kt != KERN_SUCCESS)
         {
           vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
           return NULL;
         }
     }

   /* We have valid trampoline and config pages */
   table = calloc (1, sizeof (ffi_trampoline_table));
   table->free_count = FFI_TRAMPOLINE_COUNT;
diff --git a/src/x86/ffi64.c b/src/x86/ffi64.c
index 6a8e37f..a3d51bc 100644
--- a/src/x86/ffi64.c
+++ b/src/x86/ffi64.c
@@ -809,6 +809,12 @@ out:
   return FFI_OK;
 }
 
+ffi_closure *
+ffi_find_closure_for_code(void *code)
+{
+    return (ffi_closure *) code;
+}
+ ffi_closure *ffi_find_closure_for_code_np(void *code) { return ffi_find_closure_for_code(code); }  /* Apple renamed ... */
 int FFI_HIDDEN
 ffi_closure_unix64_inner(ffi_cif *cif,
 			 void (*fun)(ffi_cif*, void*, void**, void*),
-- 
2.32.0

