Skip to content

Commit 558e438

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 558e438

File tree

4 files changed

+93
-13
lines changed

4 files changed

+93
-13
lines changed

src/emulate.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern struct target_ops gdbstub_ops;
2424
#endif
2525

2626
#include "decode.h"
27+
#include "log.h"
2728
#include "mpool.h"
2829
#include "riscv.h"
2930
#include "riscv_private.h"
@@ -1205,10 +1206,20 @@ void rv_step(void *arg)
12051206
#endif
12061207
) {
12071208
jit_translate(rv, block);
1208-
((exec_block_func_t) state->buf)(
1209-
rv, (uintptr_t) (state->buf + block->offset));
1210-
prev = NULL;
1211-
continue;
1209+
/* Only execute if translation succeeded (block is hot) */
1210+
if (block->hot) {
1211+
rv_log_debug("JIT: Executing block pc=0x%08x, offset=%u",
1212+
block->pc_start, block->offset);
1213+
((exec_block_func_t) state->buf)(
1214+
rv, (uintptr_t) (state->buf + block->offset));
1215+
prev = NULL;
1216+
continue;
1217+
}
1218+
/* Fall through to interpreter if translation failed */
1219+
rv_log_debug(
1220+
"JIT: Translation failed for block pc=0x%08x, using "
1221+
"interpreter",
1222+
block->pc_start);
12121223
}
12131224
set_reset(&pc_set);
12141225
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: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "decode.h"
4343
#include "io.h"
4444
#include "jit.h"
45+
#include "log.h"
4546
#include "riscv.h"
4647
#include "riscv_private.h"
4748
#include "utils.h"
@@ -593,24 +594,30 @@ static void update_branch_imm(struct jit_state *state,
593594
assert((imm & 3) == 0);
594595
uint32_t insn;
595596
imm >>= 2;
597+
rv_log_debug("JIT: Patching branch at offset=%u, imm=%d", offset, imm * 4);
598+
/* Read instruction while in execute mode (MAP_JIT requirement) */
596599
memcpy(&insn, state->buf + offset, sizeof(uint32_t));
597600
if ((insn & 0xfe000000U) == 0x54000000U /* Conditional branch immediate. */
598601
|| (insn & 0x7e000000U) ==
599602
0x34000000U) { /* Compare and branch immediate. */
600603
assert((imm >> 19) == INT64_C(-1) || (imm >> 19) == 0);
604+
insn &= ~(0x7ffffU << 5); /* Clear old offset bits */
601605
insn |= (imm & 0x7ffff) << 5;
602606
} else if ((insn & 0x7c000000U) == 0x14000000U) {
603607
/* Unconditional branch immediate. */
604608
assert((imm >> 26) == INT64_C(-1) || (imm >> 26) == 0);
609+
insn &= ~0x03ffffffU; /* Clear old offset bits */
605610
insn |= (imm & 0x03ffffffU) << 0;
606611
} else {
607612
assert(false);
608613
insn = BAD_OPCODE;
609614
}
610615
#if defined(__APPLE__) && defined(__aarch64__)
616+
/* Switch to write mode only for writing */
611617
pthread_jit_write_protect_np(false);
612618
#endif
613619
memcpy(state->buf + offset, &insn, sizeof(uint32_t));
620+
sys_icache_invalidate(state->buf + offset, sizeof(uint32_t));
614621
#if defined(__APPLE__) && defined(__aarch64__)
615622
pthread_jit_write_protect_np(true);
616623
#endif
@@ -2164,9 +2171,12 @@ void clear_hot(block_t *block)
21642171

21652172
static void code_cache_flush(struct jit_state *state, riscv_t *rv)
21662173
{
2174+
rv_log_info("JIT: Flushing code cache (n_blocks=%d, n_jumps=%d, offset=%u)",
2175+
state->n_blocks, state->n_jumps, state->offset);
21672176
should_flush = false;
21682177
state->offset = state->org_size;
21692178
state->n_blocks = 0;
2179+
state->n_jumps = 0; /* Reset jump count when flushing */
21702180
set_reset(&state->set);
21712181
clear_cache_hot(rv->block_cache, (clear_func_t) clear_hot);
21722182
#if RV32_HAS(T2C)
@@ -2196,6 +2206,7 @@ static void translate(struct jit_state *state, riscv_t *rv, block_t *block)
21962206

21972207
static void resolve_jumps(struct jit_state *state)
21982208
{
2209+
rv_log_debug("JIT: Resolving %d jumps", state->n_jumps);
21992210
for (int i = 0; i < state->n_jumps; i++) {
22002211
struct jump jump = state->jumps[i];
22012212
int target_loc;
@@ -2218,6 +2229,10 @@ static void resolve_jumps(struct jit_state *state)
22182229
(if (jump.target_satp == state->offset_map[i].satp), )
22192230
{
22202231
target_loc = state->offset_map[i].offset;
2232+
rv_log_debug(
2233+
"JIT: Jump %d resolved to block pc=0x%08x, "
2234+
"offset=%d",
2235+
i, jump.target_pc, target_loc);
22212236
break;
22222237
}
22232238
}
@@ -2229,6 +2244,7 @@ static void resolve_jumps(struct jit_state *state)
22292244

22302245
uint8_t *offset_ptr = &state->buf[jump.offset_loc];
22312246
memcpy(offset_ptr, &rel, sizeof(uint32_t));
2247+
sys_icache_invalidate(offset_ptr, sizeof(uint32_t));
22322248
#elif defined(__aarch64__)
22332249
int32_t rel = target_loc - jump.offset_loc;
22342250
update_branch_imm(state, jump.offset_loc, rel);
@@ -2308,23 +2324,35 @@ void jit_translate(riscv_t *rv, block_t *block)
23082324
) {
23092325
block->offset = state->offset_map[i].offset;
23102326
block->hot = true;
2327+
rv_log_debug("JIT: Cache hit for block pc=0x%08x, offset=%u",
2328+
block->pc_start, block->offset);
23112329
return;
23122330
}
23132331
}
23142332
assert(NULL);
23152333
__UNREACHABLE;
23162334
}
2335+
rv_log_debug("JIT: Starting translation for block pc=0x%08x",
2336+
block->pc_start);
23172337
restart:
23182338
memset(state->jumps, 0, MAX_JUMPS * sizeof(struct jump));
23192339
state->n_jumps = 0;
23202340
block->offset = state->offset;
23212341
translate_chained_block(state, rv, block);
23222342
if (unlikely(should_flush)) {
2343+
/* Mark block as not translated since translation was incomplete */
2344+
block->hot = false;
2345+
/* Don't reset offset - it will be set correctly on restart */
2346+
rv_log_debug("JIT: Translation triggered flush for block pc=0x%08x",
2347+
block->pc_start);
23232348
code_cache_flush(state, rv);
23242349
goto restart;
23252350
}
23262351
resolve_jumps(state);
23272352
block->hot = true;
2353+
rv_log_debug(
2354+
"JIT: Translation completed for block pc=0x%08x, offset=%u, size=%u",
2355+
block->pc_start, block->offset, state->offset - block->offset);
23282356
}
23292357

