Run Bun natively on Android/Termux with full FFI, runtime C compilation via TinyCC,
dlopenfor native libraries, and working opentui TUI rendering. Bionic-native. No proot. No glibc-runner. No userland-exec.
Keywords: bun android Β· bun termux Β· bun aarch64 Β· bun arm64 android Β· bun ffi termux Β· tinycc android Β· opentui termux Β· javascript runtime android Β· typescript termux Β· bun native android Β· bionic bun
Bun 1.3.14 is the first official Android (Bionic-linked PIE) build, but several Android/SELinux/Bionic quirks break real-world usage on Termux: the directory resolver hits EACCES on openat(O_DIRECTORY), bun install fails on linkat/symlinkat, os.cpus() returns [] because /proc/stat is restricted, DNS breaks without /etc/resolv.conf, bun --bun fails writing to /tmp, bun build --compile outputs broken PIE binaries, TinyCC is disabled, and Scudo's heap pointer tagging crashes free() on FFI pointers from libraries like opentui.
This fork fixes every one of those.
- Full FFI support β
dlopen,cc(),JSCallbackall work - TinyCC enabled on Android β compile C code at runtime
- opentui compatible β render TUI apps with yoga layout
- Heap tagging fix β no more
free(tagged_ptr) SIGABRTcrashes on FFI - ARM64 long-call veneers β TinyCC generates proper stubs for out-of-range BL
- SELinux-safe LD_PRELOAD shim β Bionic-native, zero glibc/proot overhead
bun build --compilefix β correct PIE offsets for Bionic's linker64bunxfix β resolver tolerates SELinuxEACCESon ancestor walk- DNS / os.cpus() / shebang / /tmp β all working via targeted intercepts
curl -fsSL https://raw.githubusercontent.com/bd-loser/bun-termux/main/scripts/install.sh | bashThat's it. Installs bun and bunx into $PREFIX/bin on Termux (aarch64).
bun --version # 1.3.14
bun -e "console.log(require('os').cpus().length)" # real CPU count, not 0
bunx cowsay hello # resolver walk works on Android SELinuxEvery fix here targets a concrete failure mode of stock Bun 1.3.14 on Android/Termux.
Bun's directory resolver uses raw syscalls that fail with EACCES on Android SELinux (untrusted_app_27+ blocks openat(O_DIRECTORY) on / and /data). Patched to continue walking on AccessDenied.
Fixes PIE/ASLR issues with Bionic's linker64. Uses the last writable PT_LOAD segment and writes offset (not vaddr) to BUN_COMPILED.
- Enable TinyCC for Android (disabled upstream)
CONFIG_SELINUX=1β usemmap(PROT_EXEC)via memfd instead of mprotecttccrun.coverlay βmemfd_createinstead of/tmp(Android has no/tmp)arm64-link.coverlay β generate veneer stubs for out-of-range BL calls
Adds Android system library and include paths (/system/lib64, $PREFIX/include, NDK sysroot) to TinyCC so cc() can find libc.so.
Calls mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_NONE) at the start of main() to disable Scudo's heap pointer tagging. Without this, free(tagged_ptr) from an FFI-allocated pointer crashes with SIGABRT.
Bionic-native (no glibc, no userland-exec). Intercepts SELinux-restricted syscalls:
openatwithO_DIRECTORYon/and/dataβ dup a safe fdlinkat/symlinkat/renameatβ fall back tocopyonEACCES/EXDEVfopenfor/etc/resolv.conf/nsswitch.conf/hostsβ$PREFIX/etc/mkdir/symlinkfor/tmpβ$TMPDIRexecveshebang translation (/usr/bin/env nodeβ$PREFIX/bin/env)/proc/statsynthesis foros.cpus()
| Item | Status |
|---|---|
| Architecture | aarch64 (arm64) |
| Android | 10+ (API 29+, SELinux untrusted_app_27+) |
| Termux | Termux app + Termux:API optional |
| Bun version | 1.3.14 |
| Node built-ins | os, fs, net, dns, child_process β verified working |
bun install |
Works (linkat fallback) |
bun run |
Works |
bun build --compile |
Works (PIE fix) |
bunx <pkg> |
Works (resolver fix) |
bun:ffi dlopen |
Works |
bun:ffi cc() (TinyCC) |
Works |
bun:ffi JSCallback |
Works |
opentui (@xincli/opentui-core) |
Works |
mkdir ~/opentui-test && cd ~/opentui-test
echo '{"dependencies":{"@xincli/opentui-core":"0.4.7","@xincli/opentui-react":"0.4.7"}}' > package.json
bun install
cat > app.jsx << 'EOF'
import { createCliRenderer } from "@xincli/opentui-core"
import { createRoot } from "@xincli/opentui-react"
const renderer = await createCliRenderer({ exitOnCtrlC: false })
const root = createRoot(renderer)
root.render(<box border><text>Hello opentui!</text></box>)
await new Promise(r => setTimeout(r, 3000))
renderer.destroy()
EOF
bun run app.jsxRequires NDK r27c, Zig, Rust, CMake.
git clone https://github.com/bd-loser/bun-termux.git
cd bun-termux
make buildCI builds automatically on push to main (see .github/workflows/).
See docs/BUILD.md for detailed build instructions.
- docs/SOLUTION.md β Root cause analysis for the FFI/heap-tagging fix
- docs/BUILD.md β Building from source
- docs/armv7-migration.md β Legacy ARMv7 notes
- Upstream fork ancestry: Hope2333/bun-termux (MIT) β original pure-android packaging scaffolding (Makefile, deb/pacman targets, docs skeleton). This fork extends it with source-level patches, LD_PRELOAD shim, TinyCC overlays, launcher scripts, and full-FFI support.
- Shim patterns: Happ1ness-dev/bun-termux (MIT) β
src/libbun-android-fix.cadapts patterns from theirshim.c:safe_dir_fdEACCES-dup,/proc/statmemfd synthesis,fopen/etcredirect, execve shebang translation,/tmpβ$TMPDIR,linkatEXDEV fallback,__OPEN_NEEDS_MODE. Their fork uses userland-exec + glibc; this fork ports the patterns to Bionic-native. - TinyCC β LGPL-2.1 upstream (tinycc); overlay modifications for Android SELinux + ARM64 veneers are contributed under LGPL.
- Bun β MIT Β© Oven, Inc. (oven-sh/bun)
Full attribution and scope: LICENSE.
MIT β see LICENSE for full text and third-party attribution (Hope2333, Happ1ness-dev, TinyCC/LGPL, Bun/MIT).
Suggested GitHub repo topics: bun termux android aarch64 arm64 javascript-runtime typescript ffi tinycc opentui bionic selinux bunx nodejs-alternative dlopen