接上一篇文章:【个人笔记二】ART系统OAT文件的加载解析
- 在ART上用YAHFA、Legend以及一个java层实现的Andix: http://weishu.me/2017/03/20/dive-into-art-hello-world/,发现除了framework层的类(如telephonymanager)和应用中的类有效外,对于java核心库的类(如IOBridge和Class等)的hook都无效,所以我就以telephonymanager和IOBridge这两个类为例,试图从编译解析加载等角度分析这两者的区别以及造成hook结果不同的原因,如果有大牛能指点一二的话,不胜感激。。。
上一章跟了如何加载解析OAT文件,把OAT文件加载到内存,解析出DEX存放在OatDexFile对象中,然后通过调用OatFile::OatDexFile::GetOatClass和OatFile::OatClass::GetOatMethod等方法可以获得对应的类和方法。这一章继续跟一下类和方法的解析流程,看有没有什么发现。
本章主要跟一下JNI::FindClass和JNI::GetStaticMethodID方法的流程,老规矩,先放一张本章内容的整体流程图:
通过前面Android运行时ART简要介绍和学习计划一文的学习,我们可以知道,ART运行时的入口是com.android.internal.os.ZygoteInit类的静态成员函数main,如下所示:
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
......
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
......
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
......
}
}
......
}
这个函数定义在文件frameworks/base/core/jni/AndroidRuntime.cpp中。
在AndroidRuntime类的成员函数start中,首先是通过调用函数startVm创建了一个Java虚拟机mJavaVM及其JNI接口env。这个Java虚拟机实际上就是ART运行时。在接下来的描述中,我们将不区分ART虚拟机和ART运行时,并且认为它们表达的是同一个概念。获得了ART虚拟机的JNI接口之后,就可以通过它提供的函数FindClass和GetStaticMethodID来加载com.android.internal.os.ZygoteInit类及其静态成员函数main。于是,最后就可以再通过JNI接口提供的函数CallStaticVoidMethod来调用com.android.internal.os.ZygoteInit类的静态成员函数main,以及进行到ART虚拟机里面去运行。
接下来,我们就通过分析JNI接口FindClass和GetStaticMethodID的实现,以便理解ART运行时是如何查找到指定的类和方法的。在接下来的一篇文章中,我们再分析ART运行时是如何通过JNI接口CallStaticVoidMethod来执行指定类方法的本地机器指令的。
在第一章分析JNI接口FindClass和GetStaticMethodID的实现之前,我们先要讲清楚JNI接口是如何创建的。从前面Android运行时ART加载OAT文件的过程分析一文可以知道,与ART虚拟机主线程关联的JNI接口是在函数JNI_CreateJavaVM中创建的,如下所示:
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
......
*p_env = Thread::Current()->GetJniEnv();
......
return JNI_OK;
}
这个函数定义在文件art/runtime/jni_internal.cc中。
调用Thread类的静态成员函数Current获得的是用来描述当前线程(即ART虚拟机的主线程)的一个Thread对象,再通过调用这个Thread对象的成员函数GetJniEnv就获得一个JNI接口,并且保存在输出参数p_env中。
Thread类的成员函数GetJniEnv的实现如下所示:
class PACKED(4) Thread {
public:
......
// JNI methods
JNIEnvExt* GetJniEnv() const {
return tlsPtr_.jni_env;
}
......
private:
......
struct PACKED(4) tls_ptr_sized_values {
......
// Every thread may have an associated JNI environment
JNIEnvExt* jni_env;
......
} tlsPtr_;
......
};
这个函数定义在文件art/runtime/thread.h中。
Thread类的成员函数GetJniEnv返回的是成员变量指向的一个JNIEnvExt对象。
JNIEnvExt类是从JNIEnv类继承下来的,如下所示:
struct JNIEnvExt : public JNIEnv {
......
};
这个类定义在文件art/runtime/ jni_env_ext.h。
JNIEnv类定义了JNI接口,如下所示:
typedef _JNIEnv JNIEnv;
......
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
......
jint GetVersion()
{ return functions->GetVersion(this); }
......
};
这个类定义在文件libnativehelper/include/nativehelper/jni.h中。
在JNIEnv类中,最重要的就是成员变量functions了,它指向的是一个类型为JNINativeInterface的JNI函数表。所有的JNI接口调用都是通过这个JNI函数表来实现的。例如,用来获得版本号的JNI接口GetVersion就是通过调用JNI函数表中的GetVersion函数来实现的。
那么,上述的JNI函数表是如何创建的呢?通过JNIEnvExt类的构造函数可以知道答案,如下所示:
JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in)
: self(self_in),
vm(vm_in),
local_ref_cookie(IRT_FIRST_SEGMENT),
locals(kLocalsInitial, kLocalsMax, kLocal, false),
check_jni(false),
critical(0),
monitors("monitors", kMonitorsInitial, kMonitorsMax) {
functions = unchecked_functions = GetJniNativeInterface();
if (vm->IsCheckJniEnabled()) {
SetCheckJniEnabled(true);
}
}
这个函数定义在文件art/runtime/jni_env_ext.cc中。
const JNINativeInterface* GetJniNativeInterface() {
return &gJniNativeInterface;
}
这个函数定义在文件art/runtime/jni_internal.cc中。
JNIEnvExt类的构造函数将父类JNIEnv的成员变量functions初始化为全局变量gJniNativeInterface。也就是说,JNI函数表实际是由全局变量gJniNativeInterface来描述的。
全局变量gJniNativeInterface的定义如下所示:
const JNINativeInterface gJniNativeInterface = {
JNI::GetVersion,
JNI::FindClass,
......
JNI::GetStaticMethodID,
......
JNI::CallStaticVoidMethod,
......
};
这个全局变量定义在文件art/runtime/jni_internal.cc中。
从这里可以看出,JNI函数表实际上是由JNI类的静态成员函数组成的。例如,JNI函数GetVersion是由JNI类的静态成员函数GetVersion来实现的。理解了这一点之后,我们就轻松地知道同接下来我们要分析的JNI接口FindClass和GetStaticMethodID分别是由JNI类的静态成员函数FindClass和GetStaticMethodID来实现的。事实上,如果读者看过Dalvik虚拟机的启动过程分析这篇文章,那么对上述的JNI接口定义是一目了然的。
JNI类的静态成员函数FindClass的实现如下所示:
static jclass FindClass(JNIEnv* env, const char* name) {
CHECK_NON_NULL_ARGUMENT(name);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
std::string descriptor(NormalizeJniClassDescriptor(name));
ScopedObjectAccess soa(env);
mirror::Class* c = nullptr;
if (runtime->IsStarted()) {
StackHandleScope<1> hs(soa.Self());
Handle<mirror