Tool readelf and ELF format

Table of Contents

1. ELF 简介

In computing, the Executable and Linkable Format (ELF, formerly called Extensible Linking Format) is a common standard file format for executables, object code, shared libraries, and core dumps.

ELF is flexible and extensible by design, and it is not bound to any particular processor or architecture. This has allowed it to be adopted by many different operating systems on many different platforms.

参考:
man 5 elf
Tool Interface Standard (TIS) Portable Formats Specification v1.1, 1993: https://refspecs.linuxbase.org/elf/TIS1.1.pdf
Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification v1.2, 1995: https://refspecs.linuxbase.org/elf/elf.pdf

2. ELF 文件头

ELF 文件头保存着 ELF 文件的整体布局信息。
在 Linux 中,文件 /usr/include/elf.h 中有 ELF 文件头的定义。其结构定义如下:

#define EI_NIDENT (16)
typedef struct
{
  unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
  Elf64_Half    e_type;             /* Object file type */
  Elf64_Half    e_machine;          /* Architecture */
  Elf64_Word    e_version;          /* Object file version */
  Elf64_Addr    e_entry;            /* Entry point virtual address */
  Elf64_Off     e_phoff;            /* Program header table file offset */
  Elf64_Off     e_shoff;            /* Section header table file offset */
  Elf64_Word    e_flags;            /* Processor-specific flags */
  Elf64_Half    e_ehsize;           /* ELF header size in bytes */
  Elf64_Half    e_phentsize;        /* Program header table entry size */
  Elf64_Half    e_phnum;            /* Program header table entry count */
  Elf64_Half    e_shentsize;        /* Section header table entry size */
  Elf64_Half    e_shnum;            /* Section header table entry count */
  Elf64_Half    e_shstrndx;         /* Section header string table index */
} Elf64_Ehdr;

查看 ELF 文件头,可以用命令 readelf -h

2.1. ELF 文件头查看实例

下面是查看 ELF 文件头的实例:

$ readelf -h /bin/ls
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x402770
  Start of program headers:          64 (bytes into file)
  Start of section headers:          109696 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         8
  Size of section headers:           64 (bytes)
  Number of section headers:         32
  Section header string table index: 31

上面的输出和前面介绍的定义 ELF 文件头所用的结构体(Elf64_Ehdr)一致。

Magic number (7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00)的解释:
共 16 字节。
前 4 字节是固定的,被称为 ELF 文件的魔数,45 4c 46 分别对应"E","L","F"的 ASCII 码。
第 5 字节,0x01 表示 32 位,0x02 表示 64 位。
第 6 字节,字节序,0x01 表示小端,0x02 表示大端。
第 7 字节,ELF 文件的主版本号,一般是 1,因为 ELF 标准在 1.2 版本后就没有再更新了。
后 9 字节,ELF 标准没有定义,一般填 0。

Object file type 的解释:
一般有下面四种类型,
EXEC (Executable file):可执行文件。
REL (Relocatable file):可重定位文件,一般为.o 文件。
DYN (Shared object file):共享目标文件, 一般为.so 文件。
CORE (Core file):Core dump 文件。

2.2. Program Header and Section Header

在 ELF 文件头中,可以看到有两个重要内容:Program Header 和 Section Header。

Program Header 和 Section Header 分别从加载(执行)和链接的角度来描述 EFL 文件的组织方式。

程序头表(program header table)用来告诉系统如何创建一个进程的内存映象。 被用来建立进程映象(执行一个程序)的文件必须要有一个程序头表(program header table);可重定位文件不需要这个头表。

节头表(section header table)包含了文件 sections 的基本信息。每个 section 在这个表中有一个入口;每个入口给出了该 section 的名字,大小,等等信息。 在链接过程中的文件必须有一个 section 头表;其他 object 文件中 section 头表可有可无。

四种类型文件的程序头和节头参见表 1

Table 1: 四种类型文件的程序头和节头
类型 Program Header Section Header
可重定位文件 (REL) 没有
可执行文件 (EXEC)
共享目标文件 (DYN)
Core dump 文件 (CORE) 没有

