A small dynamic binary translation (DBT) proof of concept that translates selected AArch64 instructions into x86_64 machine code and executes them with a JIT.
This project is focused on experimentation and learning. It is not a production Android NativeBridge.
- Decodes AArch64 instructions from hex words, raw bytecode, or ELF symbols.
- Translates supported instructions to x86_64 at runtime.
- Executes translated blocks with a small CPU state model.
- Supports a growing subset of integer, branch, memory, atomics, and FP instructions.
- Scalar FP compare and ALU:
FCMP/FCMPE,FADD/FSUB/FMUL/FDIV. - FP/GPR moves:
FMOV W<->S,FMOV X<->D. - FP/int conversions:
SCVTF,UCVTF,FCVTZS,FCVTZU. - Scalar FP memory forms:
LDR/STR S/D(unsigned imm, post/pre-index, unscaled), plusSTP/LDP D(signed offset). - NEON rounding multiply-accumulate/subtract high:
SQRDMLAH/SQRDMLSH(2S/4S). - Extra sign-ext memory forms:
LDURSW (unscaled)andLDRSH (W)across common immediate modes. - Execution-driven unsupported opcode handling with optional logging.
- ELF symbol runner with out-of-range
B/BLpatching, fixed import stubs, host import callbacks, and import trace logging.
cd arm64_translator_poc
makeRun inline opcodes:
./tiny_dbt D28000E0 91008C00 D65F03C0Expected result:
x0 = 42 (0x2a)
Run from raw code bytes:
./tiny_dbt --code-file /tmp/prog.binRun from ELF symbol:
./tiny_dbt --elf-file /tmp/libmain.so --elf-symbol JNI_OnLoadRun from ELF dynamic symbol index:
./tiny_dbt --elf-file /tmp/libmain.so --elf-symbol-index 42 --elf-size 64--code-file <path>: load little-endian AArch64 instruction bytes.--elf-file <path> --elf-symbol <name>: extract and run one symbol from an AArch64 ELF.--elf-file <path> --elf-symbol-index <n>: extract and run one dynamic symbol by index.--elf-size <bytes>: override symbol size when ELF reports size0.--elf-import-stub <symbol=value>: force specific PLT imports to return a fixedX0value.--elf-import-callback <symbol=op>: map PLT imports to callback ops (ret_0,ret_1,ret_neg1,ret_x0..ret_x7,add_x0_x1,sub_x0_x1,ret_sp,nonnull_x0,guest_alloc_x0,guest_free_x0,guest_calloc_x0_x1,guest_realloc_x0_x1,guest_memcpy_x0_x1_x2,guest_memset_x0_x1_x2,guest_memcmp_x0_x1_x2,guest_memmove_x0_x1_x2,guest_strnlen_x0_x1,guest_strlen_x0,guest_strcmp_x0_x1,guest_strncmp_x0_x1_x2,guest_strcpy_x0_x1,guest_strncpy_x0_x1_x2,guest_strchr_x0_x1,guest_strrchr_x0_x1,guest_strstr_x0_x1,guest_memchr_x0_x1_x2,guest_memrchr_x0_x1_x2,guest_atoi_x0,guest_strtol_x0_x1_x2,guest_strtoul_x0_x1_x2,guest_posix_memalign_x0_x1_x2,guest_basename_x0,guest_strdup_x0,guest_strtof_x0_x1,guest_pow_x0_x1,guest_sqrt_x0,guest_cos_x0,guest_tan_x0,guest_islower_x0,guest_isspace_x0,guest_isxdigit_x0,guest_isupper_x0,guest_toupper_x0,guest_tolower_x0,guest_snprintf_x0_x1_x2,guest_strtod_x0_x1,guest_sscanf_x0_x1_x2,guest_vsnprintf_x0_x1_x2_x3,guest_vsscanf_x0_x1_x2,guest_vsnprintf_chk_x0_x1_x4_x5,guest_vfprintf_x0_x1_x2,guest_vasprintf_x0_x1_x2).- Includes extended math/time/errno ops such as
guest_exp_x0,guest_log_x0,guest_sin_x0,guest_sincosf_x0_x1_x2,guest_gmtime_x0,guest_ctime_x0,guest_tzset_0, andret_neg1_eacces/enoent/eperm/etimedout.
- Includes extended math/time/errno ops such as
--elf-import-preset <name>: apply built-in callback sets (libc-basic,android-basic,android-compat).--elf-import-trace <path>: append per-symbol import patch details for ELF branch rewrites.--set-reg <name=value>: initialize registers/state, includingheap_base,heap_brk,heap_last_ptr,heap_last_size.--trace-state: print compact state before/after run.--mem-write <addr:hexbytes>and--mem-read <addr:len>: preload/dump guest memory.--log-unsupported <path>: append unsupported opcodes that are actually executed.
Environment alternatives:
TINY_DBT_LOG_UNSUPPORTEDTINY_DBT_TRACE_STATETINY_DBT_INVALIDATE_BEFORE_RUNTINY_DBT_INVALIDATE_ALL_SLOTSTINY_DBT_INVALIDATE_PC_INDEXESSMOKE_FAIL_ON_ERROR(forscripts/run_kingshot_smoke_matrix.sh)SMOKE_TIMEOUT_SEC(per-run timeout inrun_kingshot_smoke_matrix.sh)SMOKE_BLACKLIST_FILE(skip problematicliborlib:symbolrows in smoke matrix)SMOKE_ALLOW_SYMBOL_INDEX(0/1, allow smoke scripts to fall back to--elf-symbol-index)KSHOT_PROFILE_MODE(relaxed,strict,compat,minimal) for smoke/profile scripts
- Unsupported opcodes are translated into runtime stubs.
- An unsupported instruction only fails execution if that path is reached.
- Out-of-bounds guest memory access returns
x0 = UINT64_MAXin this PoC. - ELF-loaded symbols rewrite out-of-range immediate
B/BLtargets to local return stubs. - ELF-loaded symbols can be selected by name (
--elf-symbol) or dynamic index (--elf-symbol-index). - With
--elf-import-stub, known PLT imports can get symbol-specific fixed return values. - With
--elf-import-callback, known PLT imports can run host callback ops and return computedX0. - With
--elf-import-preset, common import symbols are mapped automatically (without overriding explicit--elf-import-stub/--elf-import-callbackentries). guest_calloc_x0_x1allocatesx0*x1bytes on the guest heap (16-byte aligned) and zero-fills the allocated range.guest_realloc_x0_x1resizes only the latest guest-heap allocation (PoC top-of-heap behavior).guest_memcpy_x0_x1_x2,guest_memset_x0_x1_x2, andguest_memmove_x0_x1_x2modify guest memory usingx0/x1/x2asdst/src-or-value/len.guest_memcmp_x0_x1_x2compares guest memory regions and returns a signed-style diff inx0.guest_strnlen_x0_x1scans a guest string with a max length limit and returns length inx0.guest_strlen_x0,guest_strcmp_x0_x1, andguest_strncmp_x0_x1_x2provide basic C-string helpers on guest memory.guest_strcpy_x0_x1,guest_strncpy_x0_x1_x2,guest_strchr_x0_x1,guest_strrchr_x0_x1,guest_strstr_x0_x1, andguest_memchr_x0_x1_x2add basic copy/search helpers on guest memory.guest_memrchr_x0_x1_x2,guest_atoi_x0, andguest_strtol_x0_x1_x2add reverse-search and number parsing helpers.guest_snprintf_x0_x1_x2now supports flags, width/precision (*included), length modifiers (hh/h/l/ll/j/z/t),%f/%e/%g, and%n.- Variadic callback arguments now continue from guest stack (starting at
SP) after register args (x3..x7forsnprintf,x2..x7forsscanf). guest_vsnprintf_x0_x1_x2_x3andguest_vsscanf_x0_x1_x2map guestva_listmemory to callback argument state for variadic import hooks.guest_vsnprintf_chk_x0_x1_x4_x5handles fortified__vsnprintf_chk(dst, size, flag, dstlen, fmt, va_list)by reusing the variadic formatter core.guest_vfprintf_x0_x1_x2returns formatted length (stream side effects are intentionally stubbed in this PoC).guest_vasprintf_x0_x1_x2allocates from guest heap, writes pointer to*x0, and returns formatted length (or-1on failure).guest_strtoul_x0_x1_x2andguest_posix_memalign_x0_x1_x2provide minimal unsigned parse and aligned guest-heap allocation hooks.guest_basename_x0,guest_strdup_x0, andguest_strtof_x0_x1provide minimal path, duplicate-string, and float parse hooks.guest_pow_x0_x1,guest_sqrt_x0,guest_cos_x0,guest_tan_x0,guest_exp_x0,guest_log_x0,guest_log10_x0,guest_floor_x0,guest_ceil_x0,guest_trunc_x0,guest_fmod_x0_x1,guest_sin_x0,guest_sinh_x0,guest_tanh_x0,guest_sinf_x0,guest_sincosf_x0_x1_x2,guest_exp2f_x0,guest_log2f_x0,guest_log10f_x0, andguest_lround_x0provide scalar FP math hooks.guest_islower_x0,guest_isspace_x0,guest_isxdigit_x0,guest_isupper_x0,guest_toupper_x0, andguest_tolower_x0provide ctype hooks.guest_errno_ptrreturns a guest-memory errno slot, whileret_neg1_enosys,ret_neg1_eagain,ret_neg1_eintr,ret_neg1_eacces,ret_neg1_enoent,ret_neg1_eperm, andret_neg1_etimedoutreturn-1and set that errno slot.guest_open_x0_x1_x2,guest_openat_x0_x1_x2_x3,guest_read_x0_x1_x2,guest_write_x0_x1_x2, andguest_close_x0provide a synthetic guest-FD model for file/I/O call paths.FCVTZS/FCVTZUnow use stricter PoC conversion semantics: NaN clamps to0, overflow clamps to signed/unsigned bounds, and in-range values keep trunc-toward-zero behavior.guest_handle_x0now uses a bounded per-run handle cache to keep repeated handle-like imports stable without unbounded guest allocations.guest_gmtime_x0,guest_ctime_x0,guest_tzset_0,guest_daylight_ptr, andguest_timezone_ptrexpose basic time/tz hooks via guest-memory slots.guest_strtod_x0_x1parses guest strings asdouble, writesendptr, and mirrors result bits to bothx0andd0(v0).guest_sscanf_x0_x1_x2provides a minimal parser for%d/%i/%u/%x/%X/%o/%f/%e/%g/%c/%s/%[and%nwith output pointers inx2..x7and then guest stack.- Unmapped out-of-range branches use a default local return stub and are reported as
local-retin trace output. - Import mapping scans both
REL/RELAPLT-style sections (.rel[a].plt,.rel[a].iplt) and.plt/.plt.sec-linked relocation sections. - With
--elf-import-trace, each import stub/callback patch is logged with symbol and branch count.
You can run many regression targets via make run-*.
Examples:
make run-example
make run-fdivd-example
make run-fmov-ws-roundtrip-example
make run-scvtf-fcvtzs64-example
make run-ucvtf-fcvtzu64-high-example
make run-ldursw-unscaled-example
make run-ldrstrd-example
make run-postidx-strd-example
make run-neon-sqrdmlsh4s-example
make run-elf-branch-trampoline-example
make run-elf-import-stub-example
make run-import-callback-retx1-example
make run-elf-import-callback-example
make run-elf-import-trace-example
make run-elf-import-preset-example
make run-import-callback-alloc-example
make run-import-callback-free-example
make run-import-callback-alloc-free-example
make run-import-callback-calloc-example
make run-import-callback-calloc-zero-example
make run-import-callback-realloc-example
make run-import-callback-realloc-null-example
make run-import-callback-memcpy-example
make run-import-callback-memset-example
make run-import-callback-memcmp-eq-example
make run-import-callback-memcmp-ne-example
make run-import-callback-memmove-example
make run-import-callback-strnlen-example
make run-import-callback-strnlen-max-example
make run-import-callback-strlen-example
make run-import-callback-strcmp-eq-example
make run-import-callback-strcmp-ne-example
make run-import-callback-strncmp-eq-prefix-example
make run-import-callback-strncmp-ne-example
make run-import-callback-strcpy-example
make run-import-callback-strncpy-pad-example
make run-import-callback-strchr-hit-example
make run-import-callback-strchr-miss-example
make run-import-callback-strchr-nul-example
make run-import-callback-strrchr-hit-example
make run-import-callback-strrchr-miss-example
make run-import-callback-strstr-hit-example
make run-import-callback-strstr-miss-example
make run-import-callback-strstr-empty-needle-example
make run-import-callback-memchr-hit-example
make run-import-callback-memchr-miss-example
make run-import-callback-memchr-limit-example
make run-import-callback-memrchr-hit-example
make run-import-callback-memrchr-miss-example
make run-import-callback-atoi-example
make run-import-callback-atoi-neg-example
make run-import-callback-strtol-base0-example
make run-import-callback-strtol-base16-example
make run-import-callback-strtol-invalid-base-example
make run-import-callback-retneg1-example
make run-import-callback-strtoul-example
make run-import-callback-posix-memalign-example
make run-import-callback-posix-memalign-einval-example
make run-import-callback-basename-example
make run-import-callback-strdup-example
make run-import-callback-strtof-example
make run-import-callback-pow-example
make run-import-callback-sqrt-example
make run-import-callback-cos-example
make run-import-callback-tan-example
make run-import-callback-islower-example
make run-import-callback-isspace-example
make run-import-callback-isxdigit-example
make run-import-callback-snprintf-mixed-example
make run-import-callback-snprintf-trunc-example
make run-import-callback-snprintf-widthprec-example
make run-import-callback-snprintf-starwidth-example
make run-import-callback-snprintf-float-n-example
make run-import-callback-snprintf-stack-varargs-example
make run-import-callback-vsnprintf-example
make run-import-callback-vsnprintf-chk-example
make run-import-callback-vfprintf-example
make run-import-callback-vasprintf-example
make run-import-callback-vsscanf-example
make run-import-callback-snprintf-inf-example
make run-import-callback-snprintf-trunc-edge-example
make run-import-callback-strtod-example
make run-import-callback-strtod-nan-example
make run-import-callback-sscanf-example
make run-import-callback-sscanf-float-n-scanset-example
make run-import-callback-sscanf-stack-varargs-example
make run-import-callback-sscanf-scanset-invert-example
make run-kingshot-import-profile
make run-kingshot-import-profile-strict
make run-kingshot-import-profile-all
make run-kingshot-import-profile-all-strict
make run-kingshot-import-profile-all-compat
make run-kingshot-import-profile-all-minimal
make run-kingshot-coverage-gate
make run-kingshot-smoke
make run-kingshot-smoke-index-example
make run-kingshot-smoke-matrix
make run-kingshot-smoke-matrix-ci
make run-kingshot-mode-regression-ci
make run-kingshot-e2e-batch
make run-fp-conversion-edge-check
make run-unsupported-top20-triage
make verify-kingshot
make verify-fp-conversion-ci
make verify-kingshot-ci
make verify-kingshot-modes-ci
make run-nativebridge-skeleton-build
make run-nativebridge-skeleton-demo
make run-nativebridge-skeleton-runtime-smoke
make run-nativebridge-skeleton-jni-probe
make run-unsupported-log-example
make run-elf-symbol-example
make run-elf-symbol-index-exampleGenerate a profile for kingshot libmain.so imports:
make run-kingshot-import-profileGenerate profiles for all arm64-v8a libs in the APK:
make run-kingshot-import-profile-allGenerate strict profiles (no relaxed fallback stubs for many libc/pthread/syscall symbols):
make run-kingshot-import-profile-strict
make run-kingshot-import-profile-all-strict
make run-kingshot-import-profile-all-compat
make run-kingshot-import-profile-all-minimalRun profile-mode regression checks (relaxed/strict/compat/minimal) and save per-mode reports:
make run-kingshot-mode-regression-ciRun the coverage regression gate (reports/kingshot_all_import_coverage.txt vs kingshot_coverage_baseline.txt):
make run-kingshot-coverage-gateRun a smoke execution against one extracted Kingshot ELF symbol with generated import mappings:
make run-kingshot-smokeRun smoke through explicit symbol index selection:
make run-kingshot-smoke-index-exampleRun smoke tests for the top N high-unmapped libs and selected symbols per lib
(defaults: max_libs=10, syms_per_lib=2, attempts=2):
make run-kingshot-smoke-matrixYou can pass custom matrix params directly:
./scripts/run_kingshot_smoke_matrix.sh /home/stolpee/Android/kingshot_xapk/config.arm64_v8a.apk 8 3 2Fail the script with non-zero exit if any row fails:
SMOKE_FAIL_ON_ERROR=1 ./scripts/run_kingshot_smoke_matrix.shSet timeout/blacklist behavior (defaults: SMOKE_TIMEOUT_SEC=25, blacklist file profiles/kingshot_smoke_blacklist.txt):
SMOKE_TIMEOUT_SEC=20 SMOKE_BLACKLIST_FILE=profiles/kingshot_smoke_blacklist.txt ./scripts/run_kingshot_smoke_matrix.shRun an end-to-end verification bundle:
make verify-kingshotCurrent Kingshot relaxed-profile coverage is 100.00% mapped imports (reports/kingshot_all_import_coverage.txt).
Default libmain profile output:
profiles/kingshot_libmain_import_callbacks.txtprofiles/kingshot_libmain_import_stubs.txtprofiles/kingshot_libmain_import_args.txtreports/kingshot_libmain_unmapped_imports.txtreports/kingshot_libmain_rejected_import_symbols.txt
All-lib profile output also includes:
reports/kingshot_all_import_profiles_summary.txtreports/kingshot_all_unmapped_imports.txtreports/kingshot_all_unmapped_top_symbols.txtreports/kingshot_all_rejected_import_symbols.txtreports/kingshot_all_rejected_top_symbols.txtreports/kingshot_next_callbacks.txtreports/kingshot_all_import_coverage.txtreports/kingshot_smoke_matrix_summary.txtreports/kingshot_smoke_matrix_exit_reason_summary.txtreports/kingshot_smoke_matrix_metrics.txtreports/kingshot_smoke_blacklist_suggestions.txtreports/kingshot_mode_regression_summary.txtreports/kingshot_e2e_demo_output.txt(example real-lib smoke output)reports/kingshot_e2e_batch_report.txt
You can override the APK path used by make targets with:
KSHOT_APK_PATH=/path/to/config.arm64_v8a.apk make run-kingshot-import-profile-all
KSHOT_PROFILE_MODE=compat KSHOT_APK_PATH=/path/to/config.arm64_v8a.apk make run-kingshot-smoke-matrix-ci
KSHOT_PROFILE_MODE=minimal KSHOT_APK_PATH=/path/to/config.arm64_v8a.apk make run-kingshot-import-profile-allBuild the placeholder NativeBridge-style stub and loader demo:
make run-nativebridge-skeleton-build
make run-nativebridge-skeleton-demo
make run-nativebridge-skeleton-runtime-smoke
make run-nativebridge-skeleton-jni-proberun-nativebridge-skeleton-demo now auto-generates a Kingshot libmain profile first and passes profile callback/stub files into the loader demo, then resolves and invokes a trampoline (cos(0)) through the callback table. run-nativebridge-skeleton-jni-probe validates JNI-style runtime wiring.
run-nativebridge-skeleton-runtime-smoke extends the demo by running a real ELF-symbol smoke execution through tiny_dbt after the trampoline probe.
Skeleton files live under nativebridge_skeleton/ and are intentionally minimal.
This is still a PoC. It does not run full Android ARM games yet.
Main missing pieces for that goal include:
- Much wider ISA coverage (especially NEON/SIMD breadth).
- Full exception/signal semantics.
- Android NativeBridge, linker/JNI, and ABI integration.
- Stronger compatibility/performance work.
tiny_dbt.c: CLI frontend and ELF symbol loading.tiny_dbt_runtime.h: runtime API.tiny_dbt_runtime.c: runtime core.tiny_dbt_runtime_emit.inc.c: translator/emit logic.tiny_dbt_runtime_api.inc.c: runtime API implementation details.NEXT_STEPS_ANDROID.md: roadmap notes.
See CONTRIBUTING.md.
Release readiness checklist: ALPHA_DONE_CHECKLIST.md.