链接器和加载器05

上传人:wm****3 文档编号:47261415 上传时间:2018-07-01 格式:PDF 页数:13 大小:395.18KB
返回 下载 相关 举报
链接器和加载器05_第1页
第1页 / 共13页
链接器和加载器05_第2页
第2页 / 共13页
链接器和加载器05_第3页
第3页 / 共13页
链接器和加载器05_第4页
第4页 / 共13页
链接器和加载器05_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《链接器和加载器05》由会员分享,可在线阅读,更多相关《链接器和加载器05(13页珍藏版)》请在金锄头文库上搜索。

1、第第 5 5 章 符号管理章 符号管理$Revision: 2.2 $Date: 1999/06/30 01:02:35 $符号管理是链接器的关键功能。如果没有某种方法来进行模块之间的引用,那么链接器的其它功能也就没有什么太大的用处了。绑定和名字解析绑定和名字解析链接器要处理各种类型的符号。所有的链接器都要处理各模块之间符号化的引用。每个输入模块都有一个符号表。其中的符号包括:当前模块中被定义(和可能被引用)全局符号。在被模块中被引用但未被定义的全局符号(通常成为外部符号)。段名称,通常被当作定义在段起始位置的全局符号。非全局符号,调试器或崩溃转储(crash dump)分析通常会用到它们。这

2、些符号几乎不会被链接过程用到,但有时候它们经常会和全局符号混在一起,所以链接器至少要能够跳过它们。在另一些情况中它们会在文件中一个单独的表中,或在一个单独的调试信息文件中(可选的)。链接器读入输入文件中所有的符号表,并提取出有用的信息,有时就是输入的信息,通常都是关于需要链接哪些东西的。然后它会建立链接时符号表并使用该表来指导链接过程。根据输出文件格式的不同,链接器会将部分或全部的符号信息放置在输出文件中。某些格式会在一个文件中存在多个符号表。例如ELF共享库会有一个动态链接所需信息的符号表,和一个单独的更大的用来调试和重链接的符号表。这个设计不见得糟糕。动态链接器所需的表比全部的表通常要小得

3、多,将它独立出来可以加快动态链接的速度,毕竟调试或重链接一个库的机会(相比运行这个库)还是很少的。符号表格式符号表格式链接器中的符号表与编译器中的相近,由于链接器中用到的符号一般没有编译器中的那么复杂,所以符号表通常也更简单一些。在链接器内,有一个列出输入文件和库模块的符号表,保留了每一个文件的信息。第二个符号表处理全局符号,即链接器需要在输入文件中进行解析的符号。第三个表可以处理模块内调试符号,尽管少数情况下链接器也会为调试符号建立完整的符号表,但通常都只需将输入的调试符号传递到输出文件。在链接器本身内部,符号表通常以表项组成的数组形式来保存,并通过一个hash函数来定位表项,或者是由指针组

4、成的数组,并通过hash函数来索引,相同hash的表项以链表的形式来组织。当需要在表中定位一个符号时,链接器根据符号名计算hash值,将该值用桶的个数来取模,以定位某一个 hash桶(图中的symhashh%NBUCKET,h为hash值),然后遍历其中的符号链表来查找符号。传统上,链接器仅支持短名称,从IBM主机系统的8个字符和多数DEC系统上早期 UNIX系统的 6个字符到一些消亡中的小型计算机的2个字符。现代链接器支持的名称要长得多,这既是由于程序员使用了更长的名称(或诸如在Cobol中,不再通过修改名称使它们的头八个字符唯一),也是因为编译器将名称进行了修改添加了额外的类型信息编码字符

5、。更早的名称长度受限的链接器会在查找hash链中对每一个符号名称进行字符串比较,直到找到匹配项或遍历完毕。现在的程序经常会有很多长符号在最后几个字符之前都是相同的(在 C+修改名称的情况下这经常发生),这就带来很大的字符串比较开销。一个简单的解决办法是将所有的 hash值都保存在符号表中,并且只在hash值相同的时候才进行字符串比较。根据上下文情况的不同,如果一个符号没有被找到,链接器可能会将它加入相应的链中,也可能会报一个错误。- 图 5-1:符号表具有 hash或者hash符号队列的典型符号表struct sym *symhashNBUCKET;struct sym struct sym

6、*next;int fullhash; /*全 hash值*/char *symname;.;-模块表模块表链接器需要跟踪整个链接过程中出现的每一个输入模块,即包括明确链接的模块,也包括从库中提取出来的模块。图 2所示可以产生a.out目标文件的GNU链接器的简化版模块表结构。由于每个 a.out文件的关键信息大部分都在文件头部中,该表仅仅是将文件头部复制过来。- 图 5-2:模块表/* 该文件名称 */char *filename;/* 符号名字串起始地址 */char *local_sym_name;/* 描述文件内容的布局 */* 文件的 a.out头部 */struct exec he

