在 glibc 上而不是在 musl 上覆盖 pthread 函数时出现神秘的段错误

Mysterious segfaults when overriding pthread functions on glibc but not on musl

我正在尝试覆盖 pthread_createpthread_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,问题就完全消失了。

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

来实现 __wrap_pthread_create()

这是进行这种插入的更常用的方法。

Can anyone explain to me what I'm doing wrong and what the reason for the segfaults is?

这很复杂。

您可能在 Linux/x86_64,并被 this bug. See also this original report 击中。

更新:

事实证明,符号版本与问题无关(在x86_64上,有没有多个版本pthread_createpthread_exit).

问题是 gcc 配置为将 --as-needed 传递给 linker。

当你用 pthread_exit #ifdef 编辑出 link 时,a.out 二进制文件从 libpthread.so.0 得到 pthread_exit,记录为一个 NEEDED 共享库:

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

当你在#ifdef pthread_exit中,none个真正的libpthread.so.0符号就不再需要了(引用由pthread.so满足):

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

这会导致 dlsym 失败(有 no 下一个符号到 return -- 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  = (void *) 0x0

解决方法:在-lpthread.

之前的主应用link行添加-Wl,--no-as-needed

P.S。我被提醒 rule #3 from David Agans' book(强烈推荐):别想了,看看