Emulating aarch64 in software using JIT compilation and Rust
by Manos Pitsidianakis on 2025-08-25
I was able to write a simple just-in-time compiled emulator for the aarch64 ISA (Arm A-profile A64 Instruction Set Architecture). The Armv8-A/Armv9-A specs are massive in size, so the initial scope is for basic functionality and almost no optional architectural features such as SIMD.
I wrote the emulator as an exercise in understanding how QEMU’s TCG (Tiny Code Generator) software emulation works in principle. I did not follow the C code implementation, but rather implemented the same concepts from scratch in Rust, leveraging other libraries for the heavy lifting (disassembly and JIT compilation).
In this article we’ll go through what is needed to go from a virtual machine’s instructions to native code execution.
Repository: https://github.com/epilys/simulans
$ cargo run -- release -- \ cargo runrelease -- memory 4GiB \ memory 4GiB \ -- generate - fdt \ generatefdt \ -- entry - point - address 0x40080000 \ entrypointaddress . bin test_kernelbin in 0 . 06s Finished `release` profile [optimized] target(s)06s / release / simulans -- memory 4GiB -- generate - fdt -- entry - point - address 0x40080000 test_kernel . bin` Running `targetreleasesimulansmemory 4GiBgeneratefdtentrypointaddresstest_kernelbin` world! Hello 6 devicetree nodes! Parseddevicetree /: Some ( Some ( "linux,dummy-virt" )) )) : None chosen @ 0 : None memory 0x0000000000000000 , length Some ( 4294967296 ) length : None cpus @ 0 : Some ( Some ( "arm,arm-v8" )) cpu)) 0x0000000000000000 , length None length : Some ( Some ( "arm,psci-0.2" )) psci)) . Halting the machine $
Translating an ISA to native code
The emulation is performed in these steps:
Disassembling aarch64 binary code using binja Translate each instruction with Cranelift’s JIT backend
... continue reading