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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM