elf文件格式分析

 

估计用过linux的人都知道elf文件格式吧,其实在嵌入式领域也是用到elf文件格式,比如ads软件编译生成的就是axf文件格式的文件,这个其实是elf文件格式的扩展, 用gcc在编译时包含dwarf-2格式的调试信息,就可被ads识别,其实像arm公司的最新软件,realview等可直接支持elf文件的调试。

      下面就来讲下elf文件格式:

为了更好的完整的表述elf,我以一个例子来说:

my.clibmy.so的源文件:

vi my.c

int i=1;

void show() {

    printf("share lib test:%d/n",i);

}

gcc -fPIC -shared -o libmy.so my.c

 

test.c是使用这个共享库的测试文件:

vi test.c

extern int i;

int main(){

    i++;

    show();

    printf("%d",i);

}

gcc -o test test.c ./libmy.so

 

讲到elf文件格式,不得不提到三个概念。

elf文件头,程序头表,节区头部表,

1.elf文件头。

      这个顾明思议就是在文件的开始位置。

      文件的最开始几个字节给出如何解释文件的提示信息。

typedef struct{

unsigned char e_ident[EI_NIDENT]; //magic,就是elf文件标识,是elf

Elf32_Half e_type;   //用来标示是可重定位文件(1,可执行文件(2),还是共享文件(3

Elf32_Half e_machine; //那种cpu的文件,SPARC(2),i386(3)

Elf32_Word e_version;  //版本EV_NONE 0 非法版本 V_CURRENT 1 当前版本

Elf32_Addr e_entry;  //程序入口

Elf32_Off e_phoff;  //程序头表在文件中的偏移量

Elf32_Off e_shoff; //节区头部表在文件中的偏移量

Elf32_Word e_flags; //保存与文件相关的,特定于处理器的标志

Elf32_Half e_ehsize; //ELF 头部的大小(以字节计算)

Elf32_Half e_phentsize;//程序头部表格的表项大小(按字节计算

Elf32_Half e_phnum;//程序头部表格的表项的数量

Elf32_Half e_shentsize;//节区头部表格的表项大小(按字节计算)。

Elf32_Half e_shnum;//节区头部表格的表项数目。可以为 0

Elf32_Half e_shstrndx;//节区头部表格中与节区名称字符串表相关的表项的索引。

}Elf32_Ehdr;

从上可见elf文件头是灵魂,有它才可以找到真正描述文件的 程序头表, 节区头部表的数据

上面的test的文件头如下:

jivin@jivin-desktop:~/test/dym_so$ readelf test -h

ELF Header:

  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

  Class:                             ELF32

  Data:                              2's complement, little endian

  Version:                           1 (current)

  OS/ABI:                            UNIX - System V

  ABI Version:                       0

  Type:                              EXEC (Executable file)

  Machine:                           Intel 80386

  Version:                           0x1

  Entry point address:               0x8048440  //入口是__start

  Start of program headers:          52 (bytes into file) //elf头部大小为52btye

                                              所以program headerself header

  Start of section headers:          6004 (bytes into file)

  Flags:                             0x0

  Size of this header:               52 (bytes)//elf头部大小为52btye

  Size of program headers:           32 (bytes)

  Number of program headers:         8

  Size of section headers:           40 (bytes)

  Number of section headers:         36

  Section header string table index: 33

 

2.节区头部表。

 节区头部表是给链接器用的,在编译的时候,和重定位的时候用的。

(1).目标文件中的每个节区都有对应的节区头部描述它,反过来,有节区头部不意味着有节区。

(2).每个节区占用文件中一个连续字节区域(这个区域可能长度为 0)。

(3).文件中的节区不能重叠,不允许一个字节存在于两个节区中的情况发生。

(4).目标文件中可能包含非活动空间(INACTIVE SPACE)。这些区域不属于任何头部和节区,其内容未指定。

 

所谓表就是一个数组,每个元素是一个节区描述结构。

