elf文件结构解析
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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析