publicfinalclassVMStack{ /** * Returns the defining class loader of the caller's caller. * * @return the requested class loader, or {@code null} if this is the * bootstrap class loader. */ nativepublicstatic ClassLoader getCallingClassLoader(); ..... }
/** * Allows Java applications to interface with the environment in which they are * running. Applications can not create an instance of this class, but they can * get a singleton instance by invoking {@link #getRuntime()}. * * @see System */ publicclassRuntime{
/* * Searches for and loads the given shared library using the given ClassLoader. */ voidloadLibrary(String libraryName, ClassLoader loader){ if (loader != null) { // 返回的filename是目标库的全路径。 // 此时的名字已经做好拼接.如/com/example/test/libtest.so 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". thrownew UnsatisfiedLinkError(loader + " couldn't find \"" + System.mapLibraryName(libraryName) + "\""); } String error = doLoad(filename, loader); if (error != null) { thrownew UnsatisfiedLinkError(error); } return; }
/** * A class loader that loads classes from {@code .jar} and {@code .apk} files * containing a {@code classes.dex} entry. This can be used to execute code not * installed as part of an application. * * <p>This class loader requires an application-private, writable directory to * cache optimized classes. Use {@code Context.getCodeCacheDir()} to create * such a directory: <pre> {@code * File dexOutputDir = context.getCodeCacheDir(); * }</pre> * * <p><strong>Do not cache optimized classes on external storage.</strong> * External storage does not provide access controls necessary to protect your * application from code injection attacks. */ publicclassDexClassLoaderextendsBaseDexClassLoader{ }
/** * Provides a simple {@link ClassLoader} implementation that operates on a list * of files and directories in the local file system, but does not attempt to * load classes from the network. Android uses this class for its system class * loader and for its application class loader(s). */ publicclassPathClassLoaderextendsBaseDexClassLoader{ }
/** * A pair of lists of entries, associated with a {@code ClassLoader}. * One of the lists is a dex/resource path — typically referred * to as a "class path" — list, and the other names directories * containing native code libraries. Class path entries may be any of: * a {@code .jar} or {@code .zip} file containing an optional * top-level {@code classes.dex} file as well as arbitrary resources, * or a plain {@code .dex} file (with no possibility of associated * resources). * * <p>This class also contains methods to use these lists to look up * classes and resources.</p> */ /*package*/finalclassDexPathList{ privatestaticfinal String DEX_SUFFIX = ".dex"; privatestaticfinal String zipSeparator = "!/";
/** class definition context */ privatefinal ClassLoader definingContext;
/** * List of dex/resource (class path) elements. * Should be called pathElements, but the Facebook app uses reflection * to modify 'dexElements' (http://b/7726934). */ privatefinal Element[] dexElements;
/** List of native library path elements. */ privatefinal Element[] nativeLibraryPathElements;
/** List of application native library directories. */ privatefinal List<File> nativeLibraryDirectories;
/** List of system native library directories. */ privatefinal List<File> systemNativeLibraryDirectories;
/** * Exceptions thrown during creation of the dexElements list. */ privatefinal IOException[] dexElementsSuppressedExceptions; ........ ........ /** * Element of the dex/resource file path */ /*package*/staticclassElement{ privatefinal File dir; privatefinalboolean isDirectory; privatefinal File zip; privatefinal DexFile dexFile;
/** * Finds the named native code library on any of the library * directories pointed at by this instance. This will find the * one in the earliest listed directory, ignoring any that are not * readable regular files. * * @return the complete path to the library or {@code null} if no * library was found */ public String findLibrary(String libraryName){ String fileName = System.mapLibraryName(libraryName);
for (Element element : nativeLibraryPathElements) { String path = element.findNativeLibrary(fileName);
if (path != null) { return path; } }
returnnull; }
1、首先,拼接so的全称,也就是上面的System.mapLibraryName
1 2 3 4 5 6 7 8 9 10 11
/** * Returns the platform specific file name format for the shared library * named by the argument. On Android, this would turn {@code "MyLibrary"} into * {@code "libMyLibrary.so"}. */ publicstatic String mapLibraryName(String nickname){ if (nickname == null) { thrownew NullPointerException("nickname == null"); } return"lib" + nickname + ".so"; }
publicDexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory){ .... .... // Native libraries may exist in both the system and // application library paths, and we use this search order: // // 1. This class loader's library path for application libraries (libraryPath): // 1.1. Native library directories // 1.2. Path to libraries in apk-files // 2. The VM's library path from the system property for system libraries // also known as java.library.path // // This order was reversed prior to Gingerbread; see http://b/2933456. // 两个路径,一个是当前应用的库路径,一个是系统的库路径。 // add的方式加入list中,当前应用的库路径position先于系统的 this.nativeLibraryDirectories = splitPaths(libraryPath, false); this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
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". thrownew UnsatisfiedLinkError(loader + " couldn't find \"" + System.mapLibraryName(libraryName) + "\""); } String error = doLoad(filename, loader); if (error != null) { thrownew UnsatisfiedLinkError(error); } return;
private String doLoad(String name, ClassLoader loader){ // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH, // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
// The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load // libraries with no dependencies just fine, but an app that has multiple libraries that // depend on each other needed to load them in most-dependent-first order.
// We added API to Android's dynamic linker so we can update the library path used for // the currently-running process. We pull the desired path out of the ClassLoader here // and pass it to nativeLoad so that it can call the private dynamic linker API.
// We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the // beginning because multiple apks can run in the same process and third party code can // use its own BaseDexClassLoader.
// We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any // dlopen(3) calls made from a .so's JNI_OnLoad to work too.
// So, find out what the native library search path is for the ClassLoader in question... String ldLibraryPath = null; String dexPath = null; if (loader == null) { // We use the given library path for the boot class loader. This is the path // also used in loadLibraryName if loader is null. ldLibraryPath = System.getProperty("java.library.path"); } elseif (loader instanceof BaseDexClassLoader) { BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader; ldLibraryPath = dexClassLoader.getLdLibraryPath(); } // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized // internal natives. synchronized (this) { return nativeLoad(name, loader, ldLibraryPath); } }
这里可以看到,如果classloader为空,ldLibraryPath就指定为本地文件库的路径。
在BaseDexClassLoader中,获取ldLibraryPath的方法为:
遍历pathList,拿到当前应用下的库路径(不是系统的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * @hide */ public String getLdLibraryPath(){ StringBuilder result = new StringBuilder(); for (File directory : pathList.getNativeLibraryDirectories()) { if (result.length() > 0) { result.append(':'); } result.append(directory); }
std::string error_msg; { JavaVMExt* vm = Runtime::Current()->GetJavaVM(); bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, &error_msg); if (success) { returnnullptr; } }
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF. env->ExceptionClear(); return env->NewStringUTF(error_msg.c_str()); }
// See if we've already loaded this library. If we have, and the class loader // matches, return successfully without doing anything. // TODO: for better results we should canonicalize the pathname (or even compare // inodes). This implementation is fine if everybody is using System.loadLibrary. SharedLibrary* library; Thread* self = Thread::Current(); { // TODO: move the locking (and more of this logic) into Libraries. MutexLock mu(self, *Locks::jni_libraries_lock_); // 这里的libraries_是一个LibraryList类,里面记录的是所有被Crazy Linker加载过的动态链接库 // 在这里面查一查,如果有,那就说明已经被加载了。 library = libraries_->Get(path); } // LibraryList中存在目标库 if (library != nullptr) { // 首先判断是否是由同一个Classloader加载的 // 如果不是,返回异常。因为同一个so库只能被同一个classloader加载 if (env->IsSameObject(library->GetClassLoader(), class_loader) == JNI_FALSE) { // The library will be associated with class_loader. The JNI // spec says we can't load the same library into more than one // class loader. StringAppendF(error_msg, "Shared library \"%s\" already opened by " "ClassLoader %p; can't open in ClassLoader %p", path.c_str(), library->GetClassLoader(), class_loader); LOG(WARNING) << error_msg; returnfalse; } // 已经加载了这个so库,判断以前是否加载成功 // 以前加载成功,返回true // 以前加载失败,返回false VLOG(jni) << "[Shared library \"" << path << "\" already loaded in " << " ClassLoader " << class_loader << "]"; if (!library->CheckOnLoadResult()) { StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt " "to load \"%s\"", path.c_str()); returnfalse; } returntrue; }
// Open the shared library. Because we're using a full path, the system // doesn't have to search through LD_LIBRARY_PATH. (It may do so to // resolve this library's dependencies though.)
// Failures here are expected when java.library.path has several entries // and we have to hunt for the lib.
// Below we dlopen but there is no paired dlclose, this would be necessary if we supported // class unloading. Libraries will only be unloaded when the reference count (incremented by // dlopen) becomes zero from dlclose.
// 检查打开的过程中是否存在异常 if (env->ExceptionCheck() == JNI_TRUE) { LOG(ERROR) << "Unexpected exception:"; env->ExceptionDescribe(); env->ExceptionClear(); } // Create a new entry. // TODO: move the locking (and more of this logic) into Libraries. bool created_library = false; { // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering. // 创建一个共享文件库Entry // 因为是多线程的,所以在这里需要判断我们创建的时候是否已经有其他线程先于我们创建了同一个共享文件库。 // 如果有,那么这里的加载其实就是不必要的了(因为其他线程已经加载了这个库)。 // 所以直接释放当前的资源即可,并调用CheckOnLoadResult std::unique_ptr<SharedLibrary> new_library( new SharedLibrary(env, self, path, handle, class_loader)); 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) { LOG(INFO) << "WOW: we lost a race to add shared library: " << "\"" << path << "\" ClassLoader=" << class_loader; return library->CheckOnLoadResult(); } VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
// 上面有两次加载so,所以这里需要通过标志位判断到底是用哪种方式加载的 // 对于正常加载的流程来说,使用dlsym获取JNI_OnLoad的地址 bool was_successful = false; void* sym; if (needs_native_bridge) { library->SetNeedsNativeBridge(); sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr); } else { sym = dlsym(handle, "JNI_OnLoad"); } 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);
// 获取JNI的版本信息 VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]"; typedefint(*JNI_OnLoadFn)(JavaVM*, void*); 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()); } elseif (IsBadJniVersion(version)) { StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d", path.c_str(), version); // It's unwise to call dlclose() here, but we can mark it // as bad and ensure that future load attempts will fail. // We don't know how far JNI_OnLoad got, so there could // be some partially-initialized stuff accessible through // newly-registered native method calls. We could try to // unregister them, but that doesn't seem worthwhile. } else { was_successful = true; } VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure") << " from JNI_OnLoad in \"" << path << "\"]"; }
// DT_INIT should be called before DT_INIT_ARRAY if both are present. call_function("DT_INIT", init_func_); // 调用init_func_这个函数 call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false);
/* * This is called by the VM when the shared library is first loaded. */ jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv* env = NULL; jint result = -1;
/* * Explicitly register all methods for our class. * * While we're at it, cache some class references and method/field IDs. * * Returns 0 on success. */ staticintregisterMethods(JNIEnv* env){ staticconstchar* const kClassName = "com/example/android/platform_library/PlatformLibrary"; jclass clazz;
/* look up the class */ clazz = env->FindClass(kClassName); if (clazz == NULL) { ALOGE("Can't find class %s\n", kClassName); return-1; }
/* register all the methods */ if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) { ALOGE("Failed registering methods for %s\n", kClassName); return-1; }
/* fill out the rest of the ID cache */ return cacheIds(env, clazz); }