示例#1
0
/*
Compute the width of the instruction at each address in the instruction
stream, and store it in vdata->insnFlags.  Addresses that are in the
middle of an instruction, or that are part of switch table data, are not
touched (so the caller should probably initialize "insnFlags" to zero).

The "newInstanceCount" and "monitorEnterCount" fields in vdata are
also set.

Performs some static checks, notably:
- 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)

Logs an error and returns "false" on failure.

计算指令流中每个地址上指令的宽度,并且存储在 vdata->insnFlags。在指令中间的地址,
或者部分switch表数据的地址,不做处理(因此调用者应该正确初始化“insnFlags”到0)。

在vdata中的“newInstanceCount”和“monitorEnterCount”域也要设置值。

执行一些静态检查,值得注意的是:
- 第一条指令操作码从索引0开始
- 只有记录过的指令可以出现
- 每个指令跟在最后面	
- 最后一条指令的最后字节是在(code_length-1)

失败时记录错误并返回“false”。
*/
static bool computeWidthsAndCountOps(VerifierData* vdata)
{
    const Method* meth = vdata->method;
    InsnFlags* insnFlags = vdata->insnFlags;
    size_t insnCount = vdata->insnsSize;
    const u2* insns = meth->insns;
    bool result = false;
    int newInstanceCount = 0;
    int monitorEnterCount = 0;
    int i;

    for (i = 0; i < (int) insnCount; /**/) {
        size_t width = dexGetWidthFromInstruction(insns);
        if (width == 0) {
            LOG_VFY_METH(meth, "VFY: invalid instruction (0x%04x)", *insns);
            goto bail;
        } else if (width > 65535) {
            LOG_VFY_METH(meth,
                "VFY: warning: unusually large instr width (%d)", width);
        }
        /* CodeUnit代码单元(对应第一个问题)
        即使dex文件映射在内存和指令一一对应的实际字节码,可以这样说,进过解析转码后的字节码是操作码,
        之前的是代码单元。
        */
        Opcode opcode = dexOpcodeFromCodeUnit(*insns);
        if (opcode == OP_NEW_INSTANCE)
            newInstanceCount++;
        if (opcode == OP_MONITOR_ENTER)
            monitorEnterCount++;

        insnFlags[i] |= width;
        i += width;
        insns += width;
    }
    if (i != (int) vdata->insnsSize) {
        LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)",
            i, dvmGetMethodInsnsSize(meth));
        goto bail;
    }

    result = true;
    vdata->newInstanceCount = newInstanceCount;
    vdata->monitorEnterCount = monitorEnterCount;

bail:
    return result;
}
示例#2
0
/*
 * Optimize instructions in a method.
 *
 * This does a single pass through the code, examining each instruction.
 *
 * This is not expected to fail if the class was successfully verified.
 * The only significant failure modes on unverified code occur when an
 * "essential" update fails, but we can't generally identify those: if we
 * can't look up a field, we can't know if the field access was supposed
 * to be handled as volatile.
 *
 * Instead, we give it our best effort, and hope for the best.  For 100%
 * reliability, only optimize a class after verification succeeds.
 */
