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

时间:2017-05-25 09:58:29

标签: 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,问题就完全消失了。

2 个答案:

答案 0 :(得分:2)

您可以使用-Wl,--wrap=pthread_create进行编译,并通过调用__wrap_pthread_create()来实施__real_pthread_create()

这是进行此类介入的更常用方式。

答案 1 :(得分:2)

  

任何人都可以向我解释我做错了什么以及段错误的原因是什么?

这太复杂了。

你可能在Linux / x86_64上,被this bug击中。另请参阅this original report

<强>更新

事实证明符号版本 nothing 与问题有关(在x86_64上,没有多个版本的pthread_create或{ {1}})。

问题是pthread_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定义只有一个):< / p>

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添加到主应用程序链接行。

P.S。我被提醒rule #3 from David Agans' book(我强烈推荐):退出思考,看看