在运行时用LD_PRELOAD链接函数

Linking functions with LD_PRELOAD at runtime

本文关键字:PRELOAD 链接 函数 LD 运行时      更新时间:2023-10-16

我正在编写一个库,通过运行LD_PRELOAD=mylib myexe程序来拦截对mallocfree的调用。

mallocfree的呼叫均截取良好。我的问题是,在mylib中还有另一个函数,当使用LD_PRELOAD时,我也想要拦截,我无法弄清楚为什么它不像调用mallocfree一样"只工作"。

在mylib.c:

void* malloc(size_t s)
{
    return doMyMalloc();
}
void free(void* p)
{
    doMyFree(p);
}
void otherThing(size_t)
{
    doThing();
}
在myexe.cpp:

#include <malloc.h>
extern "C" void otherThing(size_t);  // Compile with -Wl,--unresolved-symbols=ignore-all
int main(int argc, char* argv[])
{
    void* x = malloc(1000);   // Successfully intercepted.
    free(x);  // Successfully intercepted.
    otherThing(1);  // Segfault.
}

我设法使它工作的一种方法是:

typedef void (*FUNC)(size_t);
FUNC otherThing = NULL;
int main(int argc, char* argv[])
{
    otherThing = (FUNC)dlsym(RTLD_NEXT, "otherThing");
    otherThing(1);  // Successfully calls mylib's otherThing().
}

但是我不想写这些代码;我不需要为mallocfree做这些。如果缺少LD_PRELOAD前缀,程序崩溃也没关系。

我觉得你是在用一个解决方案(LD_PRELOAD)来解决两个不同的问题。首先,要对malloc()free()打补丁。你成功了,太棒了。接下来,您希望拥有一个运行时"插件"系统,在构建时不链接任何库,而只在运行时链接。这通常是使用dlopen()dlsym()完成的,我建议您使用它们。

这个想法是,您不想在构建时指定otherThing()的特定实现,但您确实需要在运行时有一些实现(或者您正确地期望崩溃)。所以让我们明确一点,在运行时使用dlsym()来解析函数名,当然,如果找不到它,会进行错误检测。

至于在哪里定义otherThing(),它可以在一个完全独立的文件给dlopen(),或在mylib(在这种情况下传递NULL作为文件名给dlopen())。

这个问题有点棘手。网上有很多关于这个的帖子,但我将试着把它分解成"让它工作"。

如果这是在Linux下,那么发生的事情是应用程序已被编译为无法使用外部符号。最快的解决方案是在主应用程序中添加与库中使用的相同的编译标志,即在主应用程序的编译中添加-fPIC标志,就像对库所做的那样。

与其使用-Wl,--unresolved-symbols=ignore-all标志,不如使用__attribute__ ((weak)),如:

extern "C" void otherThing(size_t) __attribute__ ((weak);

并在运行时检查它是否为NULL,这将允许您确定它是否已设置。

通过以与.so相同的方式编译主应用程序,您隐式地允许将其本身用作LD_PRELOAD的目标,如手册页:

LD_PRELOAD

附加的用户指定的ELF共享库列表在其他所有人之前装载。列表中的项可以用。分隔空格或冒号。这可以用来选择性地覆盖.