static void optimizeMethod(Method* method, bool essentialOnly)
{
    bool needRetBar, forSmp;
    u4 insnsSize;
    u2* insns;

    if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
        return;

    forSmp = gDvm.dexOptForSmp;
    needRetBar = needsReturnBarrier(method);

    insns = (u2*) method->insns;
    assert(insns != NULL);
    insnsSize = dvmGetMethodInsnsSize(method);

    while (insnsSize > 0) {
        Opcode opc, quickOpc, volatileOpc;
        size_t width;
        bool matched = true;

        opc = dexOpcodeFromCodeUnit(*insns);
        width = dexGetWidthFromInstruction(insns);
        volatileOpc = OP_NOP;

        /*
         * Each instruction may have:
         * - "volatile" replacement
         *   - may be essential or essential-on-SMP
         * - correctness replacement
         *   - may be essential or essential-on-SMP
         * - performance replacement
         *   - always non-essential
         *
         * Replacements are considered in the order shown, and the first
         * match is applied.  For example, iget-wide will convert to
         * iget-wide-volatile rather than iget-wide-quick if the target
         * field is volatile.
         */

        /*
         * essential substitutions:
         *  {iget,iput,sget,sput}-wide --> {op}-wide-volatile
         *  invoke-direct[/range] --> invoke-object-init/range
         *
         * essential-on-SMP substitutions:
         *  {iget,iput,sget,sput}-* --> {op}-volatile
         *  return-void --> return-void-barrier
         *
         * non-essential substitutions:
         *  {iget,iput}-* --> {op}-quick
         *
         * TODO: might be time to merge this with the other two switches
         */
        switch (opc) {
        case OP_IGET:
        case OP_IGET_BOOLEAN:
        case OP_IGET_BYTE:
        case OP_IGET_CHAR:
        case OP_IGET_SHORT:
            quickOpc = OP_IGET_QUICK;
            if (forSmp)
                volatileOpc = OP_IGET_VOLATILE;
            goto rewrite_inst_field;
        case OP_IGET_WIDE:
            quickOpc = OP_IGET_WIDE_QUICK;
            volatileOpc = OP_IGET_WIDE_VOLATILE;
            goto rewrite_inst_field;
        case OP_IGET_OBJECT:
            quickOpc = OP_IGET_OBJECT_QUICK;
            if (forSmp)
                volatileOpc = OP_IGET_OBJECT_VOLATILE;
            goto rewrite_inst_field;
        case OP_IPUT:
        case OP_IPUT_BOOLEAN:
        case OP_IPUT_BYTE:
        case OP_IPUT_CHAR:
        case OP_IPUT_SHORT:
            quickOpc = OP_IPUT_QUICK;
            if (forSmp)
                volatileOpc = OP_IPUT_VOLATILE;
            goto rewrite_inst_field;
        case OP_IPUT_WIDE:
            quickOpc = OP_IPUT_WIDE_QUICK;
            volatileOpc = OP_IPUT_WIDE_VOLATILE;
            goto rewrite_inst_field;
        case OP_IPUT_OBJECT:
            quickOpc = OP_IPUT_OBJECT_QUICK;
            if (forSmp)
                volatileOpc = OP_IPUT_OBJECT_VOLATILE;
            /* fall through */
rewrite_inst_field:
            if (essentialOnly)
                quickOpc = OP_NOP;      /* if essential-only, no "-quick" sub */
            if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
                rewriteInstField(method, insns, quickOpc, volatileOpc);
            break;

        case OP_SGET:
        case OP_SGET_BOOLEAN:
        case OP_SGET_BYTE:
        case OP_SGET_CHAR:
        case OP_SGET_SHORT:
            if (forSmp)
                volatileOpc = OP_SGET_VOLATILE;
            goto rewrite_static_field;
        case OP_SGET_WIDE:
            volatileOpc = OP_SGET_WIDE_VOLATILE;
            goto rewrite_static_field;
        case OP_SGET_OBJECT:
            if (forSmp)
                volatileOpc = OP_SGET_OBJECT_VOLATILE;
            goto rewrite_static_field;
        case OP_SPUT:
        case OP_SPUT_BOOLEAN:
        case OP_SPUT_BYTE:
        case OP_SPUT_CHAR:
        case OP_SPUT_SHORT:
            if (forSmp)
                volatileOpc = OP_SPUT_VOLATILE;
            goto rewrite_static_field;
        case OP_SPUT_WIDE:
            volatileOpc = OP_SPUT_WIDE_VOLATILE;
            goto rewrite_static_field;
        case OP_SPUT_OBJECT:
            if (forSmp)
                volatileOpc = OP_SPUT_OBJECT_VOLATILE;
            /* fall through */
rewrite_static_field:
            if (volatileOpc != OP_NOP)
                rewriteStaticField(method, insns, volatileOpc);
            break;

        case OP_INVOKE_DIRECT:
        case OP_INVOKE_DIRECT_RANGE:
            if (!rewriteInvokeObjectInit(method, insns)) {
                /* may want to try execute-inline, below */
                matched = false;
            }
            break;
        case OP_RETURN_VOID:
            if (needRetBar)
                rewriteReturnVoid(method, insns);
            break;
        default:
            matched = false;
            break;
        }


        /*
         * non-essential substitutions:
         *  invoke-{virtual,direct,static}[/range] --> execute-inline
         *  invoke-{virtual,super}[/range] --> invoke-*-quick
         */
        if (!matched && !essentialOnly) {
            switch (opc) {
            case OP_INVOKE_VIRTUAL:
                if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
                    rewriteVirtualInvoke(method, insns,
                        OP_INVOKE_VIRTUAL_QUICK);
                }
                break;
            case OP_INVOKE_VIRTUAL_RANGE:
                if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
                    rewriteVirtualInvoke(method, insns,
                        OP_INVOKE_VIRTUAL_QUICK_RANGE);
                }
                break;
            case OP_INVOKE_SUPER:
                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
                break;
            case OP_INVOKE_SUPER_RANGE:
                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
                break;
            case OP_INVOKE_DIRECT:
                rewriteExecuteInline(method, insns, METHOD_DIRECT);
                break;
            case OP_INVOKE_DIRECT_RANGE:
                rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
                break;
            case OP_INVOKE_STATIC:
                rewriteExecuteInline(method, insns, METHOD_STATIC);
                break;
            case OP_INVOKE_STATIC_RANGE:
                rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
                break;
            default:
                /* nothing to do for this instruction */
                ;
            }
        }

        assert(width > 0);
        assert(width <= insnsSize);
        assert(width == dexGetWidthFromInstruction(insns));

        insns += width;
        insnsSize -= width;
    }

    assert(insnsSize == 0);
}