Add iOS support
This commit is contained in:
225
src/arm/ffi.c
225
src/arm/ffi.c
@@ -1,5 +1,5 @@
|
||||
/* -----------------------------------------------------------------------
|
||||
ffi.c - Copyright (c) 1998, 2008 Red Hat, Inc.
|
||||
ffi.c - Copyright (c) 1998, 2008, 2011 Red Hat, Inc.
|
||||
|
||||
ARM Foreign Function Interface
|
||||
|
||||
@@ -337,6 +337,220 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
|
||||
|
||||
/* How to make a trampoline. */
|
||||
|
||||
#if FFI_EXEC_TRAMPOLINE_TABLE
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void *ffi_closure_trampoline_table_page;
|
||||
|
||||
typedef struct ffi_trampoline_table ffi_trampoline_table;
|
||||
typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
|
||||
|
||||
struct ffi_trampoline_table {
|
||||
/* contigious writable and executable pages */
|
||||
vm_address_t config_page;
|
||||
vm_address_t trampoline_page;
|
||||
|
||||
/* free list tracking */
|
||||
uint16_t free_count;
|
||||
ffi_trampoline_table_entry *free_list;
|
||||
ffi_trampoline_table_entry *free_list_pool;
|
||||
|
||||
ffi_trampoline_table *prev;
|
||||
ffi_trampoline_table *next;
|
||||
};
|
||||
|
||||
struct ffi_trampoline_table_entry {
|
||||
void *(*trampoline)();
|
||||
ffi_trampoline_table_entry *next;
|
||||
};
|
||||
|
||||
/* Override the standard architecture trampoline size */
|
||||
// XXX TODO - Fix
|
||||
#undef FFI_TRAMPOLINE_SIZE
|
||||
#define FFI_TRAMPOLINE_SIZE 12
|
||||
|
||||
/* The trampoline configuration is placed at 4080 bytes prior to the trampoline's entry point */
|
||||
#define FFI_TRAMPOLINE_CODELOC_CONFIG(codeloc) ((void **) (((uint8_t *) codeloc) - 4080));
|
||||
|
||||
/* The first 16 bytes of the config page are unused, as they are unaddressable from the trampoline page. */
|
||||
#define FFI_TRAMPOLINE_CONFIG_PAGE_OFFSET 16
|
||||
|
||||
/* Total number of trampolines that fit in one trampoline table */
|
||||
#define FFI_TRAMPOLINE_COUNT ((PAGE_SIZE - FFI_TRAMPOLINE_CONFIG_PAGE_OFFSET) / FFI_TRAMPOLINE_SIZE)
|
||||
|
||||
static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static ffi_trampoline_table *ffi_trampoline_tables = NULL;
|
||||
|
||||
static ffi_trampoline_table *
|
||||
ffi_trampoline_table_alloc ()
|
||||
{
|
||||
ffi_trampoline_table *table = NULL;
|
||||
|
||||
/* Loop until we can allocate two contigious pages */
|
||||
while (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 */
|
||||
vm_address_t trampoline_page = config_page+PAGE_SIZE;
|
||||
kt = vm_deallocate (mach_task_self (), trampoline_page, 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_prot_t cur_prot;
|
||||
vm_prot_t max_prot;
|
||||
|
||||
kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_SIZE, 0x0, FALSE, mach_task_self (), (vm_address_t) &ffi_closure_trampoline_table_page, 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;
|
||||
}
|
||||
|
||||
/* We have valid trampoline and config pages */
|
||||
table = calloc (1, sizeof(ffi_trampoline_table));
|
||||
table->free_count = FFI_TRAMPOLINE_COUNT;
|
||||
table->config_page = config_page;
|
||||
table->trampoline_page = trampoline_page;
|
||||
|
||||
/* Create and initialize the free list */
|
||||
table->free_list_pool = calloc(FFI_TRAMPOLINE_COUNT, sizeof(ffi_trampoline_table_entry));
|
||||
|
||||
uint16_t i;
|
||||
for (i = 0; i < table->free_count; i++) {
|
||||
ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
|
||||
entry->trampoline = (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
|
||||
|
||||
if (i < table->free_count - 1)
|
||||
entry->next = &table->free_list_pool[i+1];
|
||||
}
|
||||
|
||||
table->free_list = table->free_list_pool;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void *
|
||||
ffi_closure_alloc (size_t size, void **code)
|
||||
{
|
||||
/* Create the closure */
|
||||
ffi_closure *closure = malloc(size);
|
||||
if (closure == NULL)
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_lock(&ffi_trampoline_lock);
|
||||
|
||||
/* Check for an active trampoline table with available entries. */
|
||||
ffi_trampoline_table *table = ffi_trampoline_tables;
|
||||
if (table == NULL || table->free_list == NULL) {
|
||||
table = ffi_trampoline_table_alloc ();
|
||||
if (table == NULL) {
|
||||
free(closure);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Insert the new table at the top of the list */
|
||||
table->next = ffi_trampoline_tables;
|
||||
if (table->next != NULL)
|
||||
table->next->prev = table;
|
||||
|
||||
ffi_trampoline_tables = table;
|
||||
}
|
||||
|
||||
/* Claim the free entry */
|
||||
ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
|
||||
ffi_trampoline_tables->free_list = entry->next;
|
||||
ffi_trampoline_tables->free_count--;
|
||||
entry->next = NULL;
|
||||
|
||||
pthread_mutex_unlock(&ffi_trampoline_lock);
|
||||
|
||||
/* Initialize the return values */
|
||||
*code = entry->trampoline;
|
||||
closure->trampoline_table = table;
|
||||
closure->trampoline_table_entry = entry;
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
void
|
||||
ffi_closure_free (void *ptr)
|
||||
{
|
||||
ffi_closure *closure = ptr;
|
||||
|
||||
pthread_mutex_lock(&ffi_trampoline_lock);
|
||||
|
||||
/* Fetch the table and entry references */
|
||||
ffi_trampoline_table *table = closure->trampoline_table;
|
||||
ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
|
||||
|
||||
/* Return the entry to the free list */
|
||||
entry->next = table->free_list;
|
||||
table->free_list = entry;
|
||||
table->free_count++;
|
||||
|
||||
/* If all trampolines within this table are free, and at least one other table exists, deallocate
|
||||
* the table */
|
||||
if (table->free_count == FFI_TRAMPOLINE_COUNT && ffi_trampoline_tables != table) {
|
||||
/* Remove from the list */
|
||||
if (table->prev != NULL)
|
||||
table->prev->next = table->next;
|
||||
|
||||
if (table->next != NULL)
|
||||
table->next->prev = table->prev;
|
||||
|
||||
/* Deallocate pages */
|
||||
kern_return_t kt;
|
||||
kt = vm_deallocate (mach_task_self (), table->config_page, PAGE_SIZE);
|
||||
if (kt != KERN_SUCCESS)
|
||||
fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
|
||||
|
||||
kt = vm_deallocate (mach_task_self (), table->trampoline_page, PAGE_SIZE);
|
||||
if (kt != KERN_SUCCESS)
|
||||
fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
|
||||
|
||||
/* Deallocate free list */
|
||||
free (table->free_list_pool);
|
||||
free (table);
|
||||
} else if (ffi_trampoline_tables != table) {
|
||||
/* Otherwise, bump this table to the top of the list */
|
||||
table->prev = NULL;
|
||||
table->next = ffi_trampoline_tables;
|
||||
if (ffi_trampoline_tables != NULL)
|
||||
ffi_trampoline_tables->prev = table;
|
||||
|
||||
ffi_trampoline_tables = table;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&ffi_trampoline_lock);
|
||||
|
||||
/* Free the closure */
|
||||
free (closure);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX) \
|
||||
({ unsigned char *__tramp = (unsigned char*)(TRAMP); \
|
||||
unsigned int __fun = (unsigned int)(FUN); \
|
||||
@@ -349,6 +563,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
|
||||
__clear_cache((&__tramp[0]), (&__tramp[19])); \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
||||
/* the cif must already be prep'ed */
|
||||
|
||||
@@ -368,10 +583,16 @@ ffi_prep_closure_loc (ffi_closure* closure,
|
||||
else
|
||||
FFI_ASSERT (0);
|
||||
|
||||
#if FFI_EXEC_TRAMPOLINE_TABLE
|
||||
void **config = FFI_TRAMPOLINE_CODELOC_CONFIG(codeloc);
|
||||
config[0] = closure;
|
||||
config[1] = closure_func;
|
||||
#else
|
||||
FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
|
||||
closure_func, \
|
||||
codeloc);
|
||||
|
||||
#endif
|
||||
|
||||
closure->cif = cif;
|
||||
closure->user_data = user_data;
|
||||
closure->fun = fun;
|
||||
|
||||
118
src/arm/gentramp.sh
Executable file
118
src/arm/gentramp.sh
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/bin/sh
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# gentramp.sh - Copyright (c) 2010, Plausible Labs Cooperative, Inc.
|
||||
#
|
||||
# ARM Trampoline Page Generator
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# ``Software''), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
PROGNAME=$0
|
||||
|
||||
# Each trampoline is exactly 3 instructions, or 12 bytes. If any of these values change,
|
||||
# the entire arm trampoline implementation must be updated to match, too.
|
||||
|
||||
# Size of an individual trampoline, in bytes
|
||||
TRAMPOLINE_SIZE=12
|
||||
|
||||
# Page size, in bytes
|
||||
PAGE_SIZE=4096
|
||||
|
||||
# Compute the size of the reachable config page; The first 16 bytes of the config page
|
||||
# are unreachable due to our maximum pc-relative ldr offset.
|
||||
PAGE_AVAIL=`expr $PAGE_SIZE - 16`
|
||||
|
||||
# Compute the number of of available trampolines.
|
||||
TRAMPOLINE_COUNT=`expr $PAGE_AVAIL / $TRAMPOLINE_SIZE`
|
||||
|
||||
header () {
|
||||
echo "# GENERATED CODE - DO NOT EDIT"
|
||||
echo "# This file was generated by $PROGNAME"
|
||||
echo ""
|
||||
|
||||
# Write out the license header
|
||||
cat << EOF
|
||||
# Copyright (c) 2010, Plausible Labs Cooperative, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# ``Software''), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
EOF
|
||||
|
||||
# Write out the trampoline table, aligned to the page boundary
|
||||
echo ".text"
|
||||
echo ".align 12"
|
||||
echo ".globl _ffi_closure_trampoline_table_page"
|
||||
echo "_ffi_closure_trampoline_table_page:"
|
||||
}
|
||||
|
||||
|
||||
# WARNING - Don't modify the trampoline code size without also updating the relevent libffi code
|
||||
trampoline () {
|
||||
cat << END
|
||||
|
||||
// trampoline
|
||||
// Save to stack
|
||||
stmfd sp!, {r0-r3}
|
||||
|
||||
// Load the context argument from the config page.
|
||||
// This places the first usable config value at _ffi_closure_trampoline_table-4080
|
||||
// This accounts for the above 4-byte stmfd instruction, plus 8 bytes constant when loading from pc.
|
||||
ldr r0, [pc, #-4092]
|
||||
|
||||
// Load the jump address from the config page.
|
||||
ldr pc, [pc, #-4092]
|
||||
|
||||
END
|
||||
}
|
||||
|
||||
main () {
|
||||
# Write out the header
|
||||
header
|
||||
|
||||
# Write out the trampolines
|
||||
local i=0
|
||||
while [ $i -lt ${TRAMPOLINE_COUNT} ]; do
|
||||
trampoline
|
||||
local i=`expr $i + 1`
|
||||
done
|
||||
}
|
||||
|
||||
main
|
||||
@@ -1,5 +1,5 @@
|
||||
/* -----------------------------------------------------------------------
|
||||
sysv.S - Copyright (c) 1998, 2008 Red Hat, Inc.
|
||||
sysv.S - Copyright (c) 1998, 2008, 2011 Red Hat, Inc.
|
||||
|
||||
ARM Foreign Function Interface
|
||||
|
||||
@@ -39,7 +39,11 @@
|
||||
#else
|
||||
#define CNAME(x) x
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#define ENTRY(x) .globl CNAME(x); CNAME(x):
|
||||
#else
|
||||
#define ENTRY(x) .globl CNAME(x); .type CNAME(x),%function; CNAME(x):
|
||||
#endif /* __APPLE__ */
|
||||
#endif
|
||||
|
||||
#ifdef __ELF__
|
||||
@@ -48,6 +52,12 @@
|
||||
#define LSYM(x) x
|
||||
#endif
|
||||
|
||||
/* Use the SOFTFP return value ABI on Mac OS X, as per the iOS ABI
|
||||
Function Call Guide */
|
||||
#ifdef __APPLE__
|
||||
#define __SOFTFP__
|
||||
#endif
|
||||
|
||||
/* We need a better way of testing for this, but for now, this is all
|
||||
we can do. */
|
||||
@ This selects the minimum architecture level required.
|
||||
@@ -105,21 +115,33 @@
|
||||
.align 0
|
||||
.thumb
|
||||
.thumb_func
|
||||
#ifdef __APPLE__
|
||||
ENTRY($0)
|
||||
#else
|
||||
ENTRY(\name)
|
||||
#endif
|
||||
bx pc
|
||||
nop
|
||||
.arm
|
||||
UNWIND .fnstart
|
||||
/* A hook to tell gdb that we've switched to ARM mode. Also used to call
|
||||
directly from other local arm routines. */
|
||||
_L__\name:
|
||||
#ifdef __APPLE__
|
||||
_L__$0:
|
||||
#else
|
||||
_L__\name:
|
||||
#endif
|
||||
.endm
|
||||
#else
|
||||
.macro ARM_FUNC_START name
|
||||
.text
|
||||
.align 0
|
||||
.arm
|
||||
#ifdef __APPLE__
|
||||
ENTRY($0)
|
||||
#else
|
||||
ENTRY(\name)
|
||||
#endif
|
||||
UNWIND .fnstart
|
||||
.endm
|
||||
#endif
|
||||
@@ -141,8 +163,7 @@ _L__\name:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
|
||||
@ r0: fn
|
||||
@ r0: ffi_prep_args
|
||||
@ r1: &ecif
|
||||
@ r2: cif->bytes
|
||||
@ r3: fig->flags
|
||||
@@ -222,11 +243,18 @@ ARM_FUNC_START ffi_call_SYSV
|
||||
#endif
|
||||
|
||||
LSYM(Lepilogue):
|
||||
RETLDM "r0-r3,fp"
|
||||
#if defined (__INTERWORKING__)
|
||||
ldmia sp!, {r0-r3,fp, lr}
|
||||
bx lr
|
||||
#else
|
||||
ldmia sp!, {r0-r3,fp, pc}
|
||||
#endif
|
||||
|
||||
.ffi_call_SYSV_end:
|
||||
UNWIND .fnend
|
||||
#ifdef __ELF__
|
||||
.size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -247,7 +275,7 @@ ARM_FUNC_START ffi_closure_SYSV
|
||||
sub sp, sp, #16
|
||||
str sp, [sp, #8]
|
||||
add r1, sp, #8
|
||||
bl ffi_closure_SYSV_inner
|
||||
bl CNAME(ffi_closure_SYSV_inner)
|
||||
cmp r0, #FFI_TYPE_INT
|
||||
beq .Lretint
|
||||
|
||||
@@ -299,7 +327,9 @@ ARM_FUNC_START ffi_closure_SYSV
|
||||
|
||||
.ffi_closure_SYSV_end:
|
||||
UNWIND .fnend
|
||||
#ifdef __ELF__
|
||||
.size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV)
|
||||
#endif
|
||||
|
||||
|
||||
/* Below are VFP hard-float ABI call and closure implementations.
|
||||
@@ -371,7 +401,7 @@ LSYM(Lbase_args):
|
||||
@ assume no return value.
|
||||
cmp r2, #0
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
|
||||
cmp r3, #FFI_TYPE_INT
|
||||
streq r0, [r2]
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
4450
src/arm/trampoline.S
Normal file
4450
src/arm/trampoline.S
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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,11 @@
|
||||
|
||||
#if FFI_CLOSURES
|
||||
|
||||
# if FFI_MMAP_EXEC_WRIT
|
||||
# if FFI_EXEC_TRAMPOLINE_TABLE
|
||||
|
||||
// Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
|
||||
|
||||
# elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
|
||||
|
||||
#define USE_LOCKS 1
|
||||
#define USE_DL_PREFIX 1
|
||||
|
||||
Reference in New Issue
Block a user