源码
我的安卓版本:Pie 9.0.0_r
查看android源码的网站
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完全结束之后被调用的。
标签:name,流程,nullptr,so,flags,extinfo,dlopen,加载 From: https://blog.csdn.net/weixin_74305514/article/details/143086134