Skip to content

Instantly share code, notes, and snippets.

@felix021
Created May 25, 2026 09:34
Show Gist options
  • Select an option

  • Save felix021/5ebb9b3268f79f05cc939e40cf5c9b1b to your computer and use it in GitHub Desktop.

Select an option

Save felix021/5ebb9b3268f79f05cc939e40cf5c9b1b to your computer and use it in GitHub Desktop.
Box64 binfmt_misc on arm64: run x86_64 binaries transparently

Box64 + binfmt_misc on arm64

Run x86_64 Linux binaries transparently on arm64 via Box64's dynamic recompilation + kernel binfmt_misc. No wrapper scripts, no box64 prefix — just run the binary directly.

Why

Tools like Android SDK aapt, zipalign, aidl only ship x86_64 binaries. With binfmt_misc registered, the kernel forwards any x86_64 ELF to Box64 automatically — build systems (Gradle, Make, etc.) work without modification.

Prerequisites

  • arm64 Linux (Debian 12+, Ubuntu 22.04+, or any distro with binfmt_misc support)
  • Root/sudo access
  • ~200MB disk for Box64 build

Step 1: Compile & Install Box64

sudo apt-get install -y cmake gcc g++ git python3-dev libgles2-mesa-dev libegl-dev libsdl2-dev
cd /tmp
git clone --depth 1 https://github.com/ptitSeb/box64.git
cd box64 && mkdir build && cd build
cmake .. -DARM64=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
make -j$(nproc)
sudo make install

Verify:

box64 --version
# Box64 arm64 v0.x.x ...

Step 2: Suppress Verbose Output

Box64 prints banner/info to stderr by default. Add a global config to silence it:

sudo bash -c 'sed -i "/^# Note that process name/a\\
# Default: suppress verbose output\\
#\\
[*]\\
BOX64_LOG=0\\
" /etc/box64.box64rc'

Verify the config was inserted:

head -15 /etc/box64.box64rc

You should see a [*] section with BOX64_LOG=0 near the top.

Alternative: If you prefer not to modify the shipped box64.box64rc, set the env var in your shell profile:

echo 'export BOX64_LOG=0' >> ~/.bashrc

This only works for direct box64 ./binary invocations, not binfmt_misc (the kernel doesn't inherit shell env).

Step 3: Register binfmt_misc

# Mount binfmt_misc filesystem (idempotent)
sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null

# Register box64.real as the x86_64 interpreter
echo ':box64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/box64.real:POF' \
  | sudo tee /proc/sys/fs/binfmt_misc/register

Verify:

cat /proc/sys/fs/binfmt_misc/box64

Expected output:

enabled
interpreter /usr/local/bin/box64.real
flags: POF
offset 0
magic 7f454c4602010100000000000000000002003e00
mask ffffffffffffff00fffffffffffffffffeffffff

Flags explained:

  • P — Preserve argv[0] (the original binary path)
  • O — Open binary fd and pass to interpreter
  • F — Path is the binary itself, not a wrapper

Important: Point to box64.real (the compiled binary), not a shell wrapper script. The kernel cannot recursively resolve binfmt_misc for script interpreters — a #!/bin/bash wrapper will fail with Exec format error.

Step 4: Persist Across Reboots

sudo bash -c 'cat > /usr/lib/binfmt.d/box64.conf << "EOF"
:box64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/box64.real:POF
EOF'

On systemd-based systems, systemd-binfmt.service auto-loads files from /usr/lib/binfmt.d/ at boot. No extra service enable needed.

Step 5: Test

# Grab any x86_64 ELF binary to test
file /path/to/x86_64_binary
# ELF 64-bit LSB pie executable, x86-64, ...

/path/to/x86_64_binary
# Should run via Box64 transparently, no "box64" prefix needed

Usage with Android SDK

export ANDROID_SDK_ROOT=/opt/android-sdk
yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager \
  --sdk_root=$ANDROID_SDK_ROOT \
  "build-tools;35.0.0" "platforms;android-35"

# x86_64 tools now work natively via binfmt:
$ANDROID_SDK_ROOT/build-tools/35.0.0/aapt version
# Android Asset Packaging Tool, v0.2-11948202

$ANDROID_SDK_ROOT/build-tools/35.0.0/zipalign -v 4 input.apk output.apk

Troubleshooting

Symptom Cause Fix
cannot execute binary file: Exec format error binfmt not registered or kernel can't find interpreter Re-run Step 3; verify box64.real exists at the registered path
Exec format error after registering with a wrapper script Kernel can't recursively handle script interpreters in binfmt Point to box64.real directly, not a shell wrapper
Box64 banner/logs in output BOX64_LOG not set globally Add [*] BOX64_LOG=0 to /etc/box64.box64rc (Step 2)
binfmt lost after reboot No persistent config Create /usr/lib/binfmt.d/box64.conf (Step 4); on non-systemd systems, add the register command to a startup script
box64.real: error while loading shared libraries Missing x86_64 libs or wrong LD_LIBRARY_PATH Ensure Box64 can find your x86_64 libs; check with box64.real --help
Registration fails with File exists Already registered Deregister first: echo -1 | sudo tee /proc/sys/fs/binfmt_misc/box64

How It Works

User runs: ./aapt (x86_64 ELF)
    ↓
Kernel sees x86_64 magic in ELF header
    ↓
binfmt_misc matches, looks up "box64" entry
    ↓
Kernel invokes: /usr/local/bin/box64.real ./aapt [args]
    ↓
Box64 translates x86_64 instructions → arm64 at runtime
    ↓
Output is identical to native execution

Cleanup

# Remove binfmt registration
echo -1 | sudo tee /proc/sys/fs/binfmt_misc/box64

# Remove persistent config
sudo rm /usr/lib/binfmt.d/box64.conf

# Uninstall Box64
sudo rm /usr/local/bin/box64 /usr/local/bin/box64.real /usr/lib/libbox64.so
sudo rm -rf /usr/local/share/box64
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment