4 链接阶段

4 链接阶段
foresta.yang链接阶段
链接阶段是编译过程的最后阶段。顾名思义,此阶段将所有对象文件链接到一个二进制可执行文件中。在现代系统中,链接阶段有时会包含额外的优化过程,被称为链接时优化(Link-Time Optimization, LTO)。
执行链接阶段的程序被称为链接器或者链接编辑器。通常链接器与编译器相互独立,编译器通常实现前面所有的步骤。
-
对象文件是可重定位的,它们是相互独立编译的,这使得编译器无法假设对象最终会出现在任意特定的基址上。
-
对象文件可以引用其他对象文件或程序外部库中的函数或者变量。在链接阶段之前,引用代码和数据的地址尚不清楚,因此对象文件只包含重定位符号,这些符号指定最终如何解析函数和变量引用。在链接上下文中,依赖于重定位符号的引用称为符号引用。
-
当一个对象文件通过绝对地址引用自己的函数或变量时,该引用也会被符号化。
链接器的工作是获取属于程序的所有对象文件,并将它们合并为一个连贯的可执行文件,然后加载到特定的内存地址。既然现在已经知道可执行文件中所有模块的排列,链接器也就可以解析大多数的符号引用了。
根据库文件的类型,对库文件的引用可能会、也可能不会完全解析。
- 静态库(在Linux操作系统上扩展名通常为.a)被合并到二进制可执行文件中,允许完全解析对静态库的任何引用。
- 动态 (共享)库,在系统上运行的所有程序的内存中共享。
对于动态库,不是将库文件复制到使用它的每个二进制文件中,而是仅将动态库加载到内存中一次,并且任何想要使用该库的二进制文件都需要使用此共享副本。
在链接阶段,动态库将驻留的内存地址尚不清楚,因此无法解析对它们的引用。相反,即使在最终的可执行文件中,链接器也会对这些库文件留下符号引用,并且在将二进制文件实际加载到要执行的内存中之前,不会解析这些引用。
大多数编译器(包括GCC)在编译过程结束时会自动调用链接器。因此,要生成完整的二进制可执行文件,只需在没有任何特殊开关的情况下调用GCC:

默认情况下,可执行文件名为a.out,但你可以通过-o选项给GCC重写此命名,选项后跟输出文件的名称。
file实用程序现在告诉你正在处理的是一个ELF 64位LSB可执行文件❶,而不是在汇编阶段结束时看到的可重定位文件。此外还有一个重要信息是文件是动态链接的❷,这意味着它使用的某些库未合并到可执行文件中,而是在同一系统上运行的所有程序之间共享。最后,解释器/lib64/ld-linux-x86-64.so.2❸的文件输出会告诉你,当可执行文件加载到内存中执行时,哪个动态链接器将会被用来解析动态库的最终依赖关系。