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 。
类型 | 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.
Figure 1: ELF layout
图 1 摘自:https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
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).