-
Notifications
You must be signed in to change notification settings - Fork 1.6k
fix(jit): Add MAP_JIT support for 100% stable JIT on ARM64 macOS #12077
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
5241580 to
306d407
Compare
- Use mmap with MAP_JIT flag for JIT memory allocation - Call pthread_jit_write_protect_np(1) to switch to execute mode - Add sys_icache_invalidate for proper icache coherence - Custom Drop using munmap for MAP_JIT memory Fixes non-deterministic JIT execution failures on Apple Silicon.
b7d58cc to
776a366
Compare
- Use libc::MAP_JIT instead of custom constant - Add pthread_jit_write_protect_np(0) after mmap to enable write mode - Change doc comments to regular comments for implementation details - Add TODO comment about AArch64 icache reliance on mprotect
| #[cfg(all(target_arch = "aarch64", target_os = "macos"))] | ||
| unsafe { | ||
| sys_icache_invalidate(_ptr, _len); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#12133 implemented another approach for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, I had forgotten that this PR also handled this detail. Thanks for connecting the dots!
| fn pthread_jit_write_protect_np(enabled: libc::c_int); | ||
| } | ||
| unsafe { | ||
| pthread_jit_write_protect_np(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to scope those pthread_jit_write_protect_np calls to the actual section that does the writes. That would be more secure.
| ptr::null_mut(), | ||
| alloc_size, | ||
| libc::PROT_READ | libc::PROT_WRITE, | ||
| libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_JIT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should only pass MAP_JIT for pages that will actually end up being executable in the end.
Title
fix(jit): Add MAP_JIT support for 100% stable JIT execution on ARM64 macOS
Related Issue: #12076
Summary
This PR fixes non-deterministic JIT execution failures on Apple Silicon by implementing proper memory allocation and W^X mode handling required by the platform.
Before: ~56% success rate
After: 100% success rate (verified over 50+ consecutive runs)
Problem
JIT-compiled code on ARM64 macOS fails non-deterministically in multi-threaded scenarios. Symptoms include:
Root Cause
Apple Silicon enforces W^X (Write XOR Execute) at the hardware level:
MAP_JITflag so the kernel can track it for W^X enforcementpthread_jit_write_protect_np(1)before calling JIT codeThe current implementation:
MAP_JIT)pthread_jit_write_protect_np()Solution
cranelift/jit/src/memory/system.rsAdded an ARM64 macOS-specific
PtrLen::with_size()implementation that usesmmapwith theMAP_JITflag (0x0800) instead of the standard allocator. This allows macOS to properly track the memory for W^X policy enforcement.Also added a corresponding
Dropimplementation that usesmunmapto deallocate the memory, since memory allocated withmmapcannot be freed with the standard allocator.cranelift/jit/src/memory/mod.rsAfter making memory executable in
set_readable_and_executable(), added a call topthread_jit_write_protect_np(1)to switch the current thread to execute mode. This is required by Apple's W^X enforcement - threads must explicitly opt into execute mode before running JIT code.Testing
Test Script
Results
Tested with the Rayzor compiler's stdlib e2e test suite (50+ JIT-compiled runtime functions, multi-threaded):
Note: Simple standalone tests may not reliably reproduce this issue. The failure is non-deterministic and depends on timing, memory layout, and CPU core scheduling (P-core vs E-core).
Platform Impact
All changes are gated behind
#[cfg(all(target_arch = "aarch64", target_os = "macos"))].Related Issues
cranelift-jitcrate on aarch64 #2735 - Support PLT entries incranelift-jitcrate on aarch64ArgumentPurpose::StructArgumenton macOS (A64) #8852 - Cranelift: JIT assertion failure on macOS (A64)