省流:android 不兼容 glibc,而是写了一套独立的 c 运行时库 (bionic libc),为移动设备和 google 自己推的东西做了大量优化。在这套工具链里,aosp 实现了一个兼容 bionic libc 的链接器,放到系统中代替 ld。
这个链接器叫 linker
,二进制放在 /bin
和 /system/bin
目录下。
好像有点跑题了,我都忘了在哪看到的
linker
才写到这里的。可能是 frida 用了 Interceptor hook 的 linker
扯回来。在 Java 的应用层代码里写 System.loadLibrary
后,会经历如下调用栈
cpp
// java
System.loadLibrary
Runtime.loadLibrary0
// jni
// c
Runtime_nativeLoad
JVM_NativeLoad
// cc
art::JavaVMExt::LoadNativeLibrary // 检查是否是要 load 已有 library,不是的话调用 OpenNativeLibrary(dlopen) 获取 handle、创建 library 对象、查找 JNI_Onload 的 sym,没有的话直接 return true.
android::OpenNativeLibrary
OpenSystemLibrary ...(android_dlopen_ext/dlopen)
其中,从 nativeLoad
开始,执行流通过 jni 跳转到了 c 函数 Runtime_nativeLoad
。可以查看 Runtime.c - Android Code Search ,往里跟踪的其他 c 文件
看样子最后还是用的 dlopen
详解每一轮调用:
Android动态库的加载原理,真的理解了吗?一文带你游览动态库的加载流程,从 Java 到 C++,一步一步探索知识的 - 掘金
上面这篇把无关代码删了,很方便阅读。但是在 /bionic/linker/linker.cpp
的 find_libraries
函数删的太多了,有点难以理解中间变量的意义
cpp
// 该函数用于查找并加载多个共享库,处理库之间的依赖关系
// add_as_children - 是否将一级加载的库(library_names[]中的库,不包括它们的传递依赖)
// 作为start_with库的子节点。当为dlopen()调用时设为false,
// 此时新加载的库必须形成独立的树结构
bool find_libraries(android_namespace_t* ns, // 使用的命名空间
soinfo* start_with, // 起始库(父节点)
const char* const library_names[], // 要加载的库名数组
size_t library_names_count, // 库名数组长度
soinfo* soinfos[], // 输出加载的soinfo指针数组
std::vector<soinfo*>* ld_preloads, // 预加载库列表
size_t ld_preloads_count, // 预加载库数量
int rtld_flags, // 加载标志(如RTLD_LAZY等)
const android_dlextinfo* extinfo, // 扩展加载信息
bool add_as_children, // 是否作为子节点添加
std::vector<android_namespace_t*>* namespaces) { // 相关命名空间列表
// 步骤0:准备工作
std::unordered_map<const soinfo*, ElfReader> readers_map; // 用于缓存ELF读取器
LoadTaskList load_tasks; // 加载任务列表
// 为每个库名创建初始加载任务
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, ns, &readers_map));
}
// 如果soinfos数组为空,在栈上分配临时空间
// 需要这个数组来处理加载部分失败的情况(例如加载多个库时部分成功)
if (soinfos == nullptr) {
size_t soinfos_size = sizeof(soinfo*)*library_names_count;
soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size)); // 栈上分配临时内存
memset(soinfos, 0, soinfos_size); // 初始化为空指针
}
size_t soinfos_count = 0; // 已加载的库数量
// 使用范围守卫确保加载任务会被清理
auto scope_guard = android::base::make_scope_guard([&]() {
for (LoadTask* t : load_tasks) {
LoadTask::deleter(t); // 退出时清理加载任务
}
});
ZipArchiveCache zip_archive_cache; // 用于APK内文件的缓存
soinfo_list_t new_global_group_members; // 新的全局组成员
// 步骤1:扩展加载任务列表,包含所有DT_NEEDED依赖(暂时不实际加载)
for (size_t i = 0; i<load_tasks.size(); ++i) {
LoadTask* task = load_tasks[i];
soinfo* needed_by = task->get_needed_by(); // 获取依赖该库的父库
// 判断是否是DT_NEEDED依赖(非直接加载的依赖)
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); // 标记是否为DT_NEEDED依赖
// 从任务指定的命名空间开始查找(可能不同于当前命名空间)
android_namespace_t* start_ns = const_cast<android_namespace_t*>(task->get_start_from());
// 实际查找库的实现
if (!find_library_internal(start_ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) {
return false; // 查找失败直接返回
}
soinfo* si = task->get_soinfo(); // 获取找到的soinfo对象
// 如果是DT_NEEDED依赖,添加到父库的子节点
if (is_dt_needed) {
needed_by->add_child(si);
}
// 处理预加载库(LD_PRELOAD)
bool is_ld_preload = false;
if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
ld_preloads->push_back(si);
is_ld_preload = true;
}
// 填充输出数组
if (soinfos_count < library_names_count) {
soinfos[soinfos_count++] = si;
}
// 处理全局符号可见性(DF_1_GLOBAL标志)
if (is_ld_preload || (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
if (!si->is_linked() && namespaces != nullptr && !new_global_group_members.contains(si)) {
new_global_group_members.push_back(si);
// 将新全局成员添加到所有初始命名空间
for (auto linked_ns : *namespaces) {
if (si->get_primary_namespace() != linked_ns) {
linked_ns->add_soinfo(si); // 添加到命名空间
si->add_secondary_namespace(linked_ns); // 添加二级命名空间
}
}
}
}
}
// 步骤2:随机顺序加载库(防止地址预测攻击)
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);
}
}
// 处理地址保留标志
bool reserved_address_recursive = false;
if (extinfo) {
reserved_address_recursive = extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE;
}
if (!reserved_address_recursive) {
shuffle(&load_list); // 随机打乱加载顺序
}
// 设置地址空间参数
address_space_params extinfo_params, default_params;
size_t relro_fd_offset = 0;
if (extinfo) {
// 处理保留地址相关的标志
if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
extinfo_params.start_addr = extinfo->reserved_addr;
extinfo_params.reserved_size = extinfo->reserved_size;
extinfo_params.must_use_address = true;
} else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) {
extinfo_params.start_addr = extinfo->reserved_addr;
extinfo_params.reserved_size = extinfo->reserved_size;
}
}
// 实际加载库到内存
for (auto&& task : load_list) {
address_space_params* address_space =
(reserved_address_recursive || !task->is_dt_needed()) ? &extinfo_params : &default_params;
if (!task->load(address_space)) {
return false;
}
}
// 步骤3:预链接所有依赖库(广度优先顺序)
bool any_memtag_stack = false;
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
if (!si->is_linked() && !si->prelink_image()) { // 执行预链接
return false;
}
// 检查内存标签(MTE)相关设置
if (si->has_min_version(7) && si->memtag_stack()) {
any_memtag_stack = true;
}
register_soinfo_tls(si); // 注册TLS信息
}
// 处理内存标签回调
if (any_memtag_stack) {
if (auto* cb = __libc_shared_globals()->memtag_stack_dlopen_callback) {
cb();
} else {
__libc_shared_globals()->initial_memtag_stack = true;
}
}
// 步骤4:强制设置LD_PRELOAD库的DF_1_GLOBAL标志
if (ld_preloads != nullptr) {
for (auto&& si : *ld_preloads) {
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
}
}
// 步骤5:收集本地组的根节点(处理跨命名空间的情况)
std::vector<soinfo*> local_group_roots;
if (start_with != nullptr && add_as_children) {
local_group_roots.push_back(start_with); // 起始库作为根
} else {
CHECK(soinfos_count == 1);
local_group_roots.push_back(soinfos[0]); // 第一个加载的库作为根
}
// 遍历加载任务查找跨命名空间的边界
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
soinfo* needed_by = task->get_needed_by();
bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
android_namespace_t* needed_by_ns = is_dt_needed ? needed_by->get_primary_namespace() : ns;
// 如果库未链接且跨命名空间
if (!si->is_linked() && si->get_primary_namespace() != needed_by_ns) {
auto it = std::find(local_group_roots.begin(), local_group_roots.end(), si);
if (it == local_group_roots.end()) {
local_group_roots.push_back(si); // 添加为新的本地组根
}
}
}
// 步骤6:链接所有本地组
for (auto root : local_group_roots) {
soinfo_list_t local_group;
android_namespace_t* local_group_ns = root->get_primary_namespace();
// 遍历依赖树收集本地组成员
walk_dependencies_tree(root,
[&] (soinfo* si) {
if (local_group_ns->is_accessible(si)) { // 检查访问权限
local_group.push_back(si);
return kWalkContinue;
} else {
return kWalkSkip;
}
});
// 准备符号查找列表(全局组 + 本地组)
soinfo_list_t global_group = local_group_ns->get_global_group();
SymbolLookupList lookup_list(global_group, local_group);
soinfo* local_group_root = local_group.front(); // 本地组根节点
// 遍历并链接本地组成员
bool linked = local_group.visit([&](soinfo* si) {
if (!si->is_linked() && si->get_primary_namespace() == local_group_ns) {
// 处理扩展信息
const android_dlextinfo* link_extinfo = nullptr;
if (si == soinfos[0] || reserved_address_recursive) {
link_extinfo = extinfo;
}
// 调用加载hook(如果有)
if (__libc_shared_globals()->load_hook) {
__libc_shared_globals()->load_hook(si->load_bias, si->phdr, si->phnum);
}
// 处理符号查找
lookup_list.set_dt_symbolic_lib(si->has_DT_SYMBOLIC ? si : nullptr);
if (!si->link_image(lookup_list, local_group_root, link_extinfo, &relro_fd_offset) ||
!get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
return false;
}
}
return true;
});
if (!linked) {
return false;
}
}
// 步骤7:标记所有加载任务为已链接,并更新引用计数
if (start_with != nullptr && add_as_children) {
start_with->set_linked();
}
// 标记所有库为已链接状态
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
si->set_linked();
}
// 更新跨组的引用计数
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
soinfo* needed_by = task->get_needed_by();
if (needed_by != nullptr &&
needed_by != start_with &&
needed_by->get_local_group_root() != si->get_local_group_root()) {
si->increment_ref_count(); // 增加引用计数
}
}
return true; // 全部加载成功
}