LD_PRELOAD和链接 [英] LD_PRELOAD and linkage

查看:116
本文介绍了LD_PRELOAD和链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have this small testcode atfork_demo.c:

#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);
}

现在,我以两种不同的方式对其进行编译:

Now, I compile it in two different ways:

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是这样的:

My demo main atfork_demo_main.c is this:

#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();
}

我这样编译:

gcc atfork_demo_main.c -o atfork_demo_main.exec -ldl

现在,我有另一个小演示atfork_patch.c,在这里我想覆盖pthread_atfork:

Now, I have another small demo atfork_patch.c where I want to override pthread_atfork:

#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;
}

我这样编译:

gcc -shared -O2 -fPIC patch_atfork.c -o patch_atfork.so

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

And then I set LD_PRELOAD=./atfork_patch.so, and do these two calls:

./atfork_demo_main.exec ./atfork_demo1.so
./atfork_demo_main.exec ./atfork_demo2.so

在第一种情况下,pthread_atforkLD_PRELOAD替代有效,而在第二种情况下,则无效.我得到了输出:

In the first case, the LD_PRELOAD-override of pthread_atfork worked and in the second, it did not. I get the output:

Ignoring pthread_atfork call!
Hello from atfork prepare.

所以,现在是一个问题:

So, now to the question(s):

  • 为什么在第二种情况下不起作用?
  • 在第二种情况下如何使它也能工作,即也将其覆盖? 在我的实际用例中,atfork_demo是一些我无法更改的库.我也不能更改atfork_demo_main,但可以使它加载任何其他代码.我希望可以通过atfork_patch的一些更改来做到这一点.
  • Why did it not work in the second case?
  • How can I make it work also in the second case, i.e. also override it? In my real use case, atfork_demo is some library which I cannot change. I also cannot change atfork_demo_main but I can make it load any other code. I would prefer if I can just do it with some change in atfork_patch.

如果还使用LD_DEBUG=all,则将获得更多调试输出.对于第二种情况,也许有点有趣:

You get some more debug output if you also use LD_DEBUG=all. Maybe interesting is this bit, for the second case:

   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?

So, it searches for the symbol __register_atfork. I added that to atfork_patch_extended.so but it doesn't find it and uses it from libc instead. How can I make it find and use my __register_atfork?

作为旁注,我的主要目标是在调用fork()时忽略atfork处理程序,但这不是这里的问题,而是

As a side note, my main goal is to ignore the atfork handlers when fork() is called, but this is not the question here, but actually here. One solution to that, which seems to work, is to override fork() itself by this:

pid_t fork(void) {
  return syscall(SYS_clone, SIGCHLD, 0);
}

推荐答案

在回答此问题之前,我会强调 这是一个非常糟糕的主意

Before answering this question, I would stress that this is a really bad idea for any production application.

如果您使用的是放置了此类约束的第三方库,请考虑另一种解决方案,例如尽早进行分叉以维护" helper "过程,并且在您与它...然后,当您需要调用exec()时,您可以请求它代表您执行工作(fork()exec()).

If you are using a third party library that puts such constraints in place, then think about an alternative solution, such as forking early to maintain a "helper" process, with a pipe between you and it... then, when you need to call exec(), you can request that it does the work (fork(), exec()) on your behalf.

修补或以其他方式避开诸如pthread_atfork()之类的系统调用的服务只是在问问题(丢失事件,内存泄漏,崩溃等).

Patching or otherwise side-stepping the services of a system call such as pthread_atfork() is just asking for trouble (missed events, memory leaks, crashes, etc...).

正如@Sergio指出的那样,pthread_atfork()实际上是内置在atfork_demo2.so中的,因此您无法做任何事情来覆盖它...但是,检查pthread_atfork()的反汇编/源代码可以使您很好地了解如何达到您的要求:

As @Sergio pointed out, pthread_atfork() is actually built into atfork_demo2.so, so you can't do anything to override it... However examining the disassembly / source of pthread_atfork() gives you a decent hint about how achieve what you're asking:

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>

或来源(来自如您所见,pthread_atfork()除了调用__register_atfork()之外没有任何其他作用...因此请对其进行修补!

As you can see, pthread_atfork() does nothing aside from calling __register_atfork()... so patch that instead!

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

The content of atfork_patch.c now becomes: (using __register_atfork()'s prototype, from here / here)

#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;
}

这对两个演示都适用:

$ 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!

这篇关于LD_PRELOAD和链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