文章目录
System的load和loadLibrary方法
System.loadLibrary或System.load会调用到so库中的JNI_OnLoad方法进行方法注册,但是这个说是这样说,对于读者依然很模糊,到底System.loadLibrary或System.load到底是怎样的一种流程进行加载的并且调用JNI_OnLoad方法进行注册的呢?
加载.so动态链接库需要用到System.load 与 System.loadLibrary
@CallerSensitive
public static void load(String filename) {
Runtime.getRuntime().load0(VMStack.getStackClass1(), filename);
}
@CallerSensitive
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
getRuntime是Runtime.java中的方法,调用该方法返回Runtime对象,得到Java应用城的运行环境RunTime
- System.load()
- System.load 参数必须为库文件的绝对路径,可以是任意路径,例如:
- System.load(“C:\Documents and Settings\TestJNI.dll”); //Windows
- System.load("/usr/lib/TestJNI.so"); //Linux
- System.loadLibrary
- 参数为so的名称,用于加载App安装后自动冲apk包中赋值到
/data/data/paclagename/lib
下的so
- 参数为so的名称,用于加载App安装后自动冲apk包中赋值到
System的load
synchronized void load0(Class<?> fromClass, String filename) {
if (!(new File(filename).isAbsolute())) {
throw new UnsatisfiedLinkError(
"Expecting an absolute path of the library: " + filename);
}
if (filename == null) {
throw new NullPointerException("filename == null");
}
//【注释1】
String error = doLoad(filename, fromClass.getClassLoader());
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
}
在注释1处:调用doLoad方法,并传入了加载该类的类加载器
private String doLoad(String name, ClassLoader loader) {
String librarySearchPath = null;
if (loader != null && loader instanceof BaseDexClassLoader) {
BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
librarySearchPath = 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, librarySearchPath);
}
}
System的loadLibrary0
整体代码可以分为 ClassLoader不为null 和为null两个部分
synchronized void loadLibrary0(ClassLoader loader, String libname) {
//判断传入的库名称是否合法,比如我们的库是libxxx.so,我们只需要传入xxx就可以了
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
if (loader != null) {//如果类加载器不为空
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".
//不存在库文件则抛出异常
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
//如果库文件存在,就加载
String error = doLoad(filename, loader);
//加载库文件失败,抛出异常
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
//下面这些代码是类加载器为空的情况下才执行,正常情况下开发者app中开发者自己写的库文件加载时不会执行到这里
//因为传入的类加载器不会为空,系统应用才有可能走这里,这时下面获取系统默认的库存放路径才是有用的
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {//getLibPaths()用来获取系统中存放so库的文件路径,下面有相关的实现代码和解释
String candidate = directory + filename;//拼我们要加载库的绝对路径
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {//判断绝对路径上的文件是否存在
String error = doLoad(candidate, loader);//如果存在,并且是只可读,则加载该库
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}
private String[] getLibPaths() {
if (mLibPaths == null) {
synchronized(this) {
if (mLibPaths == null) {
mLibPaths = initLibPaths();//调用initLibPaths方法
}
}
}
return mLibPaths;
}
private static String[] initLibPaths() {
//可以看出系统默认的库文件存放路径是在java.library.path属性中存储的
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;
}
- 如果ClassLoader为null
- 遍历getLibPaths()方法,该方法会返回
"java.library.path"
选项胚子的路径数组 - 拼接出完整的so路径
- 调用doLoad方法
- 遍历getLibPaths()方法,该方法会返回
打印
"java.library.path"
,得到 pathList = /vendor/lib:/system/lib
- 如果ClassLoader不为null
- 调用loader的findLibrary方法得到完整路径
- 调用doLoad方法
loader的findLibrary方法
常规开发的应用基本走的都是loader的findLibrary方法得到完整路径,android系统中的 ClassLoader真正的实现 在dalvik的dalvik.system.PathClassLoader。
打开libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
来看 PathClassLoader这个class 的实现,可以看到,就只是简单的继承 BaseDexClassLoader
而已,没有任何实际的内容
public class BaseDexClassLoader extends ClassLoader {
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.originalPath = dexPath;
this.originalLibraryPath = libraryPath;
this.pathList =
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
}
DexPathList的构造函数
然后是DexPathList的构造函数:
public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
if (definingContext == null) {
throw new NullPointerException("definingContext == null");
}
if (dexPath == null) {
throw new NullPointerException("dexPath == null");
}
if (optimizedDirectory != null) {
if (!optimizedDirectory.exists()) {
throw new IllegalArgumentException(
"optimizedDirectory doesn't exist: "
+ optimizedDirectory);
}
if (!(optimizedDirectory.canRead()
&& optimizedDirectory.canWrite())) {
throw new IllegalArgumentException(
"optimizedDirectory not readable/writable: "
+ optimizedDirectory);
}
}
this.definingContext = definingContext;
this.dexElements =
makeDexElements(splitDexPath(dexPath), optimizedDirectory);
//【关注splitLibraryPath(),这个地方,实际上即是把传进来的libraryPath 又丢给splitLibraryPath来获取library path 的list】
this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}
//在用两个部分的library path list来由splitPaths构造最终的那个path list
//一个部分是,传进来的library path
//另外一个部分是,像我们前面看到的那个,是system property
private static File[] splitLibraryPath(String path) {
ArrayList<File> result = splitPaths(
path, System.getProperty("java.library.path", "."), true);
return result.toArray(new File[result.size()]);
}
ClassLoader的那个findLibrary()实际上会在两个部分的folder中去寻找System.loadLibrary()要load的那个library,一个部分是,构造ClassLoader时,传进来的那个library path,即是app folder,另外一个部分是system property。在android系统中,查找要load的library,实际上会在如下3个folder中进行:
- /vendor/lib
- /system/lib
- /data/app-lib/com.qrcode.qrcode-1
DexPathList的findLibrary
public String findLibrary(String libraryName) {
//这个方法主要目的是给传进来的name添加前缀lib和后缀.so
//最开始我们传进来的name是TestJni,所以此处处理后返回的是libTestJni.so.
String fileName = System.mapLibraryName(libraryName);
for (Element element : nativeLibraryPathElements) {
String path = element.findNativeLibrary(fileName);
if (path != null) {
return path;
}
}
return null;
}
类似余DexPath的findClass方法:
- 每一个Element 对应一个so库
- findNativeLibrary可以返回so的路径
- nativeLibraryPathElements数组在构建DexPath时初始化
这里就提供了一种so修复的方案,就是将so补丁插入到nativeLibraryPathElements数组的前部
nativeLoaded方法
libcore/ojluni/src/main/native/Runtime.c中:
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jstring javaLibrarySearchPath)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath);
}
./art/runtime/openjdkjvm/OpenjdkJvm.cc中的JVM_NativeLoad:
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jstring javaLibrarySearchPath) {
//so文件名转化为ScopedUtfChars
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
}
std::string error_msg;
{
//获取当前云高兴的虚拟机
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
//虚拟机加载so
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
javaLibrarySearchPath,
&error_msg);
if (success) {
return nullptr;
}
}
LoadNativeLibrary
- 判断是否加载过so,两次ClassLoader是否是同一个,避免重复加载
- 打开so,并得到so的举兵,失败就返回false。—>创建新的SharedLibrary,如果传入的path对应的library是空指针,就将新的SharedLibrary赋值给library存储到libraries中
- 查找JNI_OnLoad的函数指针,根据不同情况设置 was_successful的值,最终返回该值
LoadNativeLibrary的part1
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jstring library_path,
std::string* error_msg) {
error_msg->clear();
SharedLibrary* library;
Thread* self = Thread::Current();
{
MutexLock mu(self, *Locks::jni_libraries_lock_);
//【根据so的名称,从 libraries_获取对应的SharedLibrary指针】
library = libraries_->Get(path);
}
。。。。。。。
if (library != nullptr) { //【标示此前加载过该so】
// Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
if (library->GetClassLoaderAllocator() != class_loader_allocator) { //【如果两次传入的ClassLoader不同】
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;
return false;
}
VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
<< " ClassLoader " << class_loader << "]";
if (!library->CheckOnLoadResult()) { //【判断上次加载so的结果,如果有异常也返回so】
StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
"to load \"%s\"", path.c_str());
return false;
}
return true; //【不再重复加载so】
}
LoadNativeLibrary的part2
Locks::mutator_lock_->AssertNotHeld(self);
const char* path_str = path.empty() ? nullptr : path.c_str();
/**
* 【1. 打开路径 path_str的so库,得到so的句柄handle】
*/
void* handle = android::OpenNativeLibrary(env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
library_path);
bool needs_native_bridge = false;
if (handle == nullptr) {
if (android::NativeBridgeIsSupported(path_str)) {
handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);
needs_native_bridge = true;
}
}
VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
/**
* 【2,失败则返回中断】
*/
if (handle == nullptr) {
*error_msg = dlerror();
VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
return false;
}
if (env->ExceptionCheck() == JNI_TRUE) {
LOG(ERROR) << "Unexpected exception:";
env->ExceptionDescribe();
env->ExceptionClear();
}
bool created_library = false;
{
/**
* 【3 创建 SharedLibrary,并将so句柄作为参数传入】
*/
// Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
std::unique_ptr<SharedLibrary> new_library(
new SharedLibrary(env, self, path, handle, class_loader, class_loader_allocator));
MutexLock mu(self, *Locks::jni_libraries_lock_);
library = libraries_->Get(path);
//【4. 获取传入path对应的library,如果为空指针,就将新的SharedLibrary赋值给library存储到ibraries_中】
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 << "]";
LoadNativeLibrary的part3
bool was_successful = false;
void* sym;
if (needs_native_bridge) {
library->SetNeedsNativeBridge();
}
/**
* 【1 查找JNI_OnLoad函数指针(该函数朱永用于native方法的动态注册),并复制俄格sym】
*/
sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
//【2 没找到对应函数,直接标识为成功】
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);
VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);
if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
fault_manager.EnsureArtActionInFrontOfSignalChain();
}
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 (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 << "\"]";
}
library->SetResult(was_successful);
return was_successful;