Example #1
0
/*
Perform verification on a single method.

执行单个方法的校验

We do this in three passes:
 (1) Walk through all code units, determining instruction locations,
     widths, and other characteristics.
 (2) Walk through all code units, performing static checks on
     operands.
 (3) Iterate through the method, checking type safety and looking
     for code flow problems.

校验分3次处理:
 (1) 遍历所有代码单元,确定指令位置,宽度,和其它特征。
 (2) 遍历所有代码单元,对操作码执行静态检查。
 (3) 通过方法迭代,检查类型安全并且寻找代码流的问题。

Some checks may be bypassed depending on the verification mode.  We can't
turn this stuff off completely if we want to do "exact" GC.

一些检查可以避免,这取决于校验模式。我们不能完全关闭这些校验,如果想做“精确”的垃圾回收。

TODO: cite source?
Confirmed here:
- code array must not be empty
- (N/A) code_length must be less than 65536
Confirmed by computeWidthsAndCountOps():
- opcode of first instruction begins at index 0
- only documented instructions may appear
- each instruction follows the last
- last byte of last instruction is at (code_length-1)


*/
static bool verifyMethod(Method* meth)
{
    bool result = false;

    /*
    Verifier state blob.  Various values will be cached here so we
    can avoid expensive lookups and pass fewer arguments around.
    
    数据校验器数据结构体。不同的值将被缓存到这里,因此我们可以通过极少的参数进行更有效查找。
    
    可以缓存数据。数据结构参见于CodeVerify.h头文件。
    */
    VerifierData vdata;
#if 1   // ndef NDEBUG
    memset(&vdata, 0x99, sizeof(vdata));
#endif
    
    // 校验器数据初始化
    vdata.method = meth;
    vdata.insnsSize = dvmGetMethodInsnsSize(meth);
    vdata.insnRegCount = meth->registersSize;
    vdata.insnFlags = NULL;
    vdata.uninitMap = NULL;
    vdata.basicBlocks = NULL;

    /*
    If there aren't any instructions, make sure that's expected, then
    exit successfully.  Note: for native methods, meth->insns gets set
    to a native function pointer on first call, so don't use that as
    an indicator.
    
    如果没有任何指令,确保本地方法和抽象方法均可被访问,
    然后成功退出。
    */
    if (vdata.insnsSize == 0) {
        if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) {
            LOG_VFY_METH(meth,
                "VFY: zero-length code in concrete non-native method");
            goto bail;
        }

        goto success;
    }

    /*
    Sanity-check the register counts.  ins + locals = registers, so make
    sure that ins <= registers.
    
    校验寄存器个数。 指令寄存器ins + 本地寄存器locals = All,因此确保指令寄存器<= All。
    */
    if (meth->insSize > meth->registersSize) {
        LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)",
            meth->insSize, meth->registersSize);
        goto bail;
    }

    /*
    Allocate and populate an array to hold instruction data.
    
    TODO: Consider keeping a reusable pre-allocated array sitting
    around for smaller methods.
    
    给指令分配空间
    */
    vdata.insnFlags = (InsnFlags*) calloc(vdata.insnsSize, sizeof(InsnFlags));
    if (vdata.insnFlags == NULL)
        goto bail;

    /*
    Compute the width of each instruction and store the result in insnFlags.
    Count up the #of occurrences of certain opcodes while we're at it.
    
    计算每条指令宽度并且存储 insnFlags 中的结果。
    */
    if (!computeWidthsAndCountOps(&vdata))
        goto bail;

    /*
    Allocate a map to hold the classes of uninitialized instances.
    
    分配一个map来持有未初始化的实例的类(s)
    */
    vdata.uninitMap = dvmCreateUninitInstanceMap(meth, vdata.insnFlags,
        vdata.newInstanceCount);
    if (vdata.uninitMap == NULL)
        goto bail;

    /*
    Set the "in try" flags for all instructions guarded by a "try" block.
    Also sets the "branch target" flag on exception handlers.
    
    对所有try块指令设置“in try”标识。
    同时在异常句柄处设置“branch target”标识。
    */
    if (!scanTryCatchBlocks(meth, vdata.insnFlags))
        goto bail;

    /*
    Perform static instruction verification.  Also sets the "branch
    target" flags.
    
    执行静态校验。同时设置“brach target”标识。
    */
    if (!verifyInstructions(&vdata))
        goto bail;

    /*
    Do code-flow analysis.
    
    We could probably skip this for a method with no registers, but
    that's so rare that there's little point in checking.
    
    做代码流分析
    
    也许可能会跳过一个没有寄存器的方法,但这种情况不见罕见,没有必要检查。
    */
    if (!dvmVerifyCodeFlow(&vdata)) {
        //ALOGD("+++ %s failed code flow", meth->name);
        goto bail;
    }

