在Linux系统中,动态链接器是内核还是GCC库的一部分?

10
动态链接器(也称为程序解释器、链接加载器)是内核还是GCC库的一部分?
更新(28-08-16):
我发现每个二进制文件(即与共享库链接)都使用的默认路径 /lib64/ld-linux-x86-64.so.2 是指向实际动态链接器 /lib/x86_64-linux-gnu/ld-2.23.so 的链接。
它是 libc6 (2.23-0ubuntu3) 包的一部分,即 Ubuntu 中 AMD64 架构的GNU C 库:共享库
我的真正问题是:如果这个帮助程序(ld-2.23.so)不存在,所有动态链接的应用程序(现在所有应用程序都是动态链接的)会发生什么?
答案是“没有应用程序能运行,甚至是 shell 程序”。我在虚拟机上试过了。

ld.so 不是内核的一部分,但由其加载。 - Eugene Sh.
1
它实际上是 Linux 上 GNU libc 的一部分;请参见 http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html - nephtes
@nephtes 那个链接无法访问;出现了403错误。 - NotAPro
1个回答

16
在 ELF 可执行文件中,这被称为 "ELF 解释器"。在 Linux 上(例如),这是 /lib64/ld-linux-x86-64.so.2
这不是内核的一部分,通常使用 glibc 等。
当内核执行 ELF 可执行文件时,它必须将可执行文件映射到用户空间内存。然后从中查找一个特殊的子节,称为 INTERP (其中包含一个字符串,即完整路径)。
然后内核将解释器映射到用户空间内存并将控制权转移给它。然后,解释器执行必要的链接/加载并启动程序。
因为 ELF 代表 "可扩展链接器格式",所以允许 ELF 文件具有许多不同的子节。
与其让内核了解所有各种扩展的负担,不如与文件配对的 ELF 解释器知道。
虽然通常在给定系统上只使用一种格式,但在系统上可以有几种不同变体的 ELF 文件,每个文件都有自己的 ELF 解释器。
这将允许(例如)在 Linux 系统上运行 BSD ELF 文件(带有其他调整/支持),因为 ELF 文件将指向 BSD ELF 解释器而不是 Linux 的。
更新:
每个进程(例如 vlc 播放器、Chrome)都有共享库 ld.so 作为其地址空间的一部分。
是的。我假设你正在查看 /proc/<pid>/maps。这些是文件的映射(例如,类似于使用 mmap)。这与 "加载" 有所不同,后者可以意味着符号链接。
因此,在将可执行文件(代码和数据)加载到内存后,主要是加载和映射动态链接器 (.so) 到其地址空间。

主要是内核将可执行文件(代码和数据)映射到内存后,把动态链接器(.so)映射到程序的地址空间中。

这基本上是正确的。内核还会映射其他内容,如bss段和堆栈。然后它将argcargvenvp[存放环境变量的空间]“推”到堆栈上。

接下来,通过读取文件的特殊部分确定了ld.so的起始地址,并将其设置为恢复地址并启动线程。

到目前为止,一直是内核在做事情。内核几乎不进行符号链接。

现在,ld.so 接管了...

进一步加载共享库,映射和解析库的引用,然后调用入口函数(_start)

由于原始可执行文件(例如vlc)已经映射到内存中,ld.so可以查看它所需要的共享库的列表。它将这些库映射到内存中,但不一定立即链接符号。

映射很容易而且快速 - 只需一个mmap调用。

可执行文件的起始地址[不要与ld.so的起始地址混淆],取自ELF可执行文件的特殊部分。尽管与此起始地址相关联的符号传统上被称为_start,但它实际上可以被命名为任何东西(例如__my_start),因为在节数据中确定起始地址,而不是符号_start的地址。

将符号引用与符号定义链接起来是一个耗时的过程。因此,直到实际使用该符号时,才会推迟这个过程。也就是说,如果程序有对 printf 的引用,链接器在第一次程序实际调用 printf 之前并不会尝试链接它。

这有时被称为“按需链接”或“按需链接”。在这里查看我的答案:“哪些段受复制写入的影响?”以获取更详细的解释,以及当可执行文件映射到用户空间时实际发生了什么。

如果你感兴趣,你可以运行 ldd /usr/bin/vlc 来获取它使用的共享库列表。如果你查看 readelf -a /usr/bin/vlc 的输出,你会看到这些相同的共享库。此外,你还可以获得ELF解释器的完整路径,并可以运行 readelf -a <full_path_to_interpreter> 并注意一些不同之处。你可以为任何 .so 文件重复此过程,vlc 需要这些文件。

将所有这些与 /proc/<pid>maps 等结合起来,可能有助于你的理解。


@criag。我注意到,每个进程(如vlc播放器、chrome)都有共享库ld.so作为其地址空间的一部分。因此,主要的加载器在将可执行文件(代码和数据)加载到内存后,会将**动态链接器(.so)**加载和映射到其地址空间,进而加载共享库,映射和解析库引用。然后它调用入口函数(_start)。这正确吗? - user5746986
@craig。我已经使用了程序**ldd**,它可以打印共享依赖项。 - user5746986
我本以为你熟悉ldd,所以我提到了它和其他内容以便给出完整的答案。实际上,ldd是一个shell脚本。它只是设置环境变量LD_TRACE_LOADED_OBJECTS,然后执行/usr/bin/vlc。 ELF解释器[对于vlc]注意到这个变量并进行打印(而不是完全执行)。 ld.so无法使用程序参数,因此要提供选项,必须设置环境变量。手册页有一个相当完整的列表(例如man ld.so)。 - Craig Estey
@CraigEstey,好的,我明白了。 - user5746986

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接