空间与地址分配
将不同的
*.o
文件组合成一个可执行文件时,各个文件中各个段的排列顺序。
在将各个.o
文件合并成一个可执行文件时,并不时简单的将各个文件直接排列在最终的可执行文件中。
这样做的坏处是,因为对齐的原因造成内存空间中大量的内部碎片。
因此做法是合并相似的段,比如各个文件的.text
段,最终都合并在一个.text
中。
(不过顺序还不太清楚)
链接
链接器一般采用两步链接:
- 空间与地址分配
也就是上面的那步,当合并以后,计算出各段合并以后的长度和位置,建立映射关系。
也就是符号表中的值需要改变。 - 符号解析与重定位
该过程确定了每个一些段加载进虚拟地址空间的地址。
从重定位表中获取要重定位的符号,然后去符号表中的全局符号中去寻找该符号。
COMMON块
和强弱符号一样,只是针对全局符号
源于链接器不能识别类型,只能识别大小
如果模块中的一个全局符号是只声明,而未定义,那么该符号为一个弱符号。
如果声明并定义了,那么该符号是一个强符号。
而通过extern
引入的符号不是强符号也不是弱符号(应为它可能在别的地方定义了)。
当在一个模块中,处理弱符号的时候,编译器其实是无法得知该符号最终的类型的。因为其他模块可能定义该符号,编译器需要去其他模块寻找。
但是这时候,编译器,还不知道该符号的大小,因为类型未知,所以大小也未知,因此还不能放入.bss
段,因为.bss
段的类型都是类型大小已知的,也就是一些局部静态变量。
弱符号:应该只存在在符号表中,被标记为COMMON
。(其中符号表中的富豪所在段的值为0xFF2
,表示是一个COMMON)
在链接的时候,确定类弱符号的最终大小,一般都是弱符号就取最大的,如果有强符号就取强符号,但是此时如果存在弱符号大小大于强符号,链接器报错。
当该变量是为初始化的全局变量时,链接时,确认了大小,还是会被放在.bss
段中。
C++相关
重复代码问题
C++的模板,外部内联函数和虚函数表都可能在不同的便一单元中生成相同的代码。
模板这块再看看书
编译器主流的方法是,将每个模板函数放在同一个段中。
另外提供函数级别的链接,编译出来的文件小,但是编译速度慢
全局构造与析构
C++的全局对象构造在main()
之前,析构在main()
之后,分别在.init
和.finit
两个段中。
ABI
API指的是源代码级别的接口,而ABI是二进制层面的接口,其兼容性比API要严格。
ABI涉及到:
- 内置类型的大小在存储器中存放的方式
- 组合类型的存储方式和内存分布
- 符号修饰
- 函数调用方式:参数入栈顺序,返回值保存
- 堆栈分布,参数和局部变量在堆栈里的位置,参数传递方式。
- 寄存器约定
- 集成体系的内存分布
- 指向成员函数的指针的内存分布,如果通过指针调用成员函数,如何传递this
- 如何调用虚函数,虚函数表的内容和分布形式,vtable指针在obj中的位置
- 模板如何实例化
- 全局对象的构造和西沟
- 异常的产生和补货
- 标准库的一些细节
- 内嵌函数的访问细节。