C++的语言特性使之必须由编译器和链接器共同完成
1. 重复代码消除的方法:
简单的说,凡是需要自动生成的代码,都需要消除代码重复。包括:
模板templates
外部内联函数:extern,inline Function
虚函数表:virtual Function Table
1.将每个模板的实例代码都放到单独的一个段里
例如:GCC中的Link Once,VC++中的COMDAT
2.外部内联函数和虚函数表,做法类似上面的COMDAT
对于虚函数表,编译器会用相应类的多个编译单元,生成虚函数表,造成重复
外部内联函数,默认构造函数,默认拷贝构造函数和赋值操作符
函数级别的链接 Functional-level-linking
目标文件包括很多函数,有些没有用上,所以要保留有用的,剔除无用的。
2.全局构造和析构
main()调用之前:
初始化进程执行环境:
堆分配初始化(malloc,free)
线程子系统
C++全局对象的构造
Elf中的两个特殊段:
.init 保存可执行的指令,构成了进程初始化代码
.fini 进程终止代码指令
3.C++与ABI
链接两个不同编译器编出的结果,应
(1)链接器需要支持两个不同编译器产生的目标文件
(2)同样的目标文件格式,同样的符号修饰标准,同样的变量内存的分布方式,函数的调用方式,这些与可执行代码二进制兼容性相关的内容成为ABI(application Binary interface)。API属于源代码级的接口,而ABI是二级制层面的接口,比如C++的对象内存分布影响ABI的因素:硬件,编程语言,编译器,链接器,操作系统等。
C语言中以下几方面决定目标文件之间的兼容性
(1)内置类型的大小:int,float,char。存储器重的放置方式:大端,小端,对齐方式
(2)组合类型(struct,union,数组)的存储方式和内存分布
(3)外部符号(external-linkage)与用户定义的符号之间的命名方式和解析方式。如函数名func在C语言的目标文件中是否被解析为外部符号_func
(4)函数的调用方式:参数如栈顺序,返回值如何保持等
(5)堆栈的分布方式:参数和局部变量在堆栈里的位置,参数传递方法
(6)寄存器使用约定,函数调用时哪些寄存器可修改,哪些需要保存
C++语言中以下几方面决定目标文件之间的兼容性
(1)继承体系的内存分布,如基类,虚基类在继承中的位置
(2)指向成员函数的指针(pointer-to-member)的内存分布,如何通过指向成员函数的指针调用成员函数,如何传递this指针
(3)如何调用虚拟函数,vtable的内容和分布形式,btable在object中的位置
(4)template实例化
(5)外部符号修饰
(6)全局对象的构造和析构
(7)异常的产生和捕捉机制
(8)标准库的细节问题,RTTI如何实现等
(9)内嵌函数访问细节