System.loadlibary,System.load是怎么加载so库的?他们是在Runtime的启动过程中通过loadnativelibary来加载libjavacore.so的动态库,这个动态库是这两个函数的实现体。
libcore/ojluni/src/main/java/java/lang/System.java
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
直接调用了Runtime的函数loadLibary0,然后经过doload,nativeload进入native层,
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);
}
接着通过jvm的LoadNativeLibary执行实际工作,
art/runtime/openjdkjvm/openjdkjvm.cc
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jstring javaLibrarySearchPath) {
ScopedUtfChars filename(env, javaFilename);
std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
javaLibrarySearchPath,
&error_msg);
}
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
env->ExceptionClear();
return env->NewStringUTF(error_msg.c_str());
}
art/runtime/java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jstring library_path,
std::string* error_msg) {
error_msg->clear();
// 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_);
//判断是否已经加载过这个libary
library = libraries_->Get(path);
}
.......
// 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.
Locks::mutator_lock_->AssertNotHeld(self);
const char* path_str = path.empty() ? nullptr : path.c_str();
void* handle = android::OpenNativeLibrary(env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
library_path);
//是否需要nativebridge的帮助,如果handle为空指针,说明前面OpenNativeLibrary失败了。
bool needs_native_bridge = false;
if (handle == nullptr) {
if (android::NativeBridgeIsSupported(path_str)) {
handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);
needs_native_bridge = true;
}
}
// 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.
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);
if (library == nullptr) { // We won race to get libraries_lock.
library = new_library.release();
libraries_->Put(path, library);
created_library = true;
}
}
bool was_successful = false;
void* sym;
if (needs_native_bridge) {
library->SetNeedsNativeBridge();
}
sym = library->FindSymbol("JNI_OnLoad", nullptr);
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);
}
library->SetResult(was_successful);
return was_successful;
}
LoadNativeLibrary的目的就是通过各种手段去加载动态链接库,然后执行其中的JNI_OnLoad接口(这个函数是jni库的首选入口,可以利用它完成一些初始化工作)。
上面函数中有个NativeBridge的概念:这个技术用来处理当前系统平台的指令集和目标对象(Library)的指令集不一致时的兼容性问题。比如说,Android的模拟器,通常直接编译成x86版本运行在开发机器上,那么对于一个只提供了arm版本的动态链接库文件的apk,如何保证它可以在x86模拟器上运行,就是nativeBridge要解决的问题。
正常途径,加载Library利用的是OpenNativeLibrary这个函数。
system/core/libnativeloader/native_loader.cpp
void* OpenNativeLibrary(JNIEnv* env,
int32_t target_sdk_version,
const char* path,
jobject class_loader,
jstring library_path) {
#if defined(__ANDROID__)
UNUSED(target_sdk_version);
if (class_loader == nullptr) {
return dlopen(path, RTLD_NOW);
}
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
if (ns == nullptr) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr);
if (ns == nullptr) {
return nullptr;
}
}
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns;
return android_dlopen_ext(path, RTLD_NOW, &extinfo);
#else
UNUSED(env, target_sdk_version, class_loader, library_path);
return dlopen(path, RTLD_NOW);
#endif
}
先利用FindNamespaceByClassLoader查找当前的ClassLoader是否有相关的Namespace,如果没有直接跳转到android_dlopen_ext;如果有调用其Create方法创建一个Namespace。
android_dlopen_ext跟dlopen类似,第一个参数是要打开的动态库的名称,第二个参数RTLD_NOW,表示动态库中所有未定义的符号在dlopen返回前都会被解析。
接下来的实现,是调用find_libary来查找动态库,找到后,调用dlsym来查找加载的动态库中是否包含JNI_OnLoader入口函数。
接下来看下Java层声明的native函数如何与本地层的具体实现建立其正确的映射关系。
在动态库的入口函数JNI_OnLoader中,通常会调用AndroidRuntime::registerNativeMethods方法:
int ret1 = AndroidRuntime::registerNativeMethods(env,
"android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods));
registerNativeMethods就是把JNI中的函数注册到虚拟机中,这个函数是对JNIRegisterNativeMethods的中转。
libnativehelper/JNIHelp.cpp
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
scoped_local_ref<jclass> c(env, findClass(env, className));
if ((*env)->RegisterNatives(e, c.get(), gMethods, 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;
}
e->FatalError(msg);
}
return 0;
}
变量env是JNIEnv对象,是由JNI_OnLoader的JavaVM获取的,并且是线程私有的。
art/runtime/jni_internal.cc
static jint RegisterNatives(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
jint method_count) {
return RegisterNativeMethods(env, java_class, methods, method_count, true);
}
static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
jint method_count, bool return_errors) {
if (UNLIKELY(method_count < 0)) {
JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
method_count);
return JNI_ERR; // Not reached except in unit tests.
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
if (UNLIKELY(method_count == 0)) {
LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
<< PrettyDescriptor(c);
return JNI_OK;
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
const void* fnPtr = methods[i].fnPtr;
if (UNLIKELY(name == nullptr)) {
ReportInvalidJNINativeMethod(soa, c, "method name", i, return_errors);
return JNI_ERR;
} else if (UNLIKELY(sig == nullptr)) {
ReportInvalidJNINativeMethod(soa, c, "method signature", i, return_errors);
return JNI_ERR;
} else if (UNLIKELY(fnPtr == nullptr)) {
ReportInvalidJNINativeMethod(soa, c, "native function", i, return_errors);
return JNI_ERR;
}
bool is_fast = false;
// Notes about fast JNI calls:
//
// On a normal JNI call, the calling thread usually transitions
// from the kRunnable state to the kNative state. But if the
// called native function needs to access any Java object, it
// will have to transition back to the kRunnable state.
//
// There is a cost to this double transition. For a JNI call
// that should be quick, this cost may dominate the call cost.
//
// On a fast JNI call, the calling thread avoids this double
// transition by not transitioning from kRunnable to kNative and
// stays in the kRunnable state.
//
// There are risks to using a fast JNI call because it can delay
// a response to a thread suspension request which is typically
// used for a GC root scanning, etc. If a fast JNI call takes a
// long time, it could cause longer thread suspension latency
// and GC pauses.
//
// Thus, fast JNI should be used with care. It should be used
// for a JNI call that takes a short amount of time (eg. no
// long-running loop) and does not block (eg. no locks, I/O,
// etc.)
//
// A '!' prefix in the signature in the JNINativeMethod
// indicates that it's a fast JNI call and the runtime omits the
// thread state transition from kRunnable to kNative at the
// entry.
if (*sig == '!') {
is_fast = true;
++sig;
}
// Note: the right order is to try to find the method locally
// first, either as a direct or a virtual method. Then move to
// the parent.
ArtMethod* m = nullptr;
bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
for (mirror::Class* current_class = c;
current_class != nullptr;
current_class = current_class->GetSuperClass()) {
// Search first only comparing methods which are native.
m = FindMethod<true>(current_class, name, sig);
if (m != nullptr) {
break;
}
// Search again comparing to all methods, to find non-native methods that match.
m = FindMethod<false>(current_class, name, sig);
if (m != nullptr) {
break;
}
if (warn_on_going_to_parent) {
LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
<< "This is slow, consider changing your RegisterNatives calls.";
warn_on_going_to_parent = false;
}
}
if (m == nullptr) {
LOG(return_errors ? ERROR : INTERNAL_FATAL) << "Failed to register native method "
<< PrettyDescriptor(c) << "." << name << sig << " in "
<< c->GetDexCache()->GetLocation()->ToModifiedUtf8();
// Safe to pass in LOG(FATAL) since the log object aborts in destructor and only goes
// out of scope after the DumpClass is done executing.
c->DumpClass(LOG(return_errors ? ERROR : FATAL), mirror::Class::kDumpClassFullDetail);
ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static");
return JNI_ERR;
} else if (!m->IsNative()) {
LOG(return_errors ? ERROR : FATAL) << "Failed to register non-native method "
<< PrettyDescriptor(c) << "." << name << sig
<< " as native";
ThrowNoSuchMethodError(soa, c, name, sig, "native");
return JNI_ERR;
}
VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
m->RegisterNative(fnPtr, is_fast);
}
return JNI_OK;
}
注册过程的目的是建立Java函数和native函数之间的对应关系,所以先通过FindMethod找到Java_class中的Java层函数,这个注册通常是一个函数集合methods,集合中的每一个元素都是JNINativeMethod对象,包含了java函数名、signature、native函数原型。FindMethod查找到的结果是一个ArtMethod,最后有RegisterNative完成注册工作,它要做的就是将native method在内存中地址通过SetEntryPointFromJni设置为对应ArtMethod的JNI入口。在JNI环境下,当java层的函数被调用后,首先找到它的ArtMethod对象,然后通过GetEntryPoint得到JNI入口代码的地址。
这就完成了java层跟native层函数的JNI映射。