$ readelf -h sign
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)(.so文件DYN (Shared object file)、.o文件REL (Relocatable file)、Core dump文件(CORE))
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400510
Start of program headers: 64 (bytes into file)
Start of section headers: 3072 (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: 31
Section header string table index: 28
2. 显示程序头表(目标文件没有该表):
$ readelf -l sign
Elf file type is EXEC (Executable file)
Entry point 0x400510
There are 8 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001c0 0x00000000000001c0 R E 8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200 0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000008a4 0x00000000000008a4 R E 200000
LOAD 0x00000000000008a8 0x00000000006008a8 0x00000000006008a8 0x0000000000000220 0x0000000000000230 RW 200000
DYNAMIC 0x00000000000008d0 0x00000000006008d0 0x00000000006008d0 0x00000000000001a0 0x00000000000001a0 RW 8
NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c 0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x00000000000007d8 0x00000000004007d8 0x00000000004007d8 0x000000000000002c 0x000000000000002c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8
Section to Segment mapping: (段到节的映射)
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr
.gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
上述各段组成了最终在内存中执行的程序,其还提供了各段在虚拟地址空间和物理地址空间中的大小、位置、标志、访问授权和对齐方面的信息。各段语义如下:
PHDR保存程序头表
INTERP指定程序从可行性文件映射到内存之后,必须调用的解释器,它是通过链接其他库来满足未解析的引用,用于在虚拟地址空间中插入程序运行所需的动态库。
LOAD表示一个需要从二进制文件映射到虚拟地址空间的段,其中保存了常量数据(如字符串),程序目标代码等。
DYNAMIC段保存了由动态连接器(即INTERP段中指定的解释器)使用的信息。
3. 读取节头表:
$ readelf -S sign.o
There are 13 section headers, starting at offset 0x210:(这里指定的offset是相对于二进制文件)
Section Headers:
[Nr] Name Type Address Offset Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040 00000000000000e5 0000000000000000 AX 0 0 4
[ 2] .rela.text RELA 0000000000000000 000006f8 00000000000000a8 0000000000000018 11 1 8
[ 3] .data PROGBITS 0000000000000000 00000128 0000000000000000 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 00000128 0000000000000000 0000000000000000 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 00000128 000000000000000b 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 00000133 000000000000001d 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 00000150 0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 00000150 0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000007a0 0000000000000030 0000000000000018 11 8 8
[10] .shstrtab STRTAB 0000000000000000 000001a8 0000000000000061 0000000000000000 0 0 1
[11] .symtab SYMTAB 0000000000000000 00000550 0000000000000168 0000000000000018 12 9 8
[12] .strtab STRTAB 0000000000000000 000006b8 0000000000000039 0000000000000000 0 0 1
$ readelf -S sign
There are 31 section headers, starting at offset 0xc00
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 0000000000400200 00000200 000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 000000000040021c 0000021c 0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-id NOTE 000000000040023c 0000023c 0000000000000024 0000000000000000 A 0 0 4
[ 4] .hash HASH 0000000000400260 00000260 0000000000000030 0000000000000004 A 6 0 8
[ 5] .gnu.hash GNU_HASH 0000000000400290 00000290 000000000000001c 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000004002b0 000002b0 00000000000000a8 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000400358 00000358 0000000000000065 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 00000000004003be 000003be 000000000000000e 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 00000000004003d0 000003d0 0000000000000030 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 0000000000400400 00000400 0000000000000018 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000400418 00000418 0000000000000078 0000000000000018 A 6 13 8
[12] .init PROGBITS 0000000000400490 00000490 0000000000000018 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 00000000004004a8 000004a8 0000000000000060 0000000000000010 AX 0 0 4
[14] .text PROGBITS 0000000000400510 00000510 00000000000002a8 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 00000000004007b8 000007b8 000000000000000e 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 00000000004007c8 000007c8 000000000000000f 0000000000000000 A 0 0 4
[17] .eh_frame_hdr PROGBITS 00000000004007d8 000007d8 000000000000002c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000400808 00000808 000000000000009c 0000000000000000 A 0 0 8
[19] .ctors PROGBITS 00000000006008a8 000008a8 0000000000000010 0000000000000000 WA 0 0 8
[20] .dtors PROGBITS 00000000006008b8 000008b8 0000000000000010 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 00000000006008c8 000008c8 0000000000000008 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 00000000006008d0 000008d0 00000000000001a0 0000000000000010 WA 7 0 8
[23] .got PROGBITS 0000000000600a70 00000a70 0000000000000008 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 0000000000600a78 00000a78 0000000000000040 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000600ab8 00000ab8 0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000600ac8 00000ac8 0000000000000010 0000000000000000 WA 0 0 8
[27] .comment PROGBITS 0000000000000000 00000ac8 0000000000000038 0000000000000001 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00000b00 00000000000000fe 0000000000000000 0 0 1
[29] .symtab SYMTAB 0000000000000000 000013c0 0000000000000678 0000000000000018 30 47 8
[30] .strtab STRTAB 0000000000000000 00001a38 000000000000023c 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)
PROGBITS(程序必须解释的信息,如二进制代码),STRTAB用于存储与ELF格式有关的字符串,但与程序没有直接关联,如各个节的名称(.text, .comment)
.data保存初始化过的数据,这是普通程序数据的一部分,可以在程序运行期间修改。
.rodata保存了只读数据,可以读取但不能修改,例如printf语句中的所有静态字符串封装到该节。
.init和.fini保存了进程初始化和结束所用的代码,这通常是由编译器自动添加的。
.hash是一个散列表,允许在不对全表元素进行线性搜索的情况下,快速访问所有符号表项。
4. 符号表机制(readelf -s)
符号表保存了程序实现或使用的所有全局变量和函数,如果程序引用一个自身代码未定义的符号,则称之为未定义符号,这类引用必须在静态链接期间用其他目标模块或库解决,或在加载时通过动态链接解决。
实现:
.symtab确定符号的名称与其值之间的关联,其中名称不是直接以字符串形式出现的,而是表示为某一字符串数组(.strtab)的索引。
.strtab保存了字符串数组(.shstrtab包含了节名称字符串表)。
.hash保存了一个散列表,以帮助快速查找符号。
typedef struct elf64_sym {
Elf64_Word st_name; // 符号名称,字符串表中的索引
// STT_OBJECT表示符号关联到一个数据对象,如变量、数组或指针;
// STT_FUNC表示符号关联到一个函数;
// STT_NOTYPE表示符号类型未指定,用于未定义引用
unsigned char st_info; // 类型和绑定属性:STB_LOCAL/STB_GLOBAL/STB_WEAK;
unsigned char st_other; // 语义未定义,0
Elf64_Half st_shndx; // 相关节的索引,符号将绑定到该节,此外SHN_ABS指定符号是绝对值,不因重定位而改变,SHN_UNDEF标识未定义符号。
Elf64_Addr st_value; // 符号的值
Elf64_Xword st_size; // 符号的长度,如一个指针的长度或struct对象中包含的字节数。
}Elf64_Sym;