Tip or TLDR - I built a tiny, zero dependency armv7 userspace emulator in Rust I wrote a minimal viable armv7 emulator in 1.3k lines of Rust without any dependencies. It parses and validates a 32-bit arm binary, maps its segments, decodes a subset of arm instructions, translates guest and host memory interactions and forwards arm Linux syscalls into x86-64 System V syscalls. It can run a armv7 hello world binary and does so in 1.9ms (0.015ms for raw emulation without setup), while qemu takes 12.3ms (stinkarm is thus ~100-1000x slower than native armv7 execution).
After reading about the process the Linux kernel performs to execute binaries, I thought: I want to write an armv7 emulator - stinkarm . Mostly to understand the ELF format, the encoding of arm 32bit instructions, the execution of arm assembly and how it all fits together (this will help me with the JIT for my programming language I am currently designing). To fully understand everything: no dependencies. And of course Rust, since I already have enough C projects going on.
So I wrote the smallest binary I could think of:
ARMASM
1 .global _start @ declare _start as a global 2 _start: @ start is the defacto entry point 3 mov r0 , # 161 @ first and only argument to the exit syscall 4 mov r7 , # 1 @ syscall number 1 (exit) 5 svc # 0 @ trapping into the kernel (thats US, since we are translating)
To execute this arm assembly on my x86 system, I need to:
Parse the ELF, validate it is armv7 and statically executable (I don’t want to write a dynamic dependency resolver and loader) Map the segments defined in ELF into the host memory, forward memory access Decode armv7 instructions and convert them into a nice Rust enum Emulate the CPU, its state and registers Execute the instructions and apply their effects to the CPU state Translate and forward syscalls
Sounds easy? It is!
Open below if you want to see me write a build script and a nix flake: Minimalist arm setup and smallest possible arm binary
Before I start parsing ELF I’ll need a binary to emulate, so lets create a build script called bld_exmpl (so I can write a lot less) and nix flake, so the asm is converted into armv7 machine code in a armv7 binary on my non armv7 system :^)
... continue reading