目标文件--ELF目标文件

 

ELF文件

wps44C8.tmp

ELF目标文件格式的最前部是ELF文件头(ELF Header),它包含了描述整个文件的基本属性,比如ELF文件版本、目标机器型号、程序入口地址等。紧接着是ELF文件各个段。其中ELF文件中与段有关的重要结构就是段表(Section Header Table),该表描述了ELF文件包含的所有段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。接着将详细分析ELF文件头、段表等ELF关键的结构。

下面对ELF结构做介绍:

(1)文件头

   $readelf –h SimpleSection.o

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:                               REL (Relocatable file)

  Machine:                            Intel 80386

  Version:                        0x1

  Entry point address:            0x0

  Start of program headers:       0 (bytes into file)

  Start of section headers:       280 (bytes into file)

  Flags:                          0x0

  Size of this header:          52 (bytes)

  Size of program headers:        0 (bytes)

  Number of program headers:      0

  Size of section headers:        40 (bytes)

  Number of section headers:      11

  Section header string table index:  8

从上面输出的结果可以看到,ELF的文件头中定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段的数量等。

(2)段表

$ readelf -S SimpleSection.o

There are 11 section headers, starting at offset 0x118:

Section Headers:

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

[ 0]               NULL      00000000 000000 000000 00 0   0  0

[ 1] .text         PROGBITS  00000000 000034 00005b 00 AX  0  0   4

[ 2] .rel.text     REL       00000000 000428 000028 08     9  1   4

[ 3] .data         PROGBITS  00000000 000090 000008 00 WA  0  0   4

[ 4] .bss          NOBITS    00000000 000098 000004 00 WA  0  0   4

[ 5] .rodata       PROGBITS  00000000 000098 000004 00 A   0  0   1

[ 6] .comment        PROGBITS  00000000 00009c 00002a 00 0   0  1

[ 7] .note.GNU-stack PROGBITS  00000000 0000c6 000000 00 0   0  1

[ 8] .shstrtab   STRTAB    00000000 0000c6 000051 00 0   0  1

[ 9] .symtab       SYMTAB    00000000 0002d0 0000f0 10     10 10   4

[10] .strtab       STRTAB    00000000 0003c0 000066 00 0   0  1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific

段表是ELF文件中除了文件头以外最重要的结构,它描述了ELF的各个段

的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。也就是说,ELF文件的段结构就是由段表决定的,编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。

(3)重定位表

我们注意到,SimpleSection.o中有一个叫做“.rel.text”的段,它的类型(sh_type)为“SHT_REL”,也就是说它是一个重定位表(Relocation Table)。重定位表主要是告诉链接器哪条指令要进行调整,以及如何进行调整。

$ objdump -r a.o

a.o:     file format elf32-i386

RELOCATION RECORDS FOR [.text]:

OFFSET         TYPE              VALUE

0000001c         R_386_32          shared

00000027         R_386_PC32        swap

可以看出来要进行重定位的是shared和swap,以及重定位的类型。R_386_32是绝对寻址,R_386_PC32是相对寻址。

(4)符号表

ELF文件中的符号表往往是文件中的一个段,段名一般叫“.symtab”。

$ readelf –s SimpleSection.o

Symbol table '.symtab' contains 15 entries:

   Num:    Value  Size  Type    Bind   Vis      Ndx Name

     0: 00000000  0   NOTYPE  LOCAL  DEFAULT  UND

     1: 00000000  0   FILE    LOCAL  DEFAULT  ABS SimpleSection.c

     2: 00000000  0   SECTION LOCAL  DEFAULT    1

     3: 00000000  0   SECTION LOCAL  DEFAULT    3

     4: 00000000  0   SECTION LOCAL  DEFAULT    4

     5: 00000000  0   SECTION LOCAL  DEFAULT    5

     6: 00000000    4   OBJECT  LOCAL  DEFAULT    4 static_var2.1534

     7: 00000004    4   OBJECT  LOCAL  DEFAULT    3 static_var.1533

     8: 00000000  0   SECTION LOCAL  DEFAULT    7

     9: 00000000  0   SECTION LOCAL  DEFAULT    6

    10: 00000000    4   OBJECT  GLOBAL DEFAULT    3 global_init_var

    11: 00000000  27  FUNC    GLOBAL DEFAULT    1 func1

    12: 00000000  0   NOTYPE  GLOBAL DEFAULT  UND printf

    13: 0000001b  64  FUNC    GLOBAL DEFAULT    1 main

14: 00000004    4   OBJECT  GLOBAL DEFAULT  COM global_uninit_var

第一列Num表示符号表数组的下标,从0开始,共15个符号;第二列Value就是符号值,即st_value;第三列Size为符号大小,即st_size;第四列和第五列分别为符号类型和绑定信息,即对应st_info的低4位和高28位;第六列Vis目前在C/C++语言中未使用,我们可以暂时忽略它;第七列Ndx即st_shndx,表示该符号所属的段;当然最后一列也最明显,即符号名称。

符号表记录了符号的地址,在进行重定位的时候,要根据符号的地址进行重定位。

(5)字符串表

ELF文件中用到了很多字符串,比如段名、变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示它比较困难。一种很常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。比如表3-12这个字符串表。

wps4507.tmp

wps4518.tmp

通过这种方法,在ELF文件中引用字符串只须给出一个数字下标即可,不用考虑字符串长度的问题。

(6)调试信息

标文件里面还有可能保存的是调试信息。几乎所有现代的编译器都支持源代码级别的调试,比如我们可以在函数里面设置断点,可以监视变量变化,可以单步行进等,前提是编译器必须提前将源代码与目标代码之间的关系等,比如目标代码中的地址对应源代码中的哪一行、函数和变量的类型、结构体的定义、字符串保存到目标文件里面。甚至有些高级的编译器和调试器支持查看STL容器的内容,即程序员在调试过程中可以直接观察STL容器中的成员的值。如果我们在GCC编译时加上“-g”参数,编译器就会在产生的目标文件里面加上调试信息,我们通过readelf等工具可以看到,目标文件里多了很多“debug”相关的段:

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

...

  [ 4] .debug_abbrev  PROGBITS  00000000 000040 000034 00 0   0  1

  [ 5] .debug_info      PROGBITS  00000000 000074 0000af 00 0   0  1

  [ 6] .rel.debug_info  REL 00000000 000738 000038 08 9   5  4

  [ 7] .debug_line      PROGBITS  00000000 000123 000037 00 0   0  1

  [ 8] .rel.debug_line  REL 00000000 000770 000008 08 19  7  4

  [ 9] .debug_frame   PROGBITS  00000000 00015c 000034 00 0   0  4

  [10] .rel.debug_frame REL 00000000 000778 000010 08 19  9  4

  [11] .debug_loc     PROGBITS  00000000 000190 00002c 00 0   0  1

  [12] .debug_pubnames  PROGBITS  00000000 0001bc 00001a 00 0   0  1

  [13] .rel.debug_pubnam  REL 00000000 000788 000008 08 19  12 4

  [14] .debug_aranges   PROGBITS  00000000 0001d6 000020 00 0   0  1

  [15] .rel.debug_arange  REL 00000000 000790 000010 08 19  14 4

...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值