typedef struct{

Elf32_Word sh_name;  //节区的名字,比如.text,.data,.symbol,.dynmatic

Elf32_Word sh_type;  //节区类型,SHT_SYMTABSHT_PROGBITSSHT_STRTABSHT_RELA

Elf32_Word sh_flags;//一个节区中包含的内容是否可以修改、是否可以执行

Elf32_Addr sh_addr;  //如果节区将在进程的内存映像中,此成员给出节区的第一个字节应处的位置这个一般在可执行文件中才不为0,因为可重定位文件(.o)没法确定地址.

Elf32_Off sh_offset; //节区在文件中的偏移

Elf32_Word sh_size; //节区大小

Elf32_Word sh_link;//

Elf32_Word sh_info;//

Elf32_Word sh_addralign;//对齐

Elf32_Word sh_entsize;//节区每个元素数据结构的大小,比如一个.symbol的大小

}Elf32_Shdr;

 

jivin@jivin-desktop:~/test/dym_so$ readelf test -S

Section Headers:

(这个处于6004偏移处)

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

//要链接如内存映像的节区开始处

  [ 1] .interp           PROGBITS        08048134 000134 000013 00   A  0   0  1

  [ 2] .note.ABI-tag     NOTE            08048148 000148 000020 00   A  0   0  4

  [ 3] .note.gnu.build-i NOTE            08048168 000168 000024 00   A  0   0  4

  [ 4] .hash             HASH            0804818c 00018c 000048 04   A  6   0  4

  [ 5] .gnu.hash         GNU_HASH        080481d4 0001d4 000040 04   A  6   0  4

//紫色的这些节区和动态链接库链接相关的节区

  [ 6] .dynsym           DYNSYM          08048214 000214 0000d0 10   A  7   1  4

  [ 7] .dynstr           STRTAB          080482e4 0002e4 000094 00   A  0   0  1

  [ 8] .gnu.version      VERSYM          08048378 000378 00001a 02   A  6   0  2

  [ 9] .gnu.version_r    VERNEED         08048394 000394 000020 00   A  7   1  4

  [10] .rel.dyn          REL             080483b4 0003b4 000010 08   A  6   0  4

  [11] .rel.plt          REL             080483c4 0003c4 000020 08   A  6  13  4

  [12] .init             PROGBITS        080483e4 0003e4 000030 00  AX  0   0  4

  [13] .plt              PROGBITS        08048414 000414 000050 04  AX  0   0  4

  [14] .text             PROGBITS        08048470 000470 00018c 00  AX  0   0 16

  [15] .fini             PROGBITS        080485fc 0005fc 00001c 00  AX  0   0  4

  [16] .rodata           PROGBITS        08048618 000618 00000b 00   A  0   0  4

  [17] .eh_frame         PROGBITS        08048624 000624 000004 00   A  0   0  4

  [18] .ctors            PROGBITS        08049f04 000f04 000008 00  WA  0   0  4

  [19] .dtors            PROGBITS        08049f0c 000f0c 000008 00  WA  0   0  4

[20] .jcr              PROGBITS        08049f14 000f14 000004 00  WA  0   0  4

[21] .dynamic          DYNAMIC         08049f18 000f18 0000d8 08  WA  7   0  4

[22] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4

  [23] .got.plt          PROGBITS        08049ff4 000ff4 00001c 04  WA  0   0  4

  [24] .data             PROGBITS        0804a010 001010 000008 00  WA  0   0  4

  [25] .bss              NOBITS          0804a018 001018 00000c 00  WA  0   0  4

  //要链接如内存映像的节区结束处

  [26] .comment          PROGBITS        00000000 001018 000046 01  MS  0   0  1

  [27] .debug_aranges    PROGBITS        00000000 001060 000020 00      0   0  8

  [28] .debug_pubnames   PROGBITS        00000000 001080 000025 00      0   0  1

  [29] .debug_info       PROGBITS        00000000 0010a5 0000ef 00      0   0  1

  [30] .debug_abbrev     PROGBITS        00000000 001194 00005f 00      0   0  1

  [31] .debug_line       PROGBITS        00000000 0011f3 000082 00      0   0  1

  [32] .debug_str        PROGBITS        00000000 001275 000092 01  MS  0   0  1

  [33] .shstrtab         STRTAB          00000000 001307 00013e 00      0   0  1

  [34] .symtab           SYMTAB          00000000 0019e8 0004a0 10     35  52  4

  [35] .strtab           STRTAB          00000000 001e88 00020a 00      0   0  1

  

