3

私は次のことをしようとしています - pthreads ライブラリのラッパーを書き、各 API が呼び出されるたびに情報をログに記録します。記録したい情報の 1 つは、スタック トレースです。

以下は、そのままコンパイルして実行できる元のコードの最小限のスニペットです。

初期化 (ファイル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_* 関数からのスタックの出力は問題なく動作します...

4

1 に答える 1