只需一篇就让你详细了解 Java 中 so 文件的加载原理( 七 )

 
/ JVM_LoadLibrary() /
 
JVM_LoadLibrary() 是 JVM 这层加载 library 的最后一个实现,具体步骤如下:
 

  1. 定义 1024 长度的 char 数组和接收加载结果的指针
  2. 调用 dll_load() 加载 library,其细节见下章节
  3. 加载失败的话,打印 library 名称和错误 message
  4. 同时抛出 UnsatisfiedLinkError
  5. 反之将加载结果返回
 
// vm/prims/jvm.cppJVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* name))JVMWrapper2("JVM_LoadLibrary (%s)", name);char ebuf[1024];void *load_result;{ThreadToNativeFromVM ttnfvm(thread);load_result = os::dll_load(name, ebuf, sizeof ebuf);}if (load_result == NULL) {char msg[1024];jio_snprintf(msg, sizeof msg, "%s: %s", name, ebuf);Handle h_exception =Exceptions::new_exception(...);THROW_HANDLE_0(h_exception);}return load_result;JVM_END 
/ dll_load() /
 
dll_load() 的实现跟平台相关,比如 bsd 平台就是调用标准库的 dlopen(),而其最终的结果来自于 do_dlopen(),其将通过 find_library() 得到 soinfo 实例,内部将执行 to_handle() 得到 library 的指针 。
 
// bionic/libdl/libdl.cppvoid* dlopen(const char* filename, int flag) {const void* caller_addr = __builtin_return_address(0);return __loader_dlopen(filename, flag, caller_addr);}void* __loader_dlopen(const char* filename, int flags, const void* caller_addr) {return dlopen_ext(filename, flags, nullptr, caller_addr);}static void* dlopen_ext(...) {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;}void* do_dlopen(...) {...if (si != nullptr) {void* handle = si->to_handle();si->call_constructors();failure_guard.Disable();return handle;}return nullptr;} 
/ JNI_OnLoad() /
 
JNI_OnLoad() 定义在 jni.h 中,当 library 被 JVM 加载时会回调,该方法内一般会通过 registerNatives() 注册 native 方法并返回该 library 所需的 JNI 版本 。该头文件还定义了其他函数和常量,比如 JNI 1.1 等数值 。
 
// jni.h...struct JNIEnv_ {...jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,jint nMethods) {return functions->RegisterNatives(this,clazz,methods,nMethods);}jint UnregisterNatives(jclass clazz) {return functions->UnregisterNatives(this,clazz);}}.../* Defined by native libraries. */JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);#define JNI_VERSION_1_1 0x00010001... 
/ 结语 /
只需一篇就让你详细了解 Java 中 so 文件的加载原理

文章插图
 
总体流程可以归纳如下:
 
  1. System 类提供的 load() 加载 so 的完整的路径名且带文件后缀,等同于直接调用 Runtime 类提供的 load();loadLibrary() 用于加载指定 so 的名称,等同于调用 Runtime 类提供的 loadLibrary() 。
  2. 两者都将通过 SecurityManager 检查 so 的访问权限以及名称是否合法
  3. 之后调用 ClassLoader 类的 loadLibrary() 实现,区别在于前者指定的是否是绝对路径的 isAbsolute 参数是否为 true
  4. ClassLoader 首先需要通过 System 提供的 getProperty() 获取 JVM 配置的存放 usr、system library 路径字符串数组
  5. 如果 library name 非绝对路径,需要先调用 findLibrary() 获取该 name 对应的完整 so 文件,之后再调用 loadLibrary0() 继续
  6. 当 ClassLoader 不存在,分别到 system、usr 字符串数组中查找该 so 是否存在
  7. loadLibrary0() 将调用 native 方法 findBuiltinLib() 检查是否是内置的动态链接库,并到加载过 vector、加载中 context 中查找是否已经加载过、加载中
  8. 通过检查的话调用 NativeLibrary 静态内部类继续,事实上是调用 ClassLoader.c 的 load()
  9. 其将调用 jvm.cpp 的 JVM_LoadLibrary() 进行 so 的加载获得指针
  10. 根据 OS 的实现,dll_load() 通过 dlopen() 执行 so 的打开和地址返回
  11. 最后通过 findJniFunction() 获取 JNI_OnLoad() 地址进行 native 方法的注册和所需 JNI 版本的收集 。
原文链接:
https://mp.weixin.qq.com/s/HVQvjDhhUuCrkBuOP8PJZw




推荐阅读