一、映射OAT文件到内存并解析头部
若backend是Quick类型,那么会生成quick类型的机器指令。Quick类型的机器指令的函数依赖关系由JNI函数表描述,而不需要通过动态链接器进行重定位。
通过OatFile类的静态成员函数OpenElfFile
来手动加载指定的OAT文件。这种方式是按照ELF文件格式来解析要加载的OAT文件的。
由ElfFile::Open()
将oat文件的elf头和program headers通过 mmap() 映射到虚拟内存空间。由OatFile::Setup
函数解析这些头部信息,并将获得的信息存入对应的对象和成员变量中,主要是OatDexFile对象
。
begin_和end_这两个值比较关键,是Setup函数的主要依据。begin_
指向的是映射到虚拟内存中的oatdata段(即OAT头)的起始地址,这个OAT头包括OAT实际内容的所有头部信息和DEX文件内容。end_
则是虚拟内存中oatexec段的结束地址,在解析OAT头时用来验证数据的正确性。oatdata段的内容如下所示:
重点展示一下图中第3步,加载elf头和program headers
注:调用mmap()的时候仅仅申请一个vm_area_struct来建立
文件与虚拟内存的映射
,并没有建立虚拟内存与物理内存的映射。Linux并不在调用mmap()时就为进程分配物理内存空间,直到下次真正访问地址空间时发现数据不存在于物理内存空间时,触发Page Fault
即缺页中断,Linux才会将缺失的Page换入内存空间.
mmap()仅分配一个虚拟内存区域,并不分配物理内存区域。根据文件系统类型,将vma->vm_ops设为对应的file_operations. 这里有个很关键的结构体const struct file_operations *f_op;
它是文件驱动操作的入口,在open的时候,完成file_operations的绑定,open流程跟mmap类似。
二、查找类方法的所有本地机器指令
Android系统将ART运行时抽象成一个Java虚拟机。JavaVM接口,与JNIEnv接口的FindClass函数通过 classname 去加载该类所有机器指令,返回到一个OatClass对象中。并且通过LinkCode为类中的每个类方法设置好 解释器入口点 。
然后GetStaticMethodID函数通过 名字和签名 去这个Class对象中查找对应的类方法,返回一个jMethodID值,它就是一个ArtMethod对象指针。ArtMethod对象里保存着机器指令。
为什么每个类方法需要一个解释器入口点?
在ART虚拟机中,并不是所有的类方法都是有对应的本地机器指令的
,并且即使一个类方法有对应的本地机器指令,当ART虚拟机以解释模式运行时,它也需要通过解释器来执行。当以解释器执行的类方法在执行的过程中调用了其它的类方法
时,解释器就需要进一步知道被调用的类方法是应用以解释方式执行,还是本地机器指令方法执行。为了能够进行统一处理,就给每一个类方法都设置一个解释器入口点。
抽象方法声明类中是没有实现的,必须要由子类实现。因此抽象方法
在声明类中是没有对应的本地机器指令的,它们必须要通过解释器来执行
。
三、执行类方法的过程分析
找到类方法对应的ArtMethod对象后,通过CallStaticVoidMethod函数去执行。先创建一个ManagedStack类型的调用栈帧
,然后获取类方法的解释器入口点
,之后通过一个stub(汇编代码)
去间接调用该入口点。