作者:白开水 | 来源:互联网 | 2023-10-16 14:00
第三周读书笔记《程序员的自我修养》 计划对这本书是精读,这周读了3,4章。第三章目标文件里有什么 首先介绍了目标文件的格式,Windows下是pe-coff,
第三周读书笔记 《程序员的自我修养》
计划对这本书是精读,这周读了3,4章。
第三章 目标文件里有什么
首先介绍了目标文件的格式,Windows下是pe-coff,linux下是elf,他们都源于coff,这与操作系统的发展历史有关,而且不仅可执行文件按这种格式存储,动态链接库(.dll),静态链接库(.lib)都是如此存储。
在目标文件中,信息以段的形式存储(微机原理讲过),总体被分为指令与数据两部分。代码编译后的机器指令放在代码段(.text),全局变量与局部静态变量放在数据段(.data),未初始化的全局变量或者局部静态变量放在.bss段,此段为这些变量预留位置(初始化后才占用内存).
看到这里必然会疑惑为什么要分开呢?
一是是数据必然要有读写的权限,假如对于指令给予写权限可能会带来隐患
二是可以适应CPU的cache体系,现代cache就分成了指令缓存与数据缓存。
三是最重要的,系统运行多个相同程序的时候,他们可以共享指令,还有文本图片等,当然数据区是独立的,这极大的节省了内存。
之后还介绍了文件头,以及对于一段代码编译后存放的分析。引起我兴趣的是关于重定位的文件,.o文件里会有一个叫做。rel.text的文件,类型为SHT——REL,存放代码段,数据段中重定位表的位置,并以符号文件作为接口
当各种库文件出现,不可避免就要解决用户自己定义的变量与库文件变量可能重名的问题,c是在所有用户自定义编译后前面加_(但是多个用户写出的代码还是可能会冲突),之后像c++便有了命名空间之类的概念。
c++的复杂特性(类,继承,重载,命名空间)为符号管理增加了难度,人们发明了符号修饰机制解决了重载问题。对于返回变量,形参不同的函数,利用其特征生成不同的签名以区别。注意不同的编译器会采取不同的签名方法,这也导致不同的编译器不能互操作。
第四章 静态链接
讲述了两个文件链接后在输出文件中的内存位置,为了避免浪费储存空间(各个段是用页的形式储存的),大都采用相似段合并的方法。一般分为两步,1地址与空间分配,2符号解析与重定位。
C++因为其语言特性使其必须编译器与链接器共同支持才能完成工作。 第一个问题是重复代码消除,例如不同的文件中实例化了相同的模板,每个文件各自以不同参数单独占据一个段,合并时将相同的实例合并在一个段。
再就是解决目标文件中没有调用的函数的处理,因为现在的库都十分庞大,当链接时,很容易没用的函数包括进来,编译器一般会给与选项,可以让所有函数分到不同的段中,这样链接的时候就可以舍弃无用的函数段,但是因为要计算函数依赖关系,会较低编译速度。
c++中ABI(二进制层面的接口)兼容性变差,导致不同公司用相同编译器编译的二进制代码都无法兼容,除非将所有源代码一块编译,对大型项目十分不友好。
静态库就可以看成一些目标文件的集合,就是许多目标文件打包,windows上最常见的就是IDE自带的运行库。理论上我们可以从库中提取我们想要的目标文件,与我们写的代码生成的目标文件来链接来生成最终的目标文件。值得注意的是库文件中一个目标文件一般只包含一个函数,这样是为了避免链接时候将无用函数链接进来形成空间的浪费。
后面一些链接控制什么的实在是太低层了,不想看了。
为了解决不同硬件平台导致的目标文件格式的不同,这对像是GCC一样的跨平台工具提出了要求,BFD库就是解决了这一问题。其可以看作这些不同目标文件的一个接口,其把目标文件抽象成一个抽象的模型,这样就使得目标文件与编译器与链接器分割开来。
以上就是本周看的内容。