首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >LD_PRELOAD与联动

LD_PRELOAD与联动
EN

Stack Overflow用户
提问于 2017-10-20 08:48:20
回答 2查看 839关注 0票数 1

我有一个小的测试代码atfork_demo.c

代码语言:javascript
复制
#include <stdio.h>
#include <pthread.h>

void hello_from_fork_prepare() {
    printf("Hello from atfork prepare.\n");
    fflush(stdout);
}

void register_hello_from_fork_prepare() {
    pthread_atfork(&hello_from_fork_prepare, 0, 0);
}

现在,我用两种不同的方式编译它:

代码语言:javascript
复制
gcc -shared -fPIC atfork_demo.c -o atfork_demo1.so
gcc -shared -fPIC atfork_demo.c -o atfork_demo2.so -lpthread

我的演示主atfork_demo_main.c是:

代码语言:javascript
复制
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, const char** argv) {
    if(argc <= 1) {
        printf("usage: ... lib.so\n");
        return 1;
    }
    void* plib = dlopen("libpthread.so.0", RTLD_NOW|RTLD_GLOBAL);
    if(!plib) {
        printf("cannot load pthread, error %s\n", dlerror());
        return 1;
    }
    void* lib = dlopen(argv[1], RTLD_LAZY);
    if(!lib) {
        printf("cannot load %s, error %s\n", argv[1], dlerror());
        return 1;
    }
    void (*reg)();
    reg = dlsym(lib, "register_hello_from_fork_prepare");
    if(!reg) {
        printf("did not found func, error %s\n", dlerror());
        return 1;
    }
    reg();
    fork();
}

我是这样编的:

代码语言:javascript
复制
gcc atfork_demo_main.c -o atfork_demo_main.exec -ldl

现在,我有了另一个小演示atfork_patch.c,我想要覆盖pthread_atfork

代码语言:javascript
复制
#include <stdio.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
  printf("Ignoring pthread_atfork call!\n");
  fflush(stdout);
  return 0;
}

我是这样编的:

代码语言:javascript
复制
gcc -shared -O2 -fPIC patch_atfork.c -o patch_atfork.so

然后设置LD_PRELOAD=./atfork_patch.so,并执行以下两个调用:

代码语言:javascript
复制
./atfork_demo_main.exec ./atfork_demo1.so
./atfork_demo_main.exec ./atfork_demo2.so

在第一种情况下,LD_PRELOAD-override of pthread_atfork起作用,而在第二种情况下,它没有起作用。我得到了输出:

代码语言:javascript
复制
Ignoring pthread_atfork call!
Hello from atfork prepare.

因此,现在来问问题:

  • 为什么在第二种情况下不起作用呢?
  • 如何使它在第二种情况下也能工作,即也覆盖它?在我的实际用例中,atfork_demo是一些我无法更改的库。我也不能更改atfork_demo_main,但是我可以让它加载任何其他代码。我更希望我能在atfork_patch中做一些改变。

如果还使用LD_DEBUG=all,您将获得更多的调试输出。也许这一点很有趣,对于第二种情况:

代码语言:javascript
复制
   841:     symbol=__register_atfork;  lookup in file=./atfork_demo_main.exec [0]
   841:     symbol=__register_atfork;  lookup in file=./atfork_patch_extended.so [0]
   841:     symbol=__register_atfork;  lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
   841:     symbol=__register_atfork;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
   841:     binding file ./atfork_demo2.so [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__register_atfork' [GLIBC_2.3.2]

因此,它搜索符号__register_atfork。我将它添加到atfork_patch_extended.so中,但它没有找到它,而是从libc中使用它。如何使它找到并使用我的__register_atfork

顺便提一句,我的主要目标是在调用fork()时忽略atfork处理程序,但这里不是问题,实际上是这里。解决这一问题的一种解决方案似乎有效,就是通过以下方式覆盖fork()本身:

代码语言:javascript
复制
pid_t fork(void) {
  return syscall(SYS_clone, SIGCHLD, 0);
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-20 11:33:35

在回答这个问题之前,我要强调的是,对于任何生产应用程序来说,--这是一个非常糟糕的主意--

如果您正在使用一个第三方库来设置这些约束,那么请考虑另一种解决方案,比如尽早分叉来维护“助手”过程,在您和它之间有一个管道.然后,当您需要调用exec()时,可以请求它代表您完成工作(fork()exec())。

对系统调用(如pthread_atfork() )的服务进行修补或旁路处理只是在自找麻烦(遗漏的事件、内存泄漏、崩溃等)。

正如@Sergio所指出的,pthread_atfork()实际上是内置于atfork_demo2.so中的,所以您不能做任何事情来覆盖它.但是,查看pthread_atfork()的反汇编/源代码可以为您提供一个很好的提示,说明如何实现您所要求的目标:

代码语言:javascript
复制
0000000000000830 <__pthread_atfork>:
 830:   48 8d 05 f9 07 20 00    lea    0x2007f9(%rip),%rax        # 201030 <__dso_handle>
 837:   48 85 c0                test   %rax,%rax
 83a:   74 0c                   je     848 <__pthread_atfork+0x18>
 83c:   48 8b 08                mov    (%rax),%rcx
 83f:   e9 6c fe ff ff          jmpq   6b0 <__register_atfork@plt>
 844:   0f 1f 40 00             nopl   0x0(%rax)
 848:   31 c9                   xor    %ecx,%ecx
 84a:   e9 61 fe ff ff          jmpq   6b0 <__register_atfork@plt>

或源(来自这里):

代码语言:javascript
复制
int
pthread_atfork (void (*prepare) (void),
        void (*parent) (void),
        void (*child) (void))
{
  return __register_atfork (prepare, parent, child, &__dso_handle == NULL ? NULL : __dso_handle);
}

如你所见,pthread_atfork()除了给__register_atfork()打电话外,什么也不做.所以把它补上吧!

atfork_patch.c的内容现在变成:(使用__register_atfork()的原型,来自这里 / 这里)

代码语言:javascript
复制
#include <stdio.h>

int __register_atfork (void (*prepare) (void), void (*parent) (void),
                       void (*child) (void), void *dso_handle) {
  printf("Ignoring pthread_atfork call!\n");
  fflush(stdout);
  return 0;
}

这两种演示都适用于:

代码语言:javascript
复制
$ LD_PRELOAD=./atfork_patch.so ./atfork_demo_main.exec ./atfork_demo1.so
Ignoring pthread_atfork call!
$ LD_PRELOAD=./atfork_patch.so ./atfork_demo_main.exec ./atfork_demo2.so
Ignoring pthread_atfork call!
票数 2
EN

Stack Overflow用户

发布于 2017-10-20 09:18:06

它不适用于第二种情况,因为没有什么可重写的。您的第二个库静态地与p线程库链接:

代码语言:javascript
复制
$ readelf --symbols atfork_demo1.so | grep pthread_atfork
     7: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND pthread_atfork
    54: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND pthread_atfork
$ readelf --symbols atfork_demo2.so | grep pthread_atfork
    41: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS pthread_atfork.c
    47: 0000000000000830    31 FUNC    LOCAL  DEFAULT   12 __pthread_atfork
    49: 0000000000000830    31 FUNC    LOCAL  DEFAULT   12 pthread_atfork

因此,它每次都将使用本地pthread_atfork,而不考虑LD_PRELOAD或任何其他加载库。

如何克服这一问题?看起来,对于所描述的配置,这是不可能的,因为无论如何您需要修改atfork_demo库或主可执行文件。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46845496

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档