您的位置:首页 > 其它

elf格式文件分析

2016-12-20 19:39 274 查看
什么是ELF文件?

ELF = Executable and Linkable Format,可执行可连接格式的文件。具体分为以下三种:

1、可重定位的目标文件(Relocateble或Object File)

2、可执行文件(Executable)

3、共享库(Shared Object或Share Library)

ELF格式文件的组成:

.obj文件由若干个Section组成,在汇编程序中的.section会成为目标文件中的section,编译器还会自动生成一些

section。然后链接器读取这个目标文件(Section作为输入),把section合并成Segment生成可执行文件。最后加载器根
4000
据可执行文件中的Segment(Segment作为输入)信息加载运行这个程序。

ELF文件格式如下图:



左边是从链接器的角度看ELF文件,右边是从加载程序的角度看ELF文件。开头的ELF Header描述了体系结构和操作系统等信息,并指出Section Header Table 和 Program Header Table在这个ELF文件中的位置。

下面用一个具体实例分析这两种elf文件:

1、首先编写如下汇编代码max.s(找最大的数):

.section  .data    #这里的.section就是在.obj文件中的section

data_items:      #这是一个标号代表下面这组数的首地址

    .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0         #.long是告诉编译器每个数字要占4Byte

.section .text

.globl _start    #这也是一个标号(全局标号),比如有许多目标文件要链接,每个目标文件内都有代码,那程序在

                        #加载到内存时应该先执行哪块的代码呢?所以用这个标号来标示程序的入口。

_start:

    movl $0,%edi

    movl data_items(,%edi,4),%eax        #内存寻址

    movl %eax,%ebx

start_loop:

    cmpl $0,%eax                  #判断是否结束

    je loop_exit

    incl %edi

    movl data_items(,%edi,4),%eax

    cmpl %ebx,%eax             #比较大小

    jle start_loop

    movl %eax,%ebx

    jmp start_loop

loop_exit:

    movl $1,%eax

    int $0x80                        #结束退出

2、用汇编器as生成可重定位文件(目标文件)

    as max.s -o max.o

3、用readelf工具读取ELF文件如下:

    readelf -aW max.o         //-a是显示全部信息 -W是允许显示的字符超过80个

    如下图所示:



Magic:标示文件的格式。Class:文件是ELF格式64bit的。Data:数据是以2的补码形式,小端排放的。

Version:版本号。OS/ABI:UNIX系统。ABI:应用程序二进制借口版本号。Type:这个文件是可重定位文件(.obj目标文件)。Machine:机器硬件型号。Version:版本号。Entry point address:程序进入点地址,也就是这个文件加载到内存的哪个地方以及从哪里开始执行。因为现在是目标文件还没有链接所以地址为0。Start
of program headers:program headers的位置这里的位置是以文件头为参考偏移的位置。Start of section headers:section header的位置距离文件头216Byte的地方。Flags:标志位。Size of this Header:这个ELF Header的大小64Byte。

Size of program headers:0Byte 因为这是目标文件用不到program headers所以大小为0。Number
of program headers:0个。Size of section headers:64Byte每个Section的大小。Number
of section headers:共有8个section。Section header string table index:.shstrtab在section header表中的第五个。

下面通过分析section
header画出文件分布框图:



下面是通过elf
Header以及section headers画出来的框图:



可以看到文件最后是在0x3f0处结束也就是说文件的大小就是0x3f0(1008Byte)

通过命令查看也可以看出文件大小为1008Byte:



下面分析一下各个section的内容及作用:

.text:

         这个是代码段,我们要执行的程序都放到这里。一条机器语句需要指令和数据,如:movl $1,%eax。但是在我们写的汇编程序中还有这样的语句如:movl
data_items(,%edi,4),%eax。cpu在执行这条语句的时候怎么知道数据是什么,data_items是什么以及要怎么去执行?在as汇编器执行的时候就会把汇编文件中的标号替换成地址,这个地址是相对当前section的。因为还没有到链接的步骤还不知道要执行的程序有多大(还不知道到底有多少目标文件要链接),不知道应该被加载到内存的哪个地方。

 
        下面通过反汇编来看一下:



可以看出来汇编中的所有标号都被替换成地址了。_start前面的地址是0
就是相对.text section的偏移地址。movl data_items(,%edi,4),%eax中的data_items被翻译成了0x0但是我们的数据不再0x0这里啊?这就是.rela.text重定位的表现,在.rela.text中会标示出这个位置用来告诉链接器这个位置的地址是要你来重新定位的。这里暂时用0x0表示。

.rela.text:

           包含了两个要修改的数据。其中offset标识出了修改处在.text中的偏移位置。09和1a这两个偏移地址正好对应data_items所在位置。

.data:

           这是数据段,大小是0x38共56Byte = 14 * 32bit

.bss:

           未初始化的数据放到这里。注意.bss不会像.data那样占用空间。他只会占用一个section
header。

.shstrtab:

            section headers string table 段头字符串表。里面存着各个段的名字。 

.symtab:

            系统符号表,程序中用的符号这里都有记录包括as汇编器自己添加的一些标号,_start的Type是一个global的也就是全局可见的。其他的目标文件也会认识这个标号因为他是整个程序的入口。如果在汇编程序中没有用_start,那么在链接时会产生警告。因为链接器会去找_start这个标号作为程序的入口地址。前面的value是标号相对section的偏移值。

.strtab:

            代码中的标号。

通过以上的分析这里我们可以猜一下,下一步链接器要干的事情。

           首先要计算代码的大小,在内存中找到相应的空间。然后确定.data数据段的地址。这时已经确定了data_items标号的地址。再将.text中的地址换成绝对地址。加载器将程序加载到内存,最后跳转到_start地址处开始执行程序。

4、用链接器ld生成可执行文件

    ld max.o -o max

5、用readelf工具读取ELF文件如下:





先看ELF Header变化的部分:

         Type:EXEC为可执行文件类型。Entry point address:0x4000b0程序入口地址也就是_start的地址。start of program headers:64byte处。size of program headers:56Byte每个program header大小56Byte。number
of program headers:2共有两个program header。

Section headers部分:

          少了两个sention heaeder .rela.text .bss 因为.rela.text是链接器需要的所以现在没用了。.bss程序中没有未初始化的变量所以也没用。

Program Headers:

          这个信息就是加载器要使用到的了。这里描述了两个信息。type是类型,offset是在文件中位置的偏移,virtaddr是加载到内存中的地址,physaddr是物理地址这里没有用因为有分段分页机制。filesize是这个segment的大小,memsiz是内存的大小,flg是文件属性,flign是内存叶的大小。

           下面先分析第二个segment。offset是0xdd。filesize是0x38这是原来的.data section现在把它分成一个Segment了。具有可读可写的属性,加载到内存的0x6000dd 我们发现在内存页中不是加载0x600000而是偏移了0xdd这样有利于加载器和链接器的实现。

             再看一下第一个Segment 文件大小0xdd,偏移是0x00。这个segment中包括了elf
header,program header,.text section。0x40 + 0x70 + 0x2d = 0xb0 + 0x2d = 0xdd 其中0xb0正好是entry point address的偏移地址。

.symtab:

              多了几个符号这些是在多目标文件中使用的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息