Skip to content

Commit

Permalink
macos: add BigSur support to execalloc
Browse files Browse the repository at this point in the history
Apple Silicon requires that pages that will hold JIT code are
marked with MAP_JIT (even if not using the hardened runtime)
and that a call be made to a pthread function before writing
to them, so a special exception could be made to the current
thread[1]; add support for both.

since the allocator keeps the metadata about chunk/block in the
executable pages, all functions that modify that metadata will
also need to be updated.

note that since there is no need for an accurate pointer range
with the apple implementation, NULL is passed for the pointers.

historically, adding MAP_JIT was only recommended when the hardened
runtime was being used as it adds several undocumented restrictions
(like not being able to use JIT pages accross fork()) so the
new codepath won't be used if running in Intel.

Tested-by: @Keno
Fixes: #51

[1] https://developer.apple.com/documentation/apple_silicon/porting_just-in-time_compilers_to_apple_silicon?language=objc
  • Loading branch information
carenas committed Nov 16, 2020
1 parent 5d7f657 commit 0afe96a
Showing 1 changed file with 63 additions and 50 deletions.
113 changes: 63 additions & 50 deletions sljit_src/sljitExecAllocator.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
*/

#ifdef _WIN32
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)

static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
{
Expand All @@ -91,78 +92,84 @@ static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
VirtualFree(chunk, 0, MEM_RELEASE);
}

#else

#ifdef __APPLE__
#ifdef MAP_ANON
/* Configures TARGET_OS_OSX when appropriate */
#include <TargetConditionals.h>

#if TARGET_OS_OSX && defined(MAP_JIT)
#include <sys/utsname.h>
#endif /* TARGET_OS_OSX && MAP_JIT */

#ifdef MAP_JIT
#else /* POSIX */

#if defined(__APPLE__) && defined(MAP_JIT)
/*
On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a
version where it's OK to have more than one JIT block.
version where it's OK to have more than one JIT block or where MAP_JIT is
required.
On non-macOS systems, returns MAP_JIT if it is defined.
*/
#include <TargetConditionals.h>
#if TARGET_OS_OSX
#if defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86
#ifdef MAP_ANON
#include <sys/utsname.h>
#include <stdlib.h>

#define SLJIT_MAP_JIT (get_map_jit_flag())

static SLJIT_INLINE int get_map_jit_flag()
{
#if TARGET_OS_OSX
sljit_sw page_size = get_page_alignment() + 1;
sljit_sw page_size;
void *ptr;
struct utsname name;
static int map_jit_flag = -1;

/*
The following code is thread safe because multiple initialization
sets map_jit_flag to the same value and the code has no side-effects.
Changing the kernel version witout system restart is (very) unlikely.
*/
if (map_jit_flag == -1) {
struct utsname name;

if (map_jit_flag < 0) {
map_jit_flag = 0;
uname(&name);

/* Kernel version for 10.14.0 (Mojave) */
/* Kernel version for 10.14.0 (Mojave) or later */
if (atoi(name.release) >= 18) {
page_size = get_page_alignment() + 1;
/* Only use MAP_JIT if a hardened runtime is used */
ptr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON, -1, 0);

ptr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);

if (ptr == MAP_FAILED) {
map_jit_flag = MAP_JIT;
} else {
if (ptr != MAP_FAILED)
munmap(ptr, page_size);
}
else
map_jit_flag = MAP_JIT;
}
}

return map_jit_flag;
#else /* !TARGET_OS_OSX */
return MAP_JIT;
#endif /* TARGET_OS_OSX */
}

#endif /* MAP_JIT */
#endif /* MAP_ANON */
#endif /* __APPLE__ */
#else /* !SLJIT_CONFIG_X86 */
#if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
#error Unsupported architecture
#endif /* SLJIT_CONFIG_ARM */
#include <pthread.h>

#define SLJIT_MAP_JIT (MAP_JIT)
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
apple_update_wx_flags(enable_exec)

static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec)
{
pthread_jit_write_protect_np(enable_exec);
}
#endif /* SLJIT_CONFIG_X86 */
#else /* !TARGET_OS_OSX */
#define SLJIT_MAP_JIT (MAP_JIT)
#endif /* TARGET_OS_OSX */
#endif /* __APPLE__ && MAP_JIT */
#ifndef SLJIT_UPDATE_WX_FLAGS
#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
#endif /* !SLJIT_UPDATE_WX_FLAGS */
#ifndef SLJIT_MAP_JIT
#define SLJIT_MAP_JIT (0)
#endif /* !SLJIT_MAP_JIT */

static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
{
void *retval;
const int prot = PROT_READ | PROT_WRITE | PROT_EXEC;

#ifdef MAP_ANON

int flags = MAP_PRIVATE | MAP_ANON;

#ifdef MAP_JIT
flags |= get_map_jit_flag();
#endif
int flags = MAP_PRIVATE | MAP_ANON | SLJIT_MAP_JIT;

retval = mmap(NULL, size, prot, flags, -1, 0);
#else /* !MAP_ANON */
Expand All @@ -173,14 +180,15 @@ static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
#endif /* MAP_ANON */

if (retval == MAP_FAILED)
retval = NULL;
else {
if (mprotect(retval, size, prot) < 0) {
munmap(retval, size);
retval = NULL;
}
return NULL;

if (mprotect(retval, size, prot) < 0) {
munmap(retval, size);
return NULL;
}

SLJIT_UPDATE_WX_FLAGS(retval, (uint8_t *)retval + size, 0);

return retval;
}

Expand All @@ -189,7 +197,7 @@ static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
munmap(chunk, size);
}

#endif
#endif /* windows */

/* --------------------------------------------------------------------- */
/* Common functions */
Expand Down Expand Up @@ -261,6 +269,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
while (free_block) {
if (free_block->size >= size) {
chunk_size = free_block->size;
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
if (chunk_size > size + 64) {
/* We just cut a block from the end of the free block. */
chunk_size -= size;
Expand Down Expand Up @@ -326,6 +335,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
allocated_size -= header->size;

/* Connecting free blocks together if possible. */
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);

/* If header->prev_size == 0, free_block will equal to header.
In this case, free_block->header.size will be > 0. */
Expand Down Expand Up @@ -358,6 +368,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
}
}

SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
SLJIT_ALLOCATOR_UNLOCK();
}

Expand All @@ -367,6 +378,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
struct free_block* next_free_block;

SLJIT_ALLOCATOR_LOCK();
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);

free_block = free_blocks;
while (free_block) {
Expand All @@ -381,5 +393,6 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
}

SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
SLJIT_ALLOCATOR_UNLOCK();
}

0 comments on commit 0afe96a

Please sign in to comment.