目录
7.1编译器驱动程序
7.2静态链接
7.3目标文件
7.4可重定位目标文件
使用readelf -S查看hello.o
一个典型的ELF可重定位目标文件包含以下几个节:
.text:已编程序的机器代码。
.rodata:只读数据,比如printf语句中的格式串和开关语句的跳转表。
.data:已初始化的全局和静态C变量。局部C变量在运行时被保存在栈中,既不出现在.data节中,也不出现在.bss节中。
.bss:未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。在目标文件中这个节不占据实际的空间,他仅仅是一个占位符。目标文件格式区分已初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间,运行时,在内存中分配这些变量,初始值为0。
.symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。一些程序员错误的认为必须通过-g选项来编译一个程序,才能得到符号表的信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表(除非程序员特地用STRIP命令去掉它)。然而,和编译器中的符号表不同,.symtab符号表不包括局部变量的条目。
.rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非用户显示的指示链接器包含这些信息。
.rel.data:被模块引用或定义的所有全局变量的重定位信息。一般而言,任何已初始化的全局变量如果他的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改、
.debug:一个调试符号表,其条目是程序中定义的局部变量那个和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译器驱动程序时,才会得到这张表。
.line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译器驱动程序时,才会得到这张表。
.strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串的序列。
7.5符号和符号表
习题:
注意:
COMMON:未初始化的全局变量
.bss:未初始化的静态变量,以及初始化为0的全局或静态变量
7.6符号解析
学会判断:
全局符号的强/弱特性
– 函数名和已初始化的全局变量名是强符号
– 未初始化的全局变量名是弱符号
习题:
三大规则:
多重定义符号的处理规则
Rule 1: 强符号不能多次定义 –强符号只能被定义一次,否则链接错误
Rule 2: 若一个符号被定义为一次强符号和多次弱符号,则按强定义为准
–对弱符号的引用被解析为其强定义符号
Rule 3: 若有多个弱符号定义,则任选其中一个
–使用命令gcc –fno-common链接时,会告诉链接器在遇到多个弱定义的全局符号时输出一条警告信息。
符号解析时只能有一个确定的定义(即每个符号仅占一处存储空间)
习题1:
习题2:
习题3:
总结:
• 尽量避免使用全局变量
• 一定需要用的话,就按以下规则使用
–尽量使用本地变量(static)
–全局变量要赋初值
–外部全局变量要使用extern
多重定义全局变量会造成一些意想不到的错误,而且是默默发生 的,编译系统不会警告,并会在程序执行很久后才能表现出来, 且远离错误引发处。特别是在一个具有几百个模块的大型软件中, 这类错误很难修正。 大部分程序员并不了解链接器如何工作,因而养成良好的编程习 惯是非常重要的。
• 静态库(.a archive files)
–将所有相关的目标模块(.o)打包为一个单独的库文件 (.a),称为静态库文件,也称存档文件(archive)
–使用静态库,可增强链接器功能,使其能通过查找一个 或多个库文件中定义的符号来解析符号
–在构建可执行文件时,只需指定库文件名,链接器会自动到库中寻找那些应用程序用到的目标模块,并且只把用到的模块从库中拷贝出来
–在gcc命令行中无需明显指定C标准库libc.a(默认库)