java jni 原理_深入理解JNI

/*** Loads and links the dynamic library that is identified through the* specified path. This method is similar to {@link #loadLibrary(String)},* but it accepts a full path specification whereas {@code loadLibrary} just* accepts the name of the library to load.** @param pathName* the path of the file to be loaded.*/

public static void load(String pathName) {

Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());

}

/** Loads and links the given library without security checks.*/

void load(String pathName, ClassLoader loader) {

if (pathName == null) {

throw new NullPointerException("pathName == null");

}

String error = doLoad(pathName, loader);

if (error != null) {

throw new UnsatisfiedLinkError(error);

}

}

/*** Loads and links the library with the specified name. The mapping of the* specified library name to the full path for loading the library is* implementation-dependent.** @param libName* the name of the library to load.* @throws UnsatisfiedLinkError* if the library could not be loaded.*/

public static void loadLibrary(String libName) {

Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());

}

// 以 public static void loadLibrary(String libName) { 为例

/*** Provides a limited interface to the Dalvik VM stack. This class is mostly* used for implementing security checks.** @hide*/

public final class VMStack {

/*** 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.*/

native public static ClassLoader getCallingClassLoader();

//...

}

/** Searches for a library, then loads and links it without security checks.*/

void loadLibrary(String libraryName, ClassLoader loader) {

if (loader != null) {

String filename = loader.findLibrary(libraryName);// 使用classloader查找叫libraryName//名称的so if (filename == null) {

throw new UnsatisfiedLinkError("Couldn't load " + libraryName +

" from loader " + loader +

": findLibrary returned null");

}

String error = doLoad(filename, loader);//----------------------------- if (error != null) {

throw new UnsatisfiedLinkError(error);

}

return;

}

String filename = System.mapLibraryName(libraryName);

List candidates = new ArrayList();

String lastError = null;

for (String directory : mLibPaths) {

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);

}

#define JNI_LIB_PREFIX "lib"

#define JNI_LIB_SUFFIX ".so"

JNIEXPORT jstring JNICALL

System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)// 返回 "lib($libname).so"{

int len;

int prefix_len = (int) strlen(JNI_LIB_PREFIX);

int suffix_len = (int) strlen(JNI_LIB_SUFFIX);

jchar chars[256];

if (libname == NULL) {

JNU_ThrowNullPointerException(env, 0);

return NULL;

}

len = (*env)->GetStringLength(env, libname);

if (len > 240) {

JNU_ThrowIllegalArgumentException(env, "name too long");

return NULL;

}

cpchars(chars, JNI_LIB_PREFIX, prefix_len);

//static void GetStringRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, // jchar* buf)//从 java_string的start copy 长度length到buf (*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);

}

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;

if (loader != null && loader instanceof BaseDexClassLoader) {

ldLibraryPath = ((BaseDexClassLoader) loader).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);

}

}

// TODO: should be synchronized, but dalvik doesn't support synchronized internal natives. private static native String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath);

//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);

if (filename.c_str() == nullptr) {

return nullptr;

}

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;

}

}

// 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/jni/java_vm_ext.ccbool JavaVMExt::LoadNativeLibrary(JNIEnv* env,

const std::string& path,

jobject class_loader,

jclass caller_class,

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_);

library = libraries_->Get(path);

}

void* class_loader_allocator = nullptr;

std::string caller_location;

{

ScopedObjectAccess soa(env);

// As the incoming class loader is reachable/alive during the call of this function, // it's okay to decode it without worrying about unexpectedly marking it alive. ObjPtr<:classloader> loader = soa.Decode<:classloader>(class_loader);

ClassLinker* class_linker = Runtime::Current()->GetClassLinker();

if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {

loader = nullptr;

class_loader = nullptr;

if (caller_class != nullptr) {

ObjPtr<:class> caller = soa.Decode<:class>(caller_class);

ObjPtr<:dexcache> dex_cache = caller->GetDexCache();

if (dex_cache != nullptr) {

caller_location = dex_cache->GetLocation()->ToModifiedUtf8();

}

}

}

class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());

CHECK(class_loader_allocator != nullptr);

}

if (library != nullptr) {

// Use the allocator pointers for class loader equality to avoid unnecessary weak root decode. if (library->GetClassLoaderAllocator() != class_loader_allocator) {

// 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. // // This isn't very common. So spend some time to get a readable message. auto call_to_string = [&](jobject obj) -> std::string {

if (obj == nullptr) {

return "null";

}

// Handle jweaks. Ignore double local-ref. ScopedLocalRef local_ref(env, env->NewLocalRef(obj));

if (local_ref != nullptr) {

ScopedLocalRef local_class(env, env->GetObjectClass(local_ref.get()));

jmethodID to_string = env->GetMethodID(local_class.get(),

"toString",

"()Ljava/lang/String;");

DCHECK(to_string != nullptr);

ScopedLocalRef local_string(env,

env->CallObjectMethod(local_ref.get(), to_string));

if (local_string != nullptr) {

ScopedUtfChars utf(env, reinterpret_cast(local_string.get()));

if (utf.c_str() != nullptr) {

return utf.c_str();

}

}

if (env->ExceptionCheck()) {

// We can't do much better logging, really. So leave it with a Describe. env->ExceptionDescribe();

env->ExceptionClear();

}

return "(Error calling toString)";

}

return "null";

};

std::string old_class_loader = call_to_string(library->GetClassLoader());

std::string new_class_loader = call_to_string(class_loader);

StringAppendF(error_msg, "Shared library \"%s\" already opened by "

"ClassLoader %p(%s); can't open in ClassLoader %p(%s)",

path.c_str(),

library->GetClassLoader(),

old_class_loader.c_str(),

class_loader,

new_class_loader.c_str());

LOG(WARNING) << *error_msg;

return 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());

return false;

}

return true;

}

// 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.

// Retrieve the library path from the classloader, if necessary. ScopedLocalRef library_path(env, GetLibrarySearchPath(env, class_loader));

Locks::mutator_lock_->AssertNotHeld(self);

const char* path_str = path.empty() ? nullptr : path.c_str();

bool needs_native_bridge = false;

char* nativeloader_error_msg = nullptr;

//----------------------------------//1.打开动态链接库 void* handle = android::OpenNativeLibrary(

env,

runtime_->GetTargetSdkVersion(),

path_str,

class_loader,

(caller_location.empty() ? nullptr : caller_location.c_str()),

library_path.get(),

&needs_native_bridge,

&nativeloader_error_msg);

VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";

if (handle == nullptr) {// 2.检查错误信息 *error_msg = nativeloader_error_msg;

android::NativeLoaderFreeErrorMessage(nativeloader_error_msg);

VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;

return false;

}

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. std::unique_ptr new_library(

new SharedLibrary(env,

self,

path,

handle,

needs_native_bridge,

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;

}

}

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 << "]";

bool was_successful = false;

void* sym = library->FindSymbol("JNI_OnLoad", nullptr);//3. 获取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 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*);

//4. 强制转为函数指针JNI_OnLoadFn jni_on_load = reinterpret_cast(sym);

//5. 然后调用JNI_OnLoad 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);

// 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值