linux下的目标文件(.o文件)采用ELF格式。目标文件里采用段section的格式存储,比如代码段啊(存代码),数据段啊(初始化了的全局变量和局部静态变量),BSS段啊(未初始化的全局和局部静态变量),只读数据段啊(程序中用到的字符串)等。在所有的段外,有一个重要的结构叫做文件头。所以这里从文件头开始说。
1 文件头主要包含以下内容(没什么用的就不写了):
(1)e_ident:这里面一个重要的内容就是魔数和机器的位数。魔数用来标识文件的类型,比如elf啊,a.out啊,MZ啊等。机器位数就是32或者64.
(2)e_type:有三种,可重定位文件(就是目标文件),可执行文件,共享目标文件(就是动态链接库文件)
(3)e_shoff: 段表在文件中的偏移(接下来会说段表相关内容)
(4)e_ehsize :文件头本身的大小
(5)e_shentsize: 段表描述符的大小。就是段表有很多项,这个是说每一项的大小是多少(接下来会说段表相关内容)
(6)e_shnum: 段表描述符数量(接下来会说段表相关内容)
(7)e_shstrndx: 段表字符串表在段表中的下标。段表字符串表就是存储所有所有段的名字的一个表。一般段表字符串表的名字可能是.shstrtab(接下来会说段表相关内容)
从这里可以知道段表的位置(e_shoff),段表中有多少项(e_shnum),这样就能从段表中解析出所有的段。下面是段表的相关内容。
2 段表。段表有很多项。每一项主要包含以下内容:
(1)sh_name:这个值记录的是段的名字在段表字符串表的下标
(2)sh_type:段的类型。主要的类型有程序段,符号表段,字符串表,重定位表,哈希表,动态链接信息等
(3)sh_flag:段的标志位,有三种:可写,需要分配空间,可执行
(4)sh_addr:段虚拟地址,被加载后在进程空间的地址;
(5)sh_offset:段的偏移
(6)sh_size:段的长度
(7)sh_link,sh_info:段的链接信息。如果段的内容与链接有关,这两个值才有意义。比如动态链接符号表啊,重定位表啊,sh_link表示这些表使用的字符串表或符号表在段表中的下标,sh_info表示重定位表作用的段在段表中的下标。
(8)sh_addralign: 对齐信息
(9)sh_entsize:项的长度。有些段,比如符号表,每个项的大小是相同的,所以需要每个项的大小。
3 重定位表:代码段如果需要重定位,那么就会有针对代码段的重定位段,比如.rel.text;数据段如果需要重定位,那么也需要专门的重定位表,比如.rel.data。每个重定位段是专门的一个段。它的sh_link表示符号表的下标,sh_info表示它作用于哪个段。关于重定位表的详细结构在下一篇阅读笔记中说明。
4 字符串表:字符串表是将用到的字符串都放到一起,然后每个用到字符串的地方只需要给出字符串表的下标即可。一般字符串表存放程序中用到的字符串,而段表字符串表用来存储段表中用到的字符串,比如段名。
5 符号表。符号是进行链接的接口。符号表由很多项组成的。每一项包含的内容如下:
(1)st_name:符号名在字符串表的下标
(2)st_value:简单的说,这个表示符号(函数啊,变量啊)的地址,实际上大多数时候这个值表示的是符号相对于所在段的偏移。在可执行文件中,这个表示符号的虚拟地址(这个对动态链接有用)。
(3)st_size:符号大小,比如double的大小为8个字节
(4)st_info:它的第四位表示符号的绑定信息(局部符号,全局符号,弱引用),高28位表示符号类型(数据啊,函数啊等)
(5)sh_shndx:如果符号定义在本目标文件,这个表示符号所在段的下标。