【问题标题】:Change library load order at run time (like LD_PRELOAD but during execution)在运行时更改库加载顺序(如 LD_PRELOAD 但在执行期间)
【发布时间】:2010-06-18 06:10:30
【问题描述】:

如何更改函数在运行时加载的库?

例如,假设我想用新的东西替换标准的printf函数,我可以编写自己的版本并将其编译到共享库中,然后将“LD_PRELOAD=/my/library.so”放入环境中在运行我的可执行文件之前。

但是让我们说,相反,我想从程序本身更改该链接。这肯定是可能的……对吧?

编辑
不,以下内容不起作用(但如果你能告诉我如何让它工作,那就足够了)。

void* mylib = dlopen("/path/to/library.so",RTLD_NOW);
printf = dlsym(mylib,"printf");

【问题讨论】:

    标签: c linux shared-libraries


    【解决方案1】:

    AFAIK,这是不可能的。一般规则是,如果相同的符号出现在两个库中,ld.so 将优先加载首先加载的库。 LD_PRELOAD 的工作原理是确保在任何隐式加载的库之前加载指定的库。

    因此,一旦开始执行,所有隐式加载的库都将被加载,因此在它们之前加载您的库为时已晚。

    【讨论】:

      【解决方案2】:

      没有干净的解决方案,但有可能。我看到两个选项:

      1. 用跳转到替换函数覆盖 printf 函数序言。

        它是 MS Windows 中非常流行的函数挂钩解决方案。您可以在 Google 中找到通过代码重写的函数挂钩示例。

      2. 重写 ELF 重定位/链接表。

        请参阅this article on codeproject,它几乎完全符合您的要求,但仅在 dlopen()'ed 模块的范围内。在您的情况下,您还想编辑您的主(通常是非 PIC)模块。我没有尝试过,但也许它就像调用提供的代码一样简单:

        void* handle = dlopen(NULL, RTLD_LAZY);
        void* original;
        original = elf_hook(argv[0], LIBRARY_ADDRESS_BY_HANDLE(handle), printf, my_printf);
        

        如果失败,您将不得不阅读动态链接器的源代码以确定需要调整的内容。

      【讨论】:

        【解决方案3】:

        应该说,根据 ISO C/POSIX,尝试从应用程序中的 libc 替换函数具有未定义的行为,无论您是静态还是动态地执行此操作。它可能会工作(并且很大程度上会在 GNU/Linux 上工作),但依赖它工作是不明智的。如果您只想使用名称“printf”但让它在您的程序中做一些非标准的事情,那么最好的方法是在包含任何系统头文件之后使用#undef printf#define printf my_printf。这样,您就不会干扰您正在使用的库对该函数的任何内部使用......而且您的 my_printf 实现甚至可以在需要时调用系统 printf。

        另一方面,如果您的目标是干扰库正在执行的操作,那么您可能会遇到兼容性问题。更好的方法可能是弄清楚为什么库在不重新定义它使用的函数、修补它并在适当的情况下向上游提交补丁的情况下无法执行您想要的操作。

        【讨论】:

          【解决方案4】:

          你无法改变这一点。一般来说,*NIX 链接概念(或者说缺乏概念)符号是从找到它的第一个对象中挑选出来的。 (除了古怪的 AIX,它在默认情况下更像 OS/2。)

          您可以随时以编程方式尝试dlsym(RTLD_DEFAULT)dlsym(RTLD_NEXT)man dlsym 了解更多。尽管它很快就会失控。为什么很少使用。

          【讨论】:

          • 你能解释一下dlsym() 的意思吗?有关更多信息,请参阅我对原始问题的编辑。
          • 你不能printf = dlsym(mylib,"printf"); 显然,你应该可以在你需要的编译单元中使用全局变量int (*myprintf)(const char *fmt, ...) = dlsym(mylib,"printf") 然后#define printf myprintf。我认为printf 在您的情况下更像是一个例子。如果printf 专门是您的目标(并且在链接时覆盖也是一个选项),您还可以尝试告诉链接器不要链接标准库,将您的库与自定义 printf() 首先放在库列表中 - 在手动之前指定 orig printf() 所在的标准库。
          • 顺便说一句,LD_PRELOAD 是一种在 标准库之前添加库的方法。它只是在运行时执行。在链接期间,您提供给链接器的库的顺序是定义符号的优先级:符号将从第一个库查找到最后一个库。通常标准库首先被放入链接列表(通过像cc这样的前端)。但是您可以将其关闭(-nostdlib)并按照您需要的顺序手动指定它们,例如把你的 libmy.so 放在第一位。然后来自 libmy.so 的 printf() 将覆盖来自 libc 的 printf。
          【解决方案5】:

          有一个环境变量 LD_LIBRARY_PATH,链接器在其中搜索 shred 库,将您的路径添加到 LD_LIBRARY_PATH,我希望这会起作用

          【讨论】:

            【解决方案6】:

            将 dlsym() 结果存储在查找表(数组、哈希表等)中。然后#undef print#define print 使用您的查找表版本。

            【讨论】:

            • 这很好用。这就是 QT 使用 OpenSSL 的方式,以及我的一些具有运行时链接而不是编译时/加载时的代码。
            猜你喜欢
            • 2018-10-08
            • 1970-01-01
            • 1970-01-01
            • 2013-08-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多