elf文件結構解析
elf文件格式,許多文件類型都是elf格式,比如.ko、.so、.o,vmlinux也是這種格式
如下圖是elf文件結構:
查看是否為elf文件,使用file cmd
file slub_debug_test_module.ko slub_debug_test_module.ko: ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), not stripped
查看elf file header
readelf -h xxx.ko
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: AArch64 Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 122120 (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: 35 Section header string table index: 33
上述結果在code里對應如下結構體:
#define EI_NIDENT 16 typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; //開始的16個字節 Elf32_Half e_type; //文件類型 Elf32_Half e_machine; //運行的機器類型 Elf32_Word e_version; //版本 Elf32_Addr e_entry; //程序入口地址 Elf32_Off e_phoff; //程序頭表在文件中的偏移 Elf32_Off e_shoff; //節頭表在文件中的偏移 Elf32_Word e_flags; //標記 Elf32_Half e_ehsize; //elf文件頭大小 Elf32_Half e_phentsize; //程序頭表項的大小 Elf32_Half e_phnum; //程序頭表中表項項的個數 Elf32_Half e_shentsize; //節頭表項大小 Elf32_Half e_shnum; //節頭表中表項的個數 Elf32_Half e_shstrndx; //節頭表的字符串節所在節頭表中下標 } Elf32_Ehdr;
獲取elf文件里包含的section描述table:
readelf -WS xxx.ko #W option表示將一個section的結果以一行顯示,在沒有一行顯示的情況下可以加這個option
There are 35 section headers, starting at offset 0x1dd08: Section Headers: [Nr] Name Type Address Off Size ES Flg Lk Inf Al [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 0000000000000000 000040 0000ac 00 AX 0 0 4 [ 2] .rela.text RELA 0000000000000000 0000f0 000168 18 I 32 1 8 [ 3] .data PROGBITS 0000000000000000 000258 000100 00 WA 0 0 8 [ 4] .rela.data RELA 0000000000000000 000358 000048 18 I 32 3 8 [ 5] .bss NOBITS 0000000000000000 0003a0 000000 00 WA 0 0 1 [ 6] .init.text PROGBITS 0000000000000000 0003a0 00009c 00 AX 0 0 4 [ 7] .rela.init.text RELA 0000000000000000 000440 000228 18 I 32 6 8 [ 8] .exit.text PROGBITS 0000000000000000 000668 000034 00 AX 0 0 4 [ 9] .rela.exit.text RELA 0000000000000000 0006a0 000090 18 I 32 8 8 [10] .modinfo PROGBITS 0000000000000000 000730 000086 00 A 0 0 1 [11] .rodata.str1.1 PROGBITS 0000000000000000 0007b6 0000d0 01 AMS 0 0 1 [12] .debug_loc PROGBITS 0000000000000000 000886 00027d 00 0 0 1 [13] .rela.debug_loc RELA 0000000000000000 000b08 000108 18 I 32 12 8 [14] .debug_abbrev PROGBITS 0000000000000000 000c10 0005aa 00 0 0 1 [15] .debug_info PROGBITS 0000000000000000 0011ba 008b67 00 0 0 1 [16] .rela.debug_info RELA 0000000000000000 009d28 00dda0 18 I 32 15 8 [17] .debug_ranges PROGBITS 0000000000000000 017ac8 000040 00 0 0 1 [18] .rela.debug_ranges RELA 0000000000000000 017b08 000090 18 I 32 17 8 [19] .debug_str PROGBITS 0000000000000000 017b98 004739 01 MS 0 0 1 [20] .comment PROGBITS 0000000000000000 01c2d1 00009d 01 MS 0 0 1 [21] .debug_line PROGBITS 0000000000000000 01c36e 000ba8 00 0 0 1 [22] .rela.debug_line RELA 0000000000000000 01cf18 000048 18 I 32 21 8 [23] .debug_frame PROGBITS 0000000000000000 01cf60 0000a0 00 0 0 8 [24] .rela.debug_frame RELA 0000000000000000 01d000 0000c0 18 I 32 23 8 [25] __mcount_loc PROGBITS 0000000000000000 01d0c0 000018 08 A 0 0 8 [26] .rela__mcount_loc RELA 0000000000000000 01d0d8 000048 18 I 32 25 8 [27] .note.Linux NOTE 0000000000000000 01d120 000018 00 A 0 0 4 [28] .gnu.linkonce.this_module PROGBITS 0000000000000000 01d140 000340 00 WA 0 0 64 [29] .rela.gnu.linkonce.this_module RELA 0000000000000000 01d480 000030 18 I 32 28 8 [30] .note.gnu.build-id NOTE 0000000000000000 01d4b0 000018 00 A 0 0 4 [31] .note.GNU-stack PROGBITS 0000000000000000 01d4c8 000000 00 0 0 1 [32] .symtab SYMTAB 0000000000000000 01d4c8 0004c8 18 34 37 8 [33] .shstrtab STRTAB 0000000000000000 01d990 0001ce 00 0 0 1 [34] .strtab STRTAB 0000000000000000 01db5e 0001a9 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)
需要注意的是,rodata等其它section在一個 elf文件里可能不止一個,有多個,比如如下:
[1057] .rodata..Lswitch. PROGBITS 0000000000000000 0059c558 [1062] .rodata..L__const PROGBITS 0000000000000000 0059c5b0 [1069] .rodata..Lswitch. PROGBITS 0000000000000000 0059c6a0 [1070] .rodata..Lanon.22 PROGBITS 0000000000000000 0059c6e0 [1071] .rodata..Lanon.22 PROGBITS 0000000000000000 0059c730 [1072] .rodata..Lswitch. PROGBITS 0000000000000000 0059c75c
一個section header(SH)在code里使用如下結構體來描述:
typedef struct elf32_shdr { Elf32_Word sh_name; //節的名字,在符號表中的下標 Elf32_Word sh_type; //節的類型,描述符號,代碼,數據,重定位等 Elf32_Word sh_flags; //讀寫執行標記 Elf32_Addr sh_addr; //節在執行時的虛擬地址 Elf32_Off sh_offset; //節在文件中的偏移量 Elf32_Word sh_size; //節的大小 Elf32_Word sh_link; //其它節的索引 Elf32_Word sh_info; //節的其它信息 Elf32_Word sh_addralign; //節對齊 Elf32_Word sh_entsize; //當前section相對於core_layout/init_layout base地址的offset,根據這個offset,用於將efl文件(vmalloc內存)里的該section copy至core_layout/init_layout(module區)里對應位置
} Elf32_Shdr;
獲取elf文件里的symbol table
readelf -s xxx.ko
Symbol table '.symtab' contains 51 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS slub_debug_test_drv.c 2: 0000000000000000 0 NOTYPE LOCAL DEFAULT 6 $x 3: 0000000000000000 256 OBJECT LOCAL DEFAULT 3 hello_flops 4: 0000000000000000 0 NOTYPE LOCAL DEFAULT 8 $x 5: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 $x 6: 0000000000000000 136 FUNC LOCAL DEFAULT 1 hello_write 7: 0000000000000088 36 FUNC LOCAL DEFAULT 1 hello_open 8: 0000000000000000 12 OBJECT LOCAL DEFAULT 10 __UNIQUE_ID_license10 9: 0000000000000000 0 NOTYPE LOCAL DEFAULT 3 $d 10: 0000000000000010 0 NOTYPE LOCAL DEFAULT 23 $d 11: 0000000000000000 0 SECTION LOCAL DEFAULT 1 12: 0000000000000000 0 SECTION LOCAL DEFAULT 3 13: 0000000000000000 0 SECTION LOCAL DEFAULT 5 14: 0000000000000000 0 SECTION LOCAL DEFAULT 6 15: 0000000000000000 0 SECTION LOCAL DEFAULT 8 16: 0000000000000000 0 SECTION LOCAL DEFAULT 10 17: 0000000000000000 0 SECTION LOCAL DEFAULT 12 18: 0000000000000000 0 SECTION LOCAL DEFAULT 14 19: 0000000000000000 0 SECTION LOCAL DEFAULT 15 20: 0000000000000000 0 SECTION LOCAL DEFAULT 17 21: 0000000000000000 0 SECTION LOCAL DEFAULT 21 22: 0000000000000000 0 SECTION LOCAL DEFAULT 23 23: 0000000000000000 0 SECTION LOCAL DEFAULT 25 24: 0000000000000000 0 FILE LOCAL DEFAULT ABS slub_debug_test_module.mo 25: 0000000000000000 24 OBJECT LOCAL DEFAULT 27 _note_6 26: 0000000000000000 0 NOTYPE LOCAL DEFAULT 27 $d 27: 000000000000000c 50 OBJECT LOCAL DEFAULT 10 __UNIQUE_ID_vermagic10 28: 000000000000003e 28 OBJECT LOCAL DEFAULT 10 __UNIQUE_ID_name11 29: 0000000000000000 0 NOTYPE LOCAL DEFAULT 28 $d 30: 000000000000005a 9 OBJECT LOCAL DEFAULT 10 __module_depends 31: 0000000000000063 35 OBJECT LOCAL DEFAULT 10 __UNIQUE_ID_srcversion12 32: 0000000000000000 0 SECTION LOCAL DEFAULT 27 33: 0000000000000000 0 SECTION LOCAL DEFAULT 28 34: 0000000000000000 0 SECTION LOCAL DEFAULT 11 35: 0000000000000000 0 SECTION LOCAL DEFAULT 19 36: 0000000000000000 0 SECTION LOCAL DEFAULT 20 37: 0000000000000000 156 FUNC GLOBAL DEFAULT 6 init_module 38: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _mcount 39: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __register_chrdev ...
以下面的例子說明下,
readelf -s xxx.ko |grep proc_mpoolinfo_operations 5510: 0000000000114360 256 OBJECT GLOBAL DEFAULT 6 proc_mpoolinfo_operations
行為例,這行是一個object,比如是一個結構體,256是這個結構體的size,后面的6表示這個symbol是位於index為6的section里,一般是.bss或者.data section,數字3是在ndx column。這個很方便,以后想知道一個結構體的size用這種方式很快捷。
第二列表示此symbol在這個section(.data or .bss)里的offset,這個在加載ko時將根據這個offset加上section base addr確定此symbol的execution addr,這部分邏輯在simplify_symbols()
dump elf文件里的某個section
-x --hex-dump=<number|name> Dump the contents of section <number|name> as bytes -p --string-dump=<number|name> Dump the contents of section <number|name> as strings -R --relocated-dump=<number|name> Dump the contents of section <number|name> as relocated bytes
上述number、name分別是readelf -S結果了的Nr、name列,可以指定number或者name,dump某個section直接看這個section里的數據是什么
上述部分參考如下blog:
https://www.cnblogs.com/chengxuyuancc/p/3474623.html
elf64_sym structure
typedef struct elf64_sym { Elf64_Word st_name; /* Symbol name, index in string tbl */ /* strtab是所有symbol name string的table,所有的symbol name string都存在這個table里,這個table其實就是一個char*數組,st_name是某一個string在這個數組里的offset,下一個string則是在上一個string
的offset加上上一個string len作為它的offset */ unsigned char st_info; /* Type and binding attributes */ unsigned char st_other; /* No defined meaning, 0 */ Elf64_Half st_shndx; /* Associated section index */ /* 表示這個symbol是存放在哪個section里,這個section的index,比如函數symbol會存放在.text section;而變量symbol則會存在.data section */ Elf64_Addr st_value; /* Value of the symbol */ /* 這個symbol在所在的section里的offset */ Elf64_Xword st_size; /* Associated symbol size */ /* symbol size */ } Elf64_Sym;
elf section
.strtab,存放symbol name string的table
.shstrtab,存放section name string的table
.symtab,存放symbol描述信息的table,這個table會引用.strtab,里面的st_name表示這個symbol name string在strtab里的offset
獲取elf文件類型
elf文件類型有DYN (Shared object file)、EXEC(executable file)等
使用readelf -h查看elf file header,例如如下一個efl是DYN(Shared object file)
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: DYN (Shared object file) Machine: AArch64 Version: 0x1 Entry point address: 0x230
查看動態鏈接程序所使用的動態鏈接器
使用readelf -l [elf_file],查看結果中的INTERP段,比如如下一個elf可執行動態鏈接程序將使用的linker是/system/bin/bootstrap/linker64:
INTERP 0x0000000000000270 0x0000000000000270 0x0000000000000270 0x000000000000001f 0x000000000000001f R 1 [Requesting program interpreter: /system/bin/bootstrap/linker64]
查看一個elf file是由什么編譯器、鏈接器產出的
使用readelf -p .comment [elf_file],比如一個linux kernel vmlinux file執行這條cmd的結果如下,其linker是LLD,compiler是clang,只是有些elf file並沒有.comment段,那就沒法獲知了..
String dump of section '.comment': [ 0] Linker: LLD 11.0.2 (/buildbot/tmp/tmpF3FjA8 b397f81060ce6d701042b782172ed13bee898b79) [ 56] Android (6573524 based on r383902b) clang version 11.0.2 (https://android.googlesource.com/toolchain/llvm-project b397f81060ce6d701042b782172ed13bee898b79)
查看一個可執行程序所依賴的動態鏈接庫
使用readelf -d [elf_exec_file]
good reference:
https://blog.csdn.net/flydream0/article/details/8719036