GOT-PLT与linker源码分析

前言

分析linker相关,系统为android8.0

got-plt

参考:https://www.jianshu.com/p/e2a529e72d84
https://blog.csdn.net/L173864930/article/details/40507359

dynamic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Dynamic section at offset 0xdd8 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libm.so] ---> 依赖的库
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x000000000000000e (SONAME) Library soname: [libtest_dynamiclink.so]
0x0000000000000019 (INIT_ARRAY) 0x10dc0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x10dc8
0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
0x0000000000000004 (HASH) 0x228 ---> hash表的文件偏移
0x0000000000000005 (STRTAB) 0x3e0 ---> 动态字符串表的文件偏移
0x0000000000000006 (SYMTAB) 0x278 ---> 动态符号表的文件偏移
0x000000000000000a (STRSZ) 161 (bytes) ---> 动态字符串标的大小
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x10fc8 ---> GOT表的虚拟地址
0x0000000000000002 (PLTRELSZ) 72 (bytes) ---> 使用PLT重定位的函数表(.rela.plt表)的大小
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x4d8 ---> 使用PLT重定位的函数表地址(.rela.plt表文件偏移)
0x0000000000000007 (RELA) 0x4c0 ---> 需要重定位的数据表的地址(.rela.dyn表文件偏移)
0x0000000000000008 (RELASZ) 24 (bytes) ---> 重定位的数据表大小
0x0000000000000009 (RELAENT) 24 (bytes) ---> 重定位的数据表项大小
0x0000000000000018 (BIND_NOW) ---> 执行前重定位,不采用延迟绑定
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffffe (VERNEED) 0x4a0
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x482
0x000000006ffffff9 (RELACOUNT) 1
0x0000000000000000 (NULL) 0x0

DT_HASH -> .hash
DT_SYMTAB & DT_SYMENT -> .dynsym
DT_STRTAB & DT_STRSZ -> .dynstr
PLTREL(决定REL还是RELA) &(DT_REL | DT_RELA) & (DT_RELSZ | DT_RELASZ ) & (DT_RELENT | DT_RELAENT ) -> .rel.dyn
DT_JMPREL & DT_PLTRELSZ & (DT_RELENT | DT_RELAENT) -> .rel.plt
FINI_ARRAY & FINI_ARRAYSZ -> .fini_array
INIT_ARRAY & INIT_ARRAYSZ -> .init_array

意义

64位下GOT的表中每项的大小是8字节,正存放一个一个地址,其作用是提出跨模块的数据访问与函数调用地址放于数据段,数据段每个进程都有一份,从而使代码地址无关,可以进程共享。
plt是为了延迟绑定,防止linker工作过多耗费在修改got上。在代码与got间再加一层跳转,代码先跳到plt,再通过plt引用got寄存器跳转。plt存放于代码段,其与地址无关
实现简单来说就是连接器初始化时先把plt中jmp的下一条指令地址放于got中,第一次执行时代码跳回plt,plt统一跳到开头共同的处理代码,其获取got前三项的内容跳转(本ELF动态段的装载地址、本ELF的link-map数据结构描述符地址、dl-runtime-resolve函数的地址)
之后修改got为目标函数。第二次使用时经过plt引用got直接跳转到目标。

android下

android下.rel.plt是重定位got的,Android函数重定位并未采用延迟绑定,ida会解析got把got上存上自己定的extern段上的东西。plt上也没有书中精巧的指令,直接就取了got的内容之后寄存器跳转。其got前3个8字节也不用
.rela.dyn是需要重定位的数据的引用。重定位got的
.rela.plt存放需要重定位的函数引用。重定位got.plt的

R_AARCH64_JUMP_SLOT: 重定位类型,通过PLT找到目标符号的地址

以上主要决定导入问题,导出由动态连接里的SYMTAB决定

linker源码分析

着重寻找so的加载,解析过程
参考:https://zhuanlan.zhihu.com/p/31632620

linker自举

之前分析了execve在内核中装载elf文件与linker,之后由linker负责加载依赖的动态库。从这里开始进入用户态,是运行库负责的工作。

start

linker入口代码:
bionic\linker\arch\arm64\begin.S

