readelf elf文件格式分析
背景
目標(biāo)文件
首先需要介紹的概念是目標(biāo)文件(Object file)的概念。目標(biāo)文件是計(jì)算機(jī)科學(xué)中編譯器或匯編器處理源代碼后所生成的代碼(目標(biāo)代碼,Object code)的計(jì)算機(jī)文件,它常被稱作二進(jìn)制文件(binaries)。這個(gè)文件類型主要是區(qū)別于你看得懂的用人話寫的代碼文件(.c、.cpp etc.)、中間文件(.i)、匯編文件(.s)。常見的.exe、.dll、.so啥的都算目標(biāo)文件。
目標(biāo)文件有三種類型:
- 可重定位的對(duì)象文件(Relocatable file)
由匯編器匯編生成的 .o 文件(下面會(huì)詳細(xì)講到) - 可執(zhí)行的對(duì)象文件(Executable file)
可執(zhí)行應(yīng)用程序 - 可被共享的對(duì)象文件(Shared object file)
動(dòng)態(tài)庫文件
編譯過程
編譯的過程如下圖所示

代碼文件經(jīng)過語言預(yù)處理器、編譯器、匯編器和鏈接器處理,最終生成可執(zhí)行目標(biāo)文件。
下面以一個(gè)簡(jiǎn)單的c語言文件為例:
sum.c源文件內(nèi)容如下:
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; ++i) {
s += a[i];
}
return s;
}
通過運(yùn)行C預(yù)處理器(cpp)可以生成sum.i中間文件:
# 1 "sum.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "sum.c"
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; ++i) {
s += a[i];
}
return s;
}
通過運(yùn)行C編譯器(cc1),將中間文件生成為sum.s匯編文件:
.file?"sum.i"
.text
.globl sum
.type?sum, @function
sum:
.LFB0:
.cfi_startproc
movl $0, %eax
movl $0, %edx
jmp?.L2
.L3:
movslq %edx, %rcx
addl (%rdi,%rcx,4), %eax
addl $1, %edx
.L2:
cmpl %esi, %edx
jl .L3
rep ret
.cfi_endproc
.LFE0:
.size?sum, .-sum
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
.section .note.GNU-stack,"",@progbits
最終生成通過匯編器(as)生成一個(gè)可重定位目標(biāo)文件(reloacatable object file)。
什么是ELF
系統(tǒng)里的目標(biāo)文件是按照特定的目標(biāo)文件格式來組織的,各個(gè)系統(tǒng)的目標(biāo)文件格式都不相同。
從貝爾實(shí)驗(yàn)室誕生的第一個(gè)Unix系統(tǒng)使用的是a.out格式(直到今天,可執(zhí)行文件仍然稱為a.out文件)。Windows使用可移植可執(zhí)行(PortableExecutable,PE)格式。Mac OS-X使用Mach-O格式。現(xiàn)代x86-64Linux和Unix系統(tǒng)使用可執(zhí)行可鏈接格式(Executable and Linkable Format,ELF)。
ELF格式的文件在Linux系統(tǒng)下有.axf、 .bin、 .elf、 .o、 .prx、 .puff、 .ko、 .mod和.so等等
readelf指令
前面介紹了這么多ELF的背景知識(shí),下面回來來說說readelf這個(gè)指令。這個(gè)指令正是用來查看目標(biāo)文件的內(nèi)容的。
ELF可重定位目標(biāo)文件的格式
典型格式
典型的ELF可重定位目標(biāo)文件的格式如下圖:

其中:
- .text 節(jié)里裝載了程序的可執(zhí)行機(jī)器碼
- .rodata 節(jié)里裝載了只讀數(shù)據(jù)
- .data 節(jié)里面裝載了被初始化的數(shù)據(jù),包括全局和靜態(tài)C變量
- .bss 節(jié)里面裝載了未被初始化的全局和靜態(tài)C變量(在目標(biāo)文件中只是占位符,不占空間)
- .symtab 或者 .dynsym 節(jié)里面裝載了符號(hào)信息
- 以 .rel 打頭的 節(jié)里面裝載了重定位條目
- .debug 一個(gè)調(diào)試符號(hào)表,只有使用了
-g參數(shù)編譯時(shí)才會(huì)有,用于debug - .line 用于記錄C源程序的行號(hào)和.text節(jié)中機(jī)器指令之間的映射,也是只有使用了
-g參數(shù)編譯時(shí)才會(huì)有 - .strtab 或者 .dynstr 節(jié)里面裝載了字符串信息(以null結(jié)尾的字符串信息)
符號(hào)表部分解析
符號(hào)表每節(jié)定義如下:
typedef struct {
int name; /* String table offset */
char type:4, /* Function or data (4 bits) */
binding:4; /* Local or global (4 bits) */
char reserved; /* Unused */
short section; /* Section header index */
long value; /* Section offset or absolute address */
long size; /* Object size in bytes */
} Elf64_Symbol;
具體解釋如下:

舉個(gè)例子??
以上面的sum.c生成的sum.o為例,我們選取readelf的-all參數(shù)輸出全部?jī)?nèi)容:
$readelf -all sum.o
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: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 536 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 11
Section header string table index: 10
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
000000000000001b 0000000000000000 AX 0 0 1
[ 2] .data PROGBITS 0000000000000000 0000005b
0000000000000000 0000000000000000 WA 0 0 1
[ 3] .bss NOBITS 0000000000000000 0000005b
0000000000000000 0000000000000000 WA 0 0 1
1 int sum(int *a, int n)
2 {
[ 4] .comment PROGBITS 0000000000000000 0000005b
000000000000002e 0000000000000001 MS 0 0 1
[ 5] .note.GNU-stack PROGBITS 0000000000000000 00000089
0000000000000000 0000000000000000 0 0 1
[ 6] .eh_frame PROGBITS 0000000000000000 00000090
0000000000000030 0000000000000000 A 0 0 8
[ 7] .rela.eh_frame RELA 0000000000000000 000001a8
0000000000000018 0000000000000018 I 8 6 8
[ 8] .symtab SYMTAB 0000000000000000 000000c0
00000000000000d8 0000000000000018 9 8 8
[ 9] .strtab STRTAB 0000000000000000 00000198
000000000000000b 0000000000000000 0 0 1
[10] .shstrtab STRTAB 0000000000000000 000001c0
0000000000000054 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
Relocation section '.rela.eh_frame' at offset 0x1a8 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS sum.i
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
8: 0000000000000000 27 FUNC GLOBAL DEFAULT 1 sum
No version information found in this file.
其中第一部分是ELF頭(ELF header)中的描述信息。(用-h參數(shù)可以單獨(dú)得到)。
最后一部分是符號(hào)表部分(用-s參數(shù)可以單獨(dú)得到該部分),前面八個(gè)條目是鏈接器內(nèi)部使用的局部符號(hào),最后一行是全局符號(hào)sum定義的條目??梢酝ㄟ^最后一行看出,它是一個(gè)位于.text節(jié)中偏移量為0處的27字節(jié)函數(shù)。(Ndx部分表示在哪個(gè)節(jié)中,1表示.text節(jié),3表示.data節(jié),對(duì)應(yīng)上面輸出的Section Headers部分)
查看更多文章請(qǐng)?jiān)L問我的博客——左旋異構(gòu)
本文地址:左旋異構(gòu) - ELF目標(biāo)文件與readelf