我正在尝试执行以下操作 - 为 pthreads 库编写一个包装器,该包装器将在它调用的每个 API 时记录一些信息。我想记录的一条信息是堆栈跟踪。
以下是可以按原样编译和运行的原始代码的最小片段。
初始化(文件libmutex.c
):
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <dlfcn.h>
static int (*real_mutex_lock)(pthread_mutex_t *) __attribute__((__may_alias__));
static void *pthread_libhandle;
#ifdef _BIT64
#define PTHREAD_PATH "/lib64/libpthread.so.0"
#else
#define PTHREAD_PATH "/lib/libpthread.so.0"
#endif
static inline void load_real_function(char* function_name, void** real_func) {
char* msg;
*(void**) (real_func) = dlsym(pthread_libhandle, function_name);
msg = dlerror();
if (msg != NULL)
printf("init: real_%s load error %s\n", function_name, msg);
}
void __attribute__((constructor)) my_init(void) {
printf("init: trying to dlopen '%s'\n", PTHREAD_PATH);
pthread_libhandle = dlopen(PTHREAD_PATH, RTLD_LAZY);
if (pthread_libhandle == NULL) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
load_real_function("pthread_mutex_lock", (void**) &real_mutex_lock);
}
包装器和对回溯的调用。我已经尽可能多地从方法中删除了,所以是的,我知道我从不调用原始的 pthread_mutex_lock 例如。
void my_backtrace(void) {
#define SIZE 100
void *buffer[SIZE];
int nptrs;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
}
int pthread_mutex_lock(pthread_mutex_t *mutex) {
printf("In pthread_mutex_lock\n"); fflush(stdout);
my_backtrace();
return 0;
}
为了测试这一点,我使用了这个二进制文件(文件tst_mutex.c
):
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int main (int argc, char *argv[]) {
pthread_mutex_t x;
printf("Before mutex\n"); fflush(stdout);
pthread_mutex_lock(&x);
printf("after mutex\n");fflush(stdout);
return 0;
}
这是所有这些的编译方式:
rm -f *.o *.so tst_mutex
cc -Wall -D_BIT64 -c -m64 -fPIC libmutex.c
cc -m64 -o libmutex.so -shared -fPIC -ldl -lpthread libmutex.o
cc -Wall -m64 tst_mutex.c -o tst_mutex
并运行
LD_PRELOAD=$(pwd)/libmutex.so ./tst_mutex
这会在 Linux x86 上因分段错误而崩溃。在 Linux PPC 上,一切都完美无缺。我尝试了几个版本的 GCC 编译器、GLIBC 库和 Linux 发行版 - 都失败了。
输出是
init: trying to dlopen '/lib64/libpthread.so.0'
Before mutex
In pthread_mutex_lock
In pthread_mutex_lock
In pthread_mutex_lock
...
...
./run.sh: line 1: 25023 Segmentation fault LD_PRELOAD=$(pwd)/libmutex.so ./tst_mutex
暗示这里有递归。我查看了源代码backtrace()
- 其中没有调用锁定机制。它所做的只是简单地遍历堆栈帧链表。我也用 objdump 检查了库代码,但这并没有发现任何异常。
这里发生了什么?任何解决方案/解决方法?
哦,也许是最重要的事情。这只发生在 pthread_mutex_lock 函数中!!从任何其他重写的 pthread_* 函数打印堆栈都可以正常工作...