Repository navigation
Android: export curated EXECUTORCH_1_0 ABI from libexecutorch.so (hot-pluggable backends, milestone 1) - #21945
RanjithRagavan wants to merge 10 commits into
Conversation
…-pluggable backends, milestone 1) First step of the hot-pluggable backends plan (pytorch#10457) to let Android apps opt out of unused backends (pytorch#19329). Today libexecutorch.so exports only JNI_OnLoad and Java_*. A backend built as a standalone .so cannot reach the process-wide backend/kernel registries, so its static registration writes into a private copy and delegation fails with 'backend not found' / duplicate-kernel errors. This change adds a versioned EXECUTORCH_1_0 node to extension/android/jni/version_script.txt exporting the curated surface an external .so needs to self-register at load time via ELF symbol interposition: - registration entry points: register_backend, get_backend_class, register_kernels - RTTI/vtables for polymorphic public types such as BackendInterface (its key function is emitted in the core .so) - the public executorch::runtime / executorch::aten API namespaces that backend implementations are written against (Tensor, EValue, MemoryManager, FreeableBuffer, logging) - the et_pal_* C API so backend .so files share process-wide logging/allocation hooks The boundary is namespace-scoped instead of symbol-by-symbol so it stays maintainable as public headers evolve; everything else stays hidden for LTO/gc-sections. Future additive symbols go into a new version node so external .so files can depend on a stable ABI tag. The monolithic .so topology is unchanged: no target, link, or loading behavior changes for existing consumers. Verification: build_android_library.sh now asserts the built libexecutorch.so actually exports the required symbols (via NDK llvm-nm), failing the build if the ABI surface regresses. Existing Android CI exercises the monolithic path end to end.
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21945
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New Failure, 1 Unrelated FailureAs of commit 2b51364 with merge base fb60708 ( BROKEN TRUNK - The following job failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
@pytorchbot label "module: android" |
|
@pytorchbot label "release notes: none" |
|
@claude review |
|
Claude finished @kirklandsign's task in 4m 49s —— View job Code Review: EXECUTORCH_1_0 ABI exportReviewed the version script, the new 1. The
|
…sing-aliases only, no symbols bind) and document actual export surface
…fic; cross-link version_script.txt
…ed patterns lld matches quoted version-script patterns literally, so the quoted "vtable for executorch::runtime::*"-style entries were treated as exact symbol names and failed the link with 'symbol not defined' on NDK r29. Switch to the unquoted Itanium-mangled wildcard forms (_ZN/_ZTI/_ZTVN10executorch7runtimeE*), which glob correctly on both GNU ld and lld.
|
@psiddh has imported this pull request. If you are a Meta employee, you can view this in D122178024. |
|
Thanks for this! Please take a look at #21849 , which splits XNNPACK/Vulkan into same-release backend libraries and should address the size problem this PR is going after. My concern with this hot-swap approach is that EXECUTORCH_1_0 promises a stable cross-version C++ ABI we may not guarantee (for example BackendInterface/EValue layouts, TensorImpl may change often.). |
Summary
First step of the hot-pluggable backends plan (#10457), which lets Android apps opt out of unused backends to shrink binary size (#19329).
Today the Android AAR ships a single
libexecutorch.sothat links every backend via--whole-archive, and its linker version script exports onlyJNI_OnLoadandJava_*. A backend built as a standalone.sotherefore can't reach the process-wide backend/kernel registries — its self-registration silently writes into a private copy, and delegation fails. This is why external attempts at per-backend.sofiles hit "duplicate kernel" errors.The fix: add a versioned
EXECUTORCH_1_0node toextension/android/jni/version_script.txtthat exports the curated surface an external.soneeds to self-register at load time through normal ELF symbol interposition — the same mechanism the QNN backend already relies on:register_backend,get_backend_class,register_kernelsBackendInterface(its key function is emitted in the core.so, so subclasses in external.sofiles need these symbols)executorch::runtime/executorch::atenAPI namespaces backend implementations are written against (Tensor, EValue, MemoryManager, FreeableBuffer, logging)et_pal_*C API so backend.sofiles share the process-wide logging/allocation hooksThe boundary is deliberately namespace-scoped rather than symbol-by-symbol so it stays maintainable as the public headers evolve; everything else stays hidden for LTO/
--gc-sections. Additive symbols go into a new version node so external.sofiles get a stable ABI tag — answering the issue's open question in favor of a curated, versioned list.What this does NOT change: the monolithic
.sotopology is untouched — no target, link, or loading-behavior change for existing consumers. Split build mode (milestone 2) and the first carved-out backend (milestone 3) build on top of this.Test plan
scripts/build_android_library.shnow asserts — via the NDK'sllvm-nm— that the builtlibexecutorch.soactually exports the required symbols (registration functions,BackendInterfaceRTTI/vtable,etensor::Tensor,EValue,et_pal_emit_log_message,JNI_OnLoad). If the ABI surface ever regresses, the Android AAR build fails loudly instead of shipping a silently broken artifact..socan now resolve the core registry symbols atdlopentime; milestone 3's Vulkan pilot will demonstrate the end-to-end flow.Refs #10457 (milestone 1 of 7), #19329
cc @kirklandsign @cbilgin