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) <noreply@anthropic.com>
4.4 KiB
4.4 KiB