dalvik VM的解释器分析

以KK的dalvik源码为基础来解析。

使用的源码基于https://github.com/AOKP/dalvik, 可以从https://github.com/AOKP/dalvik/archive/kitkat.zip 下载。

我是在linux下,使用vim + ctags做分析的。

由于ARM架构是使用最多也是最频繁的架构,所以我分析的重点是ARM的汇编如何实现解释器的。所以我在分析过程中会忽略掉与ARM无关的代码。

入口是什么?

从dvmCallMethod开始分析。这个函数是调用一个java method的主要入口函数。
dvmCallMethod->dvmCallMethodV->dvmInterpret->dvmMterpStd->dvmMterpStdRun

这一串调用,最终走到了dvmMterpStdRun函数。这个函数是用汇编写成的,在ARM架构中,实现的文件是vm/mterp/out/InterpAsm-armv7-a-neon.S。这个是Android KK ARM版本中使用的文件,也是我们分析的重点。

说了半天,dvmMterpStdRun是干什么的?其实就是用于解释实现各个dex 指令的,是整个解释的核心。不过,在正式了解这个函数之前,我们要分别了解下dvmCallMethodV,dvmInterpret,dvmMethodStd这3个函数的主要功能和涉及到的数据结构。
这些函数并不直接执行代码,但是却为dvmMterpStdRun准备了运行环境,只有理解了这些运行环境,才能更好的理解解释器的工作原理。

dvmCallMethodV

这个函数的核心作用是创建虚拟栈。
首先,我们了解下,一个普通的java method调用链条中的虚拟栈是怎么组织的。
dvmCallMethodV中完成栈构建的函数在callPrep->dvmPushInterpFrame

普通java method的虚拟栈

DVM为解释器分配的专门的栈,每个java线程有一个。这些信息保存在Thread结构体中。
//vm/interp/InterpState.h
struct InterpSaveState {
    const u2*       pc;         // Dalvik PC
    u4*             curFrame;   // Dalvik frame pointer
    const Method    *method;    // Method being executed
    DvmDex*         methodClassDex;
    JValue          retval;
    void*           bailPtr;
....
    struct InterpSaveState* prev;  // To follow nested activations
} __attribute__ ((__packed__));

//vm/Thread.h
/*
 * Our per-thread data.
 *
 * These are allocated on the system heap.
 */
struct Thread {
    /*
     * Interpreter state which must be preserved across nested
     * interpreter invocations (via JNI callbacks).  Must be the first
     * element in Thread.
     */
    InterpSaveState interpSave;
.....
    /* current limit of stack; flexes for StackOverflowError */
    const u1*   interpStackEnd;
......
    /* start (high addr) of interp stack (subtract size to get malloc addr) */
    u1*         interpStackStart;
......
};

Thread的interpStackStart和interpStackEnd表示虚拟栈的开始和结束范围。注意,栈的增长方向是由高地址向低地址曾长,因此inpterpStackStart >interpStackEnd。
inpterpSave的curFrame记录了当前虚拟栈(Frame)的位置。

我用下面的表来表示虚拟栈的组成部分:



  • StackSaveArea保存的是一个method 栈的信息,如当前的Method, 调用的PC地址,返回值地址等,还有prevFrame,指向Caller的frame地址;
  • curFrame保存当前函数的Frane地址。这个地址不包含StackSaveArea的指针。
  • method的registersSize指出全部寄存器的个数,包括用作参数传递的寄存器个数;
  • method的insSize指出作为参数的寄存器的个数;
  • method的outsSize指出该函数调用其他函数需要的寄存器个数
关于registerSize,insSize和outsSize的关系,以下几点:
  • 参数寄存器 (ins registers)是全部寄存器的一部分,所以,insSize < registerSize;
  • outsSize是调用其他函数需要的寄存器个数,这只是一个参考值。因为一个函数可以调用N个函数,这些被调用的函数的参数个数不一样,所以,outsSize总是取被调用函数中参数最多的那个值作为它的值;
  • 假设函数A调用函数B,A 向它的out register写入调用参数,当进入B函数后,A的out register就变成了B的ins register。这样,B就可以直接访问A传递过来的参数了。
  • ins register是register的一部分,在函数内部是可以参与运算的。因为需要和调用者共用,所以ins register总是位于虚拟栈的最高地址处。
关于参数,还需有注意一点:非静态函数,参数P0被隐含定义为this指针。

dvmCallMethodV的栈特点

dvmCallMethodV要插入一个BreakStackSaveArea对象,这也是个StackSaveArea,用于模拟一个栈,保证调用链条的连续性。

如下图:



dvmInterpret

该函数是解释模式的入口函数。在介绍这个函数之前,需要介绍两个重要的概念:InterpSaveState和ExecutionSubModes

