so加载流程分析

源码

我的安卓版本: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()函数

在这里插入图片描述

__loader_dlopen()

在__loader_dlopen()调用了dlopen_ext()

在这里插入图片描述

dlopen_ext()

dlopen_ext()里调用了do_dlopen()

在这里插入图片描述

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结构体指针

在这里插入图片描述

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

在这里插入图片描述

call_construction()

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

在这里插入图片描述

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

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

JNI_OnLoad()

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值