System.loadLibrary 流程分析

admin 2023年5月15日23:47:32评论46 views字数 6710阅读22分22秒阅读模式


一、前言


    在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); } }


     不具体讨论函数中的详细逻辑流程,loadLibrary0so未加载情况下会调用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 JNICALLRuntime_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 流程分析

原文始发于微信公众号(哆啦安全):System.loadLibrary 流程分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月15日23:47:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   System.loadLibrary 流程分析https://cn-sec.com/archives/787008.html

发表评论

匿名网友 填写信息