各种讲解elf文件格式一上来就是各种数据类型,看了半天却不知道这些数据类型是干啥的,所以咱就先找个例子直接上手,这样对elf文件格式有个具体而生动的了解。
然后再去看那些手册,就完全不惧了~。
我们使用一个汇编程序max.s并对其进行编译链接产生的两个elf文件来对比分析elf文件。
例子程序max.s来自《Linux C 一站式编程》。
ps:这是一本看完可以真正可以深入理解C语言精华的书,涵盖面极广,上到数据结构、linux系统、网络通信,下到编译链接、汇编语言、内存寻址。真的很好的哦亲。
汇编程序max.s用于取一组正整数的最大值,使用的是AT&T语法,程序源代码如下
-
.section .data -
data_items: -
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0 -
.section .text -
.globl _start -
_start: -
movl $0, %edi -
movl data_items(,%edi,4), %eax # data_items+ 4*(edi) --> eax -
movl %eax, %ebx # (eax) --> ebx -
start_loop: # ebx store the max value -
cmpl $0, %eax -
je loop_exit -
incl %edi -
movl data_items(,%edi,4), %eax # data_items+ 4*(edi) --> eax -
cmpl %ebx, %eax -
jle start_loop # eax <= ebx -
movl %eax, %ebx # eax > ebx -
jmp start_loop -
loop_exit: -
movl $1, %eax # exit system call. -
int $0x80
程序解释:
在源代码中定义了2个section,一个是section名字叫.data,另一个section叫.text, 声明了_start为全局的符号。
在.data section中定义了一个符号data_items,在.text section中定义了3个符号_start 、 start_loop、loop_exit。其中 _start符号被定义为全局符号。
程序逻辑也很简单,依次遍历数组并比较就得出了最大值,将最大值存储在ebx中,最后使用系统调用退出。
编译
$as -o max.o max.s
链接
$ld -o max max.o
执行并测试程序
$./max
$echo $?
222
222就是max.s运行返回的最大值。
下面先来分析编译出的max.o文件
-
$ du -b max.o -
704 max.o #此elf文件大小为704B -
$ readelf -a max.o #读取elf文件 -
ELF Header: -
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 -
Class: ELF32 -
Data: 2's 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: 200 (bytes into file) #section headers table在文件中的偏移 -
Flags: 0x0 -
Size of this header: 52 (bytes) #elf header在文件中占了52个字节 -
Size of program headers: 0 (bytes) -
Number of program headers: 0 #文件中无program headers -
Size of section headers: 40 (bytes) #section headers table 中的每个section header descriptor有40B -
Number of section headers: 8 #文件中有8个section headers -
Section header string table index: 5 -
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 00002a 00 AX 0 0 4 #这是我们在max.s中定义的section, .text section -
[ 2] .rel.text REL 00000000 0002b0 000010 08 6 1 4 -
[ 3] .data PROGBITS 00000000 000060 000038 00 WA 0 0 4 #这是我们在max.s中定义的section, .data section,section size 为 0x38B,即56B(14*4B) -
[ 4] .bss NOBITS 00000000 000098 000000 00 WA 0 0 4 -
[ 5] .shstrtab STRTAB 00000000 000098 000030 00 0 0 1 #.shstrtab 存放各section的名字,比如".text" ".data" -
[ 6] .symtab SYMTAB 00000000 000208 000080 10 7 7 4 #.symtab 存放所有section中定义的的符号名字,比如 "data_items","start_loop" -
[ 7] .strtab STRTAB 00000000 000288 000028 00 0 0 1 -
Key to Flags: -
W (write), A (alloc), X (execute), M (merge), S (strings) -
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) -
O (extra OS processing required) o (OS specific), p (processor specific) -
There are no section groups in this file. -
There are no program headers in this file. -
Relocation section '.rel.text' at offset 0x2b0 contains 2 entries: #.rel.text 告诉链接器指令哪些地方需要定位,这里表示的是.text section中需要改动的地方,在section中的偏移是8和17 -
Offset Info Type Sym.Value Sym. Name -
00000008 00000201 R_386_32 00000000 .data -
00000017 00000201 R_386_32 00000000 .data -
There are no unwind sections in this file. -
Symbol table '.symtab' contains 8 entries: #符号就是为一个内存地址起了一个名字。 -
Num: Value Size Type Bind Vis Ndx Name #Ndx表示 符号所在的的section编号见Section Headers 中的[Nr]列 -
0: 00000000 0 NOTYPE LOCAL DEFAULT UND #Value 表示此符号在相应section中的偏移 -
1: 00000000 0 SECTION LOCAL DEFAULT 1 -
2: 00000000 0 SECTION LOCAL DEFAULT 3 -
3: 00000000 0 SECTION LOCAL DEFAULT 4 -
4: 00000000 0 NOTYPE LOCAL DEFAULT 3 data_items -
5: 0000000e 0 NOTYPE LOCAL DEFAULT 1 start_loop -
6: 00000023 0 NOTYPE LOCAL DEFAULT 1 loop_exit -
7: 00000000 0 NOTYPE GLOBAL DEFAULT 1 _start #这里_start 符号是GLOBAL的, 因为源代码中使用.globl _start 标明此符号为全局的 -
No version information found in this file.
这是max.o文件详细的区域信息
结合readelf读出的信息,可以看到,在max.o的这个elf文件中,有3种类型的数据"区域",分别是elf header、section、section headers。
[1] elf header描述了这个elf文件的一些信息,如数据格式是big-endian 或者 little-endian、运行平台、section header 的个数等。
[2] section headers是一个表,表中的每个条目描述了一个section,如section在文件中的偏移,大小等。
[3] section中就是elf文件中“真正”的信息了。
下面来依次解释max.o中的各个section。
.data 和.text 属于PROGBITS类型的section,这是将来要正常运行的程序和代码。
.shstrtab和.strtab属于STRTAB类型的section,可以在文件中看到,它们都存着字符串,shstrtab存的是section的名字,而.strtab存的是符号的名字(符号表示一个固定的内存地址)。
.symtab是属于SYMTAB类型的section,它描述了.strtab中的符号在"内存"中对应的"内存地址",当然这里的还不是真正的内存地址,只是一个偏移量,等到链接之后就是真正的了。
.rel.text是属于REL类型的section,它为链接器正确链接提供了信息,在下面会详细解释。
$objdump -d max.o
-
max.o: file format elf32-i386 -
Disassembly of section .text: -
00000000 <_start>: -
0: bf 00 00 00 00 mov $0x0,%edi -
5: 8b 04 bd 00 00 00 00 mov 0x0(,%edi,4),%eax -
c: 89 c3 mov %eax,%ebx -
0000000e <start_loop>: -
e: 83 f8 00 cmp $0x0,%eax -
11: 74 10 je 23 <loop_exit> -
13: 47 inc %edi -
14: 8b 04 bd 00 00 00 00 mov 0x0(,%edi,4),%eax -
1b: 39 d8 cmp %ebx,%eax -
1d: 7e ef jle e <start_loop> -
1f: 89 c3 mov %eax,%ebx -
21: eb eb jmp e <start_loop> -
00000023 <loop_exit>: -
23: b8 01 00 00 00 mov $0x1,%eax -
28: cd 80 int $0x80
看一下链接之后的代码
$ld -o max max.o
$objdump -d max
-
max: file format elf32-i386 -
Disassembly of section .text: -
08048074 <_start>: -
8048074: bf 00 00 00 00 mov $0x0,%edi -
8048079: 8b 04 bd a0 90 04 08 mov 0x80490a0(,%edi,4),%eax -
8048080: 89 c3 mov %eax,%ebx -
08048082 <start_loop>: -
8048082: 83 f8 00 cmp $0x0,%eax -
8048085: 74 10 je 8048097 <loop_exit> -
8048087: 47 inc %edi -
8048088: 8b 04 bd a0 90 04 08 mov 0x80490a0(,%edi,4),%eax -
804808f: 39 d8 cmp %ebx,%eax -
8048091: 7e ef jle 8048082 <start_loop> -
8048093: 89 c3 mov %eax,%ebx -
8048095: eb eb jmp 8048082 <start_loop> -
08048097 <loop_exit>: -
8048097: b8 01 00 00 00 mov $0x1,%eax -
804809c: cd 80 int $0x80
经过链接,.text代码可以真正的正确运行了,可以看到:
1.跳转指令中的跳转地址由文件偏移改成了实际的内存地址。
2.注意从.data section中取数的这句,max.o中是mov 0x0(,%edi,4),%eax ,链接后被换成了正确的mov 0x80490a0(,%edi,4),%eax。
链接后的文件max区域结构如图所示
可以看到,max文件中多了一个program headers区域,以及2个segment section。
program headers 是一张表,用于描述segment section。
segment section就是真正拷贝到内存并运行的代码。
映射图如下
再使用readelf查看经过链接后的elf文件
-
$ readelf -a max -
ELF Header: -
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 -
Class: ELF32 -
Data: 2's complement, little endian -
Version: 1 (current) -
OS/ABI: UNIX - System V -
ABI Version: 0 -
Type: EXEC (Executable file) #类型变为可执行文件 -
Machine: Intel 80386 -
Version: 0x1 -
Entry point address: 0x8048074 #elf文件的内存入口地址由0变为0x8048074了 -
Start of program headers: 52 (bytes into file) #program headers table 在文件中的偏移 -
Start of section headers: 256 (bytes into file) #section headers table 在文件中的偏移 -
Flags: 0x0 -
Size of this header: 52 (bytes) -
Size of program headers: 32 (bytes) #program headers -
Number of program headers: 2 #多了2个program headers -
Size of section headers: 40 (bytes) -
Number of section headers: 6 #少了2个section headers -
Section header string table index: 3 -
Section Headers: #与max.o文件对比可以发现少了.bss 和 .rel.text两个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 08048074 000074 00002a 00 AX 0 0 4 -
[ 2] .data PROGBITS 080490a0 0000a0 000038 00 WA 0 0 4 -
[ 3] .shstrtab STRTAB 00000000 0000d8 000027 00 0 0 1 -
[ 4] .symtab SYMTAB 00000000 0001f0 0000a0 10 5 6 4 -
[ 5] .strtab STRTAB 00000000 000290 000040 00 0 0 1 -
Key to Flags: -
W (write), A (alloc), X (execute), M (merge), S (strings) -
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) -
O (extra OS processing required) o (OS specific), p (processor specific) -
There are no section groups in this file. -
Program Headers: #此2个program headers 将被装入至内存中分别的2个物理页中 -
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align -
LOAD 0x000000 0x08048000 0x08048000 0x0009e 0x0009e R E 0x1000 #装入至物理页0x8048000~0x8049000 -
LOAD 0x0000a0 0x080490a0 0x080490a0 0x00038 0x00038 RW 0x1000 #装入至物理页0x8049000~0x804a000 -
Section to Segment mapping: -
Segment Sections... -
00 .text -
01 .data -
There is no dynamic section in this file. -
There are no relocations in this file. -
There are no unwind sections in this file. -
Symbol table '.symtab' contains 10 entries: -
Num: Value Size Type Bind Vis Ndx Name -
0: 00000000 0 NOTYPE LOCAL DEFAULT UND -
1: 08048074 0 SECTION LOCAL DEFAULT 1 -
2: 080490a0 0 SECTION LOCAL DEFAULT 2 -
3: 080490a0 0 NOTYPE LOCAL DEFAULT 2 data_items -
4: 08048082 0 NOTYPE LOCAL DEFAULT 1 start_loop -
5: 08048097 0 NOTYPE LOCAL DEFAULT 1 loop_exit -
6: 08048074 0 NOTYPE GLOBAL DEFAULT 1 _start -
7: 080490d8 0 NOTYPE GLOBAL DEFAULT ABS __bss_start -
8: 080490d8 0 NOTYPE GLOBAL DEFAULT ABS _edata -
9: 080490d8 0 NOTYPE GLOBAL DEFAULT ABS _end -
No version information found in this file.