DIGITALIS

ARM64-to-x86_64 binary translation for Android · built on AOSP 16

Latest post Bass OS ships DigitalisX64 in its Android 16 preview

Architecture

Berberis is AOSP's binary translation framework, part of Android's NativeBridge system. It provides the core infrastructure for translating guest architecture instructions to host machine code at runtime — including a JIT compiler, an interpreter, an instruction decoder, syscall emulation, and proxy libraries. Berberis currently supports RISC-V-to-x86_64 translation in upstream AOSP.

Google's own emulator images go further than the published source. The Google APIs x86_64 system images register ro.dalvik.vm.native.bridge=libndk_translation.so — from Android 14 through Android 17 (API 37) in the images we checked — and that binary is Berberis with an ARM64 backend: its symbol table carries berberis::intrinsics::Arm64ReadFpcr and Arm64ReadFpsr, and the images ship /system/etc/berberis/cpuinfo.arm64.txt, /system/etc/init/berberis_arm_or_arm64.rc and arm64_dyn/arm64_exe binfmt_misc handlers. That ARM64 implementation is not published — upstream AOSP Berberis carries only the RISC-V backend, and the plain AOSP images ship no native bridge at all. Digitalis is an open ARM64 backend built on the public framework.

Berberis (upstream AOSP)
RISC-V APK
NativeBridge
JIT / Interpreter
x86_64 Host

Digitalis extends Berberis with an ARM64 backend, enabling ARM64-only Android apps to run on x86_64 emulators. It reuses Berberis's translation infrastructure while adding ARM64 instruction decoding, JIT code generation, and interpreter support. Guest code runs through three tiers: an interpreter, a single-pass lite translator (first gear) for cold regions, and an optimizing heavy translator (second gear) that engages on hot regions. The tree is an AOSP 16 (API 36) checkout, and the emulator image everything here is measured on is built from it.

Digitalis (this project)
ARM64 APK
NativeBridge
Interpreter / Lite / Heavy JIT
x86_64 Host

Interpreter

Per-instruction fallback for syscalls, complex SIMD operations, and edge cases the JIT can't handle.

fallback path

Lite Translator (first gear)

Single-pass JIT that translates cold ARM64 regions to native x86_64 machine code. Handles ~98% of instructions. Translated regions are cached for reuse.

~98% coverage

Heavy Optimizer (second gear)

An optimizing JIT that re-compiles a region once a hotness counter says it has earned it, doing global register allocation and loop optimization across the whole region. It covers essentially the same instruction surface as the lite tier, so real-app hot loops gear up instead of settling at first-gear speed.

hot-region tier

Two ways to go deeper, same system: take the guided tour — 102 slides, in order, assuming no background — or search the full how-it-works.md, the complete walkthrough covering register mapping, translation lifecycle, NZCV flag emulation, the syscall path, proxy library forwarding, and the ARM64 → x86_64 instruction-mapping reference. Both, plus the benchmark and coverage tables, are listed under Docs.

Features

Two-Gear JIT Compiler

Translation happens in two gears. Both emit native x86_64 machine code, and both cache what they produce, so a region is translated once and then simply run.

  • First gear — the Lite Translator. A single pass over a cold region, covering ~98% of the instruction set with no optimisation. Its job is to get working native code as quickly as possible — and to keep regions whole: the register allocator holds a fixed six-register scratch reserve so that running out of registers spills through memory instead of splitting the region, an invariant that eliminated 70,000+ measured region splits at function prologues across a 14-app sweep.
  • Second gear — the Heavy Optimizer. Re-compiles a region once it has proved hot, with global register allocation and loop optimisation across the whole region. Neutral-or-faster than first gear in general, up to ~2x on register-pressure-heavy loops.
  • Checked against itself. 3,600+ host translation tests, plus differential fuzzers that run the same code through the JIT and the interpreter and compare the entire CPU state afterwards.

Interpreter Fallback

Per-instruction execution for syscalls, complex SIMD, and anything the JIT can't handle. Ensures complete ARM64 coverage.

Proxy Libraries

