目标文件里有什么


编译器编译源代码后生成的文件叫做目标文件,那么目标文件里面到底存放的是什么,或者我们的源代码在经过编译以后是怎样存储的?
我们将去探索它本质的东西。
目标文件从结构上讲,它是已经被编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。其实它本身就是按照可执行文件格式存储的,只是跟真正的可执行文件在结构上稍有不同。
可执行文件格式涵盖了程序的编译,链接,装载和执行的各个方面,了解它的结构并深入剖析对于认识系统,了解后面的机理大有好处。

目标文件格式

目标文件就是源代码编译后但未进行链接的那些中间文件,它跟可执行文件的内容与结构很相似,所以一般跟可执行文件格式一起采用一种格式存储,从广义上看,目标文件与可执行文件的格式其实几乎是一样的,所以我们可以广义地将目标文件与可执行文件看成是一种类型的文件,在Windows下,我们可以统称为PE-COFF文件格式,在Linux下,我们可以统称为ELF文件。
不光是可执行文件(Windows的.exe和Linux下的ELF可执行文件)按照可执行文件格式存储。动态链接库(Windows的.dll和Linux的.so)及静态链接库(Windows的.lib和Linux的.a)文件都按照可执行文件格式存储。它们在Windows下都按照PE-COFF格式存储,Linux下按照ELF格式存储,静态链接库稍有不同,它是把很多目标文件捆绑在一起形成一个文件,再加上一些索引,你可以简单的把它理解为一个包含了很多目标文件的文件包。ELF文件标准里面把系统中采用ELF格式的文件归为4类。
在这里插入图片描述
在这里插入图片描述

目标文件是什么样的

目标文件中的内容至少有编译后的机器指令代码,数据,除了这些内容外,目标文件还包括了链接时所需要的一些信息,比如符号表,调试信息,字符串等,一般目标文件将这些信息按不同的属性,以节的形式存储,有时候也叫段,在一般情况下,它们都表示一个一定长度的区域,基本上不加以区别,唯一的区别是在ELF的链接视图和装载视图的时候。我们默认情况下统称为段。
程序源代码编译后的机器指令经常被放在代码段里,代码段常用的名字有".code"和“.text”,全局变量和局部静态变量数据经常被放在数据段,数据段的一般名字都叫".data"。让我们来看一个简单的程序被编译成目标文件后的结构。
在这里插入图片描述
假设图中的可执行文件(目标文件)的格式是ELF,从图中可以看到,ELF文件的开头是一个“文件头”,它描述了整个文件的文件属性,包括文件是否可执行,是静态链接还是动态链接及入口地址(如果是可执行文件),目标硬件,目标操作系统等信息,文件头还包括一个段表,段表其实是一个描述文件中各个段的数组。段表描述了文件中各个段在文件中的偏移位置及段的属性等,从段表里面可以得到每个段的所有信息,文件头后面就是各个段的内容,比如代码段保存的就是程序的指令,数据段保存的就是程序的静态变量等。
从图上看,一般C语言的编译后执行语句都编译成机器代码,保存在.text段,已初始化的全局变量和局部静态变量都保存在.data段,未初始化的全局变量和局部静态变量一般放在一个叫".bss"的段里。我们知道未初始化的全局变量和局部静态变量默认值都为0,本来它们也可以放在.data段的,但是因为它们都是0,所以为它们在.data段分配空间并且存放数据0是没有必要的。程序运行的时候它们的确是要占内存空间的,并且可执行文件必须记录所有未初始化的全局变量和局部静态变量的大小总和,记为.bss段,所以.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间。
总体来说,程序源代码被编译以后主要分为两种段:程序指令和程序数据,代码段属于程序指令,而数据段和.bss属于程序数据。
很多人可能会有疑问,为什么要那么麻烦,把程序的指令和数据的存放分开?混杂地放在一个段里面不是更加简单,其实数据和指令分段的好处有很多。
1.一方面是当程序被装载后,数据和指令分别被映射到两个虚存区域,由于数据区域对于进程来说是可读写的,而指令区域对于进程来说是只读的,所以这两个虚存区域的权限可以被分别设置成可读写和只读,这样可以防止程序的指令被有意或无意地改写。
2.对于现代的CPU来说,它们有着极为强大的缓存体系,由于缓存在现代的计算机中非常的重要,所以程序必须尽量提高缓存的命中率。指令区和数据区的分离有利于提高程序的局部性,现代CPU的缓存一般都被设计成数据缓存和指令缓存分离,所以程序的指令和数据被分开存放对CPU的缓存命中率提高有好处。
3.当系统中运行着多个该程序的副本时,它们的指令都是一样的,所以内存中只须要保存一份该程序的指令部分,对于指令这种只读的区域来说是这样,对于其他的只读数据也一样,比如很多程序里面带有的图标,图片,文本等资源也是属于可以共享的,当然每个副本进程的数据区域是不一样的,它们是进程私有的,不要小看这个共享指令的概念,它在现代的操作系统里面占据了极为重要的地位,特别是在有动态链表的系统中,可以节省大量的内存,关于内存共享的更为深入的内容我们将在装载讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值