InterpSaveState
InterpSaveState 结构体保存在Thread中,用于存储解释模式中重要的数据结构。它的定义如下: (vm/interp/InterpState.h)
struct InterpSaveState {
    const u2*       pc;         // Dalvik PC
    u4*             curFrame;   // Dalvik frame pointer
    const Method    *method;    // Method being executed
    DvmDex*         methodClassDex;
    JValue          retval;
    void*           bailPtr;
#if defined(WITH_TRACKREF_CHECKS)
    int             debugTrackedRefStart;
#else
    int             unused;        // Keep struct size constant
#endif
    struct InterpSaveState* prev;  // To follow nested activations
} __attribute__ ((__packed__));
  • pc: 保存的是当前正在执行的dalvik代码的地址
  • curFrame: 当前的frame地址,与StackSaveArea中的 curFrame是一致的
  • method: 当前调用的Method对象
  • methodClassDex: 当前method所属的DvmDex对象。这个对象包含了一个Dex文件对象和解析表(ResolvedClasses, ResolvedMethod, ResolvedString和ResolevedField等)
  • retval:返回值
  • bailPtr: 保存的是寄存器sp的地址,用于恢复解释器的堆栈
  • prev:指向上一个InterpSaveState对象。这个链表的建立就是在dvmIntepreter函数中实现的。
解释器要不断的更新这些值,保持这些值与运行状态一致。

ExecutionSubModes
它是一个枚举值,它的定义如下:
/*
 * Execution sub modes, e.g. debugging, profiling, etc.
 * Treated as bit flags for fast access.  These values are used directly
 * by assembly code in the mterp interpeter and may also be used by
 * code generated by the JIT.  Take care when changing.
 */
enum ExecutionSubModes {
    kSubModeNormal            = 0x0000,   /* No active subMode */
    kSubModeMethodTrace       = 0x0001,
    kSubModeEmulatorTrace     = 0x0002,
    kSubModeInstCounting      = 0x0004,
    kSubModeDebuggerActive    = 0x0008,
    kSubModeSuspendPending    = 0x0010,
    kSubModeCallbackPending   = 0x0020,
    kSubModeCountedStep       = 0x0040,
    kSubModeCheckAlways       = 0x0080,
    kSubModeSampleTrace       = 0x0100,
    kSubModeJitTraceBuild     = 0x4000,
    kSubModeJitSV             = 0x8000,
    kSubModeDebugProfile   = (kSubModeMethodTrace |
                              kSubModeEmulatorTrace |
                              kSubModeInstCounting |
                              kSubModeDebuggerActive)
};

整个mode可以分为两类,第一类是kSubModeNormal,这种模式下,解释器正常执行dalvik的字节码;其余可以归结为另一类,这类模式下,在执行dalvik字节码之前,先要调用一个dvmCheckBefore函数,这个函数会根据不同的submode,执行不同的操作,为debug,jit的trace code等功能,提供入口。
这个枚举值记录在Thread::InterpBreak.ctl.subMode中。

他通过调用 dvmDisableSubMode/dvmEnableSubMode -> updateInterpBreak函数来实现对模式的切换。
/*
 * Update interpBreak for a single thread.
 */
void updateInterpBreak(Thread* thread, ExecutionSubModes subMode, bool enable)
{
    InterpBreak oldValue, newValue;
    do {
.....
#ifndef DVM_NO_ASM_INTERP
        newValue.ctl.curHandlerTable = (newValue.ctl.breakFlags) ?
            thread->altHandlerTable : thread->mainHandlerTable;
#endif
    } while (dvmQuasiAtomicCas64(oldValue.all, newValue.all,
             &thread->interpBreak.all) != 0);
};
#endif
    } while (dvmQuasiAtomicCas
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android虚拟机Dalvik完整源码,宝贵资源,欢迎下载! This directory contains the Dalvik virtual machine and core class library, as well as related tools, libraries, and tests. A note about the licenses and header comments --------------------------------------------- Much of the code under this directory originally came from the Apache Harmony project, and as such contains the standard Apache header comment. Some of the code was written originally for the Android project, and as such contains the standard Android header comment. Some files contain code from both projects. In these cases, the header comment is a combination of the other two, and the portions of the code from Harmony are identified as indicated in the comment. Here is the combined header comment: /* * Copyright (C) The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ---------- * * Portions of the code surrounded by "// BEGIN Harmony code" and * "// END Harmony code" are copyrighted and licensed separately, as * follows: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Native SH call bridge --------------------- Native SH call bridge is written by Shin-ichiro KAWASAKI and Contributed to Android by Hitachi, Ltd. and Renesas Solutions Corp.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值