Android连接器(一)-elf文件格式总结

728 阅读15分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路

elf包含两种视图,分别是文件视图和程序视图 描述文件视图的主要数据结构就是section,一个elf文件包含多个section,每个section是相对独立的数据。文件使用Section Headers来描述section的名称类型以及位置大小。 描述程序的视图为程序头表和程序头。链接器会根据程序头表来对数据进行加载。程序头表中存放多个程序头,程序头描述的是程序段。程序段并不在elf中,需要链接器进行内存分配,并将elf文件中的不同部分映射到分配的段内存上。 一个程序段版本多个具有相同属性(读写执行权限一致,节紧凑相邻的节)的节。节头表描述软件被加载到内存后的内存布局,包括内存的属性,位置,大小,以及对应映射elf文件的哪部分。 readelf -a 输出的elf格式信息

ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 52 (bytes into file)
Start of section headers: 193160 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 27
Section header string table index: 26

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.android.ide NOTE 00000134 000134 000098 00 A 0 0 2
[ 2] .note.gnu.build-i NOTE 000001cc 0001cc 000024 00 A 0 0 4
[ 3] .dynsym DYNSYM 000001f0 0001f0 0016c0 10 A 4 1 4
[ 4] .dynstr STRTAB 000018b0 0018b0 001832 00 A 0 0 1
[ 5] .gnu.hash GNU_HASH 000030e4 0030e4 000a58 04 A 3 0 4
[ 6] .gnu.version VERSYM 00003b3c 003b3c 0002d8 02 A 3 0 2
[ 7] .gnu.version_d VERDEF 00003e14 003e14 00001c 00 A 4 1 4
[ 8] .gnu.version_r VERNEED 00003e30 003e30 000040 00 A 4 2 4
[ 9] .rel.dyn REL 00003e70 003e70 002810 08 A 3 0 4
[10] .rel.plt REL 00006680 006680 0002e0 08 AI 3 21 4
[11] .plt PROGBITS 00006960 006960 0005d0 04 AX 0 0 16
[12] .text PROGBITS 00006f30 006f30 01e688 00 AX 0 0 16
[13] .gcc_except_table PROGBITS 000255b8 0255b8 000378 00 A 0 0 4
[14] .rodata PROGBITS 00025930 025930 002ce4 00 A 0 0 4
[15] .eh_frame PROGBITS 00028614 028614 003d24 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 0002c338 02c338 000cbc 00 A 0 0 4
[17] .fini_array FINI_ARRAY 0002e76c 02d76c 000008 04 WA 0 0 4
[18] .data.rel.ro PROGBITS 0002e774 02d774 0015a8 00 WA 0 0 4
[19] .dynamic DYNAMIC 0002fd1c 02ed1c 000108 08 WA 4 0 4
[20] .got PROGBITS 0002fe24 02ee24 000060 00 WA 0 0 4
[21] .got.plt PROGBITS 0002fe84 02ee84 00017c 00 WA 0 0 4
[22] .data PROGBITS 00030000 02f000 000020 00 WA 0 0 8
[23] .bss NOBITS 00030040 02f020 00034c 00 WA 0 0 64
[24] .comment PROGBITS 00000000 02f020 000130 01 MS 0 0 1
[25] .note.gnu.gold-ve NOTE 00000000 02f150 00001c 00 0 0 4
[26] .shstrtab STRTAB 00000000 02f16c 00011c 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)
There are no section groups in this file.

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00100 0x00100 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x2cff4 0x2cff4 R E 0x1000
LOAD 0x02d76c 0x0002e76c 0x0002e76c 0x018b4 0x01c20 RW 0x1000
DYNAMIC 0x02ed1c 0x0002fd1c 0x0002fd1c 0x00108 0x00108 RW 0x4
NOTE 0x000134 0x00000134 0x00000134 0x000bc 0x000bc R 0x4
GNU_EH_FRAME 0x02c338 0x0002c338 0x0002c338 0x00cbc 0x00cbc R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x02d76c 0x0002e76c 0x0002e76c 0x01894 0x01894 RW 0x4



