编译器编译源码后形成的文件叫做目标文件,从结构上将,他已经是经过编译后的可执行文件格式,只是还没有经过链接,其中可能有些符号或有些地址还没有经过调整,其实它本身就是按照课执行文件格式存储。
现在PC平台可执行文件格式,主要是Windows下的PE和Linux下的ELF,都是COFF格式的变种。目标文件格式和可执行文件格式相似,所以在Windows下可以统称他们为PE-COFF文件格式,在Linux下统称为ELF文件。
不光是可执行文件按照可执行文件格式存储,动态链接库(Windows的.dll文件和Linux下的.so文件),以及静态链接库(windows下的.lib和Linux下的.a文件)都是按照可执行文件格式存储,Windows下以PE-COFF格式和Linux下以ELF格式存储。
ELF目标文件是什么样的
File Header |
.text section |
.data section |
.bss section |
这个目标文件以ELF格式存储,我们大概能猜到编译后的文件内容至少有机器指令代码和数据,还应该有链接所需要的一些信息,比如符号表,调试信息,字符串等。一般目标文件将这些信息按照不同属性,以 段的方式存储,程序编译后的代码存在 .text section,初始化的全局变量和局部变量存在.data section,未初始化的变量存在.bss section中,本来他们也可以存在.data section 中,但是我们知道未初始化的变量的值为0,所以把他们放在.data section中浪费空间,不如直接放在.bss段中,为未初始化的全局变量和局部静态变量预留位置,.bss 在文件中不占据空间
比如程序
int a=1;
int b;
int foo(int x)
{
return x*2;
}
int main()
{
int c=2;
int d;
printf("%d",foo(c));
}
初始化过的变量a 变量c存在.data中,未初始化的变量b,d存在.bss中,函数foo,main的代码放在.text中
数据段和代码段分开的好处有三个
1.数据段是可读写的,代码段是只读的,分开放就可以访问这两个段的权限分别设置成 可读写和只读,防止程序指令被修改
2.有利于提高程序的局部性,提高缓存命中率
3.当系统运行该程序的多个副本的时候,只要保存一份该程序的指令部分,因为指令部分是只读的,对其他只读数据也只要保留一部分,
对于数据区域,每个程序各保留一份私有的,指令部分占内存比较多,只要可以节省很多内存