您的位置:首页 > 职场人生

目标文件、ELF文件的一点小总结 《程序员的自我修养》·笔记

2015-08-12 21:31 1041 查看

目标文件

源代码编译后但是没有进行链接的那些中间文件,比如win下的.obj文件、linux下的.o文件,与可执行文件的内容以及格式很类似。

目标文件中的内容至少有编译后的机器指令代码、数据。还包括连接时所需要的一些信息,比如符号表、调试信息、字符串等。一般,目标文件会将这些信息按照不同的属性进行分段(其实就是多个一定长度的区域)。

下面主要对linux下的可执行文件的ELF格式进行分析

ELF文件的结构

ELF文件主要由文件头(ELF header)、代码段(.text)、数据段(.data)、.bss段、只读数据段(.rodata)、段表(section table)、符号表(symtab)、字符串表()、重定位表(.rel.text)如下图所示:



代码段与数据段分开的原因:

1.对进程来说,数据段是可读写的,指令段是只读的。这样可以防止程序指令被改写。

2.指令区与数据区的分离有助于提高程序的局部性,有助于对CPU缓存命中率的提高。

3.当系统运行多个改程序的副本的时候,他们对应的指令都是一样的,此时内存只需要保留一份改程序的指令即可。当然,每个副本进程的数据区域是不一样的,他们是进程私有的

结合下图进行分析



代码段

如上图所示,一般C语言编译后的执行语句都编译成机器代码,保存在.text段。

.data段

已经初始化的全局变量局部静态变量(虽然默认会初始化为0,或者手动初始化为0,都没有必要在数据段分配空间,直接放在.bss段,就默认值为0了)都保存在.data段。

大体来说,该section包含了在内存中的程序的初始化数据;data段包含三个部分:heap(堆)、stack(栈)和静态数据区。即.data还会存放其他类型的数据,比如局部变量。

数据段只是存放数据,变量名存放在字符串表中。

.bss段

未初始化的全局变量局部静态变量都保存在.bss段。

大体来说该section包含了在内存中的程序的未初始化的数据。

由于程序加载(一般是指main之前)时,bss会被操作系统清零,所以未赋初值或初值为0的全局变量都在bss。.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间,这样可减少目标文件体积。

但程序运行时需为变量分配内存空间,故目标文件必须记录所有未初始化的静态分配变量大小总和(通过start_bss和end_bss地址写入机器代码)。当加载器(loader)加载程序时,将为BSS段分配的内存初始化为0。

.rodata段

存放只读数据,一般是程序里面的只读变量(如const修饰的变量),以及字符串常量(不一定,也可能放在.data中)。

自定义段

程序员可以指定变量所处的段。

__attribute__((section(“FOO”))) int global = 42;

则将变量放入名字为FOO的段中。

ELF文件头

ELF魔数:文件头最开始的四个字节,第一个字节是DEL控制符的ASCII码,后三个字节是ELF字母的ASCII码。这种魔数用来确认文件的类型,操作系统加载可执行文件时会确认魔数是否正确。

文件类型:枚举表示

段表偏移:段表在文件中的偏移。



段表

描述ELF文件各个段的信息,你如每个段的段名、段长度、在文件中的偏移、读写权限以及段的其他属性。是用一个数据结构来描述的。

重定位表

链接器在处理目标文件的时候,需要对目标文件的某些部位进行重定位操作,即代码段数据段中那些绝对地址的引用位置。

字符串表

字符串表中包含若干以 null 结尾的字符串,这些字符串通常是 symbol 或 section 的名字。当 ELF 文件的其它部分需要引用字符串时,只需提供该字符串在字符串表中的位置索引即可。

字符串表中首先是一个空串,用于表示一个空名字,所以字符串表的第一个字节是“\0”。

一个字符串表可能涉及该 section 中的任意字节。一个字符串可能引用不止一次;引用子串的情况是可能存在的;一个字符串也可能被引用若干次;而不被引用的字符串也是允许存在的。

如下图所示





符号表

在链接的过程中,将函数和变量统称为符号。

每一个目标文件都有一个对应的符号表。符号表主要包括:

1.符号名,也即该符号名在符号串表中的下标

2.符号值:一般是函数或者是变量的地址(.data段)。

3.该符号所在的段。

4.等等
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: