Add a hard-coded FFI_EXEC_TRAMPOLINE_TABLE arm implementation. This implements support for re-mapping a shared table of executable trampolines directly in front of a writable configuration page, working around PROT_WRITE restrictions for sandboxed applications on Apple's iOS. This implementation is for testing purposes; a proper allocator is still necessary, and ARM-specific code needs to be moved out of src/closures.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
diff --git a/src/arm/ffi.c b/src/arm/ffi.c
index f6a6475..7da69c1 100644
--- a/src/arm/ffi.c
+++ b/src/arm/ffi.c
@@ -297,10 +297,17 @@ ffi_prep_closure_loc (ffi_closure* closure,
{
FFI_ASSERT (cif->abi == FFI_SYSV);
+#if FFI_EXEC_TRAMPOLINE_TABLE
+ // XXX - hardcoded offset
+ void **config = (void **) (((uint8_t *) codeloc) - 4080);
+ config[0] = closure;
+ config[1] = ffi_closure_SYSV;
+#else
FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
&ffi_closure_SYSV, \
codeloc);
-
+#endif
+
closure->cif = cif;
closure->user_data = user_data;
closure->fun = fun;
diff --git a/src/closures.c b/src/closures.c
index 8f295dd..c214196 100644
--- a/src/closures.c
+++ b/src/closures.c
@@ -32,7 +32,7 @@
#include <ffi.h>
#include <ffi_common.h>
-#ifndef FFI_MMAP_EXEC_WRIT
+#if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
# if __gnu_linux__
/* This macro indicates it may be forbidden to map anonymous memory
with both write and execute permission. Code compiled when this
@@ -63,7 +63,89 @@
#if FFI_CLOSURES
-# if FFI_MMAP_EXEC_WRIT
+# if FFI_EXEC_TRAMPOLINE_TABLE
+
+// XXX - non-thread-safe, non-portable implementation, intended for initial testing
+#include <mach/mach.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void *ffi_closure_trampoline_table;
+
+static void *tramp_table = NULL;
+static void **config_table = NULL;
+static int tramp_table_free = 0;
+
+void *
+ffi_closure_alloc (size_t size, void **code)
+{
+ if (!code)
+ return NULL;
+
+ /* Allocate our config page and remap the trampoline table */
+ while (tramp_table == NULL) {
+ vm_address_t config_page = 0x0;
+ kern_return_t kt;
+
+ /* Try to allocate two pages */
+ kt = vm_allocate(mach_task_self(), &config_page, PAGE_SIZE*2, VM_FLAGS_ANYWHERE);
+ if (kt != KERN_SUCCESS) {
+ fprintf(stderr, "vm_allocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
+ break;
+ }
+
+ /* Now drop the second half of the allocation to make room for the trampoline table */
+ kt = vm_deallocate(mach_task_self(), config_page+PAGE_SIZE, PAGE_SIZE);
+ if (kt != KERN_SUCCESS) {
+ fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
+ break;
+ }
+
+ /* Remap the trampoline table to directly follow the config page */
+ vm_address_t table_page = config_page+PAGE_SIZE;
+ vm_prot_t cur_prot;
+ vm_prot_t max_prot;
+
+ kt = vm_remap(mach_task_self(), &table_page, PAGE_SIZE, 0x0, FALSE, mach_task_self(), (vm_address_t) &ffi_closure_trampoline_table, FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
+
+ /* If we lost access to the destination trampoline page, drop our config allocation mapping and retry */
+ if (kt != KERN_SUCCESS) {
+ /* Log unexpected failures */
+ if (kt != KERN_NO_SPACE) {
+ fprintf(stderr, "vm_remap() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
+ }
+
+ vm_deallocate(mach_task_self(), config_page, PAGE_SIZE);
+ continue;
+ }
+
+ tramp_table = (void *) table_page;
+ config_table = (void **) config_page;
+ config_table += 4; // XXX - there's a 16 byte offset into the config page on ARM
+
+ fprintf(stderr, "[DEBUG] Allocated config page at %p, trampoline page at %p, configs start at %p\n", (void *) config_page, (void *) table_page, config_table);
+ }
+
+ /* Check for permanent allocation failure */
+ if (tramp_table == NULL)
+ return *code = NULL;
+
+ *code = tramp_table + (3 * tramp_table_free);
+ tramp_table_free++;
+
+ void *closure = malloc(size);
+
+ return closure;
+}
+
+void
+ffi_closure_free (void *ptr)
+{
+ // TODO
+ //free (ptr);
+}
+
+# elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
#define USE_LOCKS 1
#define USE_DL_PREFIX 1