From e751af7e38c72c8537495aab62c30a80971efd1c Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Sun, 5 Apr 2026 08:52:55 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20link=20libc++=20statically=20=E2=80=94?= =?UTF-8?q?=20crash=20on=20launch=20due=20to=20missing=20libc++=5Fshared.s?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The app crashed immediately when loading libwzp_android.so because the cc crate's default dynamic linking produced a runtime dependency on libc++_shared.so, which was never packaged into the APK. Adding .cpp_link_stdlib(Some("c++_static")) to build.rs bakes the C++ runtime into libwzp_android.so directly, eliminating the missing .so. See issues/001-libc++-shared-crash.md for full diagnosis and logcat trace. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/wzp-android/build.rs | 2 + issues/001-libc++-shared-crash.md | 89 +++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 issues/001-libc++-shared-crash.md diff --git a/crates/wzp-android/build.rs b/crates/wzp-android/build.rs index 4746ae3..8e7d8aa 100644 --- a/crates/wzp-android/build.rs +++ b/crates/wzp-android/build.rs @@ -13,6 +13,7 @@ fn main() { cc::Build::new() .cpp(true) .std("c++17") + .cpp_link_stdlib(Some("c++_static")) .file("cpp/oboe_bridge.cpp") .include("cpp") .include(oboe_path.join("include")) @@ -24,6 +25,7 @@ fn main() { cc::Build::new() .cpp(true) .std("c++17") + .cpp_link_stdlib(Some("c++_static")) .file("cpp/oboe_stub.cpp") .include("cpp") .compile("oboe_bridge"); diff --git a/issues/001-libc++-shared-crash.md b/issues/001-libc++-shared-crash.md new file mode 100644 index 0000000..10a3651 --- /dev/null +++ b/issues/001-libc++-shared-crash.md @@ -0,0 +1,89 @@ +# Issue 001: App crashes on launch — missing libc++_shared.so + +## Status: Fix 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` + +## Logcat crash trace + +``` +E AndroidRuntime: FATAL EXCEPTION: main +E AndroidRuntime: Process: com.wzp.phone, PID: 6048 +E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" + not found: needed by /data/app/.../base.apk!/lib/arm64-v8a/libwzp_android.so + in namespace clns-9 +E AndroidRuntime: at java.lang.Runtime.loadLibrary0(Runtime.java:1097) +E AndroidRuntime: at java.lang.System.loadLibrary(System.java:1765) +E AndroidRuntime: at com.wzp.engine.WzpEngine.(WzpEngine.kt:115) +E AndroidRuntime: at com.wzp.ui.call.CallViewModel.startCall(CallViewModel.kt:52) +E AndroidRuntime: at com.wzp.ui.call.InCallScreenKt$InCallScreen$1$1$1.invoke(InCallScreen.kt:96) +``` + +## Root cause + +`crates/wzp-android/build.rs` uses the `cc` crate to compile C++17 code +(the Oboe audio bridge). On Android targets, `cc::Build` defaults to dynamically +linking the C++ standard library (`libc++_shared.so`). + +This means `libwzp_android.so` has a runtime dependency on `libc++_shared.so`. +However, the Gradle build (`cargoNdkBuild` task) only copies `libwzp_android.so` +into `jniLibs/arm64-v8a/`. The C++ runtime is never copied alongside it, so the +APK ships without `libc++_shared.so`. + +At runtime, `dlopen("libwzp_android.so")` fails because the linker can't find +the missing shared library in the app's namespace. + +### Why this wasn't caught earlier + +The previous APK (pre-QUIC wiring) was 2.0MB release / 8.9MB debug. The Oboe +C++ bridge was likely being compiled but the native library may not have been +loaded on the code path that was tested, or the dependency was satisfied by a +different linking configuration at the time. + +## Fix + +In `crates/wzp-android/build.rs`, add `.cpp_link_stdlib(Some("c++_static"))` to +all `cc::Build` invocations targeting Android. This tells the `cc` crate to link +`libc++_static.a` instead of `libc++_shared.so`, baking the C++ runtime directly +into `libwzp_android.so`. No separate shared library needed at runtime. + +```rust +cc::Build::new() + .cpp(true) + .std("c++17") + .cpp_link_stdlib(Some("c++_static")) // <-- this line + .file("cpp/oboe_bridge.cpp") + // ... +``` + +Applied to both the Oboe build path and the stub fallback path. + +### Trade-off + +Static linking increases `libwzp_android.so` size slightly (~200-400KB for +libc++ static). This is acceptable — the alternative (bundling libc++_shared.so +separately) adds complexity to the Gradle build and risks version mismatches if +multiple native libraries each bundle their own shared copy. + +## Rebuild steps + +```bash +cd android && ./gradlew assembleRelease +adb install -r app/build/outputs/apk/release/app-release.apk +``` + +## Verification + +After install, the app should: +1. Open without crashing +2. Load `libwzp_android.so` successfully (check logcat for absence of UnsatisfiedLinkError) +3. Show the call UI with CALL button