首先对于应用层的类,由于是标准的从apk经过dex2oat生成OAT文件(虽然后缀还是dex),然后加载到系统中进行类解析和方法链接等流程,这种流程在老罗博客和上述三种常见的ART hook上进行了详细分析,而上述三种hook方法正是针对这种流程设计的,所以毫无疑问有效,此处不再赘述。
接下来正式开始,首先看下源码路径
telephonymanager等框架层类位于android源码目录\framework\base路径下,经编译后生成的jar包位于android系统的/system/framework/framework.jar
IOBridge等java核心类位于android源码目录\libcore路径下,经编译后生成的jar包位于android系统的/system/framework/core.jar或者/system/framework/core-libart.jar
从这个角度看两者也没太大区别,都是从源码生成了jar包,位于了同一个路径下的不同jar包,但是这个jar包是怎么被加载到内存中去的呢?由于ART系统上执行APK时都被从DEX编译转换成了OAT文件,然后进行加载,所以不禁会问系统的JAR包被如何处理成OAT的呢?
这里就要引出boot.art和boot.oat这两个文件了,这两个文件都位于手机系统的/data/dalvik-cache/arm目录下。boot.art是一个img文件,而boot.oat文件可以将其理解为ART虚拟机的启动类,这两个文件是dex2oat命令将Android系统必须的的jar包编译生成的,这两个文件相互联系,缺一不可,boot.art这个img文件直接被映射到ART虚拟机的堆空间中,包含了boot.oat中的某些对象实例以及函数地址。老罗的文章中也讲了,我们后边再介绍,先看看这两个文件的来历。
这两个文件是从什么地方生成的呢?删除了这两个boot文件,那么在下次android启动的时候,系统就会重新生成这两个文件,通过查找手机log中的dex2oat关键字就可以查看到这两个文件的生成命令。注意删除了这两个文件后,重新启动也会重新解析apk,所以会花较长时间。
08-09 16:10:47.463: I/art(324): GenerateImage: /system/bin/dex2oat --image=/data/dalvik-cache/arm/system@framework@boot.art --dex-file=/system/framework/core-libart.jar --dex-file=/system/framework/conscrypt.jar --dex-file=/system/framework/okhttp.jar --dex-file=/system/framework/core-junit.jar --dex-file=/system/framework/bouncycastle.jar --dex-file=/system/framework/ext.jar --dex-file=/system/framework/framework.jar --dex-file=/system/framework/telephony-common.jar --dex-file=/system/framework/voip-common.jar --dex-file=/system/framework/ims-common.jar --dex-file=/system/framework/apache-xml.jar --dex-file=/system/framework/org.apache.http.legacy.boot.jar --oat-file=/data/dalvik-cache/arm/system@framework@boot.oat --instruction-set=arm --instruction-set-features=smp,div,atomic_ldrd_strd --base=0x6fc33000 --runtime-arg -Xms64m --runtime-arg -Xmx64m --image-classes=/system/etc/preloaded-classes --instruction-set-variant=krait --instruction-set-features=default
08-09 16:10:47.632: I/dex2oat(627): /system/bin/dex2oat --image=/data/dalvik-cache/arm/system@framework@boot.art --dex-file=/system/framework/core-libart.jar --dex-file=/system/framework/conscrypt.jar --dex-file=/system/framework/okhttp.jar --dex-file=/system/framework/core-junit.jar --dex-file=/system/framework/bouncycastle.jar --dex-file=/system/framework/ext.jar --dex-file=/system/framework/framework.jar --dex-file=/system/framework/telephony-common.jar --dex-file=/system/framework/voip-common.jar --dex-file=/system/framework/ims-common.jar --dex-file=/system/framework/apache-xml.jar --dex-file=/system/framework/org.apache.http.legacy.boot.jar --oat-file=/data/dalvik-cache/arm/system@framework@boot.oat --instruction-set=arm --instruction-set-features=smp,div,atomic_ldrd_strd --base=0x6fc33000 --runtime-arg -Xms64m --runtime-arg -Xmx64m --image-classes=/system/etc/preloaded-classes --instruction-set-variant=krait --instruction-set-features=default
08-09 16:10:47.639: I/dex2oat(627): setting boot class path to /system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/apache-xml.jar:/system/framework/org.apache.http.legacy.boot.jar
从log中我们可以看到,生成boot.art和boot.oat文件主要依赖了core-libart.jar和framework.jar这两个文件,所以可以理解为telephonymanager和IOBridge所在的jar包经dex2oat打包到了boot.art和boot.oat文件中,所以下一步就可以直接分析boot.art和boot.oat文件是如何被加载到内存了。
由于这两个boot文件是由多个jar包生成的,而不是像apk那样通过一个classes.dex生成,所以情况可能会特殊,所以接下来的分析可能会回退到虚拟机启动流程分析入手,有兴趣可以看下老罗的几篇关于ART虚拟机的文章。
老罗的文章:Android运行时ART加载OAT文件的过程分析前半部分和网上其他一位大牛的文章:ART虚拟机启动之image空间都讲了boot镜像加载的流程,强烈推荐自己看下第二篇文章的讲解,比较简洁易懂。下面开始分析:
首先转载下大牛的流程图:
上述流程牵涉的代码位于以下三个文件中:
framework/base/cmds/app_process/app_main.cpp
frameworks/base/core/jni/AndroidRuntime.cpp
art/runtime/jni_internal.cc
首先AndroidRuntime::start函数中会进行jni的初始化,实际上就是加载虚拟机的so库,并从中导出三个函数,其中JNI_CreateJavaVM用来启动虚拟机。Android 5.0之后默认加载的就是libart.so。
JNI_CreateJavaVM主要负责创建ART虚拟机实例,并且启动ART虚拟机,然后给app_process返回JNIEnv和JavaVM。有了JNIEnv,app_process中才能使用JNI中的FindClass等函数。
创建虚拟机实例中,最主要的是Runtime::init函数,负责创建虚拟机堆空间,绑定Thread,创建和初始化classLinker。
利用gc::Heap创建堆空间时,主要有两件事情,加载boot.ar和boot.oat初始化imgae空间,另外就是与垃圾回收机制相关的东东(垃圾回收太复杂了,暂时略过,后面详谈)。
上述过程详前半部分细解析请查看我整理精简过的老罗的博客:Android ART运行时无缝替换Dalvik虚拟机的过程分析,为了省事我不再复制粘贴过来了,请自行跳转查看,然后再回来继续。
然后我们继续分析,从AndroidRuntime::startVm调用了JNI_CreateJavaVM,跳转到libart.so中执行的JNI_CreateJavaVM函数,函数JNI_CreateJavaVM的实现如下所示:
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
ATRACE_BEGIN(__FUNCTION__);
const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
if (IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
ATRACE_END();
return JNI_EVERSION;
}
RuntimeOptions options;
for (int i = 0; i < args->nOptions; ++i) {
JavaVMOption* option = &args->options[i];
options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
}
bool ignore_unrecognized = args->ignoreUnrecognized;
if (!Runtime::Create(options, ignore_unrecognized)) {
ATRACE_END();
return JNI_ERR;
}
Runtime* runtime = Runtime::Current();
bool started = runtime->Start();
if (!started) {
delete Thread::Current()->GetJniEnv();
delete runtime->GetJavaVM();
LOG(WARNING) << "CreateJavaVM failed";
ATRACE_END();
return JNI_ERR;
}
*p_env = Thread::Current()->GetJniEnv();
*p_vm = runtime->GetJavaVM();
ATRACE_END();
retu