【发布时间】:2013-04-18 07:03:03
【问题描述】:
我正在尝试执行以下操作 - 为 pthreads 库编写一个包装器,该包装器将在它调用的每个 API 时记录一些信息。 我想记录的一条信息是堆栈跟踪。
以下是原始代码中可以按原样编译和运行的最小 sn-p。
初始化(文件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() 的源代码 - 其中有 no 调用锁定机制。它所做的只是简单地遍历堆栈帧链表。
我也用 objdump 检查了库代码,但没有发现任何异常。
这里发生了什么? 任何解决方案/解决方法?
哦,也许是最重要的事情。这只发生在 pthread_mutex_lock 函数中!! 从任何其他重写的 pthread_* 函数打印堆栈都可以正常工作......
【问题讨论】:
-
您尝试过 RTLD_NOW 吗?
-
问题在于没有打开 pthreads 库。
-
我尝试了您的步骤,但收到错误
./tst_mutex: symbol lookup error: libmutex.so: undefined symbol: dlopen。我现在不打算解决这个问题,但重现问题的步骤中可能缺少某些东西。 -
我猜
backtrace在内部某个地方调用pthread_mutex_lock,所以你最终会出现一个循环。当堆栈溢出时会发生段错误。为避免这种情况,您需要在挂钩时不要挂钩入口点。 -
@Gabriel:我已经编辑了这个问题。前两个代码段进入一个名为 libmutex.c 的文件 第三个代码段(测试)进入 tst_mutex.c #include
提供 dlopen 函数。
标签: linux gcc pthreads backtrace ld-preload