Skip to content

[Feature]: Proroot – A Zero-Overhead Replacement for Proot #5245

Description

@mablue

Feature description

What Users Actually Experience

Let me start with a concrete example.

You have a Node.js project with about 2,800 JavaScript files – a typical modern web application. On a normal Linux machine, loading these files takes milliseconds. On Termux with proot-distro, the same operation takes 8.5 seconds just to run --version.

Why? Because proot intercepts every single system call via ptrace(). Every time your program opens a file, reads a directory, or checks a stat – each of those is a system call. And each one requires two context switches: from your program to the kernel, from the kernel to proot's handler, back to the kernel, and finally back to your program.

For 2,800 files, that's thousands of round trips. The fix in that case was to bundle everything into a single file – 2,800 files became 1, and the cold start dropped from 8.5 seconds to 3.6 seconds. The entire gateway went from 20 seconds to 6.4 seconds.

The lesson is clear: when your syscall overhead is non-trivial, the number of files matters more than their total size.


The Root Cause: ptrace() Overhead

Proot works by using ptrace() – a debugging interface – to intercept every system call made by a program running inside it. This is the same mechanism debuggers use to pause and inspect programs.

For a simple bash shell, the overhead is barely noticeable. But for anything heavier – Node.js, Python with heavy I/O, Chromium, or any application that makes thousands of system calls – the overhead becomes crippling.

In UNIXbench, proot-distro scores 44% lower than docker/chroot. CPU performance drops by 7-10% compared to native, while GPU performance drops by over 7 times.


The Solution: Proroot

Proroot is a drop-in replacement for proot that eliminates the ptrace() bottleneck entirely.

