在运行时将函数与 LD_PRELOAD 链接
Posted
技术标签:
【中文标题】在运行时将函数与 LD_PRELOAD 链接【英文标题】:Linking functions with LD_PRELOAD at runtime 【发布时间】:2013-04-21 01:01:11 【问题描述】:我正在编写一个库,通过使用LD_PRELOAD=mylib myexe
运行程序,在运行时拦截对malloc
和free
的调用。
对malloc
和free
的调用可以拦截。我的问题是 mylib 中还有另一个函数,当使用 LD_PRELOAD
时我也想拦截它,我无法弄清楚为什么它不像对 malloc
和 free
的调用那样“正常工作”。
在 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().
但我不想编写所有这些代码;我不必为malloc
和free
这样做。
如果LD_PRELOAD
前缀丢失,程序崩溃也没关系。
【问题讨论】:
你是用 -lmylib 编译你的 exe 吗? @auselen 好的,但问题是现在malloc
和free
总是被“拦截”,即使我不使用LD_PRELOAD
。我不希望这样,我希望它仅在使用 LD_PRELOAD
时才使用 mylib。
最简单的方法是将它们分成两个共享库。我建议你用笔/纸来起草你的问题。这些事情很容易变成鸡蛋问题。
分段错误的本质是什么?它发生在哪里?是otherThing()
中的崩溃,还是doThing()
中的崩溃或从中调用的东西,或者是在调用otherThing()
的代码中?我不确定它会告诉你什么,但我确信这些区别很重要。特别是,如果问题出在doThing()
或它所调用的东西上,那么问题不直接在于预加载机制。如果您在没有-Wl,--unresolved-symbols=ignore-all
选项的情况下进行编译,您会收到关于malloc()
和free()
的投诉,还是只有otherThing()
?这很重要吗?
为什么要忽略未解析的符号?
【参考方案1】:
我觉得您正在应用一个解决方案 (LD_PRELOAD
) 来解决两个不同的问题。首先,您要修补malloc()
和free()
。你已经得到了那个工作 - 太棒了。接下来,您希望拥有一个运行时“插件”系统,在该系统中,您在构建时不链接任何库,而仅在运行时链接。这通常使用dlopen()
和dlsym()
完成,我建议您使用它们。
这个想法是您不想在构建时指定otherThing()
的特定实现,但您确实需要在运行时进行一些实现(或者您理所当然地期望崩溃)。所以让我们明确一点,并在运行时使用dlsym()
来解析函数名称,当然还有错误检测,以防找不到。
至于在哪里定义otherThing()
,它可以在一个完全独立的文件中给dlopen()
,或者在mylib
(在这种情况下将NULL作为文件名传递给dlopen()
)。
【讨论】:
【参考方案2】:这有点棘手。互联网上有几篇与此相关的帖子,但我会尝试将其分解为“让它发挥作用”。
如果这是在 Linux 下,那么发生的情况是应用程序已被编译为无法使用外部符号。最快的解决方案是在主应用程序中添加与库中使用的相同的编译标志,即将-fPIC
标志添加到主应用程序的编译中,就像为库所做的那样。
而不是使用-Wl,--unresolved-symbols=ignore-all
标志,您应该使用__attribute__ ((weak))
作为函数,例如:
extern "C" void otherThing(size_t) __attribute__ ((weak);
并在运行时检查它是否为 NULL,这将允许您确定它是否已设置。
通过以与.so
相同的方式编译主应用程序,您隐含地允许它本身用作LD_PRELOAD
的目标,如手册页所述:
LD_PRELOAD
附加的、用户指定的 ELF 共享库列表 在所有其他人之前加载。列表的项目可以通过 空格或冒号。这可用于选择性地覆盖 其他共享库中的函数。
【讨论】:
以上是关于在运行时将函数与 LD_PRELOAD 链接的主要内容,如果未能解决你的问题,请参考以下文章
docker-compose RabbitMQ与Nodejs接收端同时运行时的错误