A guest ARM64 library cannot call the host's x86_64 one directly. 21 proxy libraries stand on that boundary — Vulkan, libc/libm, EGL/GLES, AAudio, camera, NDK binder, NNAPI, JNI helpers — translating each call and forwarding it to the native implementation.

  • Graphics reach the real GPU. Vulkan goes through GFXStream's VkDecoder; OpenGL ES is driven by ANGLE, which implements it on top of Vulkan in-guest — giving full OpenGL ES 3.2 and hardware MSAA on the host GPU.
  • Awkward symbols are covered by hand. Anything the auto-generated marshallers can't handle gets a custom trampoline written in-surface — JNIEnv translation, host→guest callbacks, and the ~80 ANGLE extension procs a browser engine gates on but eglGetProcAddress would otherwise return NULL for.
  • The boundary is where the subtle bugs live. A zero stride from ANativeWindow_lock made a video app render solid green; a guard-page read inside the allocator wrappers looked for weeks like host memory exhaustion. Both fixed here — and the stride now comes from asking the host allocator rather than assuming the format's rule, because row padding is gralloc's choice, not the pixel format's.
  • Probed systematically, not just when apps break. Eight dedicated samples now stress the riskiest marshalling paths — host-invoked guest callbacks, buffer handles crossing unix sockets, fd passing, JNIEnv round-trips — and all of them pass; the only failures they ever found reproduced identically on native x86_64 builds, i.e. they were Android platform behaviour, not translation.
  • No reachable call aborts, audio included. The OpenSL ES and OpenMAX AL proxies wrap host interface vtables method by method, and the methods upstream could not marshal used to kill the process on first call. A named-override seam now supplies working marshalling for every one an app can reach — the streaming-decode buffer-queue callback fires from host code back into translated guest code — closing the last known app-reachable aborts across all 21 proxies.

150 Sample Apps

Every sample is an ARM64-only APK that runs end to end on the x86_64 emulator: ported NDK samples, third-party native libraries, whole UI engines, and one probe per ARM64 instruction extension.

  • Real libraries, linked the way an app links them. OpenCV, TensorFlow Lite, PyTorch, ONNX Runtime, FFmpeg, WebRTC, Qt 6, React Native, Realm, SQLCipher and dozens more — not reimplementations written to be easy to translate.
  • A wrong answer fails, loudly. The ML, audio, crypto and binder samples check their output bit-exact against a hardcoded golden, so a miscompiled NEON kernel aborts instead of quietly rendering something slightly wrong.
  • One sample per extension. NEON, FP16, BF16, dot-product, I8MM, complex arithmetic, JSCVT, PAC, BTI, the LSE and LRCPC atomics, CRC32 and the crypto instructions each get a probe that runs hot enough to reach the optimising JIT tier.
  • One probe per proxied NDK surface. Eight samples exercise the full public API of a proxy library each — AHardwareBuffer, AImageDecoder, the media data-source/muxer pair, the ADPF hint/thermal/choreographer trio, ASharedMemory, system fonts, OpenMAX AL, OpenSL ES — with golden self-checks on every call, so a marshalling bug fails a specific assertion instead of corrupting an app somewhere downstream.

The full catalogue is grouped by subsystem further down the page.

Getting Started

# Clone
$ mkdir digitalis && cd digitalis
$ repo init -b android-latest-release -u git@github.com:DigitalisX64/manifest.git
$ repo sync -c -d --no-tags --force-sync
 
# Build
$ source build/envsetup.sh
$ lunch sdk_phone64_x86_64_digitalis-trunk_staging-userdebug
$ m
 
# Run emulator
$ emulator -memory 4096 -writable-system -qemu -cpu host &
 
# Install and test
$ cd sample/hellodigitalis && ./gradlew assembleDebug
$ adb install hello-vulkan/build/outputs/apk/debug/hello-vulkan-debug.apk
$ adb shell am start -n com.example.hellodigitalis/android.app.NativeActivity

Sample Apps

ARM64-only APKs running on an x86_64 emulator via NativeBridge translation.

# Build all sample modules (ARM64-only APKs)
$ cd sample/hellodigitalis && ./gradlew assembleDebug
 
# Install and run
$ adb install hello-vulkan/build/outputs/apk/debug/hello-vulkan-debug.apk
$ adb shell am start -n com.example.hellodigitalis/android.app.NativeActivity
✓ Vulkan triangle rendered via NativeBridge translation

Graphics & GPU 20

Vulkan, OpenGL ES 1.x/2/3, EGL, and renderers

hello-vulkan
hello-gl2
gles3jni
hello-gles1
hello-gles3
hello-msaa
hello-eglext
teapots-classic
teapots-more
teapots-textured
bitmap-plasma
endless-tunnel
hello-filament
hello-gltfio
hello-filament-render
hello-rive
hello-libpag
hello-graphics-path
hello-ink
hello-webview-functor