1
2
3
4
5
6
7
8
9
#include <private/bionic_asm.h>
ENTRY(_start)
mov x0, sp
bl __linker_init
/* linker init returns the _entry address in the main image */
br x0
END(_start)

在start函数中,栈顶指针寄存器被赋值给r0寄存器作为参数调用init。init做完linker的初始化和依赖库的加载后,通过r0返回了可执行文件入口函数,接下来的bx r0 指令就会将控制权移交给可执行文件。
初始的栈顶指向命令行参数、环境变量、ELF辅助向量。这些再execve时就初始好了,基本结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
position content size (bytes) comment
------------------------------------------------------------------------
stack pointer -> [ argc = number of args ] 4
[ argv[0] (pointer) ] 4
[ argv[1] (pointer) ] 4
[ argv[..] (pointer) ] 4 * n
[ argv[n - 1] (pointer) ] 4
[ argv[n] (pointer) ] 4 = NULL
[ envp[0] (pointer) ] 4
[ envp[1] (pointer) ] 4
[ envp[..] (pointer) ] 4
[ envp[term] (pointer) ] 4 = NULL
[ auxv[0] (Elf32_auxv_t) ] 8
[ auxv[1] (Elf32_auxv_t) ] 8
[ auxv[..] (Elf32_auxv_t) ] 8
[ auxv[term] (Elf32_auxv_t) ] 8 = AT_NULL vector
[ padding ] 0 - 16
[ argument ASCIIZ strings ] >= 0
[ environment ASCIIZ str. ] >= 0
(0xbffffffc) [ end marker ] 4 = NULL 结束
(0xc0000000) < bottom of stack > 0 (virtual)

init

bionic\linker\linker_main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* This is the entry point for the linker, called from begin.S. This
* method is responsible for fixing the linker's own relocations, and
* then calling __linker_init_post_relocation().
*
* Because this method is called before the linker has fixed it's own
* relocations, any attempt to reference an extern variable, extern
* function, or other GOT reference will generate a segfault.
*/
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
KernelArgumentBlock args(raw_args);//封装参数
// AT_BASE is set to 0 in the case when linker is run by iself
// so in order to link the linker it needs to calcuate AT_BASE
// using information at hand. The trick below takes advantage
// of the fact that the value of linktime_addr before relocations
// are run is an offset and this can be used to calculate AT_BASE.
//linker镜像在内存中的地址
static uintptr_t linktime_addr = reinterpret_cast<uintptr_t>(&linktime_addr);
ElfW(Addr) linker_addr = reinterpret_cast<uintptr_t>(&linktime_addr) - linktime_addr;
//获得可执行文件的入口点,根据加载基址地址获得elf头与段头
ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
//soinfo对象,在linker里面是代表动态库的一个类,每个加载到内存的动态库都会有一个soinfo对象表示,
//同一个动态库文件dlopen两次能创建两个soinfo对象,并且动态库文件被影射到不同的内存逻辑地址上;
soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);
linker_so.base = linker_addr;
//用于计算program headers中所有PT_LOAD节的长度之和,dlopen加载ELF格式的动态库时,除了映射相应的头部数据,会将program headers中所有PT_LOAD分别map到内存
linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
//获取偏移,在execve时,偏移是每个段装载到va中的实际地址与文件中指定的va的差,load_addr(基址)也加了该偏移
//在这里是第一个段的实际装载地址-段中va....结果与之前同
linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
linker_so.dynamic = nullptr;
linker_so.phdr = phdr;
linker_so.phnum = elf_hdr->e_phnum;
//标记自己为linker
linker_so.set_linker_flag();
// Prelink the linker so we can access linker globals.
//解析.dynamic,解析出符号表、字符串表、got、plt、hash表等等数据结构的内存位置、大小和一些相关参数。
if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
// This might not be obvious... The reasons why we pass g_empty_list
// in place of local_group here are (1) we do not really need it, because
// linker is built with DT_SYMBOLIC and therefore relocates its symbols against
// itself without having to look into local_group and (2) allocators
// are not yet initialized, and therefore we cannot use linked_list.push_*
// functions at this point.
//重定位linker
if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
#if defined(__i386__)
// On x86, we can't make system calls before this point.
// We can't move this up because this needs to assign to a global.
// Note that until we call __libc_init_main_thread below we have
// no TLS, so you shouldn't make a system call that can fail, because
// it will SEGV when it tries to set errno.
__libc_init_sysinfo(args);
#endif
// Initialize the main thread (including TLS, so system calls really work).
//初始化主线程
__libc_init_main_thread(args);
// We didn't protect the linker's RELRO pages in link_image because we
// couldn't make system calls on x86 at that point, but we can now...
//将PT_GNU_RELRO段指向的内存地址通过mprotoct函数设置为PROT_READ
if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
// Initialize the linker's static libc's globals
__libc_init_globals(args);
// store argc/argv/envp to use them for calling constructors
g_argc = args.argc;
g_argv = args.argv;
g_envp = args.envp;
// Initialize the linker's own global variables
//递归了get_childred()返回列表中所有的soinfo,然后执行对应的init_func_函数和init_array_列表中的函数
//__attribute__ ((constructor))前缀可将函数自定义到INIT_ARRAY
linker_so.call_constructors();
// If the linker is not acting as PT_INTERP entry_point is equal to
// _start. Which means that the linker is running as an executable and
// already linked by PT_INTERP.
//
// This happens when user tries to run 'adb shell /system/bin/linker'
// see also https://code.google.com/p/android/issues/detail?id=63174
//检查新进程加载的可执行文件是不是linker
if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
__libc_format_fd(STDOUT_FILENO,
"This is %s, the helper program for dynamic executables.\n",
args.argv[0]);
exit(0);
}
// Initialize static variables. Note that in order to
// get correct libdl_info we need to call constructors
// before get_libdl_info().
//get_libdl_info()相当于在堆上拷贝linker_so构造了一个新的soinfo,然后添加到solist和sonext链表中
sonext = solist = get_libdl_info(kLinkerPath);
g_default_namespace.add_soinfo(solist);
// We have successfully fixed our own relocations. It's safe to run
// the main part of the linker now.
args.abort_message_ptr = &g_abort_message;
//创建可执行文件对应的soinfo、调用find_librarys加载依赖库等,完成可执行文件运行所需的一系列准备工作
ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr);
INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
// Return the address that the calling assembly stub should jump to.
return start_address;
}