Section to Segment mapping:
Segment Sections...
00 
01 .note.android.ident .note.gnu.build-id .dynsym .dynstr .gnu.hash .gnu.version .gnu.version_d .gnu.version_r .rel.dyn .rel.plt .plt .text .gcc_except_table .rodata .eh_frame .eh_frame_hdr 
02 .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss 
03 .dynamic 
04 .note.android.ident .note.gnu.build-id 
05 .eh_frame_hdr 
06 
07 .fini_array .data.rel.ro .dynamic .got .got.plt



Dynamic section at offset 0x2ed1c contains 28 entries:
Tag Type Name/Value
0x00000003 (PLTGOT) 0x2fe84
0x00000002 (PLTRELSZ) 736 (bytes)
0x00000017 (JMPREL) 0x6680
0x00000014 (PLTREL) REL
0x00000011 (REL) 0x3e70
0x00000012 (RELSZ) 10256 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 898
0x00000006 (SYMTAB) 0x1f0
0x0000000b (SYMENT) 16 (bytes)
0x00000005 (STRTAB) 0x18b0
0x0000000a (STRSZ) 6194 (bytes)
0x6ffffef5 (GNU_HASH) 0x30e4
0x00000001 (NEEDED) Shared library: [liblog.so]
0x00000001 (NEEDED) Shared library: [libm.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x00000001 (NEEDED) Shared library: [libc.so]
0x0000000e (SONAME) Library soname: [libnative-lib.so]
0x0000001a (FINI_ARRAY) 0x2e76c
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000001e (FLAGS) BIND_NOW
0x6ffffffb (FLAGS_1) Flags: NOW
0x6ffffff0 (VERSYM) 0x3b3c
0x6ffffffc (VERDEF) 0x3e14
0x6ffffffd (VERDEFNUM) 1
0x6ffffffe (VERNEED) 0x3e30
0x6fffffff (VERNEEDNUM) 2
0x00000000 (NULL) 0x0



Relocation section '.rel.dyn' at offset 0x3e70 contains 1282 entries:
Offset Info Type Sym.Value Sym. Name
0002e76c 00000008 R_386_RELATIVE 
........
0002fe4c 00013906 R_386_GLOB_DAT 0002e788 _ZTVSt20bad_array_new_
0002e7c4 00014d01 R_386_32 00009540 _ZNSt13bad_exceptionD0



Relocation section '.rel.plt' at offset 0x6680 contains 92 entries:
Offset Info Type Sym.Value Sym. Name
0002fe90 00000207 R_386_JUMP_SLOT 00000000 __cxa_finalize
......
0002fed4 00000607 R_386_JUMP_SLOT 00000000 free
.....
0002fffc 00002007 R_386_JUMP_SLOT 00000000 dl_iterate_phdr


The decoding of unwind sections for machine type Intel 80386 is not currently supported.
Symbol table '.dynsym' contains 364 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND 
......
6: 00000000 0 FUNC GLOBAL DEFAULT UND free@LIBC (2)
7: 00000000 0 FUNC GLOBAL DEFAULT UND malloc@LIBC (2)
......
363: 0002fb54 16 OBJECT GLOBAL DEFAULT 18 _ZTIPDi
Histogram for `.gnu.hash' bucket list length (total of 263 buckets):
Length Number % of total Coverage
0 73 ( 27.8%)
1 93 ( 35.4%) 28.1%
2 61 ( 23.2%) 65.0%
3 28 ( 10.6%) 90.3%
4 8 ( 3.0%) 100.0%
Version symbols section '.gnu.version' contains 364 entries:
Addr: 0000000000003b3c Offset: 0x003b3c Link: 3 (.dynsym)
000: 0 (*local*) 2 (LIBC) 2 (LIBC) 2 (LIBC) 
004: 2 (LIBC) 2 (LIBC) 2 (LIBC) 2 (LIBC) 
008: 2 (LIBC) 2 (LIBC) 2 (LIBC) 2 (LIBC) 
00c: 2 (LIBC) 2 (LIBC) 2 (LIBC) 2 (LIBC) 
010: 2 (LIBC) 2 (LIBC) 2 (LIBC) 2 (LIBC) 
......
168: 1 (*global*) 1 (*global*) 1 (*global*) 1 (*global*)


Version definition section '.gnu.version_d' contains 1 entries:
Addr: 0x0000000000003e14 Offset: 0x003e14 Link: 4 (.dynstr) 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libnative-lib.so
Version needs section '.gnu.version_r' contains 2 entries:
Addr: 0x0000000000003e30 Offset: 0x003e30 Link: 4 (.dynstr)
000000: Version: 1 File: libc.so Cnt: 1
0x0010: Name: LIBC Flags: none Version: 2
0x0020: Version: 1 File: libdl.so Cnt: 1
0x0030: Name: LIBC Flags: none Version: 3
Displaying notes found at file offset 0x00000134 with length 0x00000098:
Owner Data size Description
Android 0x00000084 NT_VERSION (version)
Displaying notes found at file offset 0x000001cc with length 0x00000024:
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 33cf7a192affee7e67ed27bde9c4c5ebc76eaaf6
Displaying notes found at file offset 0x0002f150 with length 0x0000001c:
Owner Data size Description
GNU 0x00000009 NT_GNU_GOLD_VERSION (gold version)

elf包含两种视图,分别是文件视图和程序视图 描述文件视图的主要数据结构就是section,一个elf文件包含多个section,每个section是相对独立的数据。文件使用Section Headers来描述section的名称类型以及位置大小。 描述程序的视图为程序头表和程序头。链接器会根据程序头表来对数据进行加载。程序头表中存放多个程序头,程序头描述的是程序段。程序段并不在elf中,需要链接器进行内存分配,并将elf文件中的不同部分映射到分配的段内存上。 一个程序段版本多个具有相同属性(读写执行权限一致,节紧凑相邻的节)的节。节头表描述软件被加载到内存后的内存布局,包括内存的属性,位置,大小,以及对应映射elf文件的哪部分。

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00100 0x00100 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x2cff4 0x2cff4 R E 0x1000
LOAD 0x02d76c 0x0002e76c 0x0002e76c 0x018b4 0x01c20 RW 0x1000
DYNAMIC 0x02ed1c 0x0002fd1c 0x0002fd1c 0x00108 0x00108 RW 0x4
NOTE 0x000134 0x00000134 0x00000134 0x000bc 0x000bc R 0x4
GNU_EH_FRAME 0x02c338 0x0002c338 0x0002c338 0x00cbc 0x00cbc R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x02d76c 0x0002e76c 0x0002e76c 0x01894 0x01894 RW 0x4


Section to Segment mapping:
Segment Sections...
00 
01 .note.android.ident .note.gnu.build-id .dynsym .dynstr .gnu.hash .gnu.version .gnu.version_d .gnu.version_r .rel.dyn .rel.plt .plt .text .gcc_except_table .rodata .eh_frame .eh_frame_hdr 
02 .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss 
03 .dynamic 
04 .note.android.ident .note.gnu.build-id 
05 .eh_frame_hdr 
06 
07 .fini_array .data.rel.ro .dynamic .got .got.plt

我们以上面这个程序头表举例: 这个程序头表中有8个程序头:

  1. LOAD 只有LOAD类型的程序头会申请堆存,对elf文件映射。 注意这个段是读和执行的,后面它映的节包括 .note.android.ident .note.gnu.build-id .dynsym .dynstr .gnu.hash .gnu.version .gnu.version_d .gnu.version_r .rel.dyn .rel.plt .plt .text .gcc_except_table .rodata .eh_frame .eh_frame_hdr 这么多节,都是可读可执行的(包含代码)。注意这个段映射的文件位置从0x00000000 0x2cff4,这部分还包含elf头等信息。

  2. LOAD 这部分为读写段,主要映射的文件节为.fini_array .data.rel.ro .dynamic .got .got.plt .data .bss 。注意这部分使用的内存大小是大于映射的文件大小的,多出来的部分主要用于bss段和其他可读写变量的存储。 另外 .data.rel.ro和.got .got.plt 都是可读写的,用于重定位后存在函数或者变量的内存地址信息。.data.rel.ro.loca或者.got用于存储rel.dyn节重定位后的数据指针。.got.plt用于存储rel.plt重定位后的函数指针地址。

  3. DYNAMIC 该类型的段用于指导链接器如何进行库的加载以及重定位(这个段包含重定位的必要信息(主要是这个节在内存中的位置信息))。

  4. NOTE提示信息,不是必须的

  5. 主要是调试信息

  6. GNU_RELRO 保护区域。

关于DYNAMIC段的具体内容,下面位置都是相对于虚拟地址的偏移。(如果细心可以发现文件映射部分的虚拟地址和文件偏移是相同的)(PIC)。

Dynamic section at offset 0x2ed1c contains 28 entries:
  Tag        Type                         Name/Value
 0x00000003 (PLTGOT)                     0x2fe84         //got.plt表的位置,对rel.plt重定位后在这个表中填写绝对地址。rel.plt表会记录函数指针要装填got.plt的位置。 
 0x00000002 (PLTRELSZ)                   736 (bytes)   //got.plt表的大小
 0x00000017 (JMPREL)                     0x6680          //rel.plt表的位置
 0x00000014 (PLTREL)                     REL             //rel.dyn表的位类型
 0x00000011 (REL)                        0x3e70      //rel.dyn表的位位置
 0x00000012 (RELSZ)                      10256 (bytes)  // rel.dyn大小
 0x00000013 (RELENT)                     8 (bytes)    //rel表项的大小
 0x6ffffffa (RELCOUNT)                   898
 0x00000006 (SYMTAB)                     0x1f0          //符号表偏移
 0x0000000b (SYMENT)                     16 (bytes)     
 0x00000005 (STRTAB)                     0x18b0     //字符串表偏移
 0x0000000a (STRSZ)                      6194 (bytes)  //字符串表大小
 0x6ffffef5 (GNU_HASH)                   0x30e4        // hash表,用于通过名称查找当前库中的符号。
 0x00000001 (NEEDED)                     Shared library: [liblog.so] //依赖的库
 0x00000001 (NEEDED)                     Shared library: [libm.so] //依赖的库
 0x00000001 (NEEDED)                     Shared library: [libdl.so] //依赖的库
 0x00000001 (NEEDED)                     Shared library: [libc.so] //依赖的库
 0x0000000e (SONAME)                     Library soname: [libnative-lib.so] //依赖的库
 0x0000001a (FINI_ARRAY)                 0x2e76c  //初始化数组偏移(这部分存函数指针,所以是只读的)
 0x0000001c (FINI_ARRAYSZ)               8 (bytes) //初始化数组大小
 0x0000001e (FLAGS)                      BIND_NOW //当场绑定,不支持懒加载
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x6ffffff0 (VERSYM)                     0x3b3c //下面都是版本相关信息
 0x6ffffffc (VERDEF)                     0x3e14
 0x6ffffffd (VERDEFNUM)                  1
 0x6ffffffe (VERNEED)                    0x3e30
 0x6fffffff (VERNEEDNUM)                 2
 0x00000000 (NULL)                       0x0

最后简单看下rel.dny和rel.plt以及符号表,大概了解下如何进行重定位。

Relocation section '.rel.dyn' at offset 0x3e70 contains 1282 entries:
Offset Info Type Sym.Value Sym. Name
0002e76c 00000008 R_386_RELATIVE 
........
0002fe4c 00013906 R_386_GLOB_DAT 0002e788 _ZTVSt20bad_array_new_
0002e7c4 00014d01 R_386_32 00009540 _ZNSt13bad_exceptionD0
Relocation section '.rel.plt' at offset 0x6680 contains 92 entries:
Offset Info Type Sym.Value Sym. Name
0002fe90 00000207 R_386_JUMP_SLOT 00000000 __cxa_finalize
......
0002fed4 00000607 R_386_JUMP_SLOT 00000000 free
.....
0002fffc 00002007 R_386_JUMP_SLOT 00000000 dl_iterate_phdr

这里主要包含了重定位的方法,也就是Type一栏, Sym用于描述该变量在符号表中的偏移。offset描述对应的变量信息在got中的位置。注意这是个虚拟地址。

rel.plt

Relocation section '.rel.plt' at offset 0x6680 contains 92 entries:
Offset Info Type Sym.Value Sym. Name
0002fe90 00000207 R_386_JUMP_SLOT 00000000 __cxa_finalize
......
0002fed4 00000607 R_386_JUMP_SLOT 00000000 free
.....
0002fffc 00002007 R_386_JUMP_SLOT 00000000 dl_iterate_phdr

0002fe90为对应got.plt位置, type也是描述解析方法。sym用于描述该变量在符号表中的偏移。

所以重定向的过程就是先根据sym找到对应的符号表,通过字符串表找到对应的函数或者变量名称以及版本信息。然后去自身和依赖库中根据名称和版本信息查询匹配的函数或者变量。 最后根据解析方法合成相应的内存地址,写到offset所在的内存处。