2.2.1. Two Views - Linking View and Execution View

An ELF file has two views: The program header shows the segments used at run-time, whereas the section header lists the set of sections of the binary.

elf_layout.svg

Figure 1: ELF layout

1 摘自:https://en.wikipedia.org/wiki/Executable_and_Linkable_Format

elf_two_views.png

Figure 2: Linking View and Execution View in ELF

2 摘自:Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification v1.2, 1995

2.2.2. 用 readelf (-l|--program-headers|--segments) 查看 Program Header

使用 readelf -l 可以查看 ELF 文件有哪些 Program Header,如:

$ readelf -l /bin/ls

Elf file type is EXEC (Executable file)
Entry point 0x4048c5
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000001bad4 0x000000000001bad4  R E    200000
  LOAD           0x000000000001bdf0 0x000000000061bdf0 0x000000000061bdf0
                 0x0000000000000864 0x0000000000001690  RW     200000
  DYNAMIC        0x000000000001be08 0x000000000061be08 0x000000000061be08
                 0x00000000000001f0 0x00000000000001f0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x0000000000018cdc 0x0000000000418cdc 0x0000000000418cdc
                 0x0000000000000744 0x0000000000000744  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x000000000001bdf0 0x000000000061bdf0 0x000000000061bdf0
                 0x0000000000000210 0x0000000000000210  R      1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got

2.2.3. 用 readelf (-S|--section-headers|--sections) 查看 Section Header

使用 readelf -S 可以查看 ELF 文件有哪些 Sections,如:

$ readelf -S /bin/ls
There are 27 section headers, starting at offset 0x1c748:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       0000000000000068  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           0000000000400300  00000300
       0000000000000c18  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400f18  00000f18
       0000000000000593  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           00000000004014ac  000014ac
       0000000000000102  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000004015b0  000015b0
       0000000000000090  0000000000000000   A       6     2     8
  [ 9] .rela.dyn         RELA             0000000000401640  00001640
       00000000000000a8  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004016e8  000016e8
       0000000000000a80  0000000000000018  AI       5    12     8
  [11] .init             PROGBITS         0000000000402168  00002168
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000402190  00002190
       0000000000000710  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         00000000004028a0  000028a0
       000000000000fcea  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         000000000041258c  0001258c
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         00000000004125c0  000125c0
       000000000000671c  0000000000000000   A       0     0     64
  [16] .eh_frame_hdr     PROGBITS         0000000000418cdc  00018cdc
       0000000000000744  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000419420  00019420
       00000000000026b4  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       000000000061bdf0  0001bdf0
       0000000000000008  0000000000000000  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       000000000061bdf8  0001bdf8
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         000000000061be00  0001be00
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          000000000061be08  0001be08
       00000000000001f0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         000000000061bff8  0001bff8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         000000000061c000  0001c000
       0000000000000398  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         000000000061c3c0  0001c3c0
       0000000000000294  0000000000000000  WA       0     0     64
  [25] .bss              NOBITS           000000000061c680  0001c654
       0000000000000e00  0000000000000000  WA       0     0     64
  [26] .shstrtab         STRTAB           0000000000000000  0001c654
       00000000000000ef  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

3. Tips

3.1. 查看 ELF 文件中的编译器信息(查看指定 section 的内容)

如何查看 ELF 文件中的编译器信息?编译器信息一般位于 .comment 节中,我们查看 .comment 节的内容即可。

方法一:用命令 readelf -p .comment

$ readelf -p .comment a.out

String dump of section '.comment':
  [     0]  GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)

方法二:用命令 objdump -s --section .comment

$ objdump -s --section .comment a.out

a.out:     file format elf64-x86-64

Contents of section .comment:
 0000 4743433a 2028474e 55292034 2e342e34  GCC: (GNU) 4.4.4
 0010 20323031 30303732 36202852 65642048   20100726 (Red H
 0020 61742034 2e342e34 2d313329 00        at 4.4.4-13).

Author: cig01

Created: <2012-10-13 Sat>

Last updated: <2019-05-21 Tue>

Creator: Emacs 27.1 (Org mode 9.4)