【问题标题】:When / How does Linux load shared libraries into address space?Linux何时/如何将共享库加载到地址空间?
【发布时间】:2011-07-05 01:52:23
【问题描述】:

我的问题如下:

什么时候在程序中指定共享对象的地址?在链接期间?正在加载?如果我想在程序内部的libc 中找到system 命令的内存地址,我可以在gdb 中轻松找到它,但是如果我不想将程序带入调试器怎么办?

这个地址可以从一个运行到另一个运行吗?是否有任何其他静态分析工具可以查看运行时库或函数将加载到该程序的内存空间中的位置?

编辑:我希望在程序之外获得这些信息(即使用 objdump 之类的实用程序来收集信息)

【问题讨论】:

  • 然后是prelink,它大大改变了顺序。

标签: c linux compiler-construction linker shared-libraries


【解决方案1】:

库由ld.so 加载(动态链接器或运行时链接器又名rtld、ld-linux.so.2ld-linux.so.* 在Linux 的情况下;glibc 的一部分)。它被声明为所有动态链接 ELF 二进制文件的“解释器”(INTERP;.interp 部分)。所以,当你启动程序时,Linux 会启动一个ld.so(加载到内存并跳转到它的入口点),然后ld.so 会将你的程序加载到内存中,准备好然后运行它。您也可以使用

启动动态程序
 /lib/ld-linux.so.2 ./your_program your_prog_params

ld.so 执行所有需要的 ELF 文件的实际 openmmap,包括程序的 ELF 文件和所有需要的库的 ELF 文件。此外,它填充 GOT 和 PLT 表并进行重定位解析(它将函数的地址从库写入调用站点,在许多情况下使用间接调用)。

您可以使用ldd 实用程序获得某些库的典型加载地址。它实际上是一个 bash 脚本,它设置了 ld.so 的调试环境变量(实际上是 LD_TRACE_LOADED_OBJECTS=1 在 glibc 的 rtld 的情况下)并启动一个程序。你甚至可以自己做而不需要脚本,例如使用 bash 轻松更改单次运行的环境变量:

 LD_TRACE_LOADED_OBJECTS=1 /bin/echo

ld.so 将看到这个变量,并将解析所有需要的库并打印它们的加载地址。但是有了这个变量集,ld.so 将不会真正启动程序(不确定程序或库的静态构造函数)。如果ASLR feature 被禁用,加载地址将大部分时间相同。现代 Linux 经常启用 ASLR,因此要禁用它,请使用 echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

您可以使用 binutils 中的 nm 实用程序在 libc.so 中找到 system 函数的偏移量。我认为,您应该使用nm -D /lib/libc.soobjdump -T /lib/libc.so 和grep 输出。

【讨论】:

  • 很好的信息,谢谢。您是否知道任何解释此过程如何工作(生成 GOT / PLT 表)的好文章,或者谷歌搜索会产生足够的结果?
  • 如果你想用绝对地址调用system,你可以在不使用GOT和PLT表的情况下做到这一点。在我看来,ld.so 的最佳谷歌搜索是代码搜索:google.com/…
  • 是的,我知道你可以不使用 GOT 和 PLT 来做到这一点,这只是我的好奇心! :)
  • 这非常有用!我对 ld.so 和 ldd 了解不多!
  • 这个答案是不正确的,因为ld.so not 加载主程序,内核加载。内核也不查看任何 sections(可以完全剥离),它在 PT_INTERPsegment 中找到解释器。
【解决方案2】:

“直奔源头问马……”

Drepper - How To Write Shared Libraries

Linux 库编写者的必读文档。详细解释加载机制。

【讨论】:

    【解决方案3】:

    如果你只想要一个函数的地址而不是硬编码名称,你可以dlopen()主程序:

    void *self = dlopen(NULL, RTLD_NOW);
    dlsym(self, "system"); // returns the pointer to the system() function
    

    如果您只想要在编译时知道名称的函数的地址,只需使用void *addr = &system;

    【讨论】:

    • 请参阅 OP 中的编辑,但一定要留下这个答案,因为它有助于另一个可能含糊的标题的变体。 (你是怎么回答的)
    • 可能两者都不会做 OP 想要的,因为 system 可能被解析为主程序映像中的 PLT 条目,该条目执行实际跳转到共享库中。
    【解决方案4】:

    libc.so 上使用的nm 命令将显示system 符号在libc.so 中的位置。但是,如果启用了 ASLR,则会在地址 libc.so 上加载,因此每次运行程序时,system 的最终地址会随机变化。即使没有 ASLR,您也需要确定地址 libc.so 被加载并偏移 system 的地址。

    【讨论】:

    • 这几乎正是我想要的——我假设在 libc.so 中确定系统偏移的最佳方法是在安装了调试符号的情况下再次使用 nm?或者有没有更简单/更健壮的方式来做到这一点。
    • @Ryan,nm不需要调试符号,可以直接读取符号表(ld.so使用)。
    • @osgx 那么我原来的问题是否正确,减去调试符号?
    【解决方案5】:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多