首页 > 其他分享 >so加载流程分析

so加载流程分析

时间:2024-10-19 23:18:39浏览次数:9  
标签:name 流程 nullptr so flags extinfo dlopen 加载

源码

我的安卓版本:Pie 9.0.0_r

查看android源码的网站

AndroidXRef

AOSPXRef

so的加载有两种方式,一种是在Java层加载,另一种是在Native层加载。

Java层

在Java层加载so的方式,这里有两种方式

在源码的System.java类里

  • System.load(“/data/data/app包名/lib/libnative-lib.so”)

    传入的参数是so的绝对路径。

    load直接根据给定路径加载,无需查找过程。 在这里插入图片描述

  • System.loadLibrary(“native-lib”)

    加载的是libnative-lib.so,只需要传入so的名字。

    loadLibrary会优先在应用本地路径查找,如果找不到,再到系统路径查找。
    在这里插入图片描述

跟踪load()

load()函数里调用了Runtime.getRuntime().load0()

找到load0()

在这里插入图片描述

在load0里调用nativeLoad()方法使用给定的fromClass的类加载器加载类

在这里插入图片描述

跟踪loadLibrary0()

loadLibrary()函数里调用了 Runtime.getRuntime().loadLibrary0()。

首先通过findLibrary()来查找库的完整路径,如果找不到,就到系统中去找,不管哪种方式找到,都会调用nativeLoad()。

在这里插入图片描述

首先利用ClassLoader的findLibrary方法来获取library的path;如果ClassLoader为空,则根据libraryName获取库文件的名字(System.mapLibraryName方法会给libraryName生成全名然后返回,如libxxx.so),然后到LibPaths中通过filename找到对应的file,然后返回对应的路径。

最后通过nativeLoad()来加载。

so的加载路径

如果ClassLoader不为空,执行ClassLoader的findLibrary()方法,在构造ClassLoader时,会有一个libraryPath参数,“/data/app/包名-n”;在系统资源Properties中,存在一个属性"java.library.path",它的值是"/vendor/lib:/system/lib",两个系统目录。

如果ClassLoader为空,那就在系统资源Properties中,到"/vendor/lib:/system/lib"这里的目录下读取了。

nativeLoad()

在这里插入图片描述

从上图可知,在nativeLoad()里面往下执行到了dlopen()。

Native层

源码剖析

dlopen()

从dlopen()开始,在dlopen()里调用了_loader_dlopen()函数

image.png

__loader_dlopen()

在__loader_dlopen()调用了dlopen_ext()

image.png

dlopen_ext()

dlopen_ext()里调用了do_dlopen()

image.png

do_dlopen()

do_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 = android::base::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 original_path[PATH_MAX];
    if (realpath(name, original_path) != nullptr) {
      asan_name_holder = std::string(kAsanLibDirPrefix) + original_path;
      if (file_exists(asan_name_holder.c_str())) {
        soinfo* si = nullptr;
        if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
          PRINT("linker_asan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
                asan_name_holder.c_str());
        } else {
          PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
          translated_name = asan_name_holder.c_str();
        }
      }
    }
  }

  ProtectedDataGuard guard;
  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;
}

在这里面,有调用一个find_library()来查找并加载so,返回一个soinfo结构体指针

image.png

调用soinfo的call_construction()方法,确保动态库中的所有全局或静态对象的构造函数在库加载后得到执行,从而使库的环境和状态在使用前处于正确的初始化状态。

image.png

call_construction()

在*call_construction()方法中调用了一个init_array()*函数

image.png

init_array 是一个 ELF 格式中定义的段,它包含了一组指向函数的指针,这些函数通常用于初始化共享库。在动态库加载时,系统会调用这些函数来进行库的初始化操作。init_array 通常是一个数组,其中每一个元素都是指向一个初始化函数的指针。

call_constructors() 方法中,调用 init_array 是为了确保动态库中的初始化函数在库加载时被调用。

JNI_OnLoad()

在本地库被加载时自动调用,它的主要作用是进行 JNI 环境的初始化、注册本地方法。

注意:JNI_OnLoad()是在dlopen完全结束之后被调用的。

标签:name,流程,nullptr,so,flags,extinfo,dlopen,加载
From: https://blog.csdn.net/weixin_74305514/article/details/143086134

相关文章

  • 时序约束和综合+跨时钟产生的问题+spyglass的使用+SOC设计问题
    时序约束和综合时钟频率#时钟单位为ns,2ns对应500M时钟频率create_clock-period2[getportsclk]skew#设置时钟的skew,即上升沿之间的误差,当前设置为0.3nsset_clock_uncertainty-setup0.3[get_clocksCLK]transition#设置时钟上升沿的转化时间set_clock_transi......
  • 单点登录(Single Sign-On, SSO)
    单点登录(SingleSign-On,SSO)是一种身份验证机制,允许用户使用一组凭据(如用户名和密码)登录一次,即可访问多个相关但独立的软件系统。SSO的主要目的是简化用户的登录过程,提高用户体验,同时增强安全性,因为用户不需要为每个应用程序记住不同的凭据。单点登录的工作原理SSO通常涉......
  • Deepsort算法详解
    多目标跟踪的主要步骤:获取原视频帧利用目标检测器对视频帧中的目标进行检测将检测到的目标的框中的特征提取出来,该特征包括表观特征(方便特征对比避免IDswitch)和运动特征(运动特征方便卡尔曼滤波对其进行预测)表观特征与运动特征:表观特征:描述目标的外观信息,通常包括颜色、纹......
  • Adapting Open-Source Large Language Models for Cost-Effective, Expert-Level Clin
    本文是LLM系列文章,针对《AdaptingOpen-SourceLargeLanguageModelsforCost-Effective,Expert-LevelClinicalNoteGenerationwithOn-PolicyReinforcementLearning》的翻译。采用开源大型语言模型,通过策略强化学习生成经济高效的专家级临床笔记摘要1引言2......
  • 冒泡排序(Bubble Sort)
    新人博主,创作不易,希望得到各位看官的三连支持!!!1、原理        冒泡排序是一种简单的排序算法,属于交换排序的一种。它通过重复地遍历待排序的数列,比较相邻的元素,如果它们的顺序错误就将它们交换过来。这个过程会重复进行,直到没有需要交换的元素位置,即数列已经排序完......
  • 【玩转jetson orin nano(三)PyTorch深度学习环境安装】
    文章目录一、PyTorch深度学习环境安装1.Anaconda安装1.1下载1.2安装1.3换源1.4创建环境1.5设置默认启动环境1.6卸载2.安装torch+torchvisiion2.1安装torch2.2安装torchvisiion2.3验证2.4备注(可直接跳过)一、PyTorch深度学习环境安装1.Anaconda安装1.1......
  • 类加载器和反射
    第十章——类加载器和反射1、类的加载类的加载当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。加载就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。连接验证......
  • jar包运行报错1.无主属性清单 2.外部Jar包未导入 3.Data Source url报错解决 4.端口占
    相信大家mvnpackage打包成jar包后放到服务器上面运行后遇到一些很头疼的问题,怎么按照百度、gpt、csdn上面的博客修改就是成功不了但是!今天!博主带着自己尝试多次的血泪经验为大家解答以上三大问题!接下来以“代码+解析”的方式解析大家的问题一、无主属性问题报错如下图这......
  • ArkWeb页面拦截与自定义响应 - 控制加载过程
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。简介在Web应用开发中,有时我们需要对......
  • ArkWeb页面预加载与缓存 - 提升用户体验
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。简介在Web应用开发中,页面加载速度和......