马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
源码流程分析
在Android的JNI开辟中,加载.so文件通常使用System.loadLibrary函数。以下是一个简单的示例:
- public class MainActivity extends AppCompatActivity {
- // Used to load the 'myapplication' library on application startup.
- static {
- System.loadLibrary("myapplication");
- }
- }
复制代码 接下来,我们以Android 10的源码为例,详细分析System.loadLibrary函数的实现过程。起首,System.loadLibrary的定义位于/libcore/ojluni/src/main/java/java/lang/System.java,实现如下:
- public static void loadLibrary(String libname) {
- Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
- }
复制代码 从中可以看出,System.loadLibrary实际上是调用了Runtime类的loadLibrary0方法。我们继承跟踪loadLibrary0的实现:
- void loadLibrary0(Class<?> fromClass, String libname) {
- ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);
- loadLibrary0(classLoader, fromClass, libname);
- }
复制代码 在这里,通过传入的类获取了对应的类加载器(ClassLoader),然后调用了重载的loadLibrary0方法:
- private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {
- // Check if the library name contains a directory separator
- if (libname.indexOf((int) File.separatorChar) != -1) {
- throw new UnsatisfiedLinkError(
- "Directory separator should not appear in library name: " + libname);
- }
-
- String libraryName = libname;
- // If loader is not null and not BootClassLoader, use it to find the library
- if (loader != null && !(loader instanceof BootClassLoader)) {
- String filename = loader.findLibrary(libraryName);
- if (filename == null) {
- throw new UnsatisfiedLinkError(loader + " couldn't find "" +
- System.mapLibraryName(libraryName) + """);
- }
- String error = nativeLoad(filename, loader);
- if (error != null) {
- throw new UnsatisfiedLinkError(error);
- }
- return;
- }
- // If loader is null or is BootClassLoader, initialize library paths and load the library
- getLibPaths();
- String filename = System.mapLibraryName(libraryName);
- String error = nativeLoad(filename, loader, callerClass);
- if (error != null) {
- throw new UnsatisfiedLinkError(error);
- }
- }
复制代码 这里loadLibrary0有一句关键代码:
- oader.findLibrary:
- public String findLibrary(String name) {
- return pathList.findLibrary(name);
- }
复制代码 实现如下:
- public String findLibrary(String libraryName) {
- // 最后会调用到C层中JNIEXPORT jstring JNICALL System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
- // 结果就是会返回libmyapplication.so,拼接字符串
- String fileName = System.mapLibraryName(libraryName);
- // 返回libmyapplication.so的全路径
- for (NativeLibraryElement element : nativeLibraryPathElements) {
- String path = element.findNativeLibrary(fileName);
- if (path != null) {
- return path;
- }
- }
- return null;
- }
复制代码 可以看到,findLibrary最终会通过JNI调用System.mapLibraryName方法拼接出完备的.so库文件名,并在本地路径中查找该文件。
接着看loadLibrary0中nativeLoad实现:
- private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
复制代码 发现此时已经在C层,实现如下:
- JNIEXPORT jstring JNICALL Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
- jobject javaLoader, jclass caller)
- {
- return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
- }
复制代码 看来核心代码在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());
- }
复制代码 vm->LoadNativeLibrary函数相对复杂,我们主要查看在这个函数内部调用android::OpenNativeLibrary函数:
- 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;
- }
- // Additional code (not provided) would go here
- #endif
- }
复制代码 总算追踪到了我们的老朋侪android_dlopen_ext和dlopen,最后经过追踪会调用do_dlopen函数,这就到本日的核心:
- void* do_dlopen(const char* name, int flags,const android_dlextinfo* extinfo,const void* caller_addr) {
- ...
- 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;
- }
复制代码 也就是在在so加载完成以后会调用si->call_constructors()函数:
- void soinfo::call_constructors() {
- // 主要看这样两行
- call_function("DT_INIT", init_func_, get_realpath());
- call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
- }
复制代码 这里也就阐明白so中的init和init_array的实行时机在do_dlopen内部。此外,我们还需要相识JNI_OnLoad的实行时机。回到vm->LoadNativeLibrary函数中:
- bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
- const std::string& path,
- jobject class_loader,
- jclass caller_class,
- std::string* error_msg) {
-
- void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
- if (sym == nullptr) {
- VLOG(jni) << "[No JNI_OnLoad found in "" << path << ""]";
- was_successful = true;
- } else {
- 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);
- int version = (*jni_on_load)(this, nullptr);
- }
复制代码 可以看到就是通过library->FindSymbol查找JNI_OnLoad符号,然后使用函数指针进行JNI_OnLoad的函数调用。
总结
System.loadLibrary的核心功能是获取.so库的全路径,加载该.so库文件,然后依次调用init、init_array和JNI_OnLoad函数。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |