Linker是Android系统中的一个重要组件,它负责将各个模块编译生成的so文件链接成一个整体,以及对so文件进行重定位等操作。Linker的源码位于bionic/linker
目录下,通过对Linker源码的学习、分析,我们也可以实现一个自定义的Linker加载器
Linker的加载过程主要包括以下几个步骤:
本文只讨论在dlopen函数下打开一个So的链接过程,Jni情况下的链接过程不在本文讨论范围内。
接下来使用dlopen函数来打开一个so文件,
我们来看一下dlopen函数的实现
我们可以看到最终调用的是do_dlopen函数,do_dlopen即为链接器的起始入口,我们来看一下do_dlopen函数的实现,代码的位置在bionic/linker/linker.cpp
文件中
由于篇幅的原因,这里只展示了部分核心代码,do_dlopen函数的主要功能是通过find_library函数找到指定的so文件,然后调用so文件中的构造函数(即init_array),最后返回so文件的句柄。
这里有个 android_namespace_t* ns = get_caller_namespace(caller);
这个函数是获取调用者的namespace,namespace是Android 7.0引入的概念,用于解决so文件的命名冲突问题,这里不做详细讨论。
我们来看一下find_library函数的实现
可以看到只是一个中转函数,最终调用的是find_libraries函数,find_libraries就是比较重要的部分了 我们将详细的对其进行分析
我们在for循环下面之后先不进行展开分析,我们先看一下find_library_internal函数的实现
可以看到代码非常清晰,先查找是否已经加载了so文件,如果没有则调用load_library函数进行加载,我们来看一下load_library函数的实现
先是调用open_library函数打开so文件,然后调用load_library函数进行加载,open_library 的实现就不具体展开了,由于在Android系统中有权限等机制限制,并且在Jni下的链接路径限制,看起来挺复杂,但实际就是一个open(),我们看下load_library函数的实现
load_library函数的实现比较复杂,我们只分析核心部分
task->read(realpath.c_str(), file_stat.st_size)
该函数的作用是读取ELF的文件信息,代码的位置在bionic/linker/linker_phdr.cpp
文件中
该函数的作用是读取ELF的文件信息,包括ELF头、程序头、段头、动态段等信息,这些信息是后续链接过程的基础,该部分的实现还是比较简单,主要是对ELF文件格式的解析。
我们需要看一下另外一个实现for_each_dt_needed(task->get_elf_reader(), [&](const char* name) 这个对我们自定义linker来说实现上是有区别的,
在linker源码中,这个函数的作用是获取so文件中的DT_NEEDED字段,然后将DT_NEEDED字段中的so文件加入到加载列表中,由于简化流程考虑,这里我的实现直接用dlopen打开系统库。(不包含链接第三方库so的情况)
接下来让我们回到find_libraries看下find_library_internal之后的处理
可以看到上面的实现和linker源码里是有区别的,这里同样简化了代码,由于我们是直接通过dlopen打开 extinfo_params参数是不需要的,这里有一个核心的实现 if (!task->load(address_space))
继续看一下elf_reader.Load(address_space)的实现
ReserveAddressSpace()函数是动态链接器中用于为将要加载的ELF文件预留足够的虚拟地址空间的过程。 它根据ELF文件中的程序头表信息,计算所有可加载段(loadable segments)所需的地址空间大小,
并尝试在进程的地址空间中预留这块区域。这一步是将ELF文件从磁盘映射到内存中的前置工作。
接下来的是LoadSegments()
该函数的作用是将ELF文件中的可加载段(loadable segments)映射到进程的虚拟地址空间中,
这里的实现可以拓展下,我们可知mmap如果传入文件描述符,那么最终文件的路径会在/proc/self/maps中显示,这里其实可以通过另外的方式实现,从而完成so文件的隐藏
接下来是FindPhdr()函数
这段代码的目的是在动态链接过程中,为已加载的ELF文件定位程序头表(Program Header Table, PHDR)
的内存地址。这一步骤对于后续的库重定位和初始化至关重要。
在ELF文件格式中,程序头表描述了文件的段(比如代码段、数据段等)如何映射到进程的虚拟地址空间中。
不同于phdr_table_,这是一个临时的、在链接器内部使用的拷贝,loaded_phdr_指向的是映射到内存中、即将被实际使用的程序头表的地址。
以上的话linker的加载so在内存里的申请就结束了,接下来就是重定位操作,初始化全局变量,调用构造函数等操作。由于篇幅有限,将在后续继续开始分析
先附上源码链接
ImyLinker
static
void
* dlopen_ext(
const
char
* filename,
int
flags,
const
android_dlextinfo* extinfo,
const
void
* caller_addr) {
void
* result = do_dlopen(filename, flags, extinfo, caller_addr);
return
result;
}
static
void
* dlopen_ext(
const
char
* filename,
int
flags,
const
android_dlextinfo* extinfo,
const
void
* caller_addr) {
void
* result = do_dlopen(filename, flags, extinfo, caller_addr);
return
result;
}
void
* do_dlopen(
const
char
* name,
int
flags,
const
android_dlextinfo* extinfo,
const
void
* caller_addr) {
...
soinfo*
const
caller = find_containing_library(caller_addr);
android_namespace_t* ns = get_caller_namespace(caller);
...
...
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);
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;
}
void
* do_dlopen(
const
char
* name,
int
flags,
const
android_dlextinfo* extinfo,
const
void
* caller_addr) {
...
soinfo*
const
caller = find_containing_library(caller_addr);
android_namespace_t* ns = get_caller_namespace(caller);
...
...
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);
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;
}
static
soinfo* find_library(android_namespace_t* ns,
const
char
* name,
int
rtld_flags,
const
android_dlextinfo* extinfo,
soinfo* needed_by) {
soinfo* si = nullptr;
if
(name == nullptr) {
si = solist_get_somain();
}
else
if
(!find_libraries(ns,
needed_by,
&name,
1,
&si,
nullptr,
0,
rtld_flags,
extinfo,
false
)) {
if
(si != nullptr) {
soinfo_unload(si);
}
return
nullptr;
}
si->increment_ref_count();
return
si;
}
}
static
soinfo* find_library(android_namespace_t* ns,
const
char
* name,
int
rtld_flags,
const
android_dlextinfo* extinfo,
soinfo* needed_by) {
soinfo* si = nullptr;
if
(name == nullptr) {
si = solist_get_somain();
}
else
if
(!find_libraries(ns,
needed_by,
&name,
1,
&si,
nullptr,
0,
rtld_flags,
extinfo,
false
)) {
if
(si != nullptr) {
soinfo_unload(si);
}
return
nullptr;
}
si->increment_ref_count();
return
si;
}
}
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,
std::vector<android_namespace_t *> *namespaces = nullptr) {
...
std::unordered_map<
const
soinfo *, ElfReader> readers_map;
LoadTaskList load_tasks;
for
(
size_t
i = 0; i < library_names_count; ++i) {
const
char
*name = library_names[i];
LOGI(
"load task create %s "
, name);
load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
}
}
...
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);
LOGI(
"find_libraries(ns=%s): task=%s, is_dt_needed=%d"
,
"null"
,
task->get_name(), is_dt_needed);
if
(!find_library_internal(
const_cast
<android_namespace_t *>(task->get_start_from()),
task,
&zip_archive_cache,
&load_tasks,
rtld_flags)) {
return
false
;
}
}
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,
std::vector<android_namespace_t *> *namespaces = nullptr) {
...
std::unordered_map<
const
soinfo *, ElfReader> readers_map;
LoadTaskList load_tasks;
for
(
size_t
i = 0; i < library_names_count; ++i) {
const
char
*name = library_names[i];
LOGI(
"load task create %s "
, name);
load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
}
}
...
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);
LOGI(
"find_libraries(ns=%s): task=%s, is_dt_needed=%d"
,
"null"
,
task->get_name(), is_dt_needed);
if
(!find_library_internal(
const_cast
<android_namespace_t *>(task->get_start_from()),
task,
&zip_archive_cache,
&load_tasks,
rtld_flags)) {
return
false
;
}
}
static
bool
find_library_internal(android_namespace_t *ns,
LoadTask *task,
ZipArchiveCache *zip_archive_cache,
LoadTaskList *load_tasks,
int
rtld_flags) {
soinfo *candidate;
if
(find_loaded_library_by_soname(ns, task->get_name(),
true
,
&candidate)) {
LOGI(
"find_library_internal(ns=%s, task=%s): Already loaded (by soname): %s"
,
ns->get_name(), task->get_name(), candidate->get_realpath());
task->set_soinfo(candidate);
return
true
;
}
if
(load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
true
)) {
return
true
;
}
return
false
;
}
static
bool
find_library_internal(android_namespace_t *ns,
LoadTask *task,
ZipArchiveCache *zip_archive_cache,
LoadTaskList *load_tasks,
int
rtld_flags) {
soinfo *candidate;
if
(find_loaded_library_by_soname(ns, task->get_name(),
true
,
&candidate)) {
LOGI(
"find_library_internal(ns=%s, task=%s): Already loaded (by soname): %s"
,
ns->get_name(), task->get_name(), candidate->get_realpath());
task->set_soinfo(candidate);
return
true
;
}
if
(load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
true
)) {
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();
LOGI(
"load_library(ns=%s, task=%s, flags=0x%x, search_linked_namespaces=%d): calling "
"open_library"
,
ns->get_name(), name, rtld_flags, search_linked_namespaces);
off64_t file_offset;
std::string realpath;
int
fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
if
(fd == -1) {
LOGE(
"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);
}
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();
LOGI(
"load_library(ns=%s, task=%s, flags=0x%x, search_linked_namespaces=%d): calling "
"open_library"
,
ns->get_name(), name, rtld_flags, search_linked_namespaces);
off64_t file_offset;
std::string realpath;
int
fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
if
(fd == -1) {
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2024-4-3 14:07
被IIImmmyyy编辑
,原因: