From 711137da96ecf157e692f1954ac72f1a9ce3f83b Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Thu, 9 Apr 2026 17:45:35 +0400 Subject: [PATCH] fix(android): -Wl,--exclude-libs,ALL + --no-whole-archive to stop symbol leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit llvm-nm on the crashing .so confirmed the research's smoking gun theory: 000000000130c1f0 t _Z10__init_tcbP10bionic_tcbP18pthread_internal_t 0000000000000000 a pthread_create.cpp 0000000001331108 t pthread_create All lowercase 't' (= LOCAL text symbols), zero UND dynamic references for pthread_create. So rustc's link step is pulling bionic's own pthread_create.cpp compilation unit out of libc.a as a whole-archive inclusion and binding those symbols locally inside our .so, instead of letting them stay UND and resolved against libc.so at dlopen time. Rust's libstd thread::spawn then calls the LOCAL (broken) pthread_create which calls the LOCAL __init_tcb with arguments set up for bionic's static-executable layout — crashes at __init_tcb+4 with SEGV_ACCERR. `-Wl,--exclude-libs,ALL` tells the linker to make symbols from static archives NOT appear in the dynamic symbol table of the output .so. `-Wl,--no-whole-archive` tells it to only pull archive objects that satisfy undefined references, not include the whole archive blindly. If this works, the symbol table should show pthread_create as UND (or at least not locally bound) and the app should launch. If it doesn't, the remaining fallback is the research's action #3 — extract the C++ into its own upstream cdylib crate built with cargo-ndk, and dlopen it from the Tauri cdylib at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) --- desktop/src-tauri/build.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/desktop/src-tauri/build.rs b/desktop/src-tauri/build.rs index 51ebd5c..7ebe7fe 100644 --- a/desktop/src-tauri/build.rs +++ b/desktop/src-tauri/build.rs @@ -57,6 +57,15 @@ fn main() { .file("cpp/cpp_smoke.cpp") .compile("wzp_cpp_smoke"); + // Per rust-lang/rust#104707 + the android-ndk advice: force the + // linker to keep bionic symbols (pthread_create, __init_tcb) as + // UND dynamic references resolved against libc.so at runtime, + // not bound locally from libc.a that cc-rs + cpp(true) drag in. + // llvm-nm confirmed these symbols were landing in our .so as + // LOCAL (lowercase t), which is exactly the bug. + println!("cargo:rustc-link-arg=-Wl,--exclude-libs,ALL"); + println!("cargo:rustc-link-arg=-Wl,--no-whole-archive"); + // Copy libc++_shared.so from the NDK sysroot to gen/android jniLibs // so the runtime linker can find it at dlopen time (it's now in the // .so's NEEDED list thanks to cpp_link_stdlib("c++_shared") above).