success:
    result = true;

bail:
    dvmFreeVfyBasicBlocks(&vdata);
    dvmFreeUninitInstanceMap(vdata.uninitMap);
    free(vdata.insnFlags);
    return result;
}
Example #2
0
/*
 * Perform verification on a single method.
 *
 * We do this in three passes:
 *  (1) Walk through all code units, determining instruction lengths.
 *  (2) Do static checks, including branch target and operand validation.
 *  (3) Do structural checks, including data-flow analysis.
 *
 * Some checks may be bypassed depending on the verification mode.  We can't
 * turn this stuff off completely if we want to do "exact" GC.
 *
 * - operands of getfield, putfield, getstatic, putstatic must be valid
 * - operands of method invocation instructions must be valid
 *
 * - code array must not be empty
 * - (N/A) code_length must be less than 65536
 * - opcode of first instruction begins at index 0
 * - only documented instructions may appear
 * - each instruction follows the last
 * - (below) last byte of last instruction is at (code_length-1)
 */
static bool verifyMethod(Method* meth, int verifyFlags)
{
    bool result = false;
    UninitInstanceMap* uninitMap = NULL;
    InsnFlags* insnFlags = NULL;
    int i, newInstanceCount;

    /*
     * If there aren't any instructions, make sure that's expected, then
     * exit successfully. Note: meth->insns gets set to a native function
     * pointer on first call.
     */
    if (dvmGetMethodInsnsSize(meth) == 0) {
        if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) {
            LOG_VFY_METH(meth,
                "VFY: zero-length code in concrete non-native method\n");
            goto bail;
        }

        goto success;
    }

    /*
     * Sanity-check the register counts.  ins + locals = registers, so make
     * sure that ins <= registers.
     */
    if (meth->insSize > meth->registersSize) {
        LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)\n",
            meth->insSize, meth->registersSize);
        goto bail;
    }

    /*
     * Allocate and populate an array to hold instruction data.
     *
     * TODO: Consider keeping a reusable pre-allocated array sitting
     * around for smaller methods.
     */
    insnFlags = (InsnFlags*)
        calloc(dvmGetMethodInsnsSize(meth), sizeof(InsnFlags));
    if (insnFlags == NULL)
        goto bail;

    /*
     * Compute the width of each instruction and store the result in insnFlags.
     * Count up the #of occurrences of new-instance instructions while we're
     * at it.
     */
    if (!dvmComputeCodeWidths(meth, insnFlags, &newInstanceCount))
        goto bail;

    /*
     * Allocate a map to hold the classes of uninitialized instances.
     */
    uninitMap = dvmCreateUninitInstanceMap(meth, insnFlags, newInstanceCount);
    if (uninitMap == NULL)
        goto bail;

    /*
     * Set the "in try" flags for all instructions guarded by a "try" block.
     */
    if (!dvmSetTryFlags(meth, insnFlags))
        goto bail;

    /*
     * Perform static instruction verification.
     */
    if (!verifyInstructions(meth, insnFlags, verifyFlags))
        goto bail;

    /*
     * Do code-flow analysis.  Do this after verifying the branch targets
     * so we don't need to worry about it here.
     *
     * If there are no registers, we don't need to do much in the way of
     * analysis, but we still need to verify that nothing actually tries
     * to use a register.
     */
    if (!dvmVerifyCodeFlow(meth, insnFlags, uninitMap)) {
        //LOGD("+++ %s failed code flow\n", meth->name);
        goto bail;
    }

success:
    result = true;

bail:
    dvmFreeUninitInstanceMap(uninitMap);
    free(insnFlags);
    return result;
}