目标文件格式
现在pc流行的可执行文件格式主要是windows平台下的PE(Portable Executable)和Linux的ELF(Executable Linkable Format),他们都是COFF文件的格式的变种。目标文件就是源代码文件编译后但未进行连接的哪些中间文件,他跟可执行文件相似,所以一般跟可执行文件格式一起采用格式存储。在windows平台下,统称PE--COFF文件格式。在linux下,统称为ELF文件。
不光是可执行文件可以按照可执行文件存储,动态链接库dll和静态链接库lib文件都可以按照可执行文件格式存储。他们在windows下都按照PE--COFF格式存储,LINUX下按照ELF格式存储,静态链接库稍微不同,他是把很多目标文件捆绑为一个文件,在加上一些索引,你可以简单的理解为一个包含很多目标文件的文件包。
目标文件和可执行文件的格式跟操作系统和编译器密切相关,不同的系统平台下会有不同的格式,但这些格式大同小异,目标文件格式与可执行文件科室的历史几乎是操作系统的发展史。
目标文件内容
目标文件中的内容至少有编译后的机器指令代码、数据,没错,除了这些内容以外,目标文件还包括了连接时所需要的一些信息,如符号表,调试信息,字符串。一般的目标文件将这些信息按照不同的属性,以“节”的形式存储,有时候也叫做“段”,一般都表示一定长度的区域,基本上不加区别。
程序源代码编译后的机器指令经常被放在代码段里,代码段常用名字有code或者text,全局变量和局部静态变量经常放在数据段,数据段的一般名字叫做data。如图所示:
假设图的可执行文件格式是elf,从图中可以看到,efl文件的开头是一个文件头,描述文件的属性,包括文件是否可执行,是静态链接还是动态链接以及入口地址(如果是可执行文件)文件头还有个段表。段表其实是一个ie描述文件中各个段的数组。段表描述了文件中各个段在文件中的偏移位置以及段的属性等,从段里面可以看到每个段的所有信息。文件头后米娜就是各个段的内容,比如代码段保存的就是程序的指令,数据段保存就是的程序的敬爱变量。
对照图来看,一般C语言的编译后执行语句都是机器代码,保存在text段中。已初始化的全局变量和局部变量都保存在data段中;为初始化的全局变量和局部变量一般放在一个叫做bss段里面,但是以为内他们都是0,所以在data段分配空间的时候,并且存放0是没有必要的。程序运行的时候,他们的确是要占内存空间的,并且可执行文件必须记录所有为初始化的全局变量和局部静态变量的大小总和,记为bss段。Bss段只是为初始化的全局变量和局部静态变量预留位置而已,他并没有内容,所以文件中不占空间。
总的来说,程序源代码被编译后主要分成两种段:程序指令和程序数据。代码段属于程序指令,而数据段和bss段属于程序数据。
数据和指令分段有诸多好处,主要有一下几个方面:
1. 一方面程序被加载后,数据和指令分别被映射到两个虚存区。由于数据区域对于进程来说是可读写的,而指令区域对于进程来说,是只读的,所以这两个虚存区域的权限可以被设置为读写与只读。防止程序的指令被有意或者无意改写。
2. 另一方面,对于现代CPU来说,他们有着极其强大的缓存体系,有与缓存在现代计算机中地位非常重要,所以程序尽量提高缓存。程序的指令和数据分开有利于提高程序的局部性。现代的cpu缓存都设计成指令缓存和数据缓存。
3. 第三个原因就是当系统中运行该程序的多个副本时,指令都是一样的,所以内存中只需要保存一份程序指令部分集合。对于指令这种只读区域来说是这样的,对于其他的只读数据也是这样。当然每个进程的数据区域不一样,他们是进程私有的。