Commit 14677cd5 authored by Austin Seipp's avatar Austin Seipp
Browse files

Fix freeHaskellFunPtr crash on iOS.

Authored-by: default avatarStephen Blackheath <>
Signed-off-by: default avatarAustin Seipp <>
parent f11289f6
......@@ -161,6 +161,9 @@ typedef void* AdjustorWritable;
typedef void* AdjustorExecutable;
AdjustorWritable allocateExec(W_ len, AdjustorExecutable *exec_addr);
#if defined(ios_HOST_OS)
AdjustorWritable execToWritable(AdjustorExecutable exec);
void freeExec (AdjustorExecutable p);
// Used by GC checks in external .cmm code:
......@@ -57,16 +57,57 @@ extern void *adjustorCode;
/* There are subtle differences between how libffi adjustors work on
* different platforms, and the situation is a little complex.
* libffi's ffi_closure_alloc() function gives you two pointers to a closure,
* 1. the writable pointer, and 2. the executable pointer. You write the
* closure into the writable pointer (and ffi_prep_closure_loc() will do this
* for you) and you execute it at the executable pointer.
* The RTS deals only with the executable pointer, but when it comes time to
* free the closure, libffi wants the writable pointer back that it gave you
* when you allocated it.
* On Linux we solve this problem by storing the address of the writable
* mapping into itself, then returning both writable and executable pointers
* plus 1 machine word for preparing the closure for use by the RTS (see the
* Linux version of allocateExec() in rts/sm/Storage.c). When we want to
* recover the writable address, we subtract 1 word from the executable
* address and fetch. This works because Linux kernel magic gives us two
* pointers with different addresses that refer to the same memory. Whatever
* you write into the writeable address can be read back at the executable
* address. This method is very efficient.
* On iOS this breaks for two reasons: 1. the two pointers do not refer to
* the same memory (so we can't retrieve anything stored into the writable
* pointer if we only have the exec pointer), and 2. libffi's
* ffi_closure_alloc() assumes the pointer it has returned you is a
* ffi_closure structure and treats it as such: It uses that memory to
* communicate with ffi_prep_closure_loc(). On Linux by contrast
* ffi_closure_alloc() is viewed simply as a memory allocation, and only
* ffi_prep_closure_loc() deals in ffi_closure structures. Each of these
* differences is enough make the efficient way used on Linux not work on iOS.
* Instead on iOS we use hash tables to recover the writable address from the
* executable one. This method is conservative and would almost certainly work
* on any platform, but on Linux it makes sense to use the faster method.
freeHaskellFunctionPtr(void* ptr)
ffi_closure *cl;
#if defined(ios_HOST_OS)
cl = execToWritable(ptr);
cl = (ffi_closure*)ptr;
static ffi_type * char_to_ffi_type(char c)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment