From f733c2c12bbe7b53271d1b488247a4d369f4cbcf Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Tue, 10 Sep 2024 21:37:46 +0200 Subject: resolve next fn ptr on call Resolve pthread_create on call to function rather than from static initializer as the wrapper may be called before the static initializers of the shared library are executed. Additionally implement dlsym(RTLD_NEXT) manually with dladdr1() and walking the link map, as in the past I have received wrong function pointers from dlsym, pointing to a library earlier in the link map. --- bt.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/bt.c b/bt.c index d51d35c..7302799 100644 --- a/bt.c +++ b/bt.c @@ -24,6 +24,7 @@ #define _GNU_SOURCE #include +#include #include #include @@ -169,11 +170,59 @@ static void* thread_entry(void* th_arg) { return ret; } +static void resolve_next_pthread_create() { + if (NEXT_PTHREAD_CREATE) { + return; + } + + // The following implements dlsym(RTLD_NEXT, "pthread_create") manually. + // + // In the past I have observed a case where dlsym(RTLD_NEXT) returned an + // address from a library earlier in the link map. This is problematic as + // calling the received function pointer ends in an infinite recursion. + // + // This may be a bug or I did not fully understand the case yet as I have + // only observed it in special occasions where there are other LD_PRELOAD + // libraries implementing the same wrapper function and invoking the wrapper + // before the static initializers of this library have been executed. + // + // So far this implementation yielded "better" results. + + void* ret_addr = __builtin_return_address(0); + Dl_info _info; + memset(&_info, 0, sizeof(_info)); + struct link_map* lmap; + + if (dladdr1(ret_addr, &_info, (void**)&lmap, RTLD_DL_LINKMAP) == 0) { + FAIL("dladdr1 failed while resolving next pthread_create"); + exit(1); + } + + for (lmap = lmap->l_next; lmap && !NEXT_PTHREAD_CREATE; lmap = lmap->l_next) { + if (!lmap->l_name) { + continue; + } + + void* dso = dlopen(lmap->l_name, RTLD_LOCAL | RTLD_LAZY); + if (!dso) { + WARN("failed to open %s, continue with next", lmap->l_name); + continue; + } + NEXT_PTHREAD_CREATE = dlsym(dso, "pthread_create"); + dlclose(dso); + } + + if (!NEXT_PTHREAD_CREATE) { + FAIL("failed to resolve next pthread_create function"); + exit(1); + } +} + int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg) { - assert(NEXT_PTHREAD_CREATE); + resolve_next_pthread_create(); struct thread_arg* th_arg = malloc(sizeof(struct thread_arg)); th_arg->start_routine = start_routine; @@ -184,12 +233,6 @@ int pthread_create(pthread_t* thread, // -- GLOBAL INITIALIZATION ---------------------------------------------------- static void __attribute__((constructor)) init() { - NEXT_PTHREAD_CREATE = dlsym(RTLD_NEXT, "pthread_create"); - if (NEXT_PTHREAD_CREATE == NULL) { - FAIL("failed to get pthread_create"); - exit(1); - } - if (libbt_install_sighandler() != 0) { FAIL("failed to install signal handler"); return; -- cgit v1.2.3