Skip to content

Commit e72134d

Browse files
committed
Add Arm64 TSAN support and fix JIT cache coherency
This commit adds ThreadSanitizer (TSAN) support for ARM64/Apple Silicon and fixes critical JIT instruction cache coherency issues. ARM64 TSAN Support: - Extended TSAN-compatible memory allocation to ARM64 architecture - Main memory allocated at fixed address 0x150000000000 (21TB) - JIT buffer allocated at 0x151000000000 with MAP_JIT for Apple Silicon - Both allocations avoid TSAN shadow memory and enable race detection - Note: Requires ASLR disabled on macOS (SIP restrictions may apply) JIT Cache Coherency Fixes: 1. Fixed pthread_jit_write_protect_np() ordering in update_branch_imm 2. Added sys_icache_invalidate() in update_branch_imm 3. Added cache invalidation in resolve_jumps() for x86_64
1 parent 3bdcec5 commit e72134d

File tree

3 files changed

+67
-15
lines changed

3 files changed

+67
-15
lines changed

src/emulate.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,10 +1205,14 @@ void rv_step(void *arg)
12051205
#endif
12061206
) {
12071207
jit_translate(rv, block);
1208-
((exec_block_func_t) state->buf)(
1209-
rv, (uintptr_t) (state->buf + block->offset));
1210-
prev = NULL;
1211-
continue;
1208+
/* Only execute if translation succeeded (block is hot) */
1209+
if (block->hot) {
1210+
((exec_block_func_t) state->buf)(
1211+
rv, (uintptr_t) (state->buf + block->offset));
1212+
prev = NULL;
1213+
continue;
1214+
}
1215+
/* Fall through to interpreter if translation failed */
12121216
}
12131217
set_reset(&pc_set);
12141218
has_loops = false;

