An experimental Debian/Linux compatibility runtime for Android ARM64 without root or ptrace. Milestone 0 proves direct syscall rewriting for a deliberately small class of static ELF programs. The next experiment adds a separately compiled C file inspector. It does not yet run arbitrary Debian packages, BusyBox, Python, Go, or an entire Debian userland.
The loader replaces each supported guest svc #0 with a direct branch to an
in-process gateway. The gateway implements a small syscall ABI and returns to
the original next instruction. There is no ptrace supervisor, signal-based
syscall trap, seccomp notification broker, or CPU emulator in the runtime.
QEMU is used only to test ARM64 code on x86_64 development machines.
- Cross-compiled with GCC 14 and executed with QEMU user mode 10 on Linux x86_64.
- Milestone 0's 23 tests passed on the user's Poco F3 according to their report: Android 17 / SDK 37, kernel 4.19, 4 KiB pages, Clang 21.1.8 in Termux.
- The current suite has 43 tests, adding direct-vs-runtime execution of C
programs, static PIE (
ET_DYN) relocatable binaries, stripped binaries via linear SVC scanning, full Linux auxiliary vector (auxv), thread register isolation (tpidr_el0), automaticPT_INTERPdynamic linker loading for direct binary execution, file metadata, seeking, directory traversal (getdents64), process credentials, filesystem mutation, micro-libc dynamic memory allocation (mmap,munmap,mprotect,brk), system identification (uname), and Debian/glibc direct utility execution (/bin/true,/bin/uname,/bin/echo,/bin/bash). All 43 tests pass natively on Android ARM64 (Poco F3, Android 17 / SDK 37, Clang 21.1.8 in Termux). - See validation evidence and limits. Other devices and 16 KiB pages remain unverified. Remote CI results have not been verified here.
On Debian/Ubuntu x86_64, install gcc-aarch64-linux-gnu,
libc6-dev-arm64-cross, qemu-user, make, and python3, then:
make
make testOn the Debian 13 cloud development machine, a rootless bootstrap downloads packages using Debian's signed APT metadata and extracts them outside the repo:
sh tools/bootstrap-cloud.sh
. /workspace/.linuxnative-tools/activate.sh
make testThe default tool directory is .linuxnative-tools beside this checkout; set
LN_TOOLS_DIR to override it. The bootstrap reuses its verified installation.
It follows Debian trixie package updates; it is not a bit-reproducible toolchain lock.
On native Linux ARM64:
make CC=gcc RUNNER= testKeep the checkout inside Termux's private $HOME, not /sdcard. The runtime
must be built with Termux's compiler so it uses Android's Bionic libraries.
The cloud's static glibc runtime is not the recommended Android executable.
pkg install git clang make python
git clone --branch main https://github.com/otaviomorais/LinuxNative.git ~/LinuxNative
cd ~/LinuxNative
sh tools/test-termux.shThis runs the same tests directly on the CPU, without QEMU, root, or ptrace.
It records limited device metadata, build output, and test results in
build/device-info.txt, build/device-build.log, and build/device-test.log.
It does not collect serial numbers, accounts, or environment-variable values.
See device setup instructions, including what to do
if that checkout directory already exists.
mkdir -p build/demo-root/etc
printf 'Hello from the mapped Debian directory!\n' > build/demo-root/etc/linuxnative-probe.txt
qemu-aarch64 build/lnative --verbose --root build/demo-root build/probeOn a native ARM64 machine, omit qemu-aarch64. The test program first checks
register preservation and error behavior, then reads /etc/linuxnative-probe.txt
from the selected root and writes its contents to stdout. The original ELF is
never modified on disk.
The same file inspector can run directly or through LinuxNative. In the cloud:
qemu-aarch64 build/file-scan "$PWD/build/demo-root/etc/linuxnative-probe.txt"
qemu-aarch64 build/lnative --root build/demo-root build/file-scan /etc/linuxnative-probe.txtBoth should print the same JSON metadata and content fingerprint. On the phone,
omit qemu-aarch64 after building with tools/test-termux.sh. The C executable
has no dependency on the runtime but uses a custom linker layout and no libc;
this is a step toward existing Debian binaries, not general support for them.
A standalone application compiled with examples/micro-libc.h runs directly on Linux ARM64
and through LinuxNative, demonstrating dynamic memory allocation (mini_malloc/mini_free via mmap/munmap),
process credentials, and directory listing (getdents64):
build/mini-ls build
build/lnative --root "$PWD" build/mini-ls /build- Trusted, little-endian AArch64
ET_EXECor PIE/DYN (ET_DYN) executables, including the glibc dynamic linkerld-linux-aarch64.so.1. - When ELF symbol tables and AAPCS64
$x/$dmapping symbols are present, code ranges are scanned precisely to protect data literals. When.symtabis stripped, a 32-bit linear instruction scan of executable sections is employed. - Non-overlapping load-segment pages, no writable executable segments, and an image span no larger than 32 MiB. Fixed guest addresses must be available.
- Single thread guest execution; host Bionic TLS (
tpidr_el0) andx18are preserved and swapped across the gateway boundary to isolate foreign libc (e.g. glibc) TLS state. - Valid guest pointers and 16-byte-aligned guest stack populated with standard Linux
auxiliary vector (
auxv:AT_PHDR,AT_ENTRY,AT_BASE,AT_PAGESZ,AT_RANDOM, etc.). - Syscalls:
fcntl,ioctl,mkdirat,unlinkat,openat,faccessat,read,write,readv,writev,close,fstat,newfstatat,readlinkat,lseek,getdents64,uname,getpid,getppid,getuid,geteuid,getgid,getegid,gettid,set_tid_address,set_robust_list,nanosleep,clock_gettime,rt_sigaction,rt_sigprocmask,brk,mmap,munmap,mprotect,madvise,prlimit64,getrandom,exit,exit_group. Others return raw-ENOSYS. - CLI:
lnative [--verbose] --root DIRECTORY ELF_BINARY [ARG...]. Dynamically linked binaries withPT_INTERPautomatically locate and load the dynamic linker from the guest rootfs, initializingauxv(AT_BASE,AT_ENTRY,AT_PHDR). Arguments are copied to a 16-byte-aligned guest stack with a bounded budget. - Unsupported open modes and relative paths return
-EOPNOTSUPP;..components return-EACCES. No claim of a complete Linux filesystem or process ABI.
This is not a sandbox. Guest code shares memory and privileges with the runtime. It can access inherited descriptors and host memory; symlinks can lead outside the selected root. Mapping symbols are trusted metadata, not a security boundary. Never run untrusted programs with this prototype.
The runtime cannot grant privileges denied by Android's kernel, SELinux, or seccomp. It uses RW-then-RX memory permissions, never RWX; executable mappings still require validation in the actual Android app context.
- Validate the expanded C-program suite on the Poco F3, then other Android configurations including devices with 16 KiB pages.
- Extend the syscall/process ABI toward a small independently built libc-linked application, guided by differential tests and explicit support limits.
- Add a loader and rewriting strategy for ordinary dynamic/stripped binaries.
- Address threads, signals, exec, and code generated at runtime.
- Measure correctness and performance against native Linux and PRoot workloads.
No novelty or near-native performance claim has been established. This source is a new prototype, not a copy of deb-native. See architecture.