简体   繁体   English

LD_PRELOAD 是否可能只影响主可执行文件?

[英]Is it possible for an LD_PRELOAD to only affect the main executable?

The Actual Problem实际问题

I have an executable that by default uses EGL and SDL 1.2 to handle graphics and user input respectively.我有一个默认情况下使用 EGL 和 SDL 1.2 分别处理图形和用户输入的可执行文件。 Using LD_PRELOAD , I have replaced both with GLFW.使用LD_PRELOAD ,我已将两者都替换为 GLFW。

This works normally unless the user has installed the Wayland version of GLFW, which depends on EGL itself.这可以正常工作,除非用户安装了 GLFW 的 Wayland 版本,这取决于 EGL 本身。 Because all the EGL calls are either stubbed to do nothing or call GLFW equivalents, it doesn't work (ie. eglSwapBuffers calls glfwSwapBuffers which calls eglSwapBuffers and so on).因为所有的 EGL 调用要么不做任何事情,要么调用 GLFW 等价物,所以它不起作用(即eglSwapBuffers调用glfwSwapBuffers调用eglSwapBuffers等等)。 I can't remove the EGL stubs because then it would call both EGL and GLFW and the main executable is closed-source so I can't modify that.我无法删除 EGL 存根,因为它会同时调用 EGL 和 GLFW,并且主可执行文件是封闭源代码,因此我无法对其进行修改。

Is there any way to make LD_PRELOAD affect the main executable but not GLFW?有什么方法可以使LD_PRELOAD影响主可执行文件但不影响 GLFW? Or any other solution to obtain the same effect?或任何其他解决方案以获得相同的效果?

The Simplified Problem简化的问题

I made a simplified example to demonstrate the problem.我做了一个简化的例子来演示这个问题。

Main Executable:主要可执行文件:

#include <stdio.h>

extern void do_something();

int main() {
    do_something();
    fputs("testing B\n", stderr);
}

Shared Library:共享库:

#include <stdio.h>

void do_something() {
    fputs("testing A\n", stderr);
}

Preloaded Library:预加载库:

#include <stdio.h>

int fputs(const char *str, FILE *file) {
    // Do Nothing
    return 0;
}

When the preloaded library isn't used, the output is:不使用预加载库时,output 为:

testing A
testing B

When it is used, the output is nothing.使用时,output什么都不是。

I'm looking for a way to make the preloaded library only affect the main executable, that the output would be:我正在寻找一种使预加载库仅影响主可执行文件的方法,即 output 将是:

testing A

Thank you!谢谢!

You can check if the return address is in the executable or the library, and then call either the "real" function or do your stub code, like this:您可以检查返回地址是否在可执行文件或库中,然后调用“真实” function 或执行存根代码,如下所示:

#define _GNU_SOURCE

#include <dlfcn.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>

static struct {
    ElfW(Addr) start, end;
} *segments;
static int n;
static int (*real_fputs)(const char *, FILE *);

static int callback(struct dl_phdr_info *info, size_t size, void *data) {
    n = info->dlpi_phnum;
    segments = malloc(n * sizeof *segments);
    for(int i = 0; i < n; ++i) {
        segments[i].start = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
        segments[i].end = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
    }
    return 1;
}

__attribute__((__constructor__))
static void setup(void) {
    real_fputs = dlsym(RTLD_NEXT, "fputs");
    dl_iterate_phdr(callback, NULL);
}

__attribute__((__destructor__))
static void teardown(void) {
    free(segments);
}

__attribute__((__noinline__))
int fputs(const char *str, FILE *file) {
    ElfW(Addr) addr = (ElfW(Addr))__builtin_extract_return_addr(__builtin_return_address(0));
    for(int i = 0; i < n; ++i) {
        if(addr >= segments[i].start && addr < segments[i].end) {
            // Do Nothing
            return 0;
        }
    }
    return real_fputs(str, file);
}

This has some caveats, though.不过,这有一些警告。 For example, if your executable calls a library function that tail-calls a function you're hooking, then this will incorrectly consider that library call an executable call.例如,如果您的可执行文件调用了一个库 function,该库对您正在挂钩的 function 进行尾部调用,那么这将错误地认为该库调用是一个可执行调用。 (You could mitigate this problem by adding wrappers for those library functions too, that unconditionally forward to the "real" function, and compiling the wrapper code with -fno-optimize-sibling-calls .) Also, there's no way to distinguish whether anonymous executable memory (eg, JITted code) originally came from the executable or a library. (您也可以通过为这些库函数添加包装器来缓解这个问题,无条件地转发到“真正的”function,并使用-fno-optimize-sibling-calls编译包装器代码。)此外,没有办法区分是否匿名可执行文件 memory(例如 JITted 代码)最初来自可执行文件或库。

To test this, save my code as hook_fputs.c , your main executable as main.c , and your shared library as libfoo.c .要对此进行测试,请将我的代码保存为hook_fputs.c ,您的主要可执行文件为main.c ,您的共享库为libfoo.c Then run these commands:然后运行这些命令:

clang -fPIC -shared hook_fputs.c -ldl -o hook_fputs.so
clang -fPIC -shared libfoo.c -o libfoo.so
clang main.c ./libfoo.so
LD_PRELOAD=./hook_fputs.so ./a.out

Implement the interposing library separately for the two cases.针对这两种情况分别实现插入库。

Create a wrapper script or program that uses ldd to find out the exact EGL library version and their paths the target binary is dynamically linked against;创建一个包装脚本或程序,使用ldd找出确切的 EGL 库版本及其目标二进制文件动态链接的路径; then, using ldd on the the GLFW library, to find out whether it is linked against EGL or not.然后,在 GLFW 库上使用ldd来确定它是否与 EGL 链接。 Finally, have it execute the target binary with the path to the appropriate interposing library in LD_PRELOAD environment variable.最后,让它使用LD_PRELOAD环境变量中适当的插入库的路径执行目标二进制文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM