android 动态库加载机制

省流: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.cppfind_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; // 全部加载成功
}
相关推荐
小墙程序员1 小时前
一文了解 Android 中的 UID、GID、PID
android
&有梦想的咸鱼&3 小时前
Android Compose 框架文本选择与编辑模块源码深度剖析(三)
android
二流小码农3 小时前
鸿蒙开发:远场通信服务rcp会话问题
android·ios·harmonyos
stevenzqzq6 小时前
kotlin @JvmStatic的使用
android·开发语言·kotlin
氦客6 小时前
Kotlin知识体系(二) : Kotlin的七个关键特性
android·开发语言·kotlin·安卓·特性·data class·密封类
阿豪元代码6 小时前
Perfetto 快速上手指南1 —— Trace 的抓取
android
&有梦想的咸鱼&6 小时前
Android Fresco 框架扩展模块源码深度剖析(四)
android
fatiaozhang95276 小时前
烽火HG680-KB_海思HI3798MV310_安卓9.0_U盘强刷固件包及注意点说明
android·华为·机顶盒rom·魔百盒刷机·移动魔百盒
YEAH!启动!9 小时前
WPS二次开发系列:WPS SDK事件回调
android·java·前端·pdf·word·wps·ppt
stevenzqzq11 小时前
kotlin 线程池封装
android·开发语言·kotlin