代码之家  ›  专栏  ›  技术社区  ›  tylerl

在运行时更改库加载顺序(如ld_preload,但在执行期间)

  •  7
  • tylerl  · 技术社区  · 14 年前

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

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

    但是让我们这么说吧,我想改变程序内部的链接。当然那是可能的……正确的?

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

    void* mylib = dlopen("/path/to/library.so",RTLD_NOW);
    printf = dlsym(mylib,"printf");
    
    6 回复  |  直到 12 年前
        1
  •  4
  •   R Samuel Klatchko    14 年前

    阿法克,那是不可能的。一般规则是,如果同一个符号出现在两个库中,那么ld.so将优先于首先加载的库。通过确保在任何隐式加载的库之前加载指定的库,ld_preload工作。

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

        2
  •  2
  •   Marcin Wisnicki    14 年前

    没有干净的溶液,但这是可能的。我看到两种选择:

    1. 使用跳转到替换函数覆盖printf函数prolog。

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

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

      this article on codeproject 这几乎可以满足您的要求,但仅限于dlopen()的模块范围内。在您的情况下,您还需要编辑主(通常是非pic)模块。我没有尝试过,但可能和调用一样简单,它提供了以下代码:

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

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

        3
  •  1
  •   R.. GitHub STOP HELPING ICE    14 年前

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

    另一方面,如果您的目标是干扰库所做的工作,那么您可能会遇到兼容性问题。如果不重新定义库使用的函数,修补它,并在适当的情况下向上游提交补丁,一个更好的方法可能是找出库为什么不做您想要的事情。

        4
  •  0
  •   sud03r    14 年前

    有一个环境变量ld_library_path,链接器在其中搜索碎片库,将您的路径预先设置为ld_library_path,我希望它可以工作。

        5
  •  0
  •   Dummy00001    14 年前

    你不能改变。一般来说,nix链接概念(或者更确切地说是缺乏概念)的符号是从找到它的第一个对象中选取的。(除了Oddball AIX,默认情况下它的工作方式更像OS/2。)

    通过编程,你可以尝试 dlsym(RTLD_DEFAULT) dlsym(RTLD_NEXT) . man dlsym 更多。虽然很快就失控了。为什么很少使用。

        6
  •  -2
  •   Bryan Drewery    14 年前

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