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

原文地址:https://www.cnblogs.com/aspirs/p/15522256.html