linker-init-post-relocation

dlopen

流程图

图库挂了….寻找稳定图库中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Runntime_nativeLoad
LoadNativeLibrary
dlopen
do_dlopen
find_library
find_librarys
find_library_internal
load_library()-载入内存
fd=open_library
elf_reader.Load()
soinfo_alloc()
si->LinkImage()-链接
Relocate()
soinfo_do_lookup()
resolve_symbol_address()
CallConstructors
init
init_arry
dlsym(JNI_OnLoad)
JNI_OnLoad

源码分析

入口部分

bionic\libdl\libdl.c

1
2
3
4
5
6
7
// Proxy calls to bionic loader
//路径、flag
void* dlopen(const char* filename, int flag) {
//内建函数 __builtin_return_address 返回当前函数或其调用者的返回地址
const void* caller_addr = __builtin_return_address(0);
return __loader_dlopen(filename, flag, caller_addr);
}

导出并代理了dlopen

bionic\linker\dlfcn.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#if defined(__LP64__)
#define ELFW(what) ELF64_ ## what
#else
#define ELFW(what) ELF32_ ## what
#endif
#define ELF64_SYM_INITIALIZER(name_offset, value, shndx) \
{ name_offset, \
((shndx) == 0) ? 0 : (STB_GLOBAL << 4), \
/* st_other */ 0, \
shndx, \
reinterpret_cast<Elf64_Addr>(value), \
/* st_size */ 0, \
}
//导出的定义
ELFW(SYM_INITIALIZER)( 0, &__dlopen, 1),
ELFW(SYM_INITIALIZER)( 16, &__dlclose, 1),
ELFW(SYM_INITIALIZER)( 33, &__dlsym, 1),
ELFW(SYM_INITIALIZER)( 48, &__dlerror, 1),
ELFW(SYM_INITIALIZER)( 65, &__dladdr, 1),
导出名在ANDROID_LIBDL_STRTAB中,name_offset是其偏移。0号为__loader_dlopen。
//实现的部分
void* __dlopen(const char* filename, int flags, const void* caller_addr) {
return dlopen_ext(filename, flags, nullptr, caller_addr);
}
//文件路径、flag、null、dlopen返回地址
static void* dlopen_ext(const char* filename,
int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
g_linker_logger.ResetState();
//调用do_dlopen 参数:
void* result = do_dlopen(filename, flags, extinfo, caller_addr);
if (result == nullptr) {
__bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
return nullptr;
}
return result;
}

bionic\linker\linker.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//文件路径、flag、null、dlopen返回地址
void* do_dlopen(const char* name, int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {
std::string trace_prefix = std::string("dlopen: ") + (name == nullptr ? "(nullptr)" : name);
ScopedTrace trace(trace_prefix.c_str());
ScopedTrace loading_trace((trace_prefix + " - loading and linking").c_str());
soinfo* const caller = find_containing_library(caller_addr);
android_namespace_t* ns = get_caller_namespace(caller);
LD_LOG(kLogDlopen,
"dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p) ...",
name,
flags,
android_dlextinfo_to_string(extinfo).c_str(),
caller == nullptr ? "(null)" : caller->get_realpath(),
ns == nullptr ? "(null)" : ns->get_name(),
ns);
auto failure_guard = make_scope_guard([&]() {
LD_LOG(kLogDlopen, "... dlopen failed: %s", linker_get_error_buffer());
});
if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) {
DL_ERR("invalid flags to dlopen: %x", flags);
return nullptr;
}
if (extinfo != nullptr) {
if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags);
return nullptr;
}
if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 &&
(extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without "
"ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags);
return nullptr;
}
if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0 &&
(extinfo->flags & (ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT)) != 0) {
DL_ERR("invalid extended flag combination: ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS is not "
"compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT");
return nullptr;
}
if ((extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0) {
if (extinfo->library_namespace == nullptr) {
DL_ERR("ANDROID_DLEXT_USE_NAMESPACE is set but extinfo->library_namespace is null");
return nullptr;
}
ns = extinfo->library_namespace;
}
}
std::string asan_name_holder;
const char* translated_name = name;
if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
char translated_path[PATH_MAX];
if (realpath(translated_name, translated_path) != nullptr) {
asan_name_holder = std::string(kAsanLibDirPrefix) + translated_path;
if (file_exists(asan_name_holder.c_str())) {
translated_name = asan_name_holder.c_str();
PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
}
}
}
ProtectedDataGuard guard;
// ns、库名、标志、null、dlopen的caller
//得到info对象
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
loading_trace.End();
if (si != nullptr) {
void* handle = si->to_handle();
LD_LOG(kLogDlopen,
"... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
si->get_realpath(), si->get_soname(), handle);
//初始化,在这里执行init_func 、 init_array
si->call_constructors();
failure_guard.disable();
LD_LOG(kLogDlopen,
"... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
si->get_realpath(), si->get_soname(), handle);
return handle;
}
return nullptr;
}
// ns、库名、标志、null、dlopen的caller
static soinfo* find_library(android_namespace_t* ns,
const char* name, int rtld_flags,
const android_dlextinfo* extinfo,
soinfo* needed_by) {
soinfo* si;
if (name == nullptr) {
si = solist_get_somain();
} else if (!find_libraries(ns,
needed_by,
&name,
1,
&si,
nullptr,
0,
rtld_flags,
extinfo,
false /* add_as_children */,
true /* search_linked_namespaces */)) {
return nullptr;
}
si->increment_ref_count();
return si;
}

