3

我正在尝试覆盖pthread_createand pthread_exit。覆盖应该调用原件。

我可以覆盖pthread_create,只要我退出我的主线程,它似乎就可以工作pthread_exit(0);。如果我不这样做,则会出现段错误。

如果我什至尝试覆盖pthread_exit,我会得到段错误。

我的设置如下:

#!/bin/sh

cat > test.c <<EOF
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void *thr(void *Arg)
{
    printf("i=%d\n", (int)(intptr_t)Arg);
    return 0;
}
int main()
{
    putchar('\n');
    pthread_t tids[4];
    for(int i=0; i < sizeof tids / sizeof tids[0]; i++){
        pthread_create(tids+i, 0, thr, (void*)(intptr_t)i);

    }
    pthread_exit(0); //SEGFAULTS if this isn't here
    return 0;
}
EOF
cat > pthread_override.c <<EOF

#define _GNU_SOURCE
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>

#if 1
__attribute__((__visibility__("default")))
int pthread_create(
        pthread_t *restrict Thr, 
        pthread_attr_t const *Attr,
        void *(*Fn) (void *), 
        void *Arg
        )
{
    int r;
    int (*real_pthread_create)(
        pthread_t *restrict Thr, 
        pthread_attr_t const *Attr,
        void *(*Fn) (void *), 
        void *Arg
    ) = dlsym(RTLD_NEXT, "pthread_create");
    printf("CREATE BEGIN: %p\n", (void*)Thr);
    r = real_pthread_create(Thr, Attr, Fn, Arg);
    printf("CREATE END: %p\n", (void*)Thr);
    return r;
}
#endif

#if 0 
//SEGFAULTS if this is allowed
__attribute__((__visibility__("default")))
_Noreturn
void pthread_exit(void *Retval)
{
    __attribute__((__noreturn__)) void (*real_pthread_exit)( void *Arg);
    real_pthread_exit = dlsym(RTLD_NEXT, "pthread_exit");
    printf("%p\n", (void*)real_pthread_exit);
    puts("EXIT");
    real_pthread_exit(Retval);
}
#endif
EOF

: ${CC:=gcc}
$CC -g -fpic pthread_override.c -shared -o pthread.so -ldl
$CC -g test.c $PWD/pthread.so -ldl -lpthread 
./a.out

谁能向我解释我做错了什么以及段错误的原因是什么?

如果我用 musl-gcc 代替 gcc,问题就完全消失了。

4

2 回答 2

2

您可以改为使用 编译-Wl,--wrap=pthread_create,并__wrap_pthread_create()通过调用__real_pthread_create().

这是进行这种插入的更常见的方式。

于 2017-05-25T11:50:37.337 回答
2

谁能向我解释我做错了什么以及段错误的原因是什么?

情况很复杂。

您可能在 Linux/x86_64 上,并且被这个 bug击中。另见这份原始报告

更新:

事实证明,符号版本与问题无关(在 上x86_64没有pthread_createor的多个版本pthread_exit)。

问题在于gcc配置为传递--as-needed给链接器。

当你用pthread_exit #ifdefed out 链接时,a.out二进制文件pthread_exit来自libpthread.so.0,它被记录为一个NEEDED共享库:

readelf -d a.out | grep libpthread
0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]

当您#ifdef pthread_exit进入时,不再需要任何真正的libpthread.so.0符号(引用满足pthread.so):

readelf -d a.out | grep libpthread
# no output!

然后这会导致dlsym失败(没有返回的下一个符号 -pthread.so定义唯一的一个):

Breakpoint 2, __dlsym (handle=0xffffffffffffffff, name=0x7ffff7bd8881 "pthread_create") at dlsym.c:56
56  dlsym.c: No such file or directory.
(gdb) fin
Run till exit from #0  __dlsym (handle=0xffffffffffffffff, name=0x7ffff7bd8881 "pthread_create") at dlsym.c:56
pthread_create (Thr=0x7fffffffdc80, Attr=0x0, Fn=0x40077d <thr>, Arg=0x0) at pthread_override.c:17
17      int (*real_pthread_create)(
Value returned is $1 = (void *) 0x0

解决方法:-Wl,--no-as-needed在主应用程序链接行之前添加-lpthread.

PS 我想起了David Agans 的书(我强烈推荐)中的第 3 条规则:Quit thinking and look

于 2017-05-26T03:22:03.210 回答