【说明】本文章从本人的CSDN博客搬过来的,因个人感觉CSDN的博客系统太差,so,搬到这里。
在上一篇文章中,主要介绍了几个重要的段以及这几个段中存放的信息。这里将重点介绍目标文件中的 ELF文件头 和 段表(Section Header Table)。
关于ELF文件头,它主要包含了描述整个文件的基本属性,比如ELF文件版本,目标机器型号,程序入口地址等。
关于段表,它描述了ELF文件包含的所有段的信息,比如每个段的段名,长度,在文件中的偏移,读写权限以及其他的属性。
1.再看文件头
再来看整个头文件结构
那么对于系统来讲,这个结构是怎么存储的,按什么格式存储呢?
在/user/include/elf.h中,可以找到描述这个头文件的结构体,如下所示:
对比实际的内容跟这个结构体,发现从Type (e_type)开始往下是一一对应的,而前面几条信息就对应于 e_ident[16],即e_ident中存放着前几条的信息。
现在将几个重要的字段解释一下:
e_type : ELF文件类型,主要有3种,ET_REL 可重定位,ET_EXEC可执行,ET_DYN共享目标文件,一般为.so文件
e_machine : 机器类型,如EM_386,intel的x86架构,EM_68K摩托罗拉的68000系列等
e_entry : 入口地址,可执行文件被加载后,会从这个虚拟地址开始执行指令。目标文件不会执行,所以地址为0
e_shoff : 段表在文件中的偏移,即段表所在的地址,这个很重要
e_ehsize : 头文件本身的大小
e_shentsize : 段表描述符的大小,即sizeof(Elf32_Endr),一般为40
e_shnum : 段表描述符的数量,即该文件总共有多少个段
e_shstrndx : 段表字符串表所在的段在段表中的下标,很绕口,后面再讲。
2.段表
ELF文件的段结构就是由段表决定的,编译器,连接器和装载器都是靠段表来定位和访问各个段的属性的。
现在我们来仔细看看目标文件中的各个段:
这里列出来的就是段表的信息,可以看到第一个是无效的,它的类型是NULL,共有13个有效的段。其中包括上篇里我们自定义的 special段。
那么,描述段表的结构又是怎样的呢?
段表其实是一个Elf32_Shdr的数组,每一个数组元素都是一个结构体,来描述一个段。
sh_name : 段名
sh_type : 段的类型
比如:SHT_NULL 表示无效段
SHT_PROGBITS 表示程序段 代码段 数据段
SHT_SYMTAB 表示该段的内容为符号表
等等
sh_flags : 段的标志
比如:SHF_WRITE 该段在进程空间中可以写
SHF_ALLOC 表示需要在进程空间中分配信息,代码段 数据段等,注意:bss段也是这个标志,虽然是在文件中没有,但是会在进程中分配 空间
SHF_EXECINSTR 表示可执行 像代码段
例如:.text段的类型是 SHT_PROGBITS,标志是SHF_ALLOC + SHF_EXECINSTR
sh_addr : 如果该文件被加载,表示该段在进程虚拟地址空间中的位置,否则为0
sh_offset : 段偏移,如果该段在文件中,则表示偏移,否则无意义。比如对于BSS段来讲,这个值是没有意义的
sh_size : 段的长度
sh_link sh_info : 段的链接信息
参照下表:
sh_addralign : 段地址对齐 段地址对齐于 2的sh_addralign次方,前面说过,2**2是2的2次方,即对齐4个字节,不够4的,也要占满。如果sh_addralign为1或者0,则不限制对齐。
sh_entsize : 项的长度,有些段包含了一些固定大小的项,如符号表,它的每一项占得空间都是相同的,这是该字段表示的是每个项的大小。为0则表示不含有相同大小的项。
每个段描述符的大小为sizeof(Elf32_Shdr),大小为40个字节,到文件最后结束时,大小为0x0570,即1392个字节,跟ll命令查看的大小是相同的。下面让我们看看ELF文件格式的真实存储情况:
(程序就是前面文章中用到的,环境是Ubuntu14.04 64位)
因为字节对齐的原因,中间有的段之间有一个或两个字节的空位。
3.重定位表
首先,重定位过程发生在链接时,简单的讲重定位的任务就是将编译时无法确定的地址,确定下来。
在hello.o中,有一个段,.rel.text,他的类型是SHT_REL,说明它是一个记录.text段的重定位信息的段。这个段的sh_link表示符号表的下标【为12】,sh_info表示作用于哪个段【为1,说明是 .text段】。
比如:printf函数,这个一个外部定义的函数,在编译期间是不会知道它的地址的,所以需要依靠连接器来决定它的真是运行地址(后面的文章会说到)。
4.字符串表
前面提及的几个表结构都是用固定大小的结构体来存储的,但是,字符串的大小是不固定的,不能用固定的结构来表示的。所以ELF采用了一种依赖结束符的存储结构。
从0地址取,是空串,
从1地址取,是NIHAO,
从3地址取,是HAO
这样就不用考虑字符串的长度了。
字符串表也已段的形式保存在ELF文件中,名为.strtab或.shstrtab。前者是字符串表,后者是段表字符串表。前者保存的是普通的字符串,而后者保存的则是段表用到的字符串,比如表名。
前面讲到,Elf32_Ehdr最后一个成员是e_shstrndx,他表示 段表字符串表 (.shstrtab)在 段表中的下标。在文件头里看到,最后一个成员的值是11,再查看段表11位置上的值正好是.shstrtab,即段表字符串表。
下一篇文章,将介绍ELF文件的符号,马上就会很清晰的认识这个文件格式了。
来源:oschina
链接:https://my.oschina.net/u/1428119/blog/304933