在Linux x86上调用backtrace()时出现分段错误

时间:2013-04-24 16:05:06

标签: linux gcc pthreads backtrace ld-preload

我正在尝试执行以下操作 - 为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()的源代码 - 其中有 no 调用锁定机制。它只是简单地遍历堆栈框架链表。 我也用objdump检查了库代码,但是没有透露任何异常。

这里发生了什么? 任何解决方案/解决方法?

哦,也许是最重要的事情。这只发生在pthread_mutex_lock函数中! 从任何其他重写的pthread_ *函数打印堆栈就可以了......

1 个答案:

答案 0 :(得分:0)

这是一个堆栈溢出,由无休止的递归引起(正如@Chris Dodd所说)。 backtrace()函数运行从使用pthread库编译的程序调用的不同系统调用。即使程序没有明确调用pthread函数。

这是一个使用backtrace()函数的简单程序,不使用任何pthread函数。

#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>

int main(void)
{
 void* buffer[100];
 int num_ret_addr;

 num_ret_addr=backtrace(buffer, 100); 
 printf("returned number of addr %d\n", num_ret_addr);

 return 0;
}

让我们编译它而不链接到pthread并使用strace实用程序检查程序系统调用。输出中不会出现与互斥锁相关的系统调用。

$ gcc -o backtrace_no_thread backtrace.c
$ strace -o backtrace_no_thread.out backtrace_no_thread

不允许编译将其链接到pthread库的相同代码,运行strace并查看其输出。

$ gcc -o backtrace_with_thread backtrace.c -lpthread
$ strace -o backtrace_with_thread.out backtrace_with_thread

这次输出包含与互斥锁相关的系统调用(它们的名称可能取决于平台)。这是在X86 Linux机器上获得的strace输出文件的片段。

futex(0x3240553f80, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x324480d350, FUTEX_WAKE_PRIVATE, 2147483647) = 0