Normal use:
- Termux with glibc installed (
pkg install glibc-repo; pkg install glibc-runner) - Original Bun installed with official script at
~/.bun/bin/bun(orbunoif already renamed) - Clang compiler (
pkg install clang)
For tests:
file(pkg install file)nodejs(pkg install nodejs)
makeThis will compile the wrapper and the LD_PRELOAD shim.
make installInstalls to ~/.bun/ by default (defined by BUN_INSTALL).
To remove bun-termux and restore the original Bun binary:
make uninstallThis will:
- Restore the original
bunbinary (renamed back frombuno) - Remove
bun-shim.sofrom the lib directory - Clean up empty directories
Use as normal Bun. (e.g. bun script.js)
For bun package manager, some native modules might need BUN_OPTIONS="--os=android".
For bundled binaries, see Bundled Binaries.
Also see other_projects/README.md for guides and examples of Bun-Termux usage with other projects (e.g. OpenCode).
- Wrapper uses userland exec to replace itself with glibc's dynamic linker (
ld-linux-aarch64.so.1) without callingexecve()- since the kernel never updates/proc/self/exe, it still points to the wrapper, sobun build --compileembeds the wrapper (not bun itself, and not the linker like when using grun), making compiled binaries work out of the box. - Wrapper sets
BUN_FAKE_ROOTenv var if it's unset. - Wrapper backs up original LD_PRELOAD and LD_LIBRARY_PATH to BUN_TERMUX_ORIG_LD_PRELOAD and BUN_TERMUX_ORIG_LD_LIBRARY_PATH.
--library-pathis passed to the dynamic linker to make sure glibc libraries are found.- Shim preloads via the dynamic linker's
--preloadoption and interceptsopenat()on/,/data,/data/data,/storage,/storage/emulated,/storage/emulated/0(including trailing slashes). WhenBUN_FAKE_ROOTis set, these paths are redirected to that directory to avoid permission issues on Android. - If
BUN_FAKE_ROOTis not set, the shim falls back toTMPDIR(or/data/data/com.termux/files/usr/tmp). - Shim restores original LD_PRELOAD and LD_LIBRARY_PATH from BUN_TERMUX_ORIG_* env vars, ensuring that child processes inherit them.
- Shim intercepts
execve()for shebangs beginning with/usr/bin/,/bin/,/usr/sbin/,/sbin/, and redirects them to usePREFIX. - Shim intercepts reads to
/proc/statand generates minimal CPU statistics stub, allowingos.cpus()to work in bun. - Shim stubs
linkat()and returnsEXDEV(error.NotSameFileSystem), which forces bun to fall back to copyfile when installing packages.
| Variable | Default | Scope | Description |
|---|---|---|---|
BUN_INSTALL |
~/.bun |
Both | Installation prefix. Controls where the wrapper looks for buno, bun-shim.so, and the fake-root directory |
BUN_BINARY_PATH |
$BUN_INSTALL/bin/buno if BUN_INSTALL is set, otherwise <wrapper_dir>/buno |
Runtime | Override the path to the original bun binary |
BUN_FAKE_ROOT |
$(BUN_INSTALL)/tmp/fake-root |
Runtime | Set by the wrapper only if not already present; used by the shim as the redirect target for /, /data, /data/data |
BUN_OPTIONS |
Unset | Runtime | Pass options to Bun (e.g. --os=android for native modules, --verbose for debugging install scripts) |
PREFIX |
/data/data/com.termux/files/usr |
Runtime | Termux installation prefix; used by shim for shebang path translation |
TMPDIR |
/data/data/com.termux/files/usr/tmp |
Runtime | Temporary directory for shim (fallback if BUN_FAKE_ROOT is unset) |
GLIBC_ROOT |
/data/data/com.termux/files/usr/glibc |
Build | Build-time override for glibc installation path (Makefile only) |
GLIBC_LD_SO |
/data/data/com.termux/files/usr/glibc/lib/ld-linux-aarch64.so.1 |
Both | Path to glibc's dynamic linker |
GLIBC_LIB |
/data/data/com.termux/files/usr/glibc/lib |
Both | Directory containing glibc shared libraries |
Scope: Runtime = read by wrapper/shim at runtime, Build = used by Makefile only, Both = used by both Makefile and runtime
- aarch64 only, because of hardcoded assembly and syscalls. Maybe I'll add support for other architectures in the future.
- Binaries built with
bun build --compilehave wrapper embedded, requiringbuno,bun-shim.soand glibc to be present on the system where they run. - Bun install/add/update/remove commands might require
BUN_OPTIONS="--os=android"env var if they install native modules. - If bun somehow fails to walk the current path due to permission error, it'll fail to get the current env vars too. I'll have to investigate why.
- When using
bun install, some module install scripts might fail withoutBUN_OPTIONS="--verbose".
Binaries produced with bun build --compile already have the wrapper embedded, but if you need to run a binary that's been bundled elsewhere, you can use replace_runtime.py script to replace default Bun runtime with the wrapper.
python helper_scripts/replace_runtime.py path/to/your/bundled_binary
This will back up the original binary with a .bak extension and patch it to use the wrapper.
See troubleshooting.md for common issues and solutions.