一、前言
在Android App开发中,如果涉及到 jni开发,常常会使用System.loadLibray来加载生成的so文件。以下将通过安卓10的源码,追踪System.loadLibrary的内部流程。
二、System类调用分析
System类源码路径如下:
libcoreojlunisrcmainjavajavalangSystem.java
在该类中loadLibrary函数代码如下:
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
有以上代码可分析到调用类转到了Runtime类中。
三、Runtime类调用分析
Runtime类源码路径:
libcoreojlunisrcmainjavajavalangRuntime.java
loadLibrary0函数实现如下:
//
void loadLibrary0(Class<?> fromClass, String libname) {
ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);
loadLibrary0(classLoader, fromClass, libname);
}
private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
// Android-note: BootClassLoader doesn't implement findLibrary(). http://b/111850480
// Android's class.getClassLoader() can return BootClassLoader where the RI would
// have returned null; therefore we treat BootClassLoader the same as null here.
if (loader != null && !(loader instanceof BootClassLoader)) {
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) + """);
}
String error = nativeLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
// We know some apps use mLibPaths directly, potentially assuming it's not null.
// Initialize it here to make sure apps see a non-null value.
getLibPaths();
String filename = System.mapLibraryName(libraryName);
String error = nativeLoad(filename, loader, callerClass);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
}
不具体讨论函数中的详细逻辑流程,loadLibrary0在so未加载情况下会调用nativeLoad方法。nativeLoad函数如下:
private static String nativeLoad(String filename, ClassLoader loader) {
return nativeLoad(filename, loader, null);
}
private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
最终调用的是native方法nativeLoad,所以接下来需要找到该方法的jni层实现。Runtime.java中的jni方法是由Runtime.c注册实现的。接下来分析Runtime.c中的流程。
四、Runtime.c中的流程分析
Runtime.c源码路径位于:
libcoreojlunisrcmainnativeRuntime.c
在该文件中java层native方法nativeLoad对应的jni层实现函数为Runtime_nativeLoad,函数实现如下:
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jclass caller)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
}
有以上代码逻辑中可分析调用了JVM_NativeLoad函数。通过强大的xxgrep搜索命令,搜到该方法被定义在jvm.h文件中,实现文件在OpenjdkJvm.cc中。
jvm.h文件路径:
libcoreojlunisrcmainnativejvm.h
OpenjdkJvm.cc文件路径:
art/openjdkjvm/OpenjdkJvm.cc
接下来分析OpenjdkJvm的实现。
五、OpenjdkJvm.cc中的流程分析
在OpenjdkJvm.cc中JVM_NativeLoad的实现代码如下:
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jclass caller) {
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == nullptr) {
return nullptr;
}
std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
caller,
&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());
}
以上代码中核心调用变为了JavaVMExt->LoadNativeLibrary。
六、JavaVMExt中的流程分析
JavaVMExt.cc源码路径位于:
artruntimejnijava_vm_ext.cc
该文件中LoadNativeLibrary的函数实现如下:
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jclass caller_class,
std::string* error_msg) {
//..。省略
//使用OpenNativeLibrary打开so库
void* handle = android::OpenNativeLibrary(
env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
(caller_location.empty() ? nullptr : caller_location.c_str()),
library_path.get(),
&needs_native_bridge,
&nativeloader_error_msg);
VLOG(jni) << "[Call to dlopen("" << path << "", RTLD_NOW) returned " << handle << "]";
//...省略
//查找JNI_OnLoad方法,所以我们在jni开发中动态注册就需要重写这个方法是有道理额
void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in "" << path << ""]";
was_successful = true;
} 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 << ""]";
using JNI_OnLoadFn = int(*)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
//主动去调用JNI_OnLoad方法
int version = (*jni_on_load)(this, nullptr);
//...省略
}
//...省略
}
以上方法中会打开so库并调用JNI_OnLoad方法。以上打开so使用了"android::OpenNativeLibrary",下面准备追踪一下该方法的内部实现机制。
七、OpenNativeLibrary实现
在源码中搜索,找到OpenNativeLibrary实现源码路径如下:
systemcorelibnativeloadernative_loader.cpp
该方法逻辑实现代码如下:
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
jobject class_loader, const char* caller_location, jstring library_path,
bool* needs_native_bridge, char** error_msg) {
#if defined(__ANDROID__)
UNUSED(target_sdk_version);
if (class_loader == nullptr) {
*needs_native_bridge = false;
if (caller_location != nullptr) {
android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
if (boot_namespace != nullptr) {
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = boot_namespace,
};
void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
if (handle == nullptr) {
*error_msg = strdup(dlerror());
}
return handle;
}
}
void* handle = dlopen(path, RTLD_NOW);
if (handle == nullptr) {
*error_msg = strdup(dlerror());
}
return handle;
}
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns;
if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
std::string create_error_msg;
if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
*error_msg = strdup(create_error_msg.c_str());
return nullptr;
}
}
return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
#else
//...省略不相干的
#endif
}
以上代码中会根据class_loader 、caller_location进行判断,是直接使用dlopen还是android_dlopen_ext、OpenNativeLibraryInNamespace来进行加载so。
八、总结
(1)通过分析System.loadLibrary之后就可以非常清楚为什么我们动态注册的时候需要重写JNI_OnLoad方法。
(2)System.loadLibrary流程总结如下:
System(Java).loadLibrary->
Runtime(Java).loadLibrary0->
Runtime(Java).nativeLoad->
Runtime_nativeLoad(Runtime.cc)->
JVM_NativeLoad(OpenjdkJvm.cc)->
LoadNativeLibrary(java_vm_exe.cc)->
OpenNativeLibrary(native_loader.cpp)
————————————————
原文始发于微信公众号(哆啦安全):System.loadLibrary 流程分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论