name 是字符串表中的字节偏移,指向符号的以null 结尾的字符串名字.value 是符号的地址。对于可重定位的模块来说, value 是距定义目标的节的起始位置的偏移。
每个符号都和目标文件的某个节相关联,由section 字段表示,该字段也是一个到节头部表的索引.有三个特殊的伪节(pseudo section) ,它们在节头部表中是没有条目的: ABS 代表不该被重定位的符号; UNDEF 代表未定义的符号,也就是在本目标模块中引用,但是却在其他地方定义的符号; COMMON 表示还未被分配位置的未初始化的数据目标.
7.6符号解析链接器解析符号引用的方法是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义联系起来.编译器只允许每个模块中每个本地符号只有一个定义。编译器还确保静态本地变量,也会有本地链接器符号,拥有唯一的名字。
根据强弱符号的定义, Unix 链接器使用下面的规则来处理多重定义的符号:
·规则1 :不允许有多个强符号。 ·规则2 :如果有一个强符号和多个弱符号,那么选择强符号。 ·规则3 :如果有多个弱符号,那么从这些弱符号中任意选择一个。所有的编译系统都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库(static ),它可以用做链接器的输入。将所有的标准C 画数都放在一个单独的可重定位目标模块中(如libc中),应用程序员可以把这个模块链接到他们的可执行文件中:
unix> gcc main.c /usr/lib/libc.o一个很大的缺点是系统中每个可执行文件现在都包含着一份标准函数集合的完全拷贝,这对磁盘空间是很大的浪费。我们可以通过为每个标准函数创建一个独立的可重定位文件,把它们存放在一个为大家都知道的目录中来解决其中的一些问题.
unix> gcc main.c /usr/lib/printf.o /usr/lib/scanf.o ...在Unix 系统中,静态库以一种称为存档(archive) 的特殊文件格式存放在磁盘中。。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。存档文件名由后缀.a 标识。为了创建这个可执行文件,我们要编译和链接输入文件main.o
unix> gcc -02 -c main2 .c unix> gcc -static -0 p2 main2 .o ./libvector.a 7.7重定位重定位由两步组成:
·重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节。 ·重定位节中的符号引用。在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。当汇编器生成一个目标模块时,它并不知道数据和代码最终将存放在存储器中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。代码的重定位条目放在.rel.text 中。已初始化数据的重定位
条目放在.rel.data 中。
中两种最基本的重定位类型:
R_386_PC32: 重定位一个使用32 位PC 相对地址的引用.一个PC 相对地址就是距程序计数器(PC) 的当前运行时值的偏移量。当CPU 执行一条使用PC 相对寻址的指令时,它就将在指令中编码的32 位值上加上PC 的当前运行时值,得到有效地址。 R_386_32: 重定位一个使用32 位绝对地址的引用。通过绝对寻址, CPU 直接使用在指令中编码的32 位值作为有效地址,不需要进一步修改。 7.8 可执行目标文件执行目标文件的格式类似于可重定位目标文件的格式。ELF 头部描述文件的总体格式。它还包括程序的入口点(Centry point) ,也就是当程序运行时要执行的第一条指令的地址。
.init 节定义了一个小函数,叫做init ,程序的初始化代码会调用它。因为可执行文件是完全链接的(已被重定位了),所以它不再需要.rel。
7.9 加载可执行目标文件要运行可执行目标文件p ,可以在Unix 外壳的命令行中输入它的名字:
unix> . /p