【个人笔记三】ART系统类和方法加载分析

本文深入分析了Android ART系统中类和方法的加载过程,从JNI接口FindClass和GetStaticMethodID出发,探讨了类加载、方法查找的细节,包括类加载器、ClassLinker、DexFile等组件的作用。文章通过源码解析,阐述了从类的加载到方法查找的完整流程,对理解ART运行时的内部工作机制具有指导意义。
摘要由CSDN通过智能技术生成

接上一篇文章:【个人笔记二】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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值