fix(jit): Add MAP_JIT support for 100% stable JIT on ARM64 macOS#12077
Open
darmie wants to merge 6 commits intobytecodealliance:mainfrom
Open
fix(jit): Add MAP_JIT support for 100% stable JIT on ARM64 macOS#12077darmie wants to merge 6 commits intobytecodealliance:mainfrom
darmie wants to merge 6 commits intobytecodealliance:mainfrom
Conversation
bjorn3
reviewed
Nov 24, 2025
bjorn3
reviewed
Nov 24, 2025
bjorn3
reviewed
Nov 24, 2025
bjorn3
reviewed
Nov 24, 2025
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
bjorn3
reviewed
Nov 24, 2025
bjorn3
reviewed
Nov 24, 2025
bjorn3
reviewed
Nov 24, 2025
bjorn3
reviewed
Nov 24, 2025
- 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
Member
bjorn3
reviewed
Dec 12, 2025
bjorn3
reviewed
Dec 12, 2025
bjorn3
reviewed
Dec 12, 2025
Address bjorn3 and cfallin's review feedback: 1. Drop icache changes: PR bytecodealliance#12133 already implements proper AArch64 icache coherence upstream. Remove our sys_icache_invalidate addition from libc.rs - it will be picked up on rebase. 2. Scope pthread_jit_write_protect_np calls: Move the write-mode enable (pthread_jit_write_protect_np(0)) from allocate_readexec() into with_size() right after mmap. Write mode is only enabled when new MAP_JIT pages are actually allocated, not on every bump-pointer allocation request. 3. MAP_JIT only for executable pages: Add executable bool parameter to with_size() and Memory. MAP_JIT flag is only passed for code pages (executable=true), not for readonly or writable data pages. Also adds Linux x86_64 mmap hint to keep JIT code within 2GB of runtime symbols for PC-relative relocations.
Author
|
Hi @bjorn3 , just want to remind you of this PR |
Move W^X toggles from broad allocation/finalization scope into compiled_blob.rs, tightly wrapping the actual writes to executable memory: copy_nonoverlapping in new(), write_bytes in new_zeroed(), and the relocation loop in perform_relocations(). Only toggles when memory kind is Executable, so data object writes are unaffected. Also merges upstream main for JITMemoryKind API, CompiledBlob::new() constructor, and ARM64 veneer support.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
runtime symbols for PC-relative relocations
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)