JNI principle System.loadLibrary source code analysis

Preface

This article describes the principle of System.loadLibrary.

Java layer source code flow

System#loadLibrary
-> Runtime#loadLibrary0
–> BaseDexClassLoader#findLibrary
–> DexPathList#findNativeLibrary
->Runtime#nativeLoad,走向c层源码

  • Mainly from the nativeLibraryPathElementsfind in so path, through all the paths found so exist, does not exist is thrown UnsatisfiedLinkError. Open the so logic in the c layer. If the c layer returns false, the error string will be spliced, and the Java layer will also throw it after receiving it UnsatisfiedLinkError.
    image.pngimage.png
  • You can print nativeLibraryPathElements through reflection to better understand this piece of code
            Field pathListF = BaseDexClassLoader.class.getDeclaredField("pathList");
            pathListF.setAccessible(true);
            ClassLoader dexPathClassLoader = TestSocketFragment.class.getClassLoader();
            Object pathList = pathListF.get(dexPathClassLoader);

            Field nativeLibraryPathElementsF = Class.forName("dalvik.system.DexPathList").getDeclaredField("nativeLibraryPathElements");
            nativeLibraryPathElementsF.setAccessible(true);
            Object nativeLibraryPathElements = nativeLibraryPathElementsF.get(pathList);
            android.util.Log.e("mLogU", nativeLibraryPathElements.toString());

            Object[] arr = (Object[]) nativeLibraryPathElements;
            for (Object o : arr) {
    
    
                android.util.Log.e("mLogU", o.toString());
            }

Native layer source code process

Load so from the c layer and return false will be thrown in the Java layer UnsatisfiedLinkError. The process is as follows:

-> nativeLoad
->libcore/ojluni/src/main/native/Runtime.c#Runtime_nativeLoad
->art/openjdkjvm/OpenjdkJvm.cc#JVM_NativeLoad
->art/runtime/jni/java_vm_ext.cc#LoadNativeLibrary
–>art/libnativeloader/native_loader.cpp#OpenNativeLibrary

bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                  const std::string& path,
                                  jobject class_loader,
                                  std::string* error_msg) {
    
    
  error_msg->clear();

  /*
    *  1. 从缓存中获取该library,
    *  2. 如果存在则比较classLoader是否相同,如果相同则返回成功。不同则返回失败。
    *  3. 不存在则进行加载,并放入缓存中
    */
  SharedLibrary* library;
  Thread* self = Thread::Current();
  {
    
    
    MutexLock mu(self, *Locks::jni_libraries_lock_);
    library = libraries_->Get(path);
  }
  void* class_loader_allocator = nullptr;
  {
    
    
    ScopedObjectAccess soa(env);
    ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);

    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
    if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
    
    
      loader = nullptr;
      class_loader = nullptr;
    }

    // 获取当前classloader的Allocator
    class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
    CHECK(class_loader_allocator != nullptr);
  }
  
  /*
    * The JNI spec says we can't load the same library into more than one class loader.
    * JNI规范要求不能加载相同库到同一个classLoader中
    */
  if (library != nullptr) {
    
    
    // Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
    if (library->GetClassLoaderAllocator() != class_loader_allocator) {
    
    
      // .... 拼接一大堆str
      return false;
    }

    // 已经加载过了,问题不大
    if (!library->CheckOnLoadResult()) {
    
    
      return false;
    }
    return true;
  }

  // dlopen该库,并返回句柄(指针)
  void* handle = android::OpenNativeLibrary(env,
                                            runtime_->GetTargetSdkVersion(),
                                            path_str,
                                            class_loader,
                                            library_path.get(),
                                            &needs_native_bridge,
                                            error_msg);

  if (handle == nullptr) {
    
    
    // 打开失败
    return false;
  }


  bool created_library = false;
  {
    
    
    std::unique_ptr<SharedLibrary> new_library(
        new SharedLibrary(env,
                          self,
                          path,
                          handle,
                          needs_native_bridge,
                          class_loader,
                          class_loader_allocator));

    MutexLock mu(self, *Locks::jni_libraries_lock_);
    library = libraries_->Get(path);
    if (library == nullptr) {
    
      // We won race to get libraries_lock.
      library = new_library.release();
      // 放入缓存
      libraries_->Put(path, library);
      created_library = true;
    }
  }
  if (!created_library) {
    
    
    return library->CheckOnLoadResult();
  }
  
  /*
    * 加载成功,dlsym获取JNI_OnLoad句柄,并调用JNI_OnLoad方法,根据返回值判断设置是否加载成功
    */
  bool was_successful = false;
  void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
  if (sym == nullptr) {
    
    
    was_successful = true;
  } else {
    
    
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
    int version = (*jni_on_load)(this, nullptr);

    if (version == JNI_ERR) {
    
    
      StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
    } else if (JavaVMExt::IsBadJniVersion(version)) {
    
    
      StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
                    path.c_str(), version);
    } else {
    
    
      was_successful = true;
    }
  }

  library->SetResult(was_successful);
  return was_successful;
}

Conclusion

It can be seen from the source code that System#loadLibrary will only call the JNI_OnLoadmethod once under normal circumstances ;
among them, SharedLibrary will call dlclose in the destructor to close the library;
and from the official document JNI tips, it can be seen that it is generally placed in a static code block to load so , The so life cycle is bound to the class life cycle.
This article sorts out the system#loadLibrary source code process, please correct me if there are any errors.

Guess you like

Origin blog.csdn.net/u014099894/article/details/111655768