可執行檔案的格式(ELF格式)詳解
各種講解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.