linux的目标文件格式是,【linux】目标文件、可执行文件(ELF)格式解析

ELF文件、目标文件、可执行文件的关系

目标文件是源代码经过编译但未进行链接的那些中间文件,在linux中的.o文件,它跟可执行文件的内容与结构很相似,所以一般与可执行格式采用一种方式存储,在linux下,我们可以将他们统称ELF文件。ELF文件标准里面把系统中采用ELF格式的文件归为四类:

ELF文件类型

说明

实例

可重定位文件(Relocatable File)

这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也属于这一类

linux的.o

windows的.obj

可执行文件

(Executable File)

这类文件包含了可以直接执行的程序,它的代表就是ELF可执行文件,它们一般都没有扩展名

比如/bin/bash文件;window的.exe

共享目标文件

(Shared Object File)

这种文件包含了代码和数据,可以在以下两种情况下使用。一种是连接器可以使用这种文件跟其他的可重定位文件和共享目标文件链接,产生新的目标文件。第二种是动态链接器可以将几个这种共享目标文件与可执行文件结合,作为进程映像的一部分运行。

linux的.so,如/lib/glibc-2.5.so

windows的DDL

核心转储文件

(Core Dump File)

当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件。

linux下的core dump

以以下代码编译出来的目标文件作为分析对象:

#include int global_init_var = 84;

int global_uninit_var;

void func1(int i)

{

printf("%d\n",i);

}

int main(void)

{

static int static_var = 85;

static int static_var2;

int a = 1;

int b;

func1(static_var + static_var2 + a + b);

return a;

}

使用objdump -h SimpleSection.o命令打印各个段的基本信息:

6c8e99a50b9809cd69a774bf5edd2966.png

如上图显示共有代码段、数据段、BSS段、只读段、注释信息段和堆栈提示段。对应的信息size表示段的长度,VMA File off表示段所在的位置。每个段的第二行中的“CONTENTS”、“ALLOC”等表示段的各种属性;其中“CONTENTS”表示该段在文件中存在。

.text代码段:可以使用objdump 命令;-s 参数可以将所有段的内容以十六进制的方式打印出来。-d参数可以将所有包含指令段反汇编。我们将关于代码段的信息提取出来

ad9cb1641863c611cb52ffebab64a9b7.png

f89fa13f7a08020fe9e3c0224b0f1add.png

Contents of section .text下就是.text段的内容,总共0x51,与使用-h参数输出的.text长度一致,最左边是偏移量,中间是十六进制的内容,最右边是.text段的ASCII码形式。对照下边的反汇编结果,可以很明显的看到fun1和mian函数,.text段的第一个字节"0x55"就是func1()函数的第一条“push %ebp”指令,而最后“0x50”正好是main函数的最后一条指令“ret”。

.data数据段:保存初始化了的全局变量和局部静态变量。global_init_var 和static int static_var。大小正好是8个字节。

.bss段:存放的是未初始化的全局变量和局部静态变量。也就是说global_uninit_var和static_var2应该存放在.bss段,准确来说.bss段位他们预留了位置,本应该是8个字节,而我们使用objdump -h命令显示的size是4个字节。事实上只有static_var2存放在了.bss段,而global_uninit_var只是一个未定义的“COMMON”符号;这和编译器有关,有的编译器将全局未初始化变量存放在.bss段,有的则不存放,只是预留一个未定义的全局变量符号,等到最终链接后再分配到.bss段。

.rodata段:存放的是只读数据,一般是程序里面的只读变量(如const修饰的变量)和字符串常量。在func1里面我们在调用“printf”的时候,用到了一个字符串常量“%d\n”,是4个字节与前面的长度符合。

上面通过一个实例大致了解了ELF文件的基本轮廓,总的来说就包括了指令和数据。下图是ELF文件的总体结构:

f5b8b88f78f617f7851c38f229235c13.png

上面讲述了指令段和数据段,以下是ELF文件中其它的几个重要结构:

文件头(ELF Header)

ELF32_Ehdr和 ELF64_Ehdr结构体定义了ELF文件头的相关信息;这两个结构体的成员信息一致,只不过一个是32位版本的,另一个是64版本的。这里以ELF32_Ehdr为例,定义如下:

a9e7cc9d585812c92a4798e1e5677222.png

我们可以使用readelf查看SimpleSection的目标文件头:

da35f3e0fb32a8921337f91c2b07cacf.png

e_ident(ELF魔数):该成员与readelf输出的Magic、Class、Data、Version、OS/ABI、ABI Version对应。 readelf输出的Magic的16字节正好对应e_ident这个成员;对于ELF文件前四个字节都必须相同,分别为0x7F、0x45、0x4c、0x46第一个字节对应ASCII控制,后面三个字节刚好是ELF这三个字母的ASCII,这4个字节称为ELF魔数,这种魔数用来确认文件的类型,操作系统在加载可执行文件的时候会确认魔数是否正确,如果不正确会拒绝加载。下一个字节对应Class用来表示ELF文件类的;第六个字节对应Data规定ELF文件是大端还是小端,第七个字节规定ELF文件的主版号,一般是1。对应的数据如下图所示

