From e2e023d2bce0b48739a32b6471505fc205993c2c Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Thu, 9 Apr 2026 13:52:53 +0400 Subject: [PATCH] =?UTF-8?q?fix(android):=20drop=20pthread=5Fshim=20?= =?UTF-8?q?=E2=80=94=20clang=20shim=20makes=20it=20unnecessary=20(and=20ha?= =?UTF-8?q?rmful)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once the Dockerfile rewrites every android24-clang to exec android26-clang, the linker uses the API-26 NDK sysroot and libstd's pthread_create reference resolves directly against libc.so's real runtime symbol — no interposition needed. The pthread_shim.c approach was actually fighting its own solution: our shim's dlsym() call bound at link time to libdl.a's STUB dlsym (a five-line function inside libdl_static.o that just returns NULL and sets dlerror to "libdl.a is a stub --- use libdl.so instead"). NDK r19 and glibc 2.34 both replaced libdl.a with empty stubs because dynamic loading is now part of the main libc/bionic — so no amount of link-order tinkering can make a static libdl.a dlsym actually work. Remove pthread_shim.c, the cc::Build::new().file("cpp/pthread_shim.c") step in build.rs, and the -Wl,--wrap=pthread_create rustc-link-arg. Keep getauxval_fix.c because that one DOES work at link time (the symbol override is for a function compiler-rt defines statically, not one that would depend on the stub libdl.a/libc.a). Co-Authored-By: Claude Opus 4.6 (1M context) --- desktop/src-tauri/build.rs | 14 ----- desktop/src-tauri/cpp/pthread_shim.c | 80 ---------------------------- 2 files changed, 94 deletions(-) delete mode 100644 desktop/src-tauri/cpp/pthread_shim.c diff --git a/desktop/src-tauri/build.rs b/desktop/src-tauri/build.rs index 3584776..e17495c 100644 --- a/desktop/src-tauri/build.rs +++ b/desktop/src-tauri/build.rs @@ -38,14 +38,6 @@ 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) => { @@ -112,12 +104,6 @@ fn build_oboe_android(target: &str) { // Oboe requires Android log + OpenSLES backends println!("cargo:rustc-link-lib=log"); println!("cargo:rustc-link-lib=OpenSLES"); - - // Wrap pthread_create: redirect every `pthread_create` reference to our - // `__wrap_pthread_create` in pthread_shim.c, which forwards to the real - // libc.so symbol via dlsym. Without this the linker binds to libstd's - // bundled broken static pthread_create stub (see pthread_shim.c). - println!("cargo:rustc-link-arg=-Wl,--wrap=pthread_create"); } /// Recursively add all .cpp files from a directory to a cc::Build. diff --git a/desktop/src-tauri/cpp/pthread_shim.c b/desktop/src-tauri/cpp/pthread_shim.c deleted file mode 100644 index 8fa5b44..0000000 --- a/desktop/src-tauri/cpp/pthread_shim.c +++ /dev/null @@ -1,80 +0,0 @@ -/* pthread_shim.c - * - * Interpose pthread_create via `--wrap=pthread_create` to bypass Rust libstd's - * broken static pthread_create stub. The stub (from an old bundled libc.a) - * calls __init_tcb+4 and SIGSEGVs in dlopen-loaded .so libraries. - * - * Approach: - * 1. The linker rewrites every `pthread_create` reference inside libstd - * (and everywhere else) into `__wrap_pthread_create` — our function. - * 2. Inside __wrap_pthread_create we call libc.so's real pthread_create - * via dlsym(RTLD_DEFAULT). Because --wrap redirects only references, - * the symbol `pthread_create` itself is NOT defined in our .so, so - * RTLD_DEFAULT finds the one exported by libc.so (always loaded). - * 3. We log every step via __android_log_print so we can diagnose any - * lookup failure from logcat (tag: WZP_pthread_shim). - */ - -#ifdef __ANDROID__ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -#define TAG "WZP_pthread_shim" -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) - -typedef int (*pthread_create_fn)(pthread_t *, const pthread_attr_t *, - void *(*)(void *), void *); - -static pthread_create_fn resolve_real_pthread_create(void) { - /* RTLD_DEFAULT: search the global symbol table starting with the main - * executable (app_process64), then every shared library loaded into the - * process in load order. libc.so is always loaded first, so its - * pthread_create export is the first match — and since we never define - * a symbol literally named "pthread_create" (only __wrap_pthread_create), - * there's no ambiguity. */ - pthread_create_fn fn = (pthread_create_fn)dlsym(RTLD_DEFAULT, "pthread_create"); - if (fn != NULL) { - LOGI("resolved pthread_create via RTLD_DEFAULT at %p", (void *)fn); - return fn; - } - LOGE("dlsym(RTLD_DEFAULT, pthread_create) returned NULL: %s", dlerror()); - - /* Fall back to explicit dlopen of libc.so — bionic is normally happy - * to hand back the already-loaded mapping. */ - void *libc = dlopen("libc.so", RTLD_LAZY | RTLD_NOLOAD); - if (libc == NULL) { - libc = dlopen("libc.so", RTLD_LAZY); - } - if (libc == NULL) { - LOGE("dlopen(libc.so) failed: %s", dlerror()); - return NULL; - } - fn = (pthread_create_fn)dlsym(libc, "pthread_create"); - if (fn == NULL) { - LOGE("dlsym(libc, pthread_create) returned NULL: %s", dlerror()); - return NULL; - } - LOGI("resolved pthread_create via dlopen(libc.so) at %p", (void *)fn); - return fn; -} - -int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine)(void *), void *arg) { - static pthread_create_fn real = NULL; - if (real == NULL) { - real = resolve_real_pthread_create(); - if (real == NULL) { - LOGE("__wrap_pthread_create: no real pthread_create found, returning EAGAIN"); - return 11; /* EAGAIN — best we can do */ - } - } - return real(thread, attr, start_routine, arg); -} - -#endif /* __ANDROID__ */