Android 的加载/链接器linker 主要用于实现共享库的加载与链接。它支持应用程序对库函数的隐式和显式调用。对于隐式调用,应用程序的编译与静态库大致相同,只是在静态链接的时候通过--dynamic-linker /system/bin/linker 指定动态链接器,(该信息将被存放在ELF文件的.interp节中,内核执行目标映像文件前将通过该信息加载并运行相应的解释器程序linker.)并链接相应的共享库。与ld.so不同的是,Linker目前没有提供Lazy Binding机制,所有外部过程引用都在映像执行之前解析。对于显式调用,可以同过linker中提供的接口dlopen,dlsym,dlerror和dlclose来动态加载和链接共享库。
Android中的共享库和可执行映像都默认采用ELF格式的文件,其基本格式如下:
每个ELF文件的开始部分都包含一个ELF头,其中包含了整个文件的基本信息,包括目标代码的格式,体系结构,各程序头或节头的偏移和大小,组织结构和访问权限等信息。
程序头表包含了加载到内存中的各种段的索引及属性信息,它将告诉加载器如何加载映像。每个段中有包含了一个或几个节区,每个节区应是唯一的。无论是可执行程序还是共享库都包含以下几个的节区:
1. GOT表和PLT表:
不同映像间的函数和数据引用都是通过它们实现的。GOT(全局偏移表)给出了映像中所有被引用符号(函数或变量)的值。每个普通PLT表项相当于一个函数的桩函数(stub),支持懒绑定的情况下,当发生对外部函数的调用时,程序会通过PLT表将控制交给动态连接器,后者解析出函数的绝对地址,修改GOT中相应的值,之后的调用将不再需要连接器的绑定。由于linker是不支持懒绑定的,所以在进程初始化时,动态链接器首先解析出外部过程引用的绝对地址,一次性的修改所有相应的GOT表项。对共享对象来说,由于GOT,PLT节以及代码段和数据段之间的相对位置是固定的,所有引用都是基于一个固定地址(GOT)的偏移量,所以实现了PIC代码,重定位时只需要修改可写段中的GOT表。而可执行程序在连接过程中则可能发生对不可写段的修改。如果只读段和可写段不是以固定的相对位置加载的,那么在重定位是还需要修改所有指向GOT的指针。
2. dynamic节:
与重定位有关的基本目录结构,例如:
Dynamic section at offset 0x61014 contains 20 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000c (INIT) 0xb8a8
0x0000000d (FINI) 0x555c4
0x00000004 (HASH) 0x8128
0x00000005 (STRTAB) 0xa004
0x00000006 (SYMTAB) 0x8aa4
0x0000000a (STRSZ) 2902 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x710dc
0x00000002 (PLTRELSZ) 2464 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0xaf08
0x00000011 (REL) 0xae98
0x00000012 (RELSZ) 112 (bytes)
0x00000013 (RELENT) 8 (bytes)
3. dynsym和dynstr节:
与重定位有关的符号表和字符串表:
Symbol table '.dynsym' contains 69 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
……
6: 00002568 28 FUNC GLOBAL DEFAULT 7 __ashldi3
7: 00000001 58 FUNC GLOBAL DEFAULT UND _ZNK7android7RefBase9decS
8: 00000001 32 FUNC GLOBAL DEFAULT UND ioctl
9: 00000001 18 FUNC GLOBAL DEFAULT UND _ZN7android7String8D1Ev
10: 00000001 16 FUNC GLOBAL DEFAULT UND _ZNK7android8EventHub16ge
11: 00000001 32 FUNC GLOBAL DEFAULT UND strerror
12: 00003024 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end
4. .rel.dyn和.rel.plt节:
.rel.dyn节的表项对应了出外部过程调用的符号以外的所有重定位对象,.rel.plt则对应所有外部过程调用的重定位信息。每个重定位项记录了符号的符号表索引,重定位的操作地址,重定位类型的信息(见3.3节)。重定位所在的节区往往与重定位类型有关,例如:
Relocation section '.rel.plt' at offset 0x2f08 contains 308 entries:
Offset Info Type Sym.Value Sym. Name
000710e8 00000116 R_ARM_JUMP_SLOT 0000b8d0 fileno
000710ec 00000216 R_ARM_JUMP_SLOT 0000b8dc getpagesize
000710f0 00000316 R_ARM_JUMP_SLOT 0000b8e8 fputs
000710f4 00000416 R_ARM_JUMP_SLOT 0000b8f4 abort
000710f8 00000516 R_ARM_JUMP_SLOT 0000b900 __errno_location
Relocation section '.rel.dyn' at offset 0x2e98 contains 14 entries:
Offset Info Type Sym.Value Sym. Name
000715b8 00001e15 R_ARM_GLOB_DAT 00071000 __fini_array_end
000715bc 00002f15 R_ARM_GLOB_DAT 00000000 __gmon_start__
000715c8 0000f515 R_ARM_GLOB_DAT 00071000 __fini_array_start
000715cc 00010015 R_ARM_GLOB_DAT 00071000 __init_array_end
000715d0 00012e15 R_ARM_GLOB_DAT 00071000 __init_array_start
00072a00 00002714 R_ARM_COPY 00072a00 __timezone
00072a04 00005514 R_ARM_COPY 00072a04 __daylight
R_ARM_JUMP_SLOT和R_ARM_GLOB_DAT属性的重定位地址一般位于GOT表,R_ARM_COPY和R_ARM_ABS32属性的重定位一般位于.data节或.text节中。
Linker的加载与启动
Linker是共享库的加载/链接器,也可以称为解释器(interpreter)