链接器 :链接器

更新时间:2024-09-21 00:22

链接器(Linker)是一个程序,将一个或多个由编译器或汇编器生成的目标文件外加库链接为一个可执行文件。

概述

计算机程序通常由几个部分或模块组成;这些部件/模块不需要包含在单个对象文件中,在这种情况下,它们通过符号作为地址相互引用到其他模块中,这些模块在链接执行时映射到内存地址。

虽然链接过程旨在最终将这些独立的部分组合在一起,但有很多充分的理由在源代码级别单独开发这些部分。这些原因包括易于将几个较小的部分组织在一个整体上,并且能够更好地定义每个单独部分的目的和职责,这对于管理复杂性和提高软件架构的长期可维护性至关重要。

通常,目标文件可以包含三种符号:

定义的“外部”符号,有时称为“公共”或“入口”符号,允许其他模块调用它未定义的“外部”符号,引用定义这些符号的其他模块,以及本地符号,在对象文件内部使用,以方便重新定位。

对于大多数编译器,每个目标文件都是编译一个输入源代码文件的结果。当一个程序包含多个对象文件时,链接器将这些文件组合成一个统一的可执行程序,并在符号进行时解析符号。

链接器可以从称为库或运行时库的集合中获取对象。大多数链接器不会在输出可执行文件中包含静态库中的所有目标文件;它们仅包括库中由其他对象文件或库直接或间接引用的对象文件。但是对于共享库,必须在运行时加载整个库,因为不知道在运行时将调用哪些函数或方法。因此,库链接可能是一个迭代过程,一些引用的模块需要链接其他模块,依此类推。库的存在目的多种多样,默认情况下通常链接一个或多个系统库。

链接器还负责在程序的地址空间中排列对象。这可能涉及将假定特定基址的代码重新定位到另一个基址中。由于编译器很少知道对象将驻留在何处,因此它通常假定一个固定的基本位置(例如,零)。重新定位机器代码可能涉及绝对跳转、负载和存储的重新定位。

链接器的可执行输出在最终加载到内存中时(就在执行之前)可能需要另一个重定位传递。在提供虚拟内存的硬件上通常省略此传递:每个程序都放入自己的地址空间中,因此即使所有程序都在同一基址加载,也不会发生冲突。如果可执行文件是与位置无关的可执行文件,也可以省略此传递。

在某些 unix 变体(例如 SINTRAN III)上,由链接器执行的过程(将目标文件组装到程序中)称为加载(如将可执行代码加载到文件上)。 此外,在某些操作系统中,同一个程序同时处理链接和加载程序(动态链接)的工作。

动态链接

许多操作系统环境允许动态链接,将某些未定义符号的解析推迟到程序运行。这意味着可执行代码仍包含未定义的符号,以及将为这些符号提供定义的对象或库列表。加载程序也将加载这些对象/库,并执行最终链接。

这种方法有两个优点:

也有缺点:

封闭式或虚拟环境可以进一步允许系统管理员减轻或权衡这些单独的利弊。

静态链接

静态链接是链接器将程序中使用的所有库例程复制到可执行映像中的结果。与动态链接相比,这可能需要更多的磁盘空间和内存,但更便携,因为它不需要在运行它的系统上存在库。静态链接还可以防止“DLL 地狱”,因为每个程序都包含它所需的库例程版本,并且与其他程序没有冲突。仅使用库中的几个例程的程序不需要安装整个库。

搬迁

由于编译器没有关于最终输出中对象布局的信息,因此它无法利用对另一个对象的地址提出要求的更短或更有效的指令。例如,跳转指令可以引用绝对地址或与当前位置的偏移量,并且偏移量可以根据到目标的距离用不同的长度表示。通过首先生成最保守的指令(通常是最大的相对或绝对变体,取决于平台)并添加放松提示,可以在最终链接期间替换更短或更有效的指令。在跳转优化方面,这也称为自动跳转大小。只有在读取所有输入对象并分配临时地址后,才能执行此步骤;链接器放宽传递随后会重新分配地址,这反过来可能会允许发生更多潜在的放宽。通常,替换序列较短,这使得该过程始终收敛于给定固定对象顺序的最佳解;如果不是这种情况,松弛可能会发生冲突,链接器需要权衡任一选项的优点。

虽然指令放宽通常发生在链接时,但模块内部放宽已经可以作为编译时优化过程的一部分进行。在某些情况下,松弛也可能在加载时发生,作为重定位过程的一部分,或与动态死码消除技术相结合。

联动编辑器

在 IBM System/360 大型机环境(如 OS/360)中,包括 z/建筑 大型机的 z/OS,这种类型的程序称为链接编辑器。顾名思义,链接编辑器具有允许添加、替换和/或删除单个程序部分的附加功能。操作系统(如 OS/360)具有可执行加载模块的格式,其中包含有关程序组件部分的补充数据,以便可以替换单个程序部分,并更新程序的其他部分,以便可重定位地址和其他引用可以由链接编辑器更正,作为该过程的一部分。

这样做的一个优点是,它允许维护程序,而不必保留所有中间对象文件,也不必重新编译未更改的程序部分。它还允许以小文件(最初是卡组)的形式分发程序更新,仅包含要替换的对象模块。在此类系统中,目标代码采用 80 字节穿孔卡图像的形式和格式,以便可以使用该介质将更新引入系统。在 OS/360 的更高发行版和后续系统中,装入模块包含有关组件模块版本的附加数据,以创建可跟踪的更新记录。它还允许从已链接的加载模块中添加、更改或删除叠加结构。

术语“链接编辑器”不应被解释为暗示程序在用户交互模式下运行,如文本编辑器。它用于批处理模式执行,编辑命令由用户以顺序组织的文件(如穿孔卡、DASD 或磁带)提供。

链接编辑(IBM 命名法)或合并或收集(ICL 命名法)是指链接编辑者或合并者将各个部分组合成一个可重定位的二进制文件的行为,而在目标地址加载和重定位到绝对二进制文件通常被认为是一个单独的步骤。

链接器控制脚本

一开始,链接器使用户对生成的输出对象文件的排列的控制非常有限。随着目标系统变得复杂,具有不同的内存要求,例如嵌入式系统,有必要让用户控制生成具有特定要求的输出对象文件,例如定义段的基址。链接器控制脚本用于此目的。

GNU链接器

GNU链接器(或GNU ld)是GNU项目的Unix命令ld的自由软件实现,它是GNU二进制工具(binutils)的一部分。GNU ld可以接受链接器脚本,以对链接过程行使更大的控制。除了传统的GNU ld,还有一个名为gold的精简ELF-only版本。其他链接器如LLVM项目的lld和mold也提供了与GNU工具兼容的高效替代方案。

参考资料

免责声明
隐私政策
用户协议
目录 22
0{{catalogNumber[index]}}. {{item.title}}
{{item.title}}
友情链接: