Linux Kernel 6.1下LD_PRELOAD无法拦截pthread_create问题求助
核心原因:glibc 2.34+ 中 pthread_create 的实现变更
从 glibc 2.34 版本开始,pthread_create 被重构为内联(inline)函数,其定义直接内联到调用者代码中,实际执行的是 glibc 内部的私有符号 __pthread_create,而非原来的公开导出符号 pthread_create。
在 glibc 2.31 中,pthread_create 是普通的导出函数,二进制会直接引用这个符号,因此你的 LD_PRELOAD 库可以正常拦截。但到了 glibc 2.36,当目标二进制编译时使用了新版头文件,代码会直接展开为对 __pthread_create 的调用,完全绕过公开的 pthread_create 符号,导致拦截代码失效。
而 pthread_mutex_init 等函数并未做此变更,仍保持公开导出符号的实现,所以拦截依然有效。
验证方法
你可以通过 nm 命令检查目标二进制引用的符号:
nm -D <你的目标二进制文件> | grep pthread_create
如果输出是 U __pthread_create,说明二进制确实直接引用了内部私有符号,而非 pthread_create。
修复方案
修改拦截库,改为拦截内部符号 __pthread_create,同时通过 dlsym(RTLD_NEXT) 获取真正的函数实现以避免递归调用:
修改后的拦截代码
#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <pthread.h> #include <stdio.h> #include <dlfcn.h> // 拦截 glibc 内部的 __pthread_create 符号 int __pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { printf("[p] pthread_create (intercepted)\n"); // 获取真正的 __pthread_create 函数指针 static int (*real_pthread_create)(pthread_t*, const pthread_attr_t*, void*(*)(void*), void*) = NULL; if (!real_pthread_create) { real_pthread_create = dlsym(RTLD_NEXT, "__pthread_create"); if (!real_pthread_create) { fprintf(stderr, "Failed to get real __pthread_create: %s\n", dlerror()); return -1; } } return real_pthread_create(thread, attr, start_routine, arg); } int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { printf("[p] pthread_mutex_init\n"); static int (*real_mutex_init)(pthread_mutex_t*, const pthread_mutexattr_t*) = NULL; if (!real_mutex_init) { real_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init"); } return real_mutex_init(mutex, attr); }
编译命令
编译时需要链接 dl 库以使用 dlsym:
gcc -shared -fPIC -o intercept.so intercept.c -ldl
额外说明
部分场景下,glibc 可能还保留了 pthread_create 的符号版本(如 pthread_create@GLIBC_2.2.5),但这仅为兼容旧二进制使用。如果你的目标二进制是基于新版 glibc 编译的,依然会优先使用内联调用 __pthread_create,因此拦截内部符号是最可靠的方案。
内容的提问来源于stack exchange,提问作者Victor

