动态链接中什么是链接器,什么是加载器?

动态链接中什么是链接器,什么是加载器?

计算机系统:程序员的视角 (3ed)p733 上说

7.9 加载可执行目标文件

要运行可执行目标文件 prog,我们可以在 Linux shell 的命令行中键入其名称:

Linux> ./prog

由于prog不对应于内置 shell 命令,因此 shell 假定它prog是一个可执行目标文件,它通过调用一些驻留在内存中的操作系统代码(称为装载机。任何Linux程序都可以通过调用该函数来调用加载器execve,我们将在8.4.6节中详细介绍

在 p736 中:在动态链接期间

7.10 与共享库的动态链接

创建库后,我们将其链接到图 7.7 中的示例程序:

linux> gcc -o prog2l main2.c ./libvector.so

这将创建一个可执行目标文件,其形式可以在运行时prog2l链接。libvector.so基本思想是在创建可执行文件时静态地进行一些链接,然后在加载程序时动态地完成链接过程。重要的是要认识到libvector.so 中的任何代码或数据部分实际上都没有复制到可执行文件中prog2l在此刻。反而,链接器复制一些重定位和符号表信息libvector.so这将允许在加载时解析对代码和数据的引用。

当加载器加载并运行可执行文件时,它使用第 7.9 节中讨论的技术prog2l加载部分链接的可执行文件。prog2l接下来,它注意到prog2l包含一个.interp部分,其中包含动态链接器的路径名,动态链接器本身就是一个共享对象(例如,ld-linux.so在Linux系统上)。加载程序加载并运行动态链接器,而不是像通常那样将控制权传递给应用程序。 动态链接器然后完成链接任务通过执行以下重定位:

  • 将文本和数据重新定位libc.so到某个内存段
  • 将文本和数据重定位libvector.so到另一个内存段
  • 将任何引用重新定位到由和 prog2l定义的符号libc.solibvector.so

上面的动态链接情况就是“静态加载,动态链接”的情况史蒂芬·基特的回复:

静态加载、动态链接:链接器又是 /usr/bin/ld,但带有共享库 (.so);装载机是二进制文件的解释器,例如 64 位 x86 上的 Debian 9 上的 /lib64/ld-linux-x86-64.so.2 (当前映射到 /lib/x86_64-linux-gnu/ld-2.24.so)由内核加载,内核还加载主可执行文件;

不同之处在于,CSAPP似乎说加载器是(后面的内核代码),execve()而链接器是ld-linux.so(在编译时没有发生链接ld,而实际链接在加载时发生ld-linux.so)。

动态链接中什么是链接器,什么是加载器?

谢谢。

答案1

这里重点强调一下:

动态链接器然后完成链接任务

错过了重要的词“完成”。链接器ld开始链接任务,在构建时执行尽可能多的操作并准备完成它所需的数据结构。

然后,加载程序可以在加载程序和所需的库时完成链接任务:匹配符号并执行必要的重定位。

在CSAPP的术语中,动态加载器是内核中的ELF加载器,动态链接器是ld-linux.so.

GNU C 库自己的文档ld.so称为动态链接器/加载器ld.so(或ld-linux.so) 本身执行相当多的加载以及链接,因此将这两个术语应用于它是准确的 - 内核加载可执行文件本身及其解释器(动态链接器/加载器),并且解释器加载所有其他所需的图书馆。

程序如何运行:ELF 二进制文件了解这一切如何在 Linux 系统上工作的详细信息。

答案2

动态链接器是 call ld.so。您可以在 下找到配置/etc/ld.so*。大多数配置与搜索 .so 的路径有关。

ld必须确保在完成可执行文件的链接之前所有函数都存在于共享库中(嗯......从技术上讲,不需要这样做,但确实如此 - 也就是说,如果您从从一台计算机到另一台计算机,.so 可能不同,并且具有新功能,但丢失了旧功能,并且二进制文件将无法运行)。

当链接器 ( ) 创建需要共享库的二进制文件时,它会在您的节ld中保留一定数量的符号和相应的地址。.text这就是动态链接器 ( ld.so) 在运行时用来完成链接的内容。它将搜索相应 .so 文件中的符号,并在代码中所需的任何位置保存它们的地址(对于函数,它通常是一个jump指令表,这样它们只能链接每个函数一次)。

当然,当你剥离二进制文件时,那些特殊符号不要被删除。

要查看将加载的库的列表,您可以ldd针对可执行文件运行。特别是,它会向您显示选择哪个 .so 来解析符号(完整路径)。您可以使用环境变量更改搜索路径LD_LIBRARY_PATH。这允许您针对不同或已卸载的 .so 文件进行测试(cmake、automake 也使用它们--rpath,这是相同的,只是路径直接保存在二进制文件中)。

我不太确定哪一部分准确加载文件并启动它们。可能execve()没有直接实现所有这些逻辑。但它肯定接近知道如何运行可执行文件的最低级别函数。

所以实际上,动态链接是非常简单与标准链接器相比的过程:

  1. 加载.so
  2. 在 .so 中搜索符号
  3. 保存符号地址

完毕。这就是为什么它这么快。

笔记:使用 可以实现进一步的活力dlopen(),但听起来您并不是在谈论那部分。

相关内容