NDK platform surface 24

The Android APIs a native app actually calls

native-activity
hello-hardwarebuffer
hello-imagedecoder
hello-adpf
hello-sharedmem
hello-fonts
hello-jni
hello-jniCallback
native-midi
sensor-graph
camera-basic
camera-texture-view
hello-camera-core
hello-binder-ndk
hello-nnapi
hello-libc-libm
hello-tracing-perfetto
hello-sigaction
hello-superpack-regress
sanitizers
vectorization
orderfile
exceptions
unit-test

Audio 6

Playback, capture, and the low-latency paths

native-audio
hello-aaudio
hello-oboe
hello-opus
hello-openmaxal
hello-opensles

Media & codecs 9

Players, transcoding, and real-time streams

native-codec
hello-mediandk-source
hello-ijkplayer
hello-libvlc
hello-ffmpeg-kit
hello-webrtc
hello-libwebp
hello-avif
hello-gif

Imaging, PDF & maps 9

Pixel pipelines and vector rendering

hello-fresco
hello-gpuimage
hello-libyuv
hello-renderscript-toolkit
hello-leptonica
hello-pdfium
hello-mupdf
hello-maplibre
hello-zxing

Vision, ML & speech 9

Real inference, checked bit-exact against goldens

hello-opencv
hello-tflite
hello-litert-llm
hello-pytorch
hello-onnxruntime
hello-ncnn
hello-mediapipe
hello-vosk
hello-tesseract

Crypto & secure storage 9

Libraries where a wrong bit is silent, not loud

hello-sqlcipher
hello-conscrypt
hello-libsignal
hello-libsodium
hello-argon2
hello-themis
hello-secp256k1
hello-bcrypt
hello-blowfish

Databases & compression 11

Storage engines and the codecs around them

hello-realm
hello-objectbox
hello-couchbase
hello-wcdb
hello-sqlite-bundled
hello-leveldb
hello-appsearch
hello-mmkv
hello-zstd
hello-snappy
hello-libarchive

UI engines 3

Whole frameworks, not just their leaf libraries

hello-qt
hello-reactnative
hello-lynx

JS engines & JNI bridges 8

Runtimes that generate or dispatch code themselves

hello-quickjs
hello-duktape
hello-j2v8
hello-javet
hello-lua
hello-javacpp
hello-jna
hello-fbjni

Networking 3

Stacks that fork, thread, and hold sockets open

hello-cronet
hello-wireguard
hello-libtorrent4j

Numeric, physics & parsing 8

Hot loops, and the text formats around them

hello-openblas
hello-fftw
hello-gsl
hello-box2d
hello-bullet
hello-pcre2
hello-libxml2
hello-sentry-ndk

ARM64 instruction probes 25

One extension each, asserted against hand-computed goldens

hello-neon
hello-neonmisc
hello-fp16
hello-fp16arith
hello-bf16
hello-fp-vector
hello-complex
hello-fcma
hello-fcsel
hello-dotprod
hello-i8mm-bf16
hello-widemul
hello-jscvt
hello-pac-ret
hello-bti
hello-lse
hello-lseatomics
hello-lsepair
hello-lrcpc
hello-ldxp
hello-barriers
hello-ld-interleave
hello-sha-crypto
hello-aes
hello-cntvct

Beyond the in-tree samples, real unmodified third-party ARM64 APKs are tracked on the Verified Apps page.

Attribution

Digitalis is a modification of the Android Open Source Project and its Berberis binary-translation framework, © Google LLC, used under the Apache License 2.0. Android, Berberis, ANGLE and GFXStream are projects and/or trademarks of Google LLC. Digitalis is an independent project, not affiliated with, endorsed by, or sponsored by Google.

Arm, AArch64 and NEON are trademarks of Arm Limited. Vulkan and OpenGL ES are trademarks of the Khronos Group. The sample apps link third-party libraries — among them Qt, React Native, Lynx, OpenCV, TensorFlow Lite, PyTorch, ONNX Runtime, FFmpeg, WebRTC, Realm and SQLCipher — each the property of its respective owners and used under its own license.

Third-party application and product names, logos and packages named on this site are trademarks of their respective owners and appear solely to report interoperability-testing results; no affiliation or endorsement is implied, and no third-party application is redistributed here.