ELF 主体:节区02

下面来看看 ELF 文件的主体内容:节区(Section)。

ELF 文件具有很大的灵活性,它通过文件头组织整个文件的总体结构,通过节区表 (Section Headers Table)和程序头(Program Headers Table 或者叫段表)来分别描述可重定位文件和可执行文件。但不管是哪种类型,它们都需要它们的主体,即各种节区。

在可重定位文件中,节区表描述的就是各种节区本身;而在可执行文件中,程序头描述的是由各个节区组成的段(Segment),以便程序运行时动态装载器知道如何对它们进行内存映像,从而方便程序加载和运行

下面先来看看一些常见的节区,而关于这些节区(Section)如何通过重定位构成不同的段(Segments),以及有哪些常规的段,我们将在链接部分进一步介绍。

可以通过 readelf -S 查看 ELF 的节区。(建议一边操作一边看文档,以便加深对 ELF 文件结构的理解)先来看看可重定位文件的节区信息,通过节区表来查看:

默认编译好 myprintf.c,将产生一个可重定位的文件 myprintf.o,这里通过 myprintf.o 的节区表查看节区信息。

$ gcc -c myprintf.c
$ readelf -S myprintf.o
There are 11 section headers, starting at offset 0xc0:

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 000018 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 000334 000010 08      9   1  4
  [ 3] .data             PROGBITS        00000000 00004c 000000 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 00004c 000000 00  WA  0   0  4
  [ 5] .rodata           PROGBITS        00000000 00004c 00000e 00   A  0   0  1
  [ 6] .comment          PROGBITS        00000000 00005a 000012 00      0   0  1
  [ 7] .note.GNU-stack   PROGBITS        00000000 00006c 000000 00      0   0  1
  [ 8] .shstrtab         STRTAB          00000000 00006c 000051 00      0   0  1
  [ 9] .symtab           SYMTAB          00000000 000278 0000a0 10     10   8  4
  [10] .strtab           STRTAB          00000000 000318 00001a 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)

用 objdump -d 可看反编译结果,用 -j 选项可指定需要查看的节区:

$ objdump -d -j .text   myprintf.o
myprintf.o:     file format elf32-i386

Disassembly of section .text:

00000000 <myprintf>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   83 ec 0c                sub    $0xc,%esp
   9:   68 00 00 00 00          push   $0x0
   e:   e8 fc ff ff ff          call   f <myprintf+0xf>
  13:   83 c4 10                add    $0x10,%esp
  16:   c9                      leave
  17:   c3                      ret

用 -r 选项可以看到有关重定位的信息,这里有两部分需要重定位:

$ readelf -r myprintf.o

Relocation section ".rel.text' at offset 0x334 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000a  00000501 R_386_32          00000000   .rodata
0000000f  00000902 R_386_PC32        00000000   puts

.rodata 节区包含只读数据,即我们要打印的 hello, world!

$ readelf -x .rodata myprintf.o

Hex dump of section '.rodata':
  0x00000000 68656c6c 6f2c2077 6f726c64 2100     hello, world!.

没有找到 .data 节区, 它应该包含一些初始化的数据:

$ readelf -x .data myprintf.o

Section '.data' has no data to dump.

也没有 .bss 节区,它应该包含一些未初始化的数据,程序默认初始为 0:

$ readelf -x .bss       myprintf.o

Section '.bss' has no data to dump.

.comment 是一些注释,可以看到是是 Gcc 的版本信息

$ readelf -x .comment myprintf.o

Hex dump of section '.comment':
  0x00000000 00474343 3a202847 4e552920 342e312e .GCC: (GNU) 4.1.
  0x00000010 3200                                2.

.note.GNU-stack 这个节区也没有内容:

$ readelf -x .note.GNU-stack myprintf.o

Section '.note.GNU-stack' has no data to dump.

.shstrtab 包括所有节区的名字:

$ readelf -x .shstrtab myprintf.o

Hex dump of section '.shstrtab':
  0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab
  0x00000010 002e7368 73747274 6162002e 72656c2e ..shstrtab..rel.
  0x00000020 74657874 002e6461 7461002e 62737300 text..data..bss.
  0x00000030 2e726f64 61746100 2e636f6d 6d656e74 .rodata..comment
  0x00000040 002e6e6f 74652e47 4e552d73 7461636b ..note.GNU-stack
  0x00000050 00                                  .

符号表 .symtab 包括所有用到的相关符号信息,如函数名、变量名,可用 readelf 查看:

$ readelf -symtab myprintf.o

Symbol table '.symtab' contains 10 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS myprintf.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     0 SECTION LOCAL  DEFAULT    7
     7: 00000000     0 SECTION LOCAL  DEFAULT    6
     8: 00000000    24 FUNC    GLOBAL DEFAULT    1 myprintf
     9: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

字符串表 .strtab 包含用到的字符串,包括文件名、函数名、变量名等:

$ readelf -x .strtab myprintf.o

Hex dump of section '.strtab':
  0x00000000 006d7970 72696e74 662e6300 6d797072 .myprintf.c.mypr
  0x00000010 696e7466 00707574 7300              intf.puts.

从上表可以看出,对于可重定位文件,会包含这些基本节区 .text.rel.text.data.bss.rodata.comment.note.GNU-stack.shstrtab.symtab 和 .strtab

汇编语言文件中的节区表述

为了进一步理解这些节区和源代码的关系,这里来看一看 myprintf.c 产生的汇编代码。

$ gcc -S myprintf.c
$ cat myprintf.s
        .file   "myprintf.c"
        .section        .rodata
.LC0:
        .string "hello, world!"
        .text
.globl myprintf
        .type   myprintf, @function
myprintf:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $12, %esp
        pushl   $.LC0
        call    puts
        addl    $16, %esp
        leave
        ret
        .size   myprintf, .-myprintf
        .ident  "GCC: (GNU) 4.1.2"
        .section        .note.GNU-stack,"",@progbits

是不是可以从中看出可重定位文件中的那些节区和汇编语言代码之间的关系?在上面的可重定位文件,可以看到有一个可重定位的节区,即 .rel.text,它标记了两个需要重定位的项,.rodata 和 puts。这个节区将告诉编译器这两个信息在链接或者动态链接的过程中需要重定位, 具体如何重定位?将根据重定位项的类型,比如上面的 R_386_32 和 R_386_PC32

到这里,对可重定位文件应该有了一个基本的了解,下面将介绍什么是可重定位,可重定位文件到底是如何被链接生成可执行文件和动态链接库的,这个过程除了进行一些符号的重定位外,还进行了哪些工作呢?


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值