src/io.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,32 @@ memory_t *memory_new(uint32_t size)
2727
return NULL;
2828
assert(mem);
2929
#if HAVE_MMAP
30-
#if defined(TSAN_ENABLED) && defined(__x86_64__)
30+
#if defined(TSAN_ENABLED)
3131
/* ThreadSanitizer compatibility: Use MAP_FIXED to allocate at a specific
32-
* address within TSAN's app range (0x7cf000000000 - 0x7ffffffff000).
32+
* address to avoid conflicts with TSAN's shadow memory.
33+
*/
34+
#if defined(__x86_64__)
35+
/* x86_64: Allocate within TSAN's range (0x7cf000000000 - 0x7ffffffff000).
3336
*
3437
* Fixed address: 0x7d0000000000
3538
* Size: up to 4GB (0x100000000)
3639
* End: 0x7d0100000000 (well within app range)
37-
*
38-
* This guarantees the allocation won't land in TSAN's shadow memory,
39-
* preventing "unexpected memory mapping" errors.
4040
*/
4141
void *fixed_addr = (void *) 0x7d0000000000UL;
42+
#elif defined(__aarch64__)
43+
/* ARM64 (macOS/Apple Silicon): Use higher address range.
44+
*
45+
* Fixed address: 0x150000000000 (21TB)
46+
* Size: up to 4GB (0x100000000)
47+
* End: 0x150100000000
48+
*
49+
* This avoids TSAN's shadow memory and typical process allocations.
50+
* Requires ASLR disabled via: setarch $(uname -m) -R
51+
*/
52+
void *fixed_addr = (void *) 0x150000000000UL;
53+
#else
54+
#error "TSAN is only supported on x86_64 and aarch64"
55+
#endif
4256
data_memory_base = mmap(fixed_addr, size, PROT_READ | PROT_WRITE,
4357
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
4458
if (data_memory_base == MAP_FAILED) {

src/jit.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,10 @@ static void update_branch_imm(struct jit_state *state,
593593
assert((imm & 3) == 0);
594594
uint32_t insn;
595595
imm >>= 2;
596+
#if defined(__APPLE__) && defined(__aarch64__)
597+
/* Must be in write mode to read/write MAP_JIT memory on Apple ARM64 */
598+
pthread_jit_write_protect_np(false);
599+
#endif
596600
memcpy(&insn, state->buf + offset, sizeof(uint32_t));
597601
if ((insn & 0xfe000000U) == 0x54000000U /* Conditional branch immediate. */
598602
|| (insn & 0x7e000000U) ==
@@ -607,10 +611,8 @@ static void update_branch_imm(struct jit_state *state,
607611
assert(false);
608612
insn = BAD_OPCODE;
609613
}
610-
#if defined(__APPLE__) && defined(__aarch64__)
611-
pthread_jit_write_protect_np(false);
612-
#endif
613614
memcpy(state->buf + offset, &insn, sizeof(uint32_t));
615+
sys_icache_invalidate(state->buf + offset, sizeof(uint32_t));
614616
#if defined(__APPLE__) && defined(__aarch64__)
615617
pthread_jit_write_protect_np(true);
616618
#endif
@@ -2167,6 +2169,7 @@ static void code_cache_flush(struct jit_state *state, riscv_t *rv)
21672169
should_flush = false;
21682170
state->offset = state->org_size;
21692171
state->n_blocks = 0;
2172+
state->n_jumps = 0; /* Reset jump count when flushing */
21702173
set_reset(&state->set);
21712174
clear_cache_hot(rv->block_cache, (clear_func_t) clear_hot);
21722175
#if RV32_HAS(T2C)
@@ -2229,6 +2232,7 @@ static void resolve_jumps(struct jit_state *state)
22292232

22302233
uint8_t *offset_ptr = &state->buf[jump.offset_loc];
22312234
memcpy(offset_ptr, &rel, sizeof(uint32_t));
2235+
sys_icache_invalidate(offset_ptr, sizeof(uint32_t));
22322236
#elif defined(__aarch64__)
22332237
int32_t rel = target_loc - jump.offset_loc;
22342238
update_branch_imm(state, jump.offset_loc, rel);
@@ -2320,6 +2324,9 @@ void jit_translate(riscv_t *rv, block_t *block)
23202324
block->offset = state->offset;
23212325
translate_chained_block(state, rv, block);
23222326
if (unlikely(should_flush)) {
2327+
/* Mark block as not translated since translation was incomplete */
2328+
block->hot = false;
2329+
/* Don't reset offset - it will be set correctly on restart */
23232330
code_cache_flush(state, rv);
23242331
goto restart;
23252332
}
@@ -2336,10 +2343,12 @@ struct jit_state *jit_state_init(size_t size)
23362343

23372344
state->offset = 0;
23382345
state->size = size;
2339-
#if defined(TSAN_ENABLED) && defined(__x86_64__)
2346+
#if defined(TSAN_ENABLED)
23402347
/* ThreadSanitizer compatibility: Allocate JIT code buffer at a fixed
23412348
* address above the main memory region to avoid conflicts.
2342-
*
2349+
*/
2350+
#if defined(__x86_64__)
2351+
/* x86_64 memory layout:
23432352
* Main memory: 0x7d0000000000 - 0x7d0100000000 (4GB for FULL4G)
23442353
* JIT buffer: 0x7d1000000000 + size
23452354
*
@@ -2348,7 +2357,32 @@ struct jit_state *jit_state_init(size_t size)
23482357
*/
23492358
void *jit_addr = (void *) 0x7d1000000000UL;
23502359
state->buf = mmap(jit_addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
2351-
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
2360+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
2361+
#if defined(__APPLE__)
2362+
| MAP_JIT
2363+
#endif
2364+
,
2365+
-1, 0);
2366+
#elif defined(__aarch64__)
2367+
/* ARM64 memory layout (macOS/Apple Silicon):
2368+
* Main memory: 0x150000000000 - 0x150100000000 (4GB for FULL4G)
2369+
* JIT buffer: 0x151000000000 + size
2370+
*
2371+
* Apple Silicon requires MAP_JIT for executable memory. The fixed
2372+
* address is chosen to avoid TSAN's shadow memory and typical process
2373+
* allocations. Requires ASLR disabled via: setarch $(uname -m) -R
2374+
*/
2375+
void *jit_addr = (void *) 0x151000000000UL;
2376+
state->buf = mmap(jit_addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
2377+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
2378+
#if defined(__APPLE__)
2379+
| MAP_JIT
2380+
#endif
2381+
,
2382+
-1, 0);
2383+
#else
2384+
#error "TSAN is only supported on x86_64 and aarch64"
2385+
#endif
23522386
if (state->buf == MAP_FAILED) {
23532387
free(state);
23542388
return NULL;

0 commit comments

Comments
 (0)