Linking (csapp-7)
- 编译驱动程序包括
- 预处理器(cpp)
- 将程序从ASCII源文件翻译成 .i 文件
- 编译器(ccl)
- 翻译成 .s ASCII汇编语言文件
- 汇编器(as)
- 翻译成 .o 可重定位目标文件
- 链接器(ld)
- 翻译成可执行文件
- 预处理器(cpp)
- 链接器主要任务
- 符号解析
- 将函数,全局变量,静态变量(static)的定义和引用(使用)关联起来
- 重定位
- 汇编器生成从地址0开始的代码和数据(产生相对地址),链接器将这些定义和内存位置关联起来(产生绝对地址)
- 这里的绝对地址是对于这个程序内部而言的绝对位置,并不是整个PC内的绝对位置
- 汇编器生成从地址0开始的代码和数据(产生相对地址),链接器将这些定义和内存位置关联起来(产生绝对地址)
- 符号解析
- 可重定位目标文件格式(ELF)
- 局部变量是在运行时保存在栈中,链接器不知道其存在
- 所以ELF文件的符号表中没有局部变量的条目
- 而编译器的符号表中是有的
- 所以ELF文件的符号表中没有局部变量的条目
全局符号分为强符号和弱符号
- 强符号为函数或者已经初始化的全局变量
- 弱符号是未初始化的全局变量
- 遇到同名符号,有强符号就将引用解析为强符号,否则随机选取一个弱符号解析
- 这样可能会引发错误
- 尽量避免使用全局变量
- 最好使用extern命令
对于第一个,假如随机选取p1文件中的符号作为x引用的值得话,如果向p2中的x写入值,实际上是向p1中的x的地址写入值,而p2中的x类型比p1中的大两倍,所以写入操作会改变y的值
- 对于第二个,因为p1中的x为强符号所以结果和第一个一样
- 对于第二个,因为p1中的x为强符号所以结果和第一个一样
- 静态库
- 将常用文件(如标准I/O,数学函数等)存为.a后缀的存档文件,当链接时,会且仅会将库中被调用的函数连接到程序中
- 链接器从左到右扫描命令行出现的可重定位目标文件和存档文件
- 在扫描中,链接器维护了一个引用了但是没有解析的符号集合U
- 当输入文件尾.o文件时,添加函数或变量到U中
- 当输入文件为.a文件时,匹配U中符号和存档文件中定义的符号,并将没有匹配的丢弃掉
- 如果完成扫描后U非空,则报错
- 如果一个库出现在目标文件前,链接就会失败
- 所以一般将库放在最后
- 如果库a和b会互相调用则其中一个库要输入两遍
- 在扫描中,链接器维护了一个引用了但是没有解析的符号集合U
- 重定位
- 两种基本类型
- 重定位PC相对引用
- 重定位绝对引用
- 两种基本类型
- 重定位前
-重定位后
- 加载
- 加载器将可执行文件代码和数据复制到内存中,并将PC跳转到程序的第一条指令
- 加载器将可执行文件代码和数据复制到内存中,并将PC跳转到程序的第一条指令
- 共享库和动态链接
- 动态库中的代码和数据不会再链接时复制到可执行文件中,而是复制一些重定位和符号表信息,是的运行时能够解析共享库
-
- 动态库应用
- 动态库应用
- 位置无关代码
- 动态库每次调用在内存中出现的地址是不同的
- 如果固定一个地址的话会造成许多不连贯的内存,造成巨大的内存浪费
- 所以使用PIC和PLT解决
- PIC在目标代码的数据段,包含被调用全局变量或函数的绝对内存地址
- 在数据段是因为目标代码的代码段一般不可修改
- 原理是数据段和代码段的相对位置是固定的
- PLT是在调用全局函数时与PIC共同协作的
- PIC在目标代码的数据段,包含被调用全局变量或函数的绝对内存地址
- 动态库每次调用在内存中出现的地址是不同的
对PIC和PLT更具体的解释:https://blog.csdn.net/wuhui_gdnt/article/details/51094732