fe68b8ce712b2185b371a1c6066ae6c0.png

e_type(文件类型):对应 readelf输出的Type表示ELF文件类型。包括REL (可重定位文件)、EXEC(可执行文件)、DYN(共享文件);系统是根据这三个常量判断文件类型,而不是扩展名。

剩下的成员与readelf输出的参数,一一对应,此处不再赘述。综上文件头中描述整个文件的基本信息,以及段表的位置和大小、程序头的位置和大小。

段表(Section header Table)

段表是ELF文件中除了头文件以外最重要的结构,它描述了各个段的信息,比如段的段名、长度、在文件中的偏移、读写权限等等。段表是以一个元素是“ELF32_Shdr”结构体的数组。数组中的每一个元素对应一个段。ELF32_Shdr结构体的定义如下:

1342c107db01a6509e62b9a5330568d1.png

使用readelf -S SimpleSection.o查看目标文件的段表

16e824a28b4bfc693ccfde732b9b5235.png

根据上图,总共有11个元素,ELF段表文件的第一个元素时无效的,所以有效的段描述符有十个,也就是说有十个有效的段。readelf命令的输出与ELF32_Shdr结构体定义的成员一一对应,以下是对部分参数的详细解释:

sh_type(段的类型):对应readelf命令输出的Type。段名只是在链接和编译过程中有意义,但是不能代表段的类型。比如我们有可以将一个数据段命名为.text。段对应的类型如下图所示:

6f58319fef20520ca07bd9dadd816c88.png

sh_flag(段的标志位):对应readelf命令输出的Flg;段的标志位是指该段在进程虚拟空间中的属性。比如是否可执行、可写。对应的值如下图所示:

8348088f576e9f5e3073a0052ccec49d.png

sh_link、sh_info(段的链接信息):如果段的类型是与链接有关的,比如重定位、符号表。那么这两个成员的意义如下图所示,对于其他段这两个参数没有意义。

70e75fe2307e853c5674e1946fd1dd0c.png

重定位表(Relocation Table)

重定位是链接器对目标文件中的某些部分进行地址的重新定义。这些信息都会记录在重定位表中 。可能会注意到在讲述段表的时候,使用readelf -S命令输出中就有一个“rel.text”段,该段就是重定位表,并且是作用域.text段,它对应类型是“SHT_REL”。回顾我们最开始写的SimpleSetion文件有对“printf”函数的调用,这个就是引用了绝对地址。它所对应的sh_link的值9也就是说该重定位表使用的符号表在段表中的下标是9;h_info的值为1,表示该重定位表作用于的段在段表的下标,即.text。对比上图段表的信息,与此处结果符合。

字符串表

ELF文件中用到了很多的字符串,比如段表、变量名。因为每个字符串的长度不一,所以将所有的字符串放到一张表中,如下图所示:

2863420b31385d829b15f501279b048c.png

那么偏移与所对的字符串的关系:

0663994d8d3efca2f685903c68cf5723.png

在使用字符串时,只需给出对应的偏移值即可。保存字符串常见的有.strtab和.shstrtab,分别存放普通字符串和段名字符串。在头文件结构中有e_shstrndx这样一个参数,表示是段表字符串在段表中的下标。所以,只有分析文件头,就可以得到段表和段表字符串的位置,从而解析整个elf文件。

符号表(Symbol Table)

符号的作用是当多个不同目标链接时函数和变量之间的相互引用。对于链接而言,只关心全局符号的相互引用。局部符号、段名、行号等符号是次要的。ELF文件中符号也是在一个符号表中,是一个ELF_Sym结构;结构定义如下:

d5a13324058c0427bc5fe15f82aa52e3.png

使用 readelf -s SimpleSection.o查看目标文件的符号表:

3d957db2b9db74ed8553fa897e3526a1.png

详解:

st_value(符号值):分为以下几种情况

在目标文件中,如果是符号的定义并且该符号不是“COMMON”(未初始化的全局符号)类型,则st_value表示该符号在所在段中中的偏移,即符号所对应的函数或变量位于由st_shndx指定的段,偏移st_value的位置。

在目标文件中。如果符号是“COMMON”类型的,则st_value表示该符号的对齐属性。

在可执行文件中,st_value表示符号的虚拟地址。

st_info(符号类型和绑定信息):低四位表示类型;高28位表示符号绑定信息。具体数值如下图所示:

955c6b92ad8683f6345f9b0da7fab976.png

d9150397e0261c74a48ced60c860616e.png

st_shndx(符号所在段):如果符号定义在本文件中,那么这个成员表示符号所在段在段表中的下标;还有几种特殊的情况,如下图所示:

c0b8b4f95f47771ce8e9788b76d99805.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值