7、ader;/* 调试符号段在文件内的偏移量,如果没有则为0 */int symseg_offset;/* 描述从文件中加载到内核的数据 */* 文件的符号表 */struct nlist *symbols;/* 字串表大小,以字节为单位 */int string_size;/* 指向字串表的指针 */char *strings;/* 下面两个只在 relocatable_output为真,或输出未定义引用的行号时使用 */* 文本和数据的重定位信息 */struct relocation_info *textrel;struct relocation_info *datarel;/* 该文件的

8、段与输出文件的关系 */* 该文件中文本段在输出文件核心镜像中的起始地址 */int text_start_address;/* 该文件中数据段在输出文件核心镜像中的起始地址 */int data_start_address;/* 该文件中 BSS段在输出文件核心镜像中的起始地址 */int bss_start_address;/* 该文件中第一个本地符号在输出文件中符号表中的偏移量,以字节为单位 */int local_syms_offset;- 该表中还包含了指向符号表、字串表(在一个a.out文件中,符号名称字串是在符号表外另一个单独的表中)和重定位表在内存中副本的指针,同时还有计算好的

9、文本、数据和BSS段在输出中的偏移量。如果该文件是一个库,每一个被链接的库成员还有它自己的模块表表项(细节在此略去)。第一遍扫描中,链接器从每一个输入文件中读入符号表,通常是将它们一字不差的复制到内存中。在将符号名放入单独的字串表的符号格式中,链接器还要将符号表读入,并且为了后续处理更容易一些,还要遍历符号表将每一个的名称字串偏移量转换为指向内存中名称字串的指针。全局符号表全局符号表链接器会保存一个全局符号表,在任何输入文件中被引用或者定义的符号都会有一个表项,如图 3所示。每次链接器读入一个输入文件,它会将该文件中所有的全局符号加入到这个符号表中,并将定义或引用每个符号的位置用链表组织起来。

10、当第一遍扫描完成后,每一个全局符号应当仅有一个定义,0或多个引用(这里稍微简化了一些,因为UNIX目标文件会将公共块伪装成具有非零值的未定义符号,但这是一个链接器很容易处理的特殊情况)。- 图 5-3:全局符号表/* 摘自 GNU ld a.out */struct glosym/* 指向该符号所在 hash桶中下一个符号的指针 */struct glosym *link;/* 该符号的名称 */char *name;/* 作为全局符号的符号值 */long value;/* 该符号在文件中的外部 nlist链表,包括定义和引用 */struct nlist *refs;/* 非零值则意味该符

11、号被定义为公共块,该数值即各公共块中的最大尺寸 */int max_common_size;/* 非零意味着该全局符号是存在的。库程序不能根据该数值加载 */char defined;/* 非零则意味着一个确信被加载的文件中引用了该全局符号。大于1的数值是该符号定义的n_type编码 */char referenced;/* 1表示该符号具有多个定义2 表示该符号具有多个定义,其中一些是集合元素,并且有一个已经被打印出来了 */unsigned char multiply_defined;- 由于每个输入文件中的全局符号都被加入到全局符号表中,链接器会将文件中每一个项链接到它们在全局符号表中对

12、应的表项中,如图4所示。重定位项一般通过索引模块自己的符号表来指向符号,因此对于每一个外部引用,链接器必须要对此很清楚,例如模块A中的符号 15名为fruit,模块B中的符号 12同样名为 fruit,也就是说,它们是同一个符号。每一个模块都有自己的索引集,相应也要用自己的指针向量。- 图 5-4:通过全局符号表来解析文件中的符号每一个模块项指向输入文件的符号向量,向量中的每一项均指向全局符号表表项。-符号解析符号解析在链接的第二遍扫描过程中,链接器在创建输出文件时会解析符号引用。解析的细节与重定位(见第七章)是有相互影响的,这是因为在多数目标格式中,重定位项标识了程序中对符号的引用。在最简单

13、的情况下,即链接器使用绝对地址来创建输出文件(如UNIX链接器中的数据引用),解析仅仅是用符号地址来替换符号的引用。如果符号被解析到地址20486处,则链接器会将相应的引用替换为20486。实际情况要复杂得多。诸如,引用一个符号就有很多种方法,通过数据指针,嵌入到指令中,甚至通过多条指令组合而成。此外,链接器生成的输出文件本身经常还是可以再次链接的。这就是说,如果一个符号被解析为数据区段中的偏移量426,那么在输出中引用该符号的地方要被替换为可重定位引用的数据段基址+426。输出文件通常也拥有自己的符号表,因此链接器还要新创建一个在输出文件中符号的索引向量,然后将输出重定位项中的符号编号映射到

14、这些新的索引中。特殊符号特殊符号很多系统还会使用少量链接器自己定义的特殊符号。所有的UNIX系统都要求链接器定义 etext、edata和end符号依次作为文本、数据和 BSS段的结尾。系统调用sbrk()将end的地址作为运行时内存堆的起始地址,所以堆可以连续的分配在已经存在的数据和BSS的后面。对于具有构造和析构例程的程序,很多链接器会为每一个输入文件创建指向这些例程的指针表,并通过链接器创建的诸如_CTOR_LIST_这样的符号让该语言的启动代码可以找到这个表并依次调用其中所有的例程。名称修改名称修改在目标文件符号表和链接中使用的名称,与编译目标文件的源代码程序中使用的名称往往是有差别的。主要原因有 3:避免名称冲突,名称超载,和类型检查。将源代码中的名称转换为目标文件中的名称的过程称为名称修改(name mangl

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 生活休闲 > 社会民生

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号