Instead of using ptrace() to intercept syscalls from outside the process, proroot handles everything inside the process itself using two complementary techniques:

  1. LD_PRELOAD interception – Intercepts calls to glibc functions like openat, stat, execve, and dlopen at the PLT (Procedure Linkage Table) level
  2. Binary patching – Scans the ELF binary at load time and patches raw syscall instructions (svc #0) to call through a trampoline, catching even glibc-internal syscalls that bypass LD_PRELOAD

The result: zero context switches per syscall. Path translation and environment isolation still work, but without the performance penalty.

Here's the architectural difference:

proot:
App ──ptrace──▶ Kernel ──ptrace──▶ Handler ──ptrace──▶ Kernel ──▶ Done
↑ 2 context switches per syscall

proroot:
App ──LD_PRELOAD──▶ translate() ──SVC──▶ Kernel ──▶ Done
↑ 0 context switches, in-process path translation

The Numbers That Matter

CPU Performance:

On Geekbench 6 multi-core, proroot achieves the exact same scores as native Android peak performance. In fact, one tester noted: "CPU/GPU/RAM all run at native performance. From a performance perspective, there are literally no downsides".

GPU Performance:

This is where the improvement is most dramatic. Running the Vulkan benchmark vkmark:

Environment vkmark Score
proot-distro 1,442
Proroot 10,746

That's a 7.45x improvement.

In another test, the proroot-based runtime scored 11,283 on vkmark, virtually identical to the 11,133 score of Termux's native Bionic Mesa Turnip.

Real-World Impact:

A developer working on an Android app that bundles Linux to run an AI gateway saw end-to-end performance improve from 20 seconds to 6.4 seconds – a 3x improvement – simply by reducing the number of syscalls. Now imagine that improvement applied to everything.


Beyond Performance: A Complete Linux Runtime

Proroot's ambition extends beyond being a faster proot. It's designed as a standalone glibc Linux runtime for Android.

The architecture looks like this:

[proroot]
Android host environment
├─ Android APK
│  ├─ libproroot.so (launcher - NDK/bionic)
│  ├─ libproroot-runtime.so (LD_PRELOAD runtime - glibc)
│  ├─ libproroot-bridge.so (child exec trampoline - NDK static)
│  ├─ libproroot-linker.so (clean-room glibc-compatible dynamic linker)
│  └─ files/rootfs/
│     └─ glibc Linux rootfs
└─ Existing rootfs run by proroot in an X server embedded in the APK

This means proroot can be bundled directly into an Android APK – a complete Linux system that runs with native performance. The runtime can access all Android APIs through a bridge, including hardware acceleration for video encoding/decoding via MediaCodec.

Key advantages:

· Full native performance – CPU, GPU, and RAM all run at native speed
· Universal GPU acceleration – Unlike the current Termux + proot setup, which only supports Adreno GPUs via Mesa Turnip/Freedreno KGSL, proroot can theoretically support all SoCs – Mali, Xclipse, PowerVR, and others
· Full Android API access – Through a runtime bridge
· No root required – Just like proot


Current Status

Proroot is under active development by a Korean developer (coderredlab):

· Architecture: arm64-only for now
· Distribution: Currently distributed as binaries; source code will be released after more testing and stabilization
· CLI Compatibility: Usage is kept as close to proot as possible – flags like -r, -w, and --link2symlink work the same
· Tested Workloads: Node.js 24/npm, Python 3.12, Git 2.43, curl, jq, OpenSSL, Chromium headless_shell 131 (Playwright), XFCE 4 + TigerVNC, OpenClaw, Codex CLI, esbuild
· Android Version: Android 8.0+ (API 26)


How to Integrate Proroot into Termux: A Step-by-Step Guide

Step 1: Obtain the Proroot Binaries

Download the four .so files from the proroot releases:

libproroot.so
libproroot-runtime.so
libproroot-bridge.so
libproroot-linker.so

Place them in /data/data/com.termux/files/usr/lib/proroot/:

mkdir -p $PREFIX/lib/proroot
# Copy the four .so files to $PREFIX/lib/proroot/

Step 2: Obtain a glibc Rootfs

Proroot requires a standard glibc Linux rootfs. You can use any distribution's arm64 rootfs:

# Example: Download Ubuntu arm64 rootfs
cd $HOME
wget https://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04-base-arm64.tar.gz
mkdir -p $HOME/proroot-rootfs
tar -xzf ubuntu-base-22.04-base-arm64.tar.gz -C $HOME/proroot-rootfs

Or use proot-distro to get a rootfs and then run it with proroot:

pkg install proot-distro
proot-distro install ubuntu
# The rootfs is at $PREFIX/var/lib/proot-distro/installed-rootfs/ubuntu/

Step 3: Create a Wrapper Script

Create $PREFIX/bin/proroot with the following content:

#!/data/data/com.termux/files/usr/bin/bash

PROROOT_LIB_PATH="$PREFIX/lib/proroot/libproroot-runtime.so"
PROROOT_TRAMPOLINE_PATH="$PREFIX/lib/proroot/libproroot-bridge.so"
PROROOT_LINKER_PATH="$PREFIX/lib/proroot/libproroot-linker.so"
PROROOT_TMP_DIR="$HOME/proroot-tmp"

export PROROOT_LIB_PATH
export PROROOT_TRAMPOLINE_PATH
export PROROOT_LINKER_PATH
export PROROOT_TMP_DIR

exec "$PREFIX/lib/proroot/libproroot.so" "$@"

Make it executable:

chmod +x $PREFIX/bin/proroot

Step 4: Basic Usage

To start a shell inside a proroot environment:

proroot -r $HOME/proroot-rootfs -0 --link2symlink -w /root /bin/sh -c 'bash'

To run a specific command:

proroot -r $HOME/proroot-rootfs -0 --link2symlink -w /root /bin/sh -c 'node --version'

To run with bind mounts (similar to proot):

proroot -r $HOME/proroot-rootfs -0 --link2symlink -b /sdcard:/mnt/sdcard -w /root /bin/sh

Step 5: Create a proroot-distro Replacement

Create $PREFIX/bin/proroot-distro as a drop-in replacement for proot-distro:

#!/data/data/com.termux/files/usr/bin/bash

ROOTFS="$PREFIX/var/lib/proot-distro/installed-rootfs/${1:-ubuntu}"

if [ ! -d "$ROOTFS" ]; then
    echo "Error: Rootfs not found. Install with: proot-distro install ubuntu"
    exit 1
fi

shift  # Remove the distro name from arguments

exec proroot -r "$ROOTFS" -0 --link2symlink -b /dev -b /proc -b /sys -w /root "$@"

Usage:

proroot-distro ubuntu bash
proroot-distro ubuntu node --version

Step 6: Integrate with Termux-X11 for GUI Applications

To run GUI applications with hardware acceleration:

# In Termux
pkg install termux-x11-nightly

# Start X server in Termux
termux-x11 :0 &

# In proroot environment
proroot -r $HOME/proroot-rootfs -0 --link2symlink -w /root /bin/sh -c '
    export DISPLAY=:0
    export PULSE_SERVER=127.0.0.1
    # Install and run a GUI app
    apt update && apt install -y firefox-esr
    firefox
'

Step 7: Android API Access (Advanced)

Proroot's architecture allows direct Android API access through a runtime bridge. To enable this, you would need to:

  1. Build a bridge library that exposes Android APIs to the glibc environment
  2. Preload this bridge via LD_PRELOAD alongside libproroot-runtime.so

This is an advanced feature that requires significant development work but represents the ultimate potential of proroot.


What Termux Needs to Change

  1. Package the Proroot Binaries

Add a new package proroot to the Termux package repository. The build.sh should:

TERMUX_PKG_HOMEPAGE="https://github.com/coderredlab/proroot"
TERMUX_PKG_DESCRIPTION="Zero-overhead proot replacement for Android"
TERMUX_PKG_LICENSE="OTHER"
TERMUX_PKG_VERSION="0.1.0"
TERMUX_PKG_SRCURL="https://github.com/coderredlab/proroot/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz"
TERMUX_PKG_SKIP_SRC_EXTRACT=true

termux_step_make_install() {
    # Download prebuilt binaries from releases
    mkdir -p $TERMUX_PREFIX/lib/proroot
    # Copy .so files to lib/proroot/
    # Create wrapper script at bin/proroot
}
  1. Modify proot-distro to Support Proroot

Update proot-distro to allow choosing between proot and proroot:

# Option 1: Use proot (default)
proot-distro login ubuntu

# Option 2: Use proroot
proot-distro --runtime=proroot login ubuntu

Or create a separate proroot-distro command.

  1. Update Documentation

· Add proroot to the Termux wiki
· Document performance comparison and use cases
· Provide migration guides for existing proot users

  1. Default Runtime Strategy

For performance-sensitive workloads, consider making proroot the default:

· Node.js/npm projects: proroot default
· Python with heavy I/O: proroot default
· Chromium/Playwright: proroot default
· GUI applications (XFCE, etc.): proroot default
· Simple shell scripts: proot (fallback for compatibility)


The Long-Term Vision

Proroot represents a fundamental shift in what's possible on Android. With native-level performance, full glibc compatibility, and GPU acceleration, it enables:

· Running full desktop Linux applications on Android without root
· Game development and gaming via SteamOS ARM64 + Proton
· Professional development workflows with IDE-level performance
· Complete Linux toolchains without the proot overhead

As one observer noted: "This is a total game-changer".


Conclusion

The performance objections to moving Termux toward a standard Linux foundation are no longer valid. Proroot achieves native-level performance – CPU, GPU, and memory all run at the same speed as Bionic native.

The choice is no longer between:

· "Fast but incompatible" (Bionic native)
· "Compatible but slow" (proot)

Now we have:

· "Compatible AND fast" (proroot)

Proroot represents the future of Linux on Android. Termux should embrace it.


References:

· UNIXbench: proot-distro scores 44% lower than docker/chroot
· vkmark: proot-distro 1,442 vs proroot 10,746 – 7.45x improvement
· Geekbench 6: proroot achieves native Android peak scores
· Architecture: LD_PRELOAD + binary patching
· Tested workloads: Node.js, Python, Git, Chromium, XFCE
· Proroot repository: github.com/coderredlab/proroot

Additional information

https://github.com/coderredlab/proroot

#5239

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions