Run Linux ELF binaries directly from the macOS shell -- no Docker, no
full VM image, no daemon. elfuse is a process-scoped Linux user-space
runtime: each guest runs inside a lightweight Hypervisor.framework VM
owned by the elfuse process itself, and Linux syscalls are translated
to macOS behavior in host-side handlers rather than served by a real
Linux kernel.
Native aarch64-linux executes directly on the CPU. x86_64-linux
executes through Apple's embedded Rosetta translator hosted inside the
same VM; the architecture is auto-detected from the ELF header. Both
static and dynamically linked guests are supported, with the dynamic
linker resolved against an external sysroot via --sysroot.
- Single native macOS binary (~560 KiB signed), no daemon and no disk image
- Millisecond-scale VM startup; per-syscall overhead is microseconds
- Native Apple Silicon execution through Hypervisor.framework
- Static and dynamically linked
aarch64-linuxELF binaries - Static and dynamically linked
x86_64-linuxELF binaries via Apple Rosetta (auto-detected from the ELF header, opt out with--no-rosetta) - Linux-style processes, threads (1:1 with HVF vCPUs, up to 64), signals, timers, futexes (incl. PI ops), and polling
- Guest reads and writes the macOS filesystem directly; no overlay or volume mount layer
- Synthetic
/procand selected/devemulation for user-space probes - Guest-internal FUSE:
/dev/fuseandmount("fuse")work without macFUSE / FUSE-T / FSKit - Built-in GDB Remote Serial Protocol stub usable from
gdborlldb - Self-contained test matrix that cross-checks elfuse against QEMU and exercises a separate Rosetta acceptance suite
elfuse is intentionally narrow. It runs single Linux binaries (and
their fork/exec children) with minimal overhead; it does not host a
Linux kernel, namespaces, cgroups, or kernel modules. For workloads
that need full kernel features, container orchestration, or systemd,
prefer a full VM tool (Lima, UTM, OrbStack) or Docker Desktop. For
single-binary tooling, language runtimes, test harnesses, and
debugger-driven workflows, elfuse removes the disk-image and
boot-time overhead those tools impose.
- macOS on Apple Silicon
- macOS 13 or newer
- Xcode Command Line Tools,
clang,codesign, and GNUmake - GNU
objcopyfrom Homebrewbinutils, orllvm-objcopy - Hypervisor entitlement:
com.apple.security.hypervisor
For guest test binaries, the project also expects an AArch64 Linux cross
toolchain. The default paths in mk/toolchain.mk target the toolchain layout
used by the repository test harness, but CROSS_COMPILE and
BAREMETAL_CROSS are overridable.
To run make check, install the Homebrew AArch64 embedded toolchain first:
brew install --cask gcc-aarch64-embeddedgit clone https://github.com/sysprog21/elfuse
cd elfuse
make elfuse
make test-busybox
build/elfuse build/busyboxReplace build/busybox with an aarch64-linux or x86_64-linux executable.
The guest architecture is auto-detected from the ELF header.
For dynamically linked guests:
build/elfuse --sysroot /path/to/sysroot ./path/to/programFor x86_64-linux guests, Rosetta is on by default. To disable:
build/elfuse --no-rosetta ./path/to/aarch64-only-binaryFor early debugging:
build/elfuse --gdb 1234 --gdb-stop-on-entry ./path/to/program--gdb is rejected for x86_64 guests because the stub serves the
aarch64 view Rosetta produces, not the original x86_64 architectural
state.
The build signs build/elfuse before use. Override the signing identity with
SIGN_IDENTITY="Developer ID ..." when needed.
- docs/usage.md: command-line options, x86_64 via
Rosetta, dynamic linking via
--sysroot, and attachinggdb/lldbto the built-in stub. - docs/testing.md: build prerequisites, the
make checkflow, the QEMU and Rosetta cross-check matrices, and fixture handling. - docs/internals.md: canonical technical reference -- runtime lifecycle, HVF constraints, EL1 shim and HVC protocol, page-table splitting, syscall translation tables, threads / futex, fork / clone IPC, signals, ptrace, and the GDB stub.
Most common targets:
make elfuse # build and codesign build/elfuse
make check # quick unit suite + BusyBox applet smoke
make test-gdbstub # debugger integration
make test-matrix # cross-check elfuse against QEMU on the same corpus
make lint # clang-tidymake check is the recommended pre-commit gate. make test-matrix is the
recommended gate for changes touching procfs, dynamic linking, networking,
or process semantics. make test-rosetta-all covers the x86_64 acceptance
suites in isolation. See docs/testing.md for the full
target list, fixture flow, and validation-by-change-type guidance.
elfuse runs single Linux user-space processes (and their fork /
exec children). It is not a Linux kernel.
That framing shapes both what it does and what it explicitly will not
do.
- Linux kernel features that have no user-space-syscall analog:
namespaces, cgroups, kernel modules, eBPF,
io_uring, KVM, perf events. - Intel Macs. Apple Silicon only (M1 and later).
- Hosting a VM from inside a guest. The guest cannot use HVF or KVM.
- One guest process tree per
elfusehost process. HVF allows one VM per host process; Linux-styleforkis implemented byposix_spawn-ing a freshelfusehost process and transferring state (see docs/internals.md). - Up to 64 concurrent guest threads per VM (
MAX_THREADS = 64). - Around 213 syscalls implemented; anything outside
src/syscall/dispatch.tblreturns-ENOSYSrather than silently succeeding. FUTEX_LOCK_PIand friends behave as plain mutex acquire / release; true priority-inheritance scheduling is not modeled.sched_setaffinityis honored as a no-op (returns the all-CPUs mask); the host scheduler picks the actual CPU./proc,/dev, and mount data are synthetic compatibility views, not host pass-throughs.
Apache License 2.0. See LICENSE.
Copyright 2026 elfuse contributors
Copyright 2025 Moritz Angermann, zw3rk pte. ltd.