fix(android): interpose pthread_create to bypass libstd's broken static stub
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:
@@ -38,6 +38,14 @@ fn build_oboe_android(target: &str) {
|
|||||||
.file("cpp/getauxval_fix.c")
|
.file("cpp/getauxval_fix.c")
|
||||||
.compile("getauxval_fix");
|
.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();
|
let oboe_dir = fetch_oboe();
|
||||||
match oboe_dir {
|
match oboe_dir {
|
||||||
Some(oboe_path) => {
|
Some(oboe_path) => {
|
||||||
|
|||||||
45
desktop/src-tauri/cpp/pthread_shim.c
Normal file
45
desktop/src-tauri/cpp/pthread_shim.c
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/* pthread_shim.c
|
||||||
|
*
|
||||||
|
* Interpose pthread_create to bypass the broken static stub that Rust's
|
||||||
|
* pre-compiled libstd for aarch64-linux-android drags in.
|
||||||
|
*
|
||||||
|
* The stub (from an old NDK libc.a) calls __init_tcb(bionic_tcb*, ...)+4,
|
||||||
|
* which SIGSEGVs in .so libraries because __init_tcb expects TCB state that
|
||||||
|
* only the static-libc init path sets up. In a dlopen-loaded shared lib
|
||||||
|
* nothing ever initialises that state.
|
||||||
|
*
|
||||||
|
* By providing our own pthread_create at link time (which takes priority
|
||||||
|
* over the one dragged in by libstd) and forwarding it via dlsym(RTLD_NEXT)
|
||||||
|
* to the REAL pthread_create in libc.so, we completely sidestep the static
|
||||||
|
* stub — libc.so's pthread_create is the fully working runtime version.
|
||||||
|
*
|
||||||
|
* The same trick handles `getauxval` via getauxval_fix.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef int (*pthread_create_fn)(pthread_t *, const pthread_attr_t *,
|
||||||
|
void *(*)(void *), void *);
|
||||||
|
|
||||||
|
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
void *(*start_routine)(void *), void *arg) {
|
||||||
|
static pthread_create_fn real_pthread_create = NULL;
|
||||||
|
if (real_pthread_create == NULL) {
|
||||||
|
/* RTLD_NEXT: skip the symbol we're currently defining and return
|
||||||
|
* the next one in the search order — which is the real pthread_create
|
||||||
|
* exported from libc.so. */
|
||||||
|
real_pthread_create =
|
||||||
|
(pthread_create_fn)dlsym(RTLD_NEXT, "pthread_create");
|
||||||
|
if (real_pthread_create == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return real_pthread_create(thread, attr, start_routine, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __ANDROID__ */
|
||||||
Reference in New Issue
Block a user