各种讲解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.