2022年2022年链接器和加载器 2.pdf
《2022年2022年链接器和加载器 2.pdf》由会员分享,可在线阅读,更多相关《2022年2022年链接器和加载器 2.pdf(11页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第 7章 重定位$Revision: 2.2 $Date: 1999/06/30 01:02:35 $为了决定段的大小、符号定义、符号引用,并指出包含那些库模块、将这些段放置在输出地址空间的什么地方,链接器会将所有的输入文件进行扫描。扫描完成后的下一步就是链接过程的核心,重定位。由于重定位过程的两个步骤,判断程序地址计算最初的非空段,和解析外部符号的引用,是依次、共同处理的,所以我们讲重定位即同时涉及这两个过程。链接器的第一次扫描会列出各个段的位置,并收集程序中全局符号与段相关的值。一旦链接器确定了每一个段的位置,它需要修改所有的相关存储地址以反映这个段的新位置。在大多数体系结构中,数据中的地
2、址是绝对的,那些嵌入到指令中的地址可能是绝对或者相对的。链接器因此需要对它们进行修改,我们稍后会讨论这个问题。第一遍扫描也会建立第五章中所讲的全局符号表。链接器还会将符号表中的地址解析为引用全局符号时所存储的地址。硬件和软件重定位由于几乎所有的现代计算机都具有硬件重定位,可能会有人疑问为什么链接器或加载器还需要进行软件重定位(当我于60年代后期在 PDP-6上编程时,这个问题就困惑着我,而从那以后情况就变得更复杂了)。答案部分在于性能的考虑,部分在于绑定时间。硬件重定位允许操作系统为每个进程从一个固定共知的位置开始分配独立的地址空间,这就使程序容易加载,并且可以避免在一个地址空间中的程序错误破
3、坏其它地址空间中的程序。软件链接器或加载器重定位将输入文件合并为一个大文件以加载到硬件重定位提供的地址空间中,然后就根本不需要任何加载时的地址修改了。在诸如 286或 386那样有几千个段的机器上,实际上有可能 做到为每一个 例程或全局数据分配一个段,独立的进行软件重定位。每一个例程或数据可以从各 自段的 0 位置开始,所有的全局引用 通过查找系统段表中的段间引用 来处理并在程序 运行时绑定。不 幸的是,x86段查找非常的慢,而且如 果程序对每一个段间模块 调用或全局数据引用都要进行段查找的话那速度要比传统程序慢的多。同样重要的时, 虽然运行时绑定会对此有一些 帮助(这是我们将在第 10章涉及
4、的 话题), 但大多数程序都 没有采用(鉴于当前的硬件性能和容 量对于程序运行都颇为富余)。由于可 信的理由,程序文件最 好绑定在一 起并且在链接时确定地址,这样它们在 调试时静止不变而出 货后仍能保持一致性。当一个程序 运行的库 超出了作者 预期的版本时,库 二进制兼容是程序错误的一个 长期并且 难以发现的来源。(MSW in dow s 应用程名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 11 页 - - - - - - - - - 序由于使用了大 量的共享库,就
5、倾向于存在这 种问题。由于 某些库的不同 版本会因 安装各种应用程序 被加载到同一个计算机上)。即使不考虑286风格段的限制,动态链接比起静态 链接而言也要慢的多,而且 没有理由为不需要的 东西付钱 。链接时重定位和加载时重定位很多系统即 执行链接时重定位,也 执行加载时重定位。链接器将一系列的输入文件合并成一个 准备加载到特定地址的 单一输出文件。当这个程序被加载后,所存储的那个地址是无效的,加载器 必须重新定位 被加载得程序以反 应实际的加载地址。在包 括 MS -DOS 和 MVS在内的一些系统上,每一个程序都按照加载到地址 0 的位置而 被链接。实 际的地址是 跟据有效的存储空间而定的
6、,这个程序在被加载时 总是会被重定位的。在其它的一些系统上,尤其是 MSW in dows,程序按照被 加载到一个固定有 效地址的方 式来链接,并且一 般不会进行加载时重定位, 除非发生该 地址已被别 的程序所 占用之类的异常情况(当 前版本的 W in dow s 实际上从不对可 执行程序进行加载时重定位,但是对 DLL共享库会进行重定位。相 似的,UNIX系统从不对 ELF程序进行重定位, 虽然它们对 ELF共享库会进行重定位)。加载时重定位和链接时重定位比起来 就颇为简单了。在链接时,不同的地址需要根据段的大小和位置重定位为不同的位置。在加载时,整个程序在重定位过程中会 被认为是大的单一
7、段,加载器 只需要判断 名义上的加载地址和实 际加载地址的 差异即可。符号和段重定位链接器的第一遍扫描将各个段的位置列出,并收集程序中所有全局符号和段相关的值。一旦链接器决定了每一个段的位置,它就需要调整存储地址。数据地址和段 内绝对程序地址引用需要进行调整。例如,如 果一个指 针指向位置 100,但是段基址被重定位为 1000 ,那么这个指针就需要 被调整到位置 1000 。. 程序中的段间引用也需要 被调整 。绝对地址引用要 调整为可以反映 目标地址段的新位置,同样相对地址需要调整为可以同时反映 目标段和引用所在段的新位置。对全局符号的引用需要进行解析。如果一个指令 调用了例程 deton
8、ate,并且 detonate 位于起始地址为 1000的段的 偏移地址 500,在这个指令中涉及到的地址要 调整为 1500 。重定位和符号解析所要 求的条件有些许不同。对于重定位,基址的数 量相当小,也就是一个输入文件中的段的个数,不过目标文件格式允许对任何段中任何地址的引用进行重定位。对于符号解析,符号的数量远远 大的多, 但是大多数情况下链接器 只需要对符号 做一件事即将符号的值 插入到程序的一个 字大小的空间中。很多链接器将段重定位和符号重定位统一对待,这是因为它们将段当作是一种值为段基址的“伪符号”。这使得和段相关的重定位就成了和符号相关的重定位的特例。即使在将两种重定位统一对 待
9、的链接器中,此 二者仍有一个重要 区别: 一个符号引用包 括两个加数,名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 11 页 - - - - - - - - - 即符号所在段的 基值和符号在段 内的偏移地址( 译者注:这里作者少说了半句话 ,即将段作为符号处理时,这个 特殊符号只有段基址,没有段内偏移量 )。有一些链接器在开始进入重定位阶段之前就会预先计算所有的符号地址,将段基址加到符号表中符号的值中。当每一项被重定位时会 查找到段基址并相加( 译者注:即对符号地址中所
10、包含段 基址进行修改)。大多数情况下,并 没有强制的理由要以这 种或那种方法来进行这 种操作。在 少数链接器, 尤其是那些 针对实模 式 x86代码的链接器中,一个地址可以被重定位到和 若干不同段相关的多个地址上,因此链接器 只需要确定在上下文中一个 特定引用的符号在 特定段中的地址。符号查找目标代码格式总 是将每个文件中的符号当作数组对待,并在 内部使用一个小 整数指代符号,即数 组的索引。这对链接器 带来了一些小 麻烦,就像第五章所讨论的,每一个输入文件均有不同的 索引,如 果输出文件是可以重链接的话那它们也会有不同的 索引。最 直截了当的解决 办法是为每个输入文件 保留一个指 针数组,指
11、向全局符号表中的表 项。基本的重定位 技术每一个可重定位的 目标文件都含有一个重定位表,其中是在文件中各个段里需要被重定位的一系列地址。链接器读入段的 内容,处理重定位 项,然后 再解决整个段, 通常就是将它写入到输出文件中。 通常而不总是,重定位是一次操作,处理后的结果文件不能 被重定位第二次。但一些目标文件格式,尤其是 IBM 360 的,是可以重定位的并在输出文件中包含所有重定位 信息(在 360的情况中,输出文件在加载的时候需要被重定位,因此它 必须包含所有的重定位 信息)。对于 UNIX链接器,有一个 选项能产生可再次链接的输出文件,在 某些情况下, 尤其是共 享库,由于它在加载时需
12、要被重新定位因此 总是带有重定位 信息。在最简单的情况中,如 图 1,一个段的重定位 信息仅 是段中需要 被重定位的位置列表。在链接器处理段时,它将段基址加上由重定位 项标识 的每个位置的地址。这就处理了直接寻址和内存中指 向某个段的指 针数值。-Fi gure 7-1: Si mple rel ocation entryaddress | a ddress | address | .-由于支持多个段和 寻址模式的原因,在现代计算机上实 际的程序会 比这更复杂一些。 经典的UNIX a.o ut 格式,如图2,可能是解决这些问题的最 简单的实例。-Fi gure 7-2: a.out r el
13、 ocation entryint address / * o ff set in text o r data se gm ent * /unsi gned int r_symb ol num : 2 4 , / * o rd inal n umb er o f a dd s ymb ol*/r_pcr el : 1, /* 1 i f va lu e s hould be pc-r el ative */r_l engt h : 2, /* l og base 2 o f va lu es wi dt h */名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - -
14、- - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 11 页 - - - - - - - - - r_exte r n : 1, /* 1 i f need to a dd s ymb ol to valu e */-每个目标文件都有两个重定位 项集合,一个是文本段的,一个是数据段的(bss 段被定义为全 0,因此没有什么需要重定位的)。每一个重定位项都有标志位 r_exte rn 指明它是段相关或者符号相关的 项。如果该位为空,它是段相关的并且r_symb ol num实际上是段的一个代码,可能是 N_TEXT(4) , N_ D ATA(6),或者 N
15、_BBS( 8) 。pc_rel ative 位指明该引用针对当前位置( 译者注:当前位置是指程序计数器,即指令指针寄存器而 言)是绝对还是相对的。每一个重定位 项的其它多 余信息 是和它的 类型及对应的段相关的。在下 面的讨论中, TR, DR 和 BR依次分 别是文本段、数据段、 BSS段的重定位后 基址。对同一个段中的指 针或直接地址,链接器将地址TR或 DR 加到段中 已经保 存的数值上。对于从一个段到 另一个段的指 针或直接地址,链接器将 目标段的重定位 基址,TR, DR或 BR,加到存储的数值上。由于a.o ut 格式的输入文件中 已经带 有每一个重定位到新文件的段中的 目标地址
16、,这就是所有 必须的了。 例如,假定在输入文件中,文本从地址0 开始,数据从地址 2000开始,并且在文本段中的一个指针指向数据段中 偏移量为 200的位置。在输入文件中, 被存储的指 针的值为 2200 。如 果最后在输出文件中数据段的重定位位置为15000 ,那么 DR 将为 13000 ,链接器将会把 13000加入到 已存在的 2200产生最后的数值 15200 。一些体系结构的地址具有不同的尺寸。IBM 360 和 I ntel 386 都具有 16位和 32位的地址,链接器一 般都支持对这两 种尺寸 的重定位。确 保程序地址 满足十六 位的限制是程序员自己的责任,链接器不会对地址有
17、 效性进行更多的确 认。指令重定位由于多 种指令格式的缘由,重定位指令中的地址要比重定位数据的指 针麻烦 一些。上面描述的 a.o ut 格式只 有两个重定位 格式,绝对的,与程序计数器相对的,但是大多数计算机体系结构需要更 长的重定位 格式以处理所有的指令 格式。X 86指令重定位不考虑 x86指令的复杂编 码方式,从链接器的 角度看 这种体系结构是易于处理的,因为它只需要处理两 种地址, 直接地址和与程序计数器相对的地址(我们在这里像大多数 32位链接器那样 忽略段)。引用数据的指令可以带有 32位目标地址,链接器可以 像其它 32位地址那样对其进行重定位,加上目标所在段的段 基址。cal
18、l 和 jump指令使用相对 寻址,因此指令中的地址是指令当前地址和 目标地址的 差值。对于相同段 内的 call和 jmp指令,由于一个段 内的相对地址是 永不会改变的因此不需要进行重定位。对于段间jump链接器加上 目标段重定位地址并 减去指令段的地址。 例如,对于从文本段到数据段的jump,重定位值将为 DR-TR(译者注:原 文这里说得比较含糊,我们 只需要明白这里需要进行一个 差值的转换即可)。S PAR C指令重定位很少有体系结构能 像 x86那样提供对链接器方 便的指令编 码。例如 SPAR C,没有直接寻址,有 四种不同的分 支指令格式,有一些 专门用于合成 32位地址的 特殊
19、指令,还有个 别只名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 11 页 - - - - - - - - - 包含部分地址的指令。链接器需要处理所有这些情况。不像 x86架构,没有一个 SPARC指令的 格式中为自己保留 了一个 32位地址的空间。这意味着在输入文件中,一个指令在内存中引用的重定位的 目标地址不能 通过存储在指令中。作为替代,如 图 3,SPAR C的重定位 项中有一个 额外的域 r_addend 包含了 32位的引用地址。鉴于 SPARC的重定位不能
20、像 x86 的那样 简单描述,一系列的 类型标识 位被标识 重定位 格式的代码域 r_t ype 代替。同样,不 仅仅使用一个位 去区分段或者符号重定位,每一个输入文件定义了符号.te xt , . data 和. bss ,用来标识 各自对应段的起始位置,并且段的重定位会涉及到这些符号。-Fi gure 7-3: SPAR C r el ocation entryint r_address; / * o ff set o f o f data to r el ocate * /int r_in dex:2 4, / * s ymb ol ta bl e in dex o f s ymb ol
21、*/r_t ype:8; / * r el ocation type* /int r_addend; / * dat um a ddend*/-SPAR C重定位有 3 类:数据指 针的绝对地址重定位, 各种尺寸 的分支和调用指令的相对地址重定位,和有 点黑客味道 的特殊的 SETHI绝对地址重定位。绝对地址的重定位和x86上几乎一样,链接器将TR, DR 或 B R加到存储的数值上。这 种情况下,由于在 被存储的值中有足够的空间 保存整个地址,因此重定位 项中的加数实 际上并不是 必须的,但是链接器为了一致性会将加数加到存储的值上(译者注:不使用重定位 项中的加数的加 法,和使用这个加数的加
22、 法,肯定加的数是不一样的)。对于分 支指令, 鉴于加数就是 距离目标的偏移量 (即目标地址和存储的值 之间的差值),因此存储的 偏移量 值通常是 0。链接器通过将适当的重定位数值加到这个加数上得到重定位的相对地址。 鉴于 SPARC的相对地址不 保存低 2 位,还会将这个相对地址 向右移 2位,然后检查确认移位后的数值符合有 效的位数(根据不同 格式可能为 16位、 19位、 22 位或 30位), 通过位掩码取出移位后地址的有 效位数来并将他们加到指令中。16位格式的有效位数的低 14位存储在指令的 低 14位中, 但第 15位和第 16位却存储在指令的第 20和 21 位中(译者注:从这
23、里看,有效地址的最 低位应该是从 1 开始数的)。链接器需要进行适当的位移和位掩码操作来存储这些有 效位并不修改指令中的其它位。特殊的 SETHI黑客方法通过 SETHI指令合成了一个 32 位地址,它从指令中 获得 22位的地址并将其放置在 某个寄存器的 高 22位,然后接着 通过一个 O R操作将地址的 低 10位赋予相同的寄存器。链接器 通过两种特殊 的重定位模 式来处理这种情况,其一将重定位地址(加数加上相 应的重定位段 基址)的 高 22位放置在存储值的 高 22位,其 二将重定位地址的 低 10位放置在存储值的 低 10位。不 像上面的分支模式那样,这些重定位模 式不检查每一个值是
24、 否都是都能 满足所存储的位数,因为两 种模式下存储的位数都不能表 示整个地址值。在其它体系结构上的重定位会使用和SP AR C不同的 技术,包括对每一个可对 内存寻址的指令格式采 用不同的重定位 类型。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 11 页 - - - - - - - - - ECOFF 段重定位M i crosof t 的 COFF 目标文件格式是 COFF 格式(从 a.out 格式演变而来)的扩展版本,因此 W in32 的重定位和 a.o ut
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年2022年链接器和加载器 2022 链接 加载
限制150内