c - 在 glibc 上覆盖 pthread 函数但在 musl 上不覆盖时出现神秘的段错误

标签 c pthreads glibc dynamic-linking 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,问题就会完全消失。

最佳答案

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

这很复杂。

您可能使用的是 Linux/x86_64,并且被 this bug 击中。 。另请参阅this original report .

更新:

事实证明,符号版本与问题无关(在 x86_64 上,不存在 pthread_createpthread_exit 的多个版本)。

问题是gcc配置为传递 --as-needed到链接器。

当您链接到pthread_exit#ifdef编辑出来,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 之前的主应用程序链接行.

附注我想起了rule #3 from David Agans' book (我强烈推荐):停止思考,看看

关于c - 在 glibc 上覆盖 pthread 函数但在 musl 上不覆盖时出现神秘的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44177595/

相关文章:

c - 从 char * 指向的内存的一个字节中检索 long int 和 float 的值

c - 如何调用Oracle的olog()?

c - 高效素数函数

c - Pthread信号丢失/条件缓慢?

c - C 中的 Pthread_cond_wait

c++ - 关于C++内存分配和删除的问题

c - C项目中的段错误

c++ - DLL函数调用问题如何解决

c - 管道上的非阻塞读取

c - 如何将新的源文件添加到 glibc makefile 中?