笔记 —— 《程序员的自我修养:链接,装载和库》 之 静态链接
正如康斯坦丁·斯坦尼斯拉夫斯基的《演员的自我修养》,作为一个有专业素养的程序员,应该对程序的运行有所了解,知其原理,了其规则。
第三章 目标文件
目标文件
自我认为没有必要记清楚每一个平台系统下的目标文件的格式,只需要知道他们都是由段结构组成的文件或文件集合,而且不论是目标文件、可执行文件,还是库,他们的文件格式类型都是一致的。
命名空间、符号修饰,和函数签名
命名空间(Namespace)
早期C语言时期,很多现有编译器默认如果原语言的变量或函数名是foo,则编译后生成的目标文件中该变量和函数名也应该是foo,这会导致如果运行时库中定义了某个变量,则编码人员就不能再用这个变量名,所以C语言的解决办法是编译器给变量加上 _ 的前缀,比如编译后foo改为叫_foo.,Fortran也是这样的类似解决办法。
后来C++设计时,从一开始就考虑了这个问题,从而引入了 命名空间 的概念,来解决多模块之间命名冲突的问题。
C++的符号修饰
出现原因: 解决编译器对待 函数重载 的问题。 ==》 通过一种算法,使得 函数签名 =》修饰后名称 唯一。这种修饰办法有时也用来解决不同模块下静态变量的区别。
函数签名
包含了一个函数信息:函数名,参数类型,所在的类,命名空间,etc.
强符号和弱符号
强符号不可被重复定义,编译器默认函数和初始化的全局变量为强符号,未初始化的全局变量为弱符号。
若强弱符号同时存在定义,链接器优先选择强符号;若多个弱符号存在,链接器选择占用空间大的一个,比如(double > int) ——这种策略称为 COMMON块。
强引用和弱引用
链接器对待强引用要求符号必须被定义,对于弱引用,则不认为未定义是一个错误。
第四章 静态链接
链接
gcc -c a.c b.c ==> a.o b.o
-c means only to do compile operation.
链接器要把多个目标文件从多模块组合起来成为一个可执行文件。
一般都把不同目标文件的相同的段们组合在一起。
链接器为目标文件分配地址和空间。
这个空间,在链接阶段,主要指的是虚拟地址空间的分配。
关于可执行文件本身的空间分配则并不体现在这个阶段,而是后面的装载阶段。
两步链接
step1. 空间地址分配
step2. 符号解析 + 重定位 (core)
ld a.o b.o -e main -o ab ===> ab.out
-e main means that the program’s entry is main function
-o is a naming directive.
What if I run ld b.o a.o -e main -o ab ==> ?
you can use objdump cmdlet to see the mapping records.
objdump -h xxx
you can use objdump cmdlet to see the assembly code of .o files.
objdump -d x.o
在编译阶段,每个目标文件中就包含了重定位段,其中包含了哪些函数、变量是要被重定向的等信息, 可以通过 objdump -r x.o 查看。
undefined reference to … – 链接未定义 错误, 一般是链接时缺少了某个库,或者输入目标文件的路径不正确,或符号的声明与定义不一样。
通过 readelf s a.o 可以查看a.o的符号表,其中会有 UND 的,就是undefined.
COMMON块
为了解决多弱符号定义的情况下,链接器如何选区符号的问题。
表面原因是允许定义多个弱符号,本质原因则是链接器不支持符号类型。
函数级别链接
用类似模板函数保存在一个段里构建函数表的形式给所有函数构建在一个表里。
全局构造与析构
对于C++来说,全局对象的构造函数发生在main之前,全局对象的析构发生在main结束之后。
ABI - Applicable Binary Interface
二进制层面的接口,符号修饰标准、变量内存布局、函数调用方式等的标准。
用于不同编译器结果的目标文件之间进行链接的标准。
C++语言的复杂性导致C++的二进制兼容性不好,不同的编译器编译的结果之间并不通用。现在形成以微软Visual C++和GNU Linux的GCC两大阵营。
静态库链接
静态库其实也是一组目标文件的集合,Linux下是放在某一目录下的静态库,Windows下一般是IDE提供的运行库的一部分。
第五章 Windows PE/COFF
PE跟ELF同根同源,也来自COFF。
cl /c /Za a.c => a.obj
dumpbin /all a.obj > a.txt
windows 下最常见的目标文件格式,COFF,其中有一个.drectve 段,用来存储编译器要传输给链接器的参数。
windows下的可执行文件是PE格式,相较于COFF多了一些结构,但很像。