美文网首页
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