下面讲下特殊节区:

1..symtab,.dynsym;(符号表节区)

这个节区也是一个数组,每个元素描述一个符号:

 

其结构如下:

typedef struct {

Elf32_Word st_name; //符号名字,比如变量i,b

Elf32_Addr st_value;//符号的取值。依赖于具体的上下文,它可能是一个绝对值、一个地址等等

Elf32_Word st_size;//这个符号的对应的数据的大小(int,long等),如没有大小此成员为 0

unsigned char st_info;//此成员给出符号的类型和绑定属性。下面给出若干取值和含义的绑定关系。

unsigned char st_other;//预留的,没有使用

Elf32_Half st_shndx;每个符号表项都以和其他节区间的关系的方式给出定义。此成员给出相关的节区                头部表索引。某些索引具有特殊含义。

} Elf32_sym;

 

st_info这个内容比较丰富:

#define ELF32_ST_BIND(i) ((i)>>4)//高四位用于绑定信息

#define ELF32_ST_TYPE(i) ((i)&0xf) //第四位用于符号类型

#define ELF32_ST_INFO(b, t) (((b)<<4) + ((t)&0xf))

 

绑定信息主要是局部变量,全局变量等描述:

STB_LOCAL 0

局部符号在包含该符号定义的目标文件以外不可见。相同名称的局部符号可以存在于多个文件中,互不影响。

STB_GLOBAL 1

全局符号对所有将组合的目标文件都是可见的。一个文件中对某个全局符号的定义将满足另一个文件对相同全局符号的未定义引用。

STB_WEAK 2 弱符号与全局符号类似,不过他们的定义优先级比较低。

STB_LOPROC 13

STB_HIPROC 15  处于13-15这个范围的取值是保留给处理器专用语义的。

 

符号类型主要是说是变量,还是函数,还是其他的东西:

STT_OBJECT  1

符号与某个数据对象相关,比如一个变量、数组等等

STT_FUNC   2

符号与某个函数或者其他可执行代码相关

STT_SECTION 3

符号与某个节区相关。这种类型的符号表项主要用于重定位,通常具有 STB_LOCAL 绑定。

STT_FILE 4

传统上,符号的名称给出了与目标文件相关的源文件的名称。文件符号具有 STB_LOCAL 绑定,其节区索引是

jivin@jivin-desktop:~/test/dym_so$ readelf test -s

Symbol table '.dynsym' contains 13 entries:

  Num:    Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND show

     6: 0804a018     4 OBJECT  GLOBAL DEFAULT   25 i

    10: 0804a018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start

    11: 080483e4     0 FUNC    GLOBAL DEFAULT   12 _init

    12: 080485fc     0 FUNC    GLOBAL DEFAULT   15 _fini

 

 

Symbol table '.symtab' contains 74 entries:

   Num:    Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

    33: 00000000     0 FILE    LOCAL  DEFAULT  ABS init.c  

    62: 0804a018     4 OBJECT  GLOBAL DEFAULT   25 i

    63: 0804a014     0 OBJECT  GLOBAL HIDDEN   24 __dso_handle

     67: 0804a018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start

    68: 00000000     0 FUNC    GLOBAL DEFAULT  UND show

    69: 0804a024     0 NOTYPE  GLOBAL DEFAULT  ABS _end

    72: 08048524    50 FUNC    GLOBAL DEFAULT   14 main

    73: 080483e4     0 FUNC    GLOBAL DEFAULT   12 _init

2..strtab节区:

     这个节区包含字符串,不在是一个结构的数组

其组成形式如下:

      sjj0412/0 rtlab/0

那么如果我们使用sjj0412这个字符串,则是使用了这个节区的0索引的字符串,而rtlab就是1索引的字符串。

字符串表索引可以引用节区中任意字节。

你可能感兴趣的:(elf文件格式分析)