Android9.0.0_r61 so加载流程
一: Android so加载整体流程
System.loadLibrary() → \rightarrow → nativeLoad (Runtime_NativeLoad) → \rightarrow →JVM_NativeLoad → \rightarrow →vm → \rightarrow →LoadNativeLibrary
- dlopen
- dlsym找到JNI_Onload符号执行
首先我们先看一张图,这张图就是详细的Android so加载整体流程。
孔子曾说:工欲善其事必先利其器,所以我们一起来分析下so文件是怎么加载的吧
二:Android系统源码链接
Android系统源码在线查看网址:
但是推荐使用第一个AOSP,服务器在国内。
第二个网址并没有收录最新的安卓源码,并且网站在国外所以访问很慢。
三:JAVA层
System.loadLibrary() → \rightarrow → nativeLoad (Runtime_NativeLoad)
System.loadLibrary("libxxx.so")
JAVA层加载library代码位于
/libcore/ojluni/src/main/java/java/lang/Runtime.java
1. loadLibrary
@CallerSensitive
public void loadLibrary(String libname) {
loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
2. loadLibrary0
synchronized void loadLibrary0(ClassLoader loader, String libname) {
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
if (loader != null) {
// 通过findLibrary寻找该lib
String filename = loader.findLibrary(libraryName);
// 找不到
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
// 找到了就使用nativeLoad去加载lib
String error = nativeLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
String error = nativeLoad(candidate, loader);
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}
3. nativeLoad
从其函数签名来看,nativeLoad是一个native函数
private static native String nativeLoad(String filename, ClassLoader loader);
四:Native层
Native层代码位于
/libcore/ojluni/src/main/native/Runtime.c
1. Runtime_nativeLoad
// 导出JNI函数
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jstring javaLibrarySearchPath)
{
// 调用了JVM_NativeLoad函数
return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath);
}
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
NATIVE_METHOD(Runtime, gc, "()V"),
NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
// 动态加载,注册nativeLoad
NATIVE_METHOD(Runtime, nativeLoad,
"(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
"Ljava/lang/String;"),
};
2. JVM_NativeLoad
JVM_NativeLoad声明于
/libcore/ojluni/src/main/native/jvm.h
JVM_NativeLoad实现于
/art/runtime/openjdkjvm/OpenjdkJvm.cc
OpenjdkJvm.cc文件头注释显示:这是对jvm.h中的接口的实现
This file implements interfaces from the file jvm.h.
This implementationis licensed under the same terms as the file jvm.h.
在JVM_NativeLoad中使用了vm
→
\rightarrow
→LoadNativeLibrary来对so文件进行加载。
源码如下
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jstring javaLibrarySearchPath) {
// 检查文件名
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
}
std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
// 使用vm的LoadNativeLibrary来进行加载
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
javaLibrarySearchPath,
&error_msg);
// 如果成功
if (success) {
return nullptr;
}
}
// 失败抛出异常
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
env->ExceptionClear();
return env->NewStringUTF(error_msg.c_str());
}
3. vm->LoadNativeLibrary
LoadNativeLibrary位于
/art/runtime/java_vm_ext.cc
在该方法中,使用OpenNativeLibrary()来调用dlopen该系统调用。
这部分代码过多,感兴趣可以自行看源码# 766
// /art/runtime/java_vm_ext.cc#836
void *handle = android::OpenNativeLibrary(env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
library_path,
&needs_native_bridge,
error_msg);
VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
if (handle == nullptr)
{
VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
return false;
}
会发现该函数中使用了JNI_Onload,JNI_Onload的加载在加载so之后
// /art/runtime/java_vm_ext.cc#885
bool was_successful = false;
// 寻找JNI_Onload
void *sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr)
{
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
was_successful = true;
}
// 加载JNI_Onload
else
{
// Call JNI_OnLoad. We have to override the current class
// loader, which will always be "null" since the stuff at the
// top of the stack is around Runtime.loadLibrary(). (See
// the comments in the JNI FindClass function.)
ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
self->SetClassLoaderOverride(class_loader);
VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
... //省略
}
3. android::OpenNativeLibrary
OpenNativeLibrary位于
/system/core/libnativeloader/native_loader.cpp#458
在OpenNativeLibrary中调用了dlopen来进行so的加载
源码如下
void* OpenNativeLibrary(JNIEnv* env,
int32_t target_sdk_version,
const char* path,
jobject class_loader,
jstring library_path,
bool* needs_native_bridge,
std::string* error_msg) {
// 首次加载
#if defined(__ANDROID__)
UNUSED(target_sdk_version);
if (class_loader == nullptr) {
*needs_native_bridge = false;
return dlopen(path, RTLD_NOW);
}
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace ns;
if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
if (!g_namespaces->Create(env,
target_sdk_version,
class_loader,
false,
library_path,
nullptr,
&ns,
error_msg)) {
return nullptr;
}
}
if (ns.is_android_namespace()) {
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns.get_android_ns();
void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
if (handle == nullptr) {
*error_msg = dlerror();
}
*needs_native_bridge = false;
return handle;
} else {
void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
if (handle == nullptr) {
*error_msg = NativeBridgeGetError();
}
*needs_native_bridge = true;
return handle;
}
#else
UNUSED(env, target_sdk_version, class_loader, library_path);
*needs_native_bridge = false;
void* handle = dlopen(path, RTLD_NOW);
if (handle == nullptr) {
if (NativeBridgeIsSupported(path)) {
*needs_native_bridge = true;
handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
if (handle == nullptr) {
*error_msg = NativeBridgeGetError();
}
} else {
*needs_native_bridge = false;
*error_msg = dlerror();
}
}
return handle;
#endif
}
4. dlopen
dlopen位于
/bionic/linker/libdl.cpp
源码如下
// Proxy calls to bionic loader
__attribute__((__weak__))
void* dlopen(const char* filename, int flag) {
const void* caller_addr = __builtin_return_address(0);
return __loader_dlopen(filename, flag, caller_addr);
}
__loader_dlopen()位于
/bionic/linker/dlfcn.cpp
源码如下
// 函数 __loader_dlopen()
void* __loader_dlopen(const char* filename, int flags, const void* caller_addr) {
return dlopen_ext(filename, flags, nullptr, caller_addr);
}
// 函数dlopen_ext
static void* dlopen_ext(const char* filename,
int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
g_linker_logger.ResetState();
void* result = do_dlopen(filename, flags, extinfo, caller_addr);
if (result == nullptr) {
__bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
return nullptr;
}
return result;
}
5. do_dlopen()
do_dlopen函数签名为
void* do_dlopen(const char* name, int flags,
const android_dlextinfo* extinfo,
const void* caller_addr)
源码位于
/bionic/linker/linker.cpp
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);
// ...省略
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;
// 加载并链接so
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;
}
soinfo结构体结构如下所示
#define SOINFO_NAME_LEN 128
struct link_map
{
uintptr_t l_addr;
char * l_name;
uintptr_t l_ld;
struct link_map * l_next;
struct link_map * l_prev;
};
typedef struct soinfo soinfo;
struct soinfo
{
const char name[SOINFO_NAME_LEN];
Elf32_Phdr *phdr;
int phnum;
unsigned entry;
unsigned base;
unsigned size;
unsigned *dynamic;
unsigned wrprotect_start;
unsigned wrprotect_end;
soinfo *next;
unsigned flags;
const char *strtab;
Elf32_Sym *symtab;
unsigned strsz; //添加strsz字段,便于操作
unsigned nbucket;
unsigned nchain;
unsigned *bucket;
unsigned *chain;
unsigned *plt_got;
Elf32_Rel *plt_rel;
unsigned plt_rel_count;
Elf32_Rel *rel;
unsigned rel_count;
unsigned *preinit_array;
unsigned preinit_array_count;
unsigned *init_array;
unsigned init_array_count;
unsigned *fini_array;
unsigned fini_array_count;
void(*init_func)(void);
void(*fini_func)(void);
#ifdef ANDROID_ARM_LINKER
unsigned *ARM_exidx;
unsigned ARM_exidx_count;
#endif
unsigned refcount;
struct link_map linkmap;
};
在这里最关键的代码如下所示
- 在这里find_library返回的是一个soinfo的结构体指针,find_library主要是so文件的加载和链接
- 在加载完so文件之后,我们可以看到si->call_constructors()也就是调用构造函数,也就是.init_xxx函数。
ProtectedDataGuard guard;
// 加载并链接so
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);
// 构造函数初始化,即调用.init_xxx
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;
}
6. soinfo::call_constructors()
**soinfo::call_constructors()**位于
/bionic/linker/linker_soinfo.cpp
源码如下
void soinfo::call_constructors() {
if (constructors_called) {
return;
}
// We set constructors_called before actually calling the constructors, otherwise it doesn't
// protect against recursive constructor calls. One simple example of constructor recursion
// is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
// 1. The program depends on libc, so libc's constructor is called here.
// 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
// 3. dlopen() calls the constructors on the newly created
// soinfo for libc_malloc_debug_leak.so.
// 4. The debug .so depends on libc, so CallConstructors is
// called again with the libc soinfo. If it doesn't trigger the early-
// out above, the libc constructor will be called again (recursively!).
constructors_called = true;
if (!is_main_executable() && preinit_array_ != nullptr) {
// The GNU dynamic linker silently ignores these, but we warn the developer.
PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath());
}
get_children().for_each([] (soinfo* si) {
si->call_constructors();
});
if (!is_linker()) {
bionic_trace_begin((std::string("calling constructors: ") + get_realpath()).c_str());
}
// DT_INIT should be called before DT_INIT_ARRAY if both are present.
call_function("DT_INIT", init_func_, get_realpath());
call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
if (!is_linker()) {
bionic_trace_end();
}
}
结合源码和注释
我们可以理解为
- 全局不允许频繁调用该构造方法,也就是说在APP一次生命周期中so只能初始化一次,并且内存地址不变。
- 如果so没有被加载过,就调用DT_INIT和DT_INIT_ARRAY 也就是初始化。
五: 总结
- Java层加载so API:System.loadLibrary(“libxxx.so”)
- so层调用nativeLoadLibrary()
- vm中调用加载
- 通过find_library对该so库进行加载和链接并且返回其句柄
通过如上分析,我们可以对刚开始的图有了更深刻的了解