美文网首页Android安全-源码分析
Dalvik虚拟机启动全程解析

Dalvik虚拟机启动全程解析

作者: difcareer | 来源:发表于2017-06-15 18:26 被阅读282次

    本文试图记录Dalvik虚拟机启动时全过程。

    0x01:启动点

    我们知道,Android基于linux,linux的第一个进程为init,它启动别的进程,在Android中,它启动了zygote:

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        class main
        socket zygote stream 660 root system
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart media
        onrestart restart netd
    

    从此, 代码从linux的世界进入到了Android的世界。

    在上面可以看到,启动的脚本是/system/bin/app_process,在后面的分析中,我们发现,这个就是Dalvik虚拟机的启动点。app_process除了在init.rc中启动,也可以通过命令执行,比如am命令就是调用了app_process:

    #!/system/bin/sh
    #
    # Script to start "am" on the device, which has a very rudimentary
    # shell.
    #
    base=/system
    export CLASSPATH=$base/framework/am.jar
    exec app_process $base/bin com.android.commands.am.Am "$@"
    

    0x02:app_process

    既然确定了启动点,我们就来跟踪一下,为了简单,我们就分析init.rc的调用场景。

    app_process的源码是app_main.cpp,其main函数主要做了几个事情:

    1. 参数解析,通过源码,我们可以看到,启动虚拟机可以有很多很多的参数
    2. 设置相关环境变量
    3. 调用runtime.start,这个是真正启动虚拟机的地方,其参数为:com.android.internal.os.ZygoteInit,表明在启动虚拟机后,开始执行ZygoteInit来做Zygote的初始化。对于Zygote的初始化及后续的事情,本文不关心,我们还是集中于虚拟机的启动。

    0x03:runtime.start

    源码见AndroidRuntime.cpp

    AndroidRuntime::start中,主要做了几件事:

    1. startVm,启动虚拟机
    2. onVmCreated 执行启动后的回调
    3. startReg,注册Andorid系统相关的native函数
    4. 调用参数里面指定的类的main

    重点是startvmstartReg,继续跟踪。

    0x04:startVm

    源码同样在AndroidRuntime.cpp

    AndroidRuntime::startVm中,主要做了几件事:

    1. 大段的参数解析,解析到了JavaVMInitArgs
    2. JNI_CreateJavaVM,函数里面需要设置pJavaVM, pEnv指针,是为了后续的销毁虚拟机等操作。

    0x05:JNI_CreateJavaVM

    源码见Jni.cpp

    JNI_CreateJavaVM中,主要做了几件事情:

    1. 根据参数,填充了gDvm中的相关参数
      关于gDvm:它的定义在Globals.h,其作用是保存许许多多的全局变量,在整个虚拟机中使用。
    2. 初始化了JavaVMExt、JNIEnvExt等指针,这些分别对应的JNI中的JavaVM、JNIEnv,在JNI调用中必不可少。
    3. dvmStartup 继续启动,重点是这个

    0x06:dvmStartup

    源码见Init.cpp

    dvmStartup中,主要做了几件事:

    1. 继续解析参数,填充gDvm中的相关参数,参数是真多啊,说明虚拟机的可配置项还是挺多的。
    2. dvmCheckAsmConstants,检查mterp interpreter用到的若干常量
    3. dvmQuasiAtomicsStartup, 初始化原子操作的mutex
    4. dvmGcStartup,初始化metex,初始化GC堆区
    5. dvmThreadStartup,初始化线程相关的环境
    6. dvmInlineNativeStartup,分配InlineNative相关的内存,将gDvm.inlinedMethods指向这块内存,所谓InlineNative就是指一些Java方法,使用了效率更高的C函数实现,执行这些方法时,直接调用到这些C函数,不用在执行字节码,和inLine有异曲同工之妙。
    7. dvmRegisterMapStartup,分配MapStats的内存,将gDvm.registerMapStats指向这块内存,MapStats应该是和RegisterMap相关。
    8. bool dvmInstanceofStartup(), 分配instanceofCache的内存,将gDvm.instanceofCache指向这块内存,目测这块内存会在instanceof操作时使用。
    9. dvmClassStartup,比较复杂,分别说:
      9.1. 创建了一个Hash表,用来存放所有加载的类,将gDvm.loadedClasses指向这个Hash表
      9.2. 分配InitiatingLoaderList的内存,将gDvm.initiatingLoaderList指向这块内存,InitiatingLoaderList在寻找加载的类的时候会用到
      9.3. createInitialClasses,构建Ljava/lang/Class;的类对象,和所有的原类型的类对象:void,boolean,byte,short,char,int,long,float,double,至此,我们就可以使用这些类了。
      9.4. processClassPath,加载bootClassPath下的所有的jar(其实是dex)到gDvm.loadedClasses中,bootClassPath为:

      至此,Android framework相关的类也被加载了。
    10. dvmFindRequiredClassesAndMembers,主要是将相关常用到的类、属性、构造器、方法等,直接关联到gDvm的相关指针上,省得每次都要去Hash表里面查。
    11. dvmStringInternStartup,创建了internedStrings和literalStrings的Hash表,作用暂时不明。
    12. dvmNativeStartup,创建用于保存sharedLib(也就是so)的Hash表,将gDvm.nativeLibs指向此表。以后每个加载的so的相关信息都会被保存到这个表中,其中一个作用当然是避免重复加载。
    13. dvmInternalNativeStartup, 遍历很多类,设置这些类的classDescriptorHash,这些类如下:

      这些类来自于libcore中,对应的c实现在dalvik下。
    14. dvmJniStartup,初始化了jni的全局引用表和全局弱引用表。(关于JNI,以后单独写一篇文章)
    15. dvmProfilingStartup, 貌似和跟踪功能相关的初始化
    16. dvmCreateInlineSubsTable,这里才将InlineNative类的Native方法和c实现建立了关联表,将gDvm.inlineSubs指向关联表。
    17. dvmValidateBoxClasses,校验盒类对象,简单校验对象的属性只有一个:即value的定义。
    18. dvmPrepMainForJni,创建了一个假的栈帧,将JNIEnv设置到当前的Thread中
    19. dvmInitClass(gDvm.classJavaLangClass),初始化Class类对象
    20. registerSystemNatives, 将Class中的getDex注册到JNI中,加载libjavacore.so和libnativehelper.so
    21. dvmCreateStockExceptions, 创建一些固定的Exception对象,并关联到gDvm
    22. dvmPrepMainThread, 创建相关的线程对象,将当前线程命令为main线程
    23. dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0 简单校验线程相关的local引用表当前是空的
    24. dvmDebuggerStartup, 初始化调试相关的东西,比如断点什么的
    25. dvmGcStartupClasses, 调用Ljava/lang/Daemons;的start方法,启动三个和GC相关的线程:
        public static void start() {
            ReferenceQueueDaemon.INSTANCE.start();
            FinalizerDaemon.INSTANCE.start();
            FinalizerWatchdogDaemon.INSTANCE.start();
        }
    
    1. initZygote,挂在相关的文件系统,设置进程属性等。
    2. 最后dvmCheckException,检查是否有错。

    至此dvmStartup分析完毕。总结一下就是,分配相关内存,建立相关的联系,初始化相关环境,启动相关的线程。(相关这个词真是好用)

    此时执行完0x03:runtime.start中中的第一步:startVm,第二步是一个回调,实现为空,继续分析第三步:startReg

    0x07: startReg

    源码见AndroidRuntime.cpp

    AndroidRuntime::startReg中,主要做了几件事:

    1. register_jni_procs,将Android framework中所有的native方法注册到JNI中,这些类在framework.jar和framework2.jar中,c逻辑在libandroid_runtime.so中

    至此,虚拟机的启动完成了,之后就会执行com.android.internal.os.ZygoteInitmain方法,开始在虚拟机中对Zygote进行初始化操作了。

    相关文章

      网友评论

        本文标题:Dalvik虚拟机启动全程解析

        本文链接:https://www.haomeiwen.com/subject/jycuqxtx.html