调用find_libraries
参数:ns、dlopen的caller、库名、1、返回的info、null、0、flag、null、false、true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// add_as_children - add first-level loaded libraries (i.e. library_names[], but
// not their transitive dependencies) as children of the start_with library.
// This is false when find_libraries is called for dlopen(), when newly loaded
// libraries must form a disjoint tree.
//ns、dlopen的caller、库名、1、返回的info、null、0、flag、null、false、true
bool find_libraries(android_namespace_t* ns,
soinfo* start_with,
const char* const library_names[],
size_t library_names_count,
soinfo* soinfos[],
std::vector<soinfo*>* ld_preloads,
size_t ld_preloads_count,
int rtld_flags,
const android_dlextinfo* extinfo,
bool add_as_children,
bool search_linked_namespaces) {
// Step 0: prepare.
//第一步,准备
LoadTaskList load_tasks;
std::unordered_map<const soinfo*, ElfReader> readers_map;
for (size_t i = 0; i < library_names_count; ++i) {
const char* name = library_names[i];
load_tasks.push_back(LoadTask::create(name, start_with, &readers_map));
}
// Construct global_group.
soinfo_list_t global_group = make_global_group(ns);
// soinfos数组为null则堆栈上分配一个
// 这个数组在发生错误时需要。例如
// when library_names[] = {libone.so, libtwo.so} and libone.so
// is loaded correctly but libtwo.so failed for some reason. 第一个加载成功但第二个失败
// 返回时要卸载 libone.so
// See also implementation of failure_guard below.
if (soinfos == nullptr) {
size_t soinfos_size = sizeof(soinfo*)*library_names_count;
soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size));
memset(soinfos, 0, soinfos_size);
}
// 需要链接的库- see step 2.
size_t soinfos_count = 0;
auto scope_guard = make_scope_guard([&]() {
for (LoadTask* t : load_tasks) {
LoadTask::deleter(t);
}
});
auto failure_guard = make_scope_guard([&]() {
// Housekeeping
soinfo_unload(soinfos, soinfos_count);
});
ZipArchiveCache zip_archive_cache;
// Step 1: expand the list of load_tasks to include
// all DT_NEEDED libraries (do not load them just yet)
//扩大load_tasks包含全部需要的库
//广度优先
for (size_t i = 0; i<load_tasks.size(); ++i) {
LoadTask* task = load_tasks[i];
soinfo* needed_by = task->get_needed_by();
bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
task->set_extinfo(is_dt_needed ? nullptr : extinfo);
task->set_dt_needed(is_dt_needed);
if (!find_library_internal(ns,
task,
&zip_archive_cache,
&load_tasks,
rtld_flags,
search_linked_namespaces || is_dt_needed)) {
return false;
}
soinfo* si = task->get_soinfo();
if (is_dt_needed) {
needed_by->add_child(si);
if (si->is_linked()) {
si->increment_ref_count();
}
}
// When ld_preloads is not null, the first
// ld_preloads_count libs are in fact ld_preloads.
if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
ld_preloads->push_back(si);
}
if (soinfos_count < library_names_count) {
soinfos[soinfos_count++] = si;
}
}
// Step 2: Load libraries in random order (see b/24047022)
//加载库
LoadTaskList load_list; //要加载的列表
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
auto pred = [&](const LoadTask* t) {
return t->get_soinfo() == si;
};
if (!si->is_linked() &&
std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) {
load_list.push_back(task);
}
}
shuffle(&load_list);
for (auto&& task : load_list) {
if (!task->load()) {
return false;
}
}
// Step 3: pre-link all DT_NEEDED libraries in breadth first order.
//以广度优先连接加载的库
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
if (!si->is_linked() && !si->prelink_image()) {
return false;
}
}
// Step 4: Add LD_PRELOADed libraries to the global group for
// future runs. There is no need to explicitly add them to
// the global group for this run because they are going to
// appear in the local group in the correct order.
if (ld_preloads != nullptr) {
for (auto&& si : *ld_preloads) {
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
}
}
// Step 5: link libraries.
//链接加载的库
soinfo_list_t local_group;
walk_dependencies_tree(
(start_with != nullptr && add_as_children) ? &start_with : soinfos,
(start_with != nullptr && add_as_children) ? 1 : soinfos_count,
[&] (soinfo* si) {
if (ns->is_accessible(si)) {
local_group.push_back(si);
return kWalkContinue;
} else {
return kWalkSkip;
}
});
bool linked = local_group.visit([&](soinfo* si) {
if (!si->is_linked()) {
if (!si->link_image(global_group, local_group, extinfo) ||
!get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
return false;
}
}
return true;
});
if (linked) {
local_group.for_each([](soinfo* si) {
if (!si->is_linked()) {
si->set_linked();
}
});
failure_guard.disable();
}
return linked;
}

主要为三个部分:
通过load_tasks栈广度优先搜索依赖库。
调用find_library_internal依次载入库。
link_image依次连接,链接与加载的顺序相反。

