Previous fix linked c++_static but not c++abi. Android NDK splits the static C++ runtime into two archives: libc++_static.a (STL) and libc++abi.a (RTTI/exceptions). Without c++abi, dlopen fails on _ZTVN10__cxxabiv117__class_type_infoE. Now using cpp_link_stdlib(None) to suppress cc crate auto-linking, then explicitly linking both c++_static and c++abi via cargo:rustc-link-lib. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.0 KiB
Issue 001: App crashes on launch — C++ runtime not linked correctly
Status: Fix v2 committed, needs rebuild
Symptom
App opens, shows splash screen, then immediately crashes back to home screen.
Crash occurs when user taps CALL (which triggers System.loadLibrary("wzp_android")),
or on any code path that first loads the native library.
Device tested
- Nothing Phone, arm64-v8a, Android 15
- ADB device ID:
00142151B000973
Crash history
Attempt 1: libc++_shared.so not found
E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so"
not found: needed by .../libwzp_android.so
at com.wzp.engine.WzpEngine.<clinit>(WzpEngine.kt:115)
Cause: cc::Build defaults to dynamic C++ linking. libc++_shared.so never
packaged into APK.
Attempted fix: .cpp_link_stdlib(Some("c++_static")) — link STL statically.
Attempt 2: missing __class_type_info vtable (RTTI)
E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol
"_ZTVN10__cxxabiv117__class_type_infoE" referenced by .../libwzp_android.so
at com.wzp.engine.WzpEngine.<clinit>(WzpEngine.kt:115)
Cause: Android NDK splits the static C++ runtime into two archives:
libc++_static.a— STL (containers, strings, algorithms)libc++abi.a— ABI layer (RTTI typeinfo vtables, exception handling)
The cc crate's .cpp_link_stdlib(Some("c++_static")) only emits
cargo:rustc-link-lib=static=c++_static. It does NOT pull in libc++abi.a,
so all RTTI symbols (__class_type_info, __si_class_type_info, etc.)
are unresolved at dlopen time.
Root cause (full)
crates/wzp-android/build.rs uses the cc crate to compile C++17 code
(the Oboe audio bridge). Two things go wrong:
- Dynamic linking by default →
libc++_shared.sonot in APK - Even with
.cpp_link_stdlib("c++_static"), the ABI half (libc++abi.a) is not linked, leaving RTTI symbols unresolved
Fix (v2)
Suppress the cc crate's automatic C++ stdlib linking with .cpp_link_stdlib(None),
then explicitly link both static archives:
cc::Build::new()
.cpp(true)
.std("c++17")
.cpp_link_stdlib(None) // suppress cc crate's automatic linking
.file("cpp/oboe_bridge.cpp")
// ...
.compile("oboe_bridge");
// Manually link both halves of the Android NDK static C++ runtime
println!("cargo:rustc-link-lib=static=c++_static");
println!("cargo:rustc-link-lib=static=c++abi");
This is placed once after the match block (applies to both Oboe and stub paths).
Trade-off
Static linking increases libwzp_android.so by ~300-500KB. Acceptable for
avoiding shared library packaging complexity.
Rebuild steps
cd android && ./gradlew clean assembleRelease
adb install -r app/build/outputs/apk/release/app-release.apk
Use clean to ensure the native library is fully relinked.
Verification
After install, the app should:
- Open without crashing
- Load
libwzp_android.sosuccessfully (no UnsatisfiedLinkError in logcat) - Show the call UI with CALL button