美文网首页
System.loadLibrary(libname)

System.loadLibrary(libname)

作者: MelanDawn | 来源:发表于2021-09-13 23:41 被阅读0次

    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterApp.java

    public class AdapterApp extends Application {
        static {
            System.loadLibrary("bluetooth_jni");
        }
    }
    

    bluetooth_jni 对应的 so 库文件全称为:libbluetooth_jni.so,加载过程中应该会补全。

    libcore/ojluni/src/main/java/java/lang/System.java

        @CallerSensitive
        public static void loadLibrary(String libname) {
            Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
        }
    

    由于 Runtime 中存在 loadLibrary0() 方法重载,首先需要确定第一个参数的类型。

    libcore/ojluni/src/main/java/sun/reflect/Reflection.java

        public static Class<?> getCallerClass() {
            return VMStack.getStackClass2();
        }
    

    libcore/ojluni/src/main/java/java/lang/Runtime.java

        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) {
    ......
            String libraryName = libname;
            if (loader != null && !(loader instanceof BootClassLoader)) {
                String filename = loader.findLibrary(libraryName);         // 1.1
                if (filename == null &&
                        (loader.getClass() == PathClassLoader.class ||
                         loader.getClass() == DelegateLastClassLoader.class)) {
                    filename = System.mapLibraryName(libraryName);        // 1.2
                }
                if (filename == null) {
                    throw new UnsatisfiedLinkError(l"......");
                }
                String error = nativeLoad(filename, loader);              // 1.3
                if (error != null) {
                    throw new UnsatisfiedLinkError(error);
                }
                return;
            }
            getLibPaths();                                               // 2.1
            String filename = System.mapLibraryName(libraryName);        // 2.2
            String error = nativeLoad(filename, loader, callerClass);    // 2.3
            if (error != null) {
                throw new UnsatisfiedLinkError(error);
            }
        }
    
    1. 第一个分支
      • 1.1 ClassLoader.findLibrary(libraryName)
      • 1.2 System.mapLibraryName(libraryName)
      • 1.3 nativeLoad(filename, loader)
    2. 第二个分支
      • 2.1 getLibPaths()
      • 2.2 System.mapLibraryName(libraryName)
      • 2.3 nativeLoad(filename, loader, callerClass)

    在 Android 11 平台的 Pixel 5 上测试,实际执行的是 1.1 和 1.3,

    1.1 ClassLoader.findLibrary()

    libcore/ojluni/src/main/java/java/lang/ClassLoader.java

    public abstract class ClassLoader {
        protected String findLibrary(String libname) {
            return null;
        }
    }
    

    libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

    public class BaseDexClassLoader extends ClassLoader {
        public String findLibrary(String name) {
            return pathList.findLibrary(name);
        }
    
    
    
        public BaseDexClassLoader(String dexPath,
                String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
                boolean isTrusted) {
    ......
            this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    ......
        }
    }
    

    libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

    public final class DexPathList {
        public String findLibrary(String libraryName) {
            String fileName = System.mapLibraryName(libraryName);              // 1
    
            for (NativeLibraryElement element : nativeLibraryPathElements) {   // 2
                String path = element.findNativeLibrary(fileName);             // 3
    
                if (path != null) {
                    return path;
                }
            }
    
            return null;
        }
    }
    
    1.1.1 System.mapLibraryName()
    public final class System {
        public static native String mapLibraryName(String libname);
    }
    

    libcore/ojluni/src/main/native/System.c

    JNIEXPORT jstring JNICALL
    System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
    {
        int len;
        int prefix_len = (int) strlen(JNI_LIB_PREFIX);
        int suffix_len = (int) strlen(JNI_LIB_SUFFIX);
    
        jchar chars[256];
        len = (*env)->GetStringLength(env, libname);
    
    ...... // 长度校验,不能超过 240
    
        cpchars(chars, JNI_LIB_PREFIX, prefix_len);
        (*env)->GetStringRegion(env, libname, 0, len, chars + prefix_len);
        len += prefix_len;
        cpchars(chars + len, JNI_LIB_SUFFIX, suffix_len);
        len += suffix_len;
    
        return (*env)->NewString(env, chars, len);
    }
    

    将 JNI_LIB_PREFIX、libname、JNI_LIB_SUFFIX 三者拼接成一个字符串,从下面的头文件可知,最终拼接的字符串是:libbluetooth_jni.so,即为目标库名称。

    libcore/ojluni/src/main/native/jvm_md.h

    #define JNI_LIB_PREFIX "lib"
    #define JNI_LIB_SUFFIX ".so"
    
    1.1.2 nativeLibraryPathElements
    public final class DexPathList {
        public DexPathList(ClassLoader definingContext, String librarySearchPath) {
    ......
            this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);      // 1
            this.systemNativeLibraryDirectories =
                    splitPaths(System.getProperty("java.library.path"), true);         // 2
            this.nativeLibraryPathElements = makePathElements(getAllNativeLibraryDirectories()); // 3
        }
    
    
    
        private List<File> getAllNativeLibraryDirectories() {
            List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
            allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
            return allNativeLibraryDirectories;
        }
    }
    

    语句 // 1 与 语句 // 2 构成语句 // 3 所在的集合

    1.1.2.1 librarySearchPath

    由外部传递

    1.1.2.2 System.getProperty("java.library.path")

    找了台 Android R 的手机测试一下 “java.library.path”的值 System.getProperty("java.library.path") = /system/lib64:/system_ext/lib64
    在 Android R 手机上查看一下这两个目录,说明通过这种方式可以找到目标:libblutooth_jni.so

    xxxxxx:/ $ ls system/lib64 | grep libbluetooth
    libbluetooth.so
    libbluetooth_jni.so
    xxxxxx:/ $ ls system_ext/lib64 | grep libbluetooth
    1|xxxxxx:/ $
    
    1.1.3 NativeLibraryElement.findNativeLibrary()
    public final class DexPathList {
        /*package*/ static class NativeLibraryElement {
            public String findNativeLibrary(String name) {
                maybeInit();
    
                if (zipDir == null) {
                    String entryPath = new File(path, name).getPath();
                    if (IoUtils.canOpenReadOnly(entryPath)) {
                        return entryPath;
                    }
                } else if (urlHandler != null) {
                    // Having a urlHandler means the element has a zip file.
                    // In this case Android supports loading the library iff
                    // it is stored in the zip uncompressed.
                    String entryName = zipDir + '/' + name;
                    if (urlHandler.isEntryStored(entryName)) {
                      return path.getPath() + zipSeparator + entryName;
                    }
                }
    
                return null;
            }
        }
    }
    

    根据绝对路径确定库文件存在性、读写情况等。

    1.2 System.mapLibraryName()

    如前文 1.1.1 System.mapLibraryName()

    1.3 nativeLoad(filename, loader)

    public class Runtime {
        private static String nativeLoad(String filename, ClassLoader loader) {
            return nativeLoad(filename, loader, null);
        }
    }
    
    public class Runtime {
        private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
    }
    

    二者 nativeLoad 方法的区别在于最后一个参数的不同。
    以下分析为两个分支的相同流程,仅最后一个参数不同。
    libcore/ojluni/src/main/native/Runtime.c

    JNIEXPORT jstring JNICALL
    Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                       jobject javaLoader, jclass caller)
    {
        return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
    }
    

    art/openjdkjvm/OpenjdkJvm.cc

    JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
                                     jstring javaFilename,
                                     jobject javaLoader,
                                     jclass caller) {
      ScopedUtfChars filename(env, javaFilename);
    
      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;
        }
      }
      env->ExceptionClear();
      return env->NewStringUTF(error_msg.c_str());
    }
    

    art/runtime/jni/java_vm_ext.cc

    bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                      const std::string& path,
                                      jobject class_loader,
                                      jclass caller_class,
                                      std::string* error_msg) {
    ...... // 待填坑
      bool was_successful = false;
      void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
      if (sym == nullptr) {
        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);
    
        if (IsSdkVersionSetAndAtMost(runtime_->GetTargetSdkVersion(), SdkVersion::kL)) {
          // Make sure that sigchain owns SIGSEGV.
          EnsureFrontOfChain(SIGSEGV);
        }
    
        self->SetClassLoaderOverride(old_class_loader.get());
    
        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;
    }
    

    从以上代码可以看出,若 libbluetooth_jni.so 加载成功,会调用其中的 JNI_OnLoad() 方法。
    JNI_OnLoad() 源码如下,

    packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

    jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
      JNIEnv* e;
      int status;
    
      // Check JNI version
      if (jvm->GetEnv((void**)&e, JNI_VERSION_1_6)) {
        return JNI_ERR;
      }
    
      status = android::register_com_android_bluetooth_btservice_AdapterService(e);
      if (status < 0) {
        return JNI_ERR;
      }
    ......
      return JNI_VERSION_1_6;
    }
    
    
    int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
      return jniRegisterNativeMethods(
          env, "com/android/bluetooth/btservice/AdapterService", sMethods,
          NELEM(sMethods));
    }
    
    
    // Java 与 Native 方法映射表
    static JNINativeMethod sMethods[] = {
        /* name, signature, funcPtr */
        {"classInitNative", "()V", (void*)classInitNative},
        {"initNative", "(ZZI[Ljava/lang/String;Z)Z", (void*)initNative},
    ......
    }
    

    XXXX jniRegisterNativeMethods

    找到2个实现,按道理在 Android R 版本应该是 art 虚拟机。为了严谨,后续在 pixel 5上实测一下,填个坑

    1. art/benchmark/micro-native/micro_native.cc
    void jniRegisterNativeMethods(JNIEnv* env,
                                  const char* className,
                                  const JNINativeMethod* methods,
                                  int numMethods) {
        jclass c = env->FindClass(className);
        if (c == nullptr) {
            char* tmp;
            const char* msg;
            if (asprintf(&tmp,
                         "Native registration unable to find class '%s'; aborting...",
                         className) == -1) {
                // Allocation failed, print default warning.
                msg = "Native registration unable to find class; aborting...";
            } else {
                msg = tmp;
            }
            env->FatalError(msg);
        }
    
        if (env->RegisterNatives(c, methods, numMethods) < 0) {
            char* tmp;
            const char* msg;
            if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
                // Allocation failed, print default warning.
                msg = "RegisterNatives failed; aborting...";
            } else {
                msg = tmp;
            }
            env->FatalError(msg);
        }
    }
    
    1. libnativehelper/JNIHelp.c
    int jniRegisterNativeMethods(JNIEnv* env, const char* className,
        const JNINativeMethod* methods, int numMethods)
    {
        ALOGV("Registering %s's %d native methods...", className, numMethods);
        jclass clazz = (*env)->FindClass(env, className);
        ALOG_ALWAYS_FATAL_IF(clazz == NULL,
                             "Native registration unable to find class '%s'; aborting...",
                             className);
        int result = (*env)->RegisterNatives(env, clazz, methods, numMethods);
        (*env)->DeleteLocalRef(env, clazz);
        if (result == 0) {
            return 0;
        }
    
        // Failure to register natives is fatal. Try to report the corresponding exception,
        // otherwise abort with generic failure message.
        jthrowable thrown = (*env)->ExceptionOccurred(env);
        if (thrown != NULL) {
            struct ExpandableString summary;
            ExpandableStringInitialize(&summary);
            if (GetExceptionSummary(env, thrown, &summary)) {
                ALOGF("%s", summary.data);
            }
            ExpandableStringRelease(&summary);
            (*env)->DeleteLocalRef(env, thrown);
        }
        ALOGF("RegisterNatives failed for '%s'; aborting...", className);
        return result;
    }
    

    2.1 getLibPaths()

        private String[] getLibPaths() {
            if (mLibPaths == null) {
                synchronized(this) {
                    if (mLibPaths == null) {
                        mLibPaths = initLibPaths();
                    }
                }
            }
            return mLibPaths;
        }
    
    
        private static String[] initLibPaths() {
            String javaLibraryPath = System.getProperty("java.library.path");
            if (javaLibraryPath == null) {
                return EmptyArray.STRING;
            }
            String[] paths = javaLibraryPath.split(":");
            // Add a '/' to the end of each directory so we don't have to do it every time.
            for (int i = 0; i < paths.length; ++i) {
                if (!paths[i].endsWith("/")) {
                    paths[i] += "/";
                }
            }
            return paths;
        }
    

    如前文 1.1.2.2 System.getProperty("java.library.path")

    2.2 System.mapLibraryName()

    如前文 1.1.1 System.mapLibraryName()

    2.3 nativeLoad(filename, loader, callerClass)

    如前文 1.3 nativeLoad(filename, loader)

    相关文章

      网友评论

          本文标题:System.loadLibrary(libname)

          本文链接:https://www.haomeiwen.com/subject/dontgltx.html