加载库

find_library_internal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
static bool find_library_internal(android_namespace_t* ns,
LoadTask* task,
ZipArchiveCache* zip_archive_cache,
LoadTaskList* load_tasks,
int rtld_flags,
bool search_linked_namespaces) {
soinfo* candidate;
//判断是否已经加载
if (find_loaded_library_by_soname(ns, task->get_name(), search_linked_namespaces, &candidate)) {
task->set_soinfo(candidate);
return true;
}
// Library might still be loaded, the accurate detection
// of this fact is done by load_library.
TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]",
task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
//进行加载
if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags, search_linked_namespaces)) {
return true;
}
if (search_linked_namespaces) {
// if a library was not found - look into linked namespaces
for (auto& linked_namespace : ns->linked_namespaces()) {
if (find_library_in_linked_namespace(linked_namespace,
task,
rtld_flags)) {
return true;
}
}
}
return false;
}
static bool load_library(android_namespace_t* ns,
LoadTask* task,
ZipArchiveCache* zip_archive_cache,
LoadTaskList* load_tasks,
int rtld_flags,
bool search_linked_namespaces) {
const char* name = task->get_name();
soinfo* needed_by = task->get_needed_by();
const android_dlextinfo* extinfo = task->get_extinfo();
off64_t file_offset;
std::string realpath;
if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
file_offset = 0;
if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
file_offset = extinfo->library_fd_offset;
}
if (!realpath_fd(extinfo->library_fd, &realpath)) {
PRINT("warning: unable to get realpath for the library \"%s\" by extinfo->library_fd. "
"Will use given name.", name);
realpath = name;
}
task->set_fd(extinfo->library_fd, false);
task->set_file_offset(file_offset);
return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}
// Open the file.
int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
if (fd == -1) {
DL_ERR("library \"%s\" not found", name);
return false;
}
task->set_fd(fd, true);
task->set_file_offset(file_offset);
return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}

load_library

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
static bool load_library(android_namespace_t* ns,
LoadTask* task,
LoadTaskList* load_tasks,
int rtld_flags,
const std::string& realpath,
bool search_linked_namespaces) {
off64_t file_offset = task->get_file_offset();
const char* name = task->get_name();
const android_dlextinfo* extinfo = task->get_extinfo();
...检查
struct stat file_stat;
...检查
...省略
soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags);
if (si == nullptr) {
return false;
}
task->set_soinfo(si);
// Read the ELF header and some of the segments.
if (!task->read(realpath.c_str(), file_stat.st_size)) {
soinfo_free(si);
task->set_soinfo(nullptr);
return false;
}
// find and set DT_RUNPATH and dt_soname
// Note that these field values are temporary and are
// going to be overwritten on soinfo::prelink_image
// with values from PT_LOAD segments.
const ElfReader& elf_reader = task->get_elf_reader();
for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_RUNPATH) {
si->set_dt_runpath(elf_reader.get_string(d->d_un.d_val));
}
if (d->d_tag == DT_SONAME) {
si->set_soname(elf_reader.get_string(d->d_un.d_val));
}
}
for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map()));
});
return true;
}

创建 soinfo 对象,解析elf并将该.so 文件的依赖库添加到待加载的队列中。
加载详见ElfReader
基本过程为:
校验头部。
遍历段头表,找到可加载段,并通过 mmap 将可加载段从文件映射到内存。

  1. ReadElfHeader():从.so 文件中读取 ELF 头;
  2. VerifyElfHeader():校验 ELF 头;
  3. ReadProgramHeader():将.so 文件的 program header table 映射到内存;
  4. ReserveAddressSpace():开辟匿名内存空间;
  5. LoadSegments():将可加载段加载到 ReserveAddressSpace 开辟的空间中;
  6. FindPhdr():校验 program header table 是否在内存中。

之后解析dynamic获取DT_HASH、DT_STRTAB、 DT_STRSZ、DT_SYMTAB、DT_NEEDED,用于链接。

链接库

