A Rust-based binary translator that runs x86_64 (Intel) macOS binaries on Apple Silicon (ARM64) Macs. Rustmute uses a Rosetta-style syscall-boundary architecture: user-space x86 code is translated to ARM64 via JIT or AOT, while kernel syscalls and Mach traps are bridged natively.
| Host OS | macOS 26 (Tahoe) or later |
| CPU | Apple Silicon (M1 / M2 / M3 / M4) |
| Rust | 1.78 or later (rustup update stable) |
git clone https://github.com/suma/rustmute
cd macbin
cargo build --release -p rustmute-cli
# → target/release/rustmuteA debug build is fine for development:
cargo build -p rustmute-cli
# → target/debug/rustmuterustmute run-with-dyld [--timings] <x86_64-binary> [args...]JIT-executes the binary using the real x86_64 dyld and dyld shared cache, providing a full runtime environment including libSystem. This is the primary mode for running x86_64 system binaries on Apple Silicon.
rustmute run-with-dyld /bin/ls -l /usr/lib
rustmute run-with-dyld /bin/cp src.txt dst.txt
rustmute run-with-dyld /usr/bin/file /usr/bin/archStartup sequence:
- Load the main binary +
/usr/lib/dyld(x86_64 slice) mmapthe dyld shared cache (/var/db/dyld/dyld_shared_cache_x86_64h)- Build the kernel argument stack (including
ptr_munge/apple[]entries) - Map the x86_64 commpage at
0x7fff_ffe0_0000 - Install native DNS intercepts (
getaddrinfo/freeaddrinfo) - Set up TLS at
0x7fff_ec00_0000 - Start the JIT engine and enter the dispatch loop
Pass --timings to print an execution breakdown to stderr on exit. Hot-path overhead is zero — all instrumentation is gated behind a single relaxed AtomicBool load.
rustmute run-with-dyld --timings /bin/ls -l /usr 2>&1 | tail -35Example output:
rustmute timings [run-with-dyld]:
load : 3.2 ms
stack-setup : 0.1 ms
cache-map : 142.5 ms
lift : 18.3 ms (2043 calls)
codegen : 45.1 ms (2043 calls)
execute : 398.7 ms (1418174 calls)
syscall : 11.2 ms (311 calls)
───────────────────────────────────
measured : 619.1 ms
total wall : 782.4 ms
block cache:
hot hit : 1401430 (98.82%)
slow hit : 890 ( 0.06%)
cold miss : 2043 ( 0.14%)
smc : 0 ( 0.00%)
─ total : 1404363
dispatch exits:
jump : 1403484 (98.96%) ← chainable
syscall : 311 ( 0.02%)
ret : 1 ( 0.00%)
trap : 0 ( 0.00%)
─ total : 1403796 (== execute ✓ )
chaining estimate:
loop overhead : 0.8 ns/iter
potential save : 1121.3 ms (= jump × overhead)
Reading the output:
| Section | Description |
|---|---|
| Phase times | load / stack-setup / cache-map are startup costs. lift / codegen are block translation costs. execute / syscall are steady-state loop costs. |
| block cache | hot hit: fastest cache path (inline lookup). slow hit: slower cache path. cold miss: first translation of a block. smc: recompile triggered by self-modifying code. |
| dispatch exits | Exit type after each block execution. jump (simple branch to the next block) is a candidate for block chaining, which would eliminate the round-trip back to the Rust dispatch loop. |
| chaining estimate | Theoretical savings if block chaining were implemented. A large potential save indicates high expected benefit. |
| Crate | Role |
|---|---|
rustmute-core |
Shared types, GuestCpu state, guest memory abstraction (GuestMemory), error types, phase timers (timings) |
rustmute-loader |
Mach-O parsing, dyld emulation (dependency resolution, relocations, symbol binding) |
rustmute-cache |
Parser and mapper for the x86_64 dyld shared cache (Phase 7.6) |
rustmute-ir |
Rustmute IR definitions, IR builder, optimization passes |
rustmute-decoder |
x86_64 decoding via iced-x86, x86 → IR lifter |
rustmute-arm64 |
ARM64 encoder (dynasm-based), IR → ARM64 lowering |
rustmute-interp |
IR interpreter — correctness reference backend for testing |
rustmute-jit |
JIT engine, block cache, dispatch loop |
rustmute-aot |
AOT compiler, translated Mach-O / object file output |
rustmute-runtime |
syscall / Mach trap translation, signal handling, TLS, guest↔host bridge |
rustmute-cli |
rustmute run, rustmute run-with-dyld, rustmute compile commands |
Crate dependency order:
cli → {jit, aot} → {arm64, interp, decoder, ir, runtime} → loader → {cache, core}
Phase 7.6 M3 is complete. Real binaries including /bin/ls -l, /bin/cp, /usr/bin/file, and /usr/bin/seq run correctly under run-with-dyld.
See design-docs/ROADMAP.md for the implementation roadmap and design-docs/DESIGN.md for architecture details.
Licensed under either of
at your option.