zl程序教程

您现在的位置是:首页 >  前端

当前栏目

自己动手构造编译系统:编译、汇编与链接2.3 ELF文件格式

链接 编译 自己 2.3 构造 汇编 动手 文件格式
2023-09-11 14:16:02 时间

      ELF文件格式描述了Linux下可执行文件、可重定位目标文件、共享目标文件、核心转储文件的存储格式。本书设计的编译系统只关心可执行文件和可重定位目标文件的格式,如果要设计动态链接器的话,则还需要了解共享目标文件的内容。

ELF文件信息的一般存储形式如图2-11所示。

在Linux下,可以使用readelf命令查看ELF文件的信息。如果要查看1.3.3节生成的hello.o的信息,可以使用如下命令查看ELF的所有关键信息:

readelf –a hello.o

在ELF文件中,最开始的52个字节记录ELF文件头部的信息,通过它可以确定ELF文件内程序头表和段表的位置及大小。以下列出了hello.o文件头信息。

ELF Header:

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

  Class:                                        ELF32

  Data:                                              2s 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:               224 (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

紧接着文件头便是程序头表,它记录程序运行时操作系统如何将文件加载到内存,因此只有可执行文件包含程序头表。使用readelf查看1.3.4节静态链接生成的hello文件,可以看到它的程序头表,类型为LOAD的表项表示需要加载的段。以下列出它的程序头表信息。

Program Headers:

  Type        Offset     VirtAddr    PhysAddr     FileSiz  MemSiz     Flg  Align

  LOAD        0x000000  0x08048000  0x08048000  0x84fd2  0x84fd2  R E  0x1000

  LOAD        0x085f8c  0x080cdf8c  0x080cdf8c  0x007d4  0x02388  RW    0x1000

  NOTE        0x0000f4  0x080480f4  0x080480f4  0x00044  0x00044  R     0x4

  TLS         0x085f8c  0x080cdf8c  0x080cdf8c  0x00010  0x00028  R     0x4

  GNU_STACK  0x000000  0x00000000  0x00000000  0x00000  0x00000  RW   0x4

  GNU_RELRO  0x085f8c  0x080cdf8c  0x080cdf8c  0x00074  0x00074  R     0x1

ELF文件最关键的结构是段表,这里的段表示文件内的信息块,与汇编语言内的段并非同一个概念。段表记录了ELF文件内所有段的位置和大小等信息。在所有的段中,有保存代码二进制信息的代码段、存储数据的数据段、保存段表名称的段表字符串表段和存储程序字符串常量的字符串表段。符号表段记录汇编代码中定义的符号信息,重定位表段记录可重定位目标文件中需要重定位的符号信息。hello.o的段表如下:

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  00001d 00  AX  0    0    4

  [2] .rel.text          REL        00000000  000350  000010    08        9    1    4

  [3] .data               PROGBITS  00000000  000054  000000     00  WA 0    0    4

  [4] .bss                NOBITS     00000000  000054  000000    00  WA 0    0    4

  [5] .rodata            PROGBITS  00000000  000054  00000d 00  A    0    0    1

  [6] .comment           PROGBITS  00000000  000061  00002c    01  MS  0    0    1

  [7] .note.GNU-stack  PROGBITS  00000000  00008d  000000   00        0    0    1

  [8] .shstrtab          STRTAB    00000000  00008d  000051  00        0    0    1

  [9] .symtab            SYMTAB    00000000  000298  0000a0     10        10  8    4

  [10].strtab            STRTAB    00000000  000338  000015  00        0    0    1

符号表段是按照表格形式存储符号信息的,我们可以看到主函数和printf函数的符号项。

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   hello.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    29       FUNC      GLOBAL   DEFAULT       1           main

     9: 00000000     0       NOTYPE        GLOBAL   DEFAULT       UND printf

重定位表也是按照表格形式存储的,很明显,printf作为外部符号是需要重定位的。

Relocation section .rel.text at offset 0x350 contains 2 entries:

Offset         Info           Type                Sym.Value   Sym.Name

0000000a    00000501    R_386_32        00000000          .rodata

00000012    00000902    R_386_PC32    00000000          printf

从ELF文件格式的设计中可以看出,可执行文件其实就是按照一定标准将二进制数据和代码等信息包装起来,方便操作系统进行管理和使用。从文件头可以找到程序头表和段表,从段表可以找到其他所有的段。因此,在汇编语言输出目标文件的时候,就需要收集这些段的信息,并按照ELF格式组装目标文件。这样做不仅有利于使用操作系统现有的工具调试文件信息,也为后期链接器的实现提供了方便。

另外需要说明的是,对于ELF文件格式的定义,Linux提供了头文件描述。在系统目录/usr/include/elf.h提供的elf.h头文件中描述了标准ELF文件的数据结构的定义,在实现汇编器和链接器的代码中都使用了该头文件。