链接可以在编译时由静态编译器来完成,也可以在加载时和运行时由动态链接器来完成。链接器处理包含二进制代码和数据的目标文件。目标文件有三种不同形式:
- 可重定位目标文件:
在编译时由静态链接器合并成一个可执行的目标文件,可以加载到内存中并执行。 - 可执行目标文件:
可被直接复制到内存并执行。 - 共享目标文件(共享库):
在加载或运行时被动态地加载进内存并链接。
链接器的两个主要任务:
符号解析:将目标文件中的每个全局符号都绑定到一个唯一的定义
重定位:确定每个符号的最终内存地址,并修改对那些目标的引用
静态链接器是由像 GCC 这样的编译驱动程序调用的。它们将多个可重定位目标文件合并成一个单独的可执行目标文件。多个目标文件可以定义相同的符号,而链接器解析这些多重定义的规则时可能在用户程序中引入微妙的错误。
多个目标文件可以被链接到一个单独的静态库中。链接器用库来解析其他目标模块中的符号引用。许多链接器通过从左到右的顺序扫描来解析符号引用,这是另一个引起链接时错误的来源。
加载器将可执行文件的内容映射到内存,并运行这个程序。链接器还可能生成部分链接的可执行目标文件,这些文件中有对定义在共享库中的例程和数据的未解析引用。在加载时,加载器将部分链接的可执行文件映射到内存,然后调用动态链接器,它通过加载共享库和重定位程序中的引用来完成链接任务。
被编译为位置无关代码的共享库可以加载到任何地方,也可以在运行时被多个进程共享。为了加载、链接和访问共享库的函数和数据,应用程序也可以在运行时使用动态链接器。