Skip to content

Commit

Permalink
Use ffi_closure_alloc() on Apple silicon to fix crash
Browse files Browse the repository at this point in the history
  • Loading branch information
Watson1978 committed Jul 18, 2020
1 parent d15fa06 commit 2c9c517
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 2 deletions.
67 changes: 67 additions & 0 deletions ext/ffi_c/ClosurePool.c
Expand Up @@ -66,6 +66,11 @@

#include "ClosurePool.h"

#if (defined(__arm64__) && defined(__APPLE__))
#define USE_FFI_ALLOC 1
#else
#define USE_FFI_ALLOC 0
#endif

#ifndef roundup
# define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
Expand Down Expand Up @@ -115,7 +120,11 @@ cleanup_closure_pool(ClosurePool* pool)

for (memory = pool->blocks; memory != NULL; ) {
Memory* next = memory->next;
#if !USE_FFI_ALLOC
freePage(memory->code);
#else
ffi_closure_free(memory->code);
#endif
free(memory->data);
free(memory);
memory = next;
Expand All @@ -134,6 +143,8 @@ rbffi_ClosurePool_Free(ClosurePool* pool)
}
}

#if !USE_FFI_ALLOC

Closure*
rbffi_Closure_Alloc(ClosurePool* pool)
{
Expand Down Expand Up @@ -169,6 +180,7 @@ rbffi_Closure_Alloc(ClosurePool* pool)
closure->next = &list[i + 1];
closure->pool = pool;
closure->code = ((char *)code + (i * trampolineSize));
closure->pcl = closure->code;

if (!(*pool->prep)(pool->ctx, closure->code, closure, errmsg, sizeof(errmsg))) {
goto error;
Expand Down Expand Up @@ -205,6 +217,57 @@ rbffi_Closure_Alloc(ClosurePool* pool)
return NULL;
}

#else

Closure*
rbffi_Closure_Alloc(ClosurePool* pool)
{
Closure *closure = NULL;
Memory* block = NULL;
void *code = NULL;
void *pcl = NULL;
char errmsg[256];

block = calloc(1, sizeof(*block));
closure = calloc(1, sizeof(*closure));
pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);

if (block == NULL || closure == NULL || pcl == NULL) {
snprintf(errmsg, sizeof(errmsg), "failed to allocate a page. errno=%d (%s)", errno, strerror(errno));
goto error;
}

closure->pool = pool;
closure->code = code;
closure->pcl = pcl;

if (!(*pool->prep)(pool->ctx, closure->code, closure, errmsg, sizeof(errmsg))) {
goto error;
}

/* Track the allocated page + Closure memory area */
block->data = closure;
block->code = pcl;
pool->blocks = block;

/* Thread the new block onto the free list, apart from the first one. */
pool->refcnt++;

return closure;

error:
free(block);
free(closure);
if (pcl != NULL) {
ffi_closure_free(pcl);
}

rb_raise(rb_eRuntimeError, "%s", errmsg);
return NULL;
}

#endif /* !USE_FFI_ALLOC */

void
rbffi_Closure_Free(Closure* closure)
{
Expand Down Expand Up @@ -240,6 +303,8 @@ getPageSize()
#endif
}

#if !USE_FFI_ALLOC

static void*
allocatePage(void)
{
Expand Down Expand Up @@ -272,6 +337,8 @@ protectPage(void* page)
#endif
}

#endif /* !USE_FFI_ALLOC */

void
rbffi_ClosurePool_Init(VALUE module)
{
Expand Down
2 changes: 2 additions & 0 deletions ext/ffi_c/ClosurePool.h
Expand Up @@ -36,6 +36,8 @@ struct Closure_ {
void* info; /* opaque handle for storing closure-instance specific data */
void* function; /* closure-instance specific function, called by custom trampoline */
void* code; /* The native trampoline code location */
void* pcl;

struct ClosurePool_* pool;
Closure* next;
};
Expand Down
2 changes: 1 addition & 1 deletion ext/ffi_c/Function.c
Expand Up @@ -849,7 +849,7 @@ callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errm
FunctionType* fnInfo = (FunctionType *) ctx;
ffi_status ffiStatus;

ffiStatus = ffi_prep_closure_loc(code, &fnInfo->ffi_cif, callback_invoke, closure, code);
ffiStatus = ffi_prep_closure_loc(closure->pcl, &fnInfo->ffi_cif, callback_invoke, closure, code);
if (ffiStatus != FFI_OK) {
snprintf(errmsg, errmsgsize, "ffi_prep_closure_loc failed. status=%#x", ffiStatus);
return false;
Expand Down
2 changes: 1 addition & 1 deletion ext/ffi_c/MethodHandle.c
Expand Up @@ -140,7 +140,7 @@ prep_trampoline(void* ctx, void* code, Closure* closure, char* errmsg, size_t er
#if defined(USE_RAW)
ffiStatus = ffi_prep_raw_closure(code, &mh_cif, attached_method_invoke, closure);
#else
ffiStatus = ffi_prep_closure_loc(code, &mh_cif, attached_method_invoke, closure, code);
ffiStatus = ffi_prep_closure_loc(closure->pcl, &mh_cif, attached_method_invoke, closure, code);
#endif
if (ffiStatus != FFI_OK) {
snprintf(errmsg, errmsgsize, "ffi_prep_closure_loc failed. status=%#x", ffiStatus);
Expand Down

0 comments on commit 2c9c517

Please sign in to comment.