link_image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
const android_dlextinfo* extinfo) {
local_group_root_ = local_group.front();
if (local_group_root_ == nullptr) {
local_group_root_ = this;
}
if ((flags_ & FLAG_LINKER) == 0 && local_group_root_ == this) {
target_sdk_version_ = get_application_target_sdk_version();
}
VersionTracker version_tracker;
if (!version_tracker.init(this)) {
return false;
}
#if !defined(__LP64__)
if (has_text_relocations) {
// Fail if app is targeting M or above.
if (get_application_target_sdk_version() >= __ANDROID_API_M__) {
DL_ERR_AND_LOG("\"%s\" has text relocations", get_realpath());
return false;
}
// Make segments writable to allow text relocations to work properly. We will later call
// phdr_table_protect_segments() after all of them are applied.
DL_WARN("\"%s\" has text relocations. This is wasting memory and prevents "
"security hardening. Please fix.", get_realpath());
add_dlwarning(get_realpath(), "text relocations");
if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
DL_ERR("can't unprotect loadable segments for \"%s\": %s",
get_realpath(), strerror(errno));
return false;
}
}
#endif
if (android_relocs_ != nullptr) {
// check signature
if (android_relocs_size_ > 3 &&
android_relocs_[0] == 'A' &&
android_relocs_[1] == 'P' &&
android_relocs_[2] == 'S' &&
android_relocs_[3] == '2') {
DEBUG("[ android relocating %s ]", get_realpath());
bool relocated = false;
const uint8_t* packed_relocs = android_relocs_ + 4;
const size_t packed_relocs_size = android_relocs_size_ - 4;
relocated = relocate(
version_tracker,
packed_reloc_iterator<sleb128_decoder>(
sleb128_decoder(packed_relocs, packed_relocs_size)),
global_group, local_group);
if (!relocated) {
return false;
}
} else {
DL_ERR("bad android relocation header.");
return false;
}
}
#if defined(USE_RELA)
if (rela_ != nullptr) {
DEBUG("[ relocating %s ]", get_realpath());
if (!relocate(version_tracker,
plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
return false;
}
}
if (plt_rela_ != nullptr) {
DEBUG("[ relocating %s plt ]", get_realpath());
if (!relocate(version_tracker,
plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
return false;
}
}
#else
if (rel_ != nullptr) {
DEBUG("[ relocating %s ]", get_realpath());
if (!relocate(version_tracker,
plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) {
return false;
}
}
if (plt_rel_ != nullptr) {
DEBUG("[ relocating %s plt ]", get_realpath());
if (!relocate(version_tracker,
plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) {
return false;
}
}
#endif
#if defined(__mips__)
if (!mips_relocate_got(version_tracker, global_group, local_group)) {
return false;
}
#endif
DEBUG("[ finished linking %s ]", get_realpath());
#if !defined(__LP64__)
if (has_text_relocations) {
// All relocations are done, we can protect our segments back to read-only.
if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) {
DL_ERR("can't protect segments for \"%s\": %s",
get_realpath(), strerror(errno));
return false;
}
}
#endif
// We can also turn on GNU RELRO protection if we're not linking the dynamic linker
// itself --- it can't make system calls yet, and will have to call protect_relro later.
if (!is_linker() && !protect_relro()) {
return false;
}
/* Handle serializing/sharing the RELRO segment */
if (extinfo && (extinfo->flags & ANDROID_DLEXT_WRITE_RELRO)) {
if (phdr_table_serialize_gnu_relro(phdr, phnum, load_bias,
extinfo->relro_fd) < 0) {
DL_ERR("failed serializing GNU RELRO section for \"%s\": %s",
get_realpath(), strerror(errno));
return false;
}
} else if (extinfo && (extinfo->flags & ANDROID_DLEXT_USE_RELRO)) {
if (phdr_table_map_gnu_relro(phdr, phnum, load_bias,
extinfo->relro_fd) < 0) {
DL_ERR("failed mapping GNU RELRO section for \"%s\": %s",
get_realpath(), strerror(errno));
return false;
}
}
notify_gdb_of_load(this);
return true;
}

基本流程为:
通过重定位表的r_offset找到需要重定位的地址,通过r_info确定重定位类型与符号表索引。
之后用符号表索引找到符号,根据该符号去已加载的符号表中寻找,找到后即得到该符号地址等信息,根据重定位类型在重定位地点重定位。