23302358
struct jit_state *jit_state_init(size_t size)
@@ -2336,10 +2364,12 @@ struct jit_state *jit_state_init(size_t size)
23362364

23372365
state->offset = 0;
23382366
state->size = size;
2339-
#if defined(TSAN_ENABLED) && defined(__x86_64__)
2367+
#if defined(TSAN_ENABLED)
23402368
/* ThreadSanitizer compatibility: Allocate JIT code buffer at a fixed
23412369
* address above the main memory region to avoid conflicts.
2342-
*
2370+
*/
2371+
#if defined(__x86_64__)
2372+
/* x86_64 memory layout:
23432373
* Main memory: 0x7d0000000000 - 0x7d0100000000 (4GB for FULL4G)
23442374
* JIT buffer: 0x7d1000000000 + size
23452375
*
@@ -2348,7 +2378,32 @@ struct jit_state *jit_state_init(size_t size)
23482378
*/
23492379
void *jit_addr = (void *) 0x7d1000000000UL;
23502380
state->buf = mmap(jit_addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
2351-
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
2381+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
2382+
#if defined(__APPLE__)
2383+
| MAP_JIT
2384+
#endif
2385+
,
2386+
-1, 0);
2387+
#elif defined(__aarch64__)
2388+
/* ARM64 memory layout (macOS/Apple Silicon):
2389+
* Main memory: 0x150000000000 - 0x150100000000 (4GB for FULL4G)
2390+
* JIT buffer: 0x151000000000 + size
2391+
*
2392+
* Apple Silicon requires MAP_JIT for executable memory. The fixed
2393+
* address is chosen to avoid TSAN's shadow memory and typical process
2394+
* allocations. Requires ASLR disabled via: setarch $(uname -m) -R
2395+
*/
2396+
void *jit_addr = (void *) 0x151000000000UL;
2397+
state->buf = mmap(jit_addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
2398+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
2399+
#if defined(__APPLE__)
2400+
| MAP_JIT
2401+
#endif
2402+
,
2403+
-1, 0);
2404+
#else
2405+
#error "TSAN is only supported on x86_64 and aarch64"
2406+
#endif
23522407
if (state->buf == MAP_FAILED) {
23532408
free(state);
23542409
return NULL;

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ int main(int argc, char **args)
304304
.args_offset_size = ARGS_OFFSET_SIZE,
305305
.argc = prog_argc,
306306
.argv = prog_args,
307-
.log_level = LOG_TRACE,
307+
.log_level = LOG_INFO,
308308
.run_flag = run_flag,
309309
.profile_output_file = prof_out_file,
310310
.cycle_per_step = CYCLE_PER_STEP,

0 commit comments

Comments
 (0)