fix(android): interpose pthread_create to bypass libstd's broken static stub
Some checks failed
Mirror to GitHub / mirror (push) Failing after 36s
Build Release Binaries / build-amd64 (push) Failing after 3m52s

Builds #7, #8 and #9 all crashed at launch with the same SIGSEGV inside
__init_tcb(bionic_tcb*, pthread_internal_t*)+4 called via pthread_create
from std::sys::thread::unix::Thread::new.

Digging further: the problem is NOT the final linker we pass to cargo.
It's that rustup ships a PRE-COMPILED libstd for aarch64-linux-android
which was built statically against an old NDK libc archive. That archive
has a pthread_create stub which calls a static __init_tcb stub that
assumes libc's static init path has set up the TCB — which never happens
in a .so loaded via dlopen. Bumping minSdk to 26 or forcing the
android26-clang linker (903a07c) doesn't rebuild libstd and therefore
doesn't fix the bundled broken stub.

The legacy wzp-android crate dodged this with a getauxval_fix.c shim that
interposes getauxval via RTLD_NEXT. The same trick works for pthread_create
here: define our own `int pthread_create(...)` in cpp/pthread_shim.c that
forwards to `dlsym(RTLD_NEXT, "pthread_create")` — the real, fully working
version exported from libc.so. The linker processes our static lib before
libstd.rlib, so libstd's unresolved pthread_create reference binds to our
symbol, and the broken libc.a stub inside libstd is never pulled in.

build.rs compiles cpp/pthread_shim.c right after cpp/getauxval_fix.c so
both symbol overrides are in place before any Rust code gets linked.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-09 13:04:18 +04:00
parent 903a07c1d4
commit 79e876126c
2 changed files with 53 additions and 0 deletions

View File

@@ -38,6 +38,14 @@ fn build_oboe_android(target: &str) {
.file("cpp/getauxval_fix.c")
.compile("getauxval_fix");
// pthread_shim: interpose pthread_create so Rust libstd can't use the
// broken static pthread_create stub (which calls __init_tcb, crashing
// in a .so). Our shim forwards to libc.so's real one via RTLD_NEXT.
// Compiled as its own static lib so the linker links it ahead of libstd.
cc::Build::new()
.file("cpp/pthread_shim.c")
.compile("pthread_shim");
let oboe_dir = fetch_oboe();
match oboe_dir {
Some(oboe_path) => {