kvm类加载-007

之前的几篇文章介绍了将一个类从RAW到linked的过程.这里我们再次回顾一下kvm内部的class的状态:

#define CLASS_RAW       0 /* this value must be 0 */
#define CLASS_LOADING   1
#define CLASS_LOADED    2
#define CLASS_LINKED    3
#define CLASS_VERIFIED  4
#define CLASS_READY     5
#define CLASS_ERROR    -1

此时,class的状态只是CLASS_LINKED,还不能使用.后续还需要进行VERIFIE.分配内存,实例化,才能到达ready的状态.

VERIFIE

VERIFIE 是在initializeClass 中进行的,代码如下:

void initializeClass(INSTANCE_CLASS thisClass)
{
    if (thisClass->status == CLASS_ERROR) {
        raiseException(NoClassDefFoundError);
    } else if (thisClass->status < CLASS_READY) {
        if (thisClass->status < CLASS_VERIFIED) {
            verifyClass(thisClass); // 1. 验证class
        }
        
        // 2. 如果该类不存在父类或父类的状态为CLASS_READY,同时该类没有clinit方法,则直接修改该类的状态为CLASS_READY
        if ((thisClass->superClass == NULL ||
            thisClass->superClass->status == CLASS_READY) &&
            getSpecialMethod(thisClass,clinitNameAndType) == NULL) {
            setClassStatus(thisClass,CLASS_READY);
        }
        else {
        // 3. 其他情况,则通过字节码的方式,执行clinit 方法
            TRY {
                pushFrame(RunCustomCodeMethod);
                pushStackAsType(CustomCodeCallbackFunction, &runClinit);
                pushStackAsType(INSTANCE_CLASS, thisClass);
                pushStackAsType(long, 1);
            } CATCH (e) {
                /* Stack overflow */
                setClassStatus(thisClass, CLASS_ERROR);
                THROW(e);
            } END_CATCH
        }
    }
}

这里的步骤很简单:

  1. 验证class . 这是在这节要介绍的
  2. 如果该类不存在父类或父类的状态为CLASS_READY,同时该类没有clinit方法,则直接修改该类的状态为CLASS_READY.
  3. 其他情况,则通过字节码的方式,执行clinit 方法

其中,第3步会在进后续文章中介绍.

这里主要调用的是verifyClass.其代码如下:

int verifyClass(INSTANCE_CLASS thisClass) {
    static int Vfy_verifyMethod(METHOD vMethod);
    int i;
    int result = 0;
#if USESTATIC
    CONSTANTPOOL cp = thisClass->constPool;
#endif
     // 1. 如果该类有方法表的话,也即该类中定义了方法
    if (thisClass->methodTable) {
        //  此处为宏,直接返回false
        if (!checkVerifiedClassList(thisClass)) {
            /* 则验证所有的方法 */
            for (i = 0; i < thisClass->methodTable->length; i++) {
                METHOD thisMethod = &thisClass->methodTable->methods[i];

                /* 1.1 跳过 runCustomCode */
                if (thisMethod == RunCustomCodeMethod) {
                    continue;
                }
                /* 1.2 跳过本地,抽象方法 */
                if (thisMethod->accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) {
                    continue;
                }

               /*
                * 1.3 进行验证
                */
                result = Vfy_verifyMethod(thisMethod);
                if (result != 0) {
                    break;
                }
            }

            if (result == 0) {
                /* 1.4 如果验证失败的话,此处为宏  */
                appendVerifiedClassList(thisClass);
            }
        }

        /* 2. 验证StackMap */
        for (i = 0; i < thisClass->methodTable->length; i++) {
            METHOD thisMethod = &thisClass->methodTable->methods[i];
            if (thisMethod->u.java.stackMaps.verifierMap != NULL) {
                STACKMAP newStackMap = (result == 0)
                     // 进行真正的验证
                     ? rewriteVerifierStackMapsAsPointerMaps(thisMethod)
                     :  NULL;
#if !USESTATIC
                thisMethod->u.java.stackMaps.pointerMap = newStackMap;
#else
                int offset = (char *)&thisMethod->u.java.stackMaps.pointerMap
                           - (char *)cp;
                modifyStaticMemory(cp, offset, &newStackMap, sizeof(STACKMAP));
#endif
            }
        }
    }
    // 3. 如果验证没有问题的话,则将状态修改为CLASS_VERIFIED
    if (result == 0) {
        thisClass->status = CLASS_VERIFIED;
    } else {
        // 3.2 如果有问题的话,则抛出异常
        START_TEMPORARY_ROOTS
            DECLARE_TEMPORARY_ROOT(char*, className, getClassName((CLASS)thisClass));
            thisClass->status = CLASS_ERROR;
            if (className) {
                raiseExceptionWithMessage(VerifyError, className);
            } else {
                raiseException(VerifyError);
            }
        END_TEMPORARY_ROOTS
    }

    return result;
}

此处重要的方法为: Vfy_verifyMethod, rewriteVerifierStackMapsAsPointerMaps.以下分别进行讲解.

Vfy_verifyMethod

此处的代码为:

static int Vfy_verifyMethod(METHOD vMethod) {

    void Vfy_verifyMethodOrAbort(const METHOD vMethod);
    static bool_t Vfy_checkNewInstructions(METHOD vMethod);
    int res;

#if INCLUDEDEBUGCODE
    Vfy_printVerifyStartMessage(vMethod);
#endif

    START_TEMPORARY_ROOTS

        // 1.  获得maxStack,frameSize,并进行内存分配
        vMaxStack = vMethod->u.java.maxStack;
        vFrameSize = vMethod->frameSize;
        // VERIFIERTYPE = unsigned short
        IS_TEMPORARY_ROOT(vStack,  (VERIFIERTYPE*) callocObject(ByteSizeToCellSize(vMaxStack  * sizeof(VERIFIERTYPE)), GCT_NOPOINTERS));
        IS_TEMPORARY_ROOT(vLocals, (VERIFIERTYPE*) callocObject(ByteSizeToCellSize(vFrameSize * sizeof(VERIFIERTYPE)), GCT_NOPOINTERS));

       /*
        * This bitmap keeps track of all the NEW instructions that we have
        * already seen.
        */
        IS_TEMPORARY_ROOT(NEWInstructions, NULL); // NEWInstructions = null

        vSP = 0;                   /* 初始化 sp */

       /*
        * All errors from Vfy_verifyMethodOrAbort are longjmped here. If no error
        * is thrown then res will be zero from the call to setjmp
        * 设置当Vfy_verifyMethodOrAbort出错时,跳回的地方
        */
        res = setjmp(vJumpBuffer);
        if (res == 0) {
           /*
            * Setup the verification context and call the core function.
            */
            methodBeingVerified    = vMethod;
            bytecodesBeingVerified = Mth_getBytecodes(vMethod);

            // 进行真正的验证
            Vfy_verifyMethodOrAbort(vMethod);
            if (!Vfy_checkNewInstructions(vMethod)) {
            	// 如果出现异常,则会跳回到setjmp处,且返回值为VE_BAD_NEW_OFFSET
                Vfy_throw(VE_BAD_NEW_OFFSET);
            }
        }

    END_TEMPORARY_ROOTS

#if INCLUDEDEBUGCODE
    Vfy_printVerifyEndMessage(vMethod, res);
#endif

    return res;
}

这里的关键点是用到了 setjmp和longjmp. 当Vfy_verifyMethodOrAbort 抛出异常时,最终会经Vfy_throw处理,其代码如下:

void Vfy_throw(int code) {
     longjmp(vJumpBuffer, code);
 }

其会再次返回setjmp 处,但是此时的返回值非0,因此会直接返回res.

关于这点可以参考如下链接:

Vfy_verifyMethodOrAbort

该方法的核心思想是通过模拟执行引擎,执行字节码,验证操作栈,本地变量表上的类型是否和stackmap中的一致.其代码如下:


void Vfy_verifyMethodOrAbort(const METHOD vMethod) {

   /*
    * The following variables are constant in this function
    * 1. 获取变量,并初始化一些变量
    */
    const CLASS vClass       = Mth_getClass(vMethod);
    const CLASS vSuperClass  = Cls_getSuper(vClass);
    const int codeLength     = Mth_getBytecodeLength(vMethod);
    const int handlerCount   = Mth_getExceptionTableLength(vMethod);
    const CONSTANTPOOL vPool = Cls_getPool(vClass);

   /*
    * The virtual IP used by the verifier 设置虚拟ip值
    */
    IPINDEX ip = 0;

   /*
    * The following is set to TRUE where is no direct control
    * flow from current instruction to the next instruction
    * in sequence.
    * 当没有从当前指令到下一条指令的直接控制流时设置为真。
    */
    bool_t noControlFlow = FALSE;

   /*
    * Pointer to the "current" stackmap entry 指向stackmap entry 的下标
    */
    int currentStackMapIndex = 0;

   /*
    * Check that a virtual method does not subclass a "final"
    * method higher up the inheritance chain
    *
    * Was bug 4336036
    */
    // 2. 如果该方法不是静态的,则检查是否存在重写final方法的情况
    if (!Mth_isStatic(vMethod)) {

       /*
        * Dont check methods in java.lang.Object. 如果该方法所对应的类为java.lang.Object,则不进行检查
        */
        if (!Cls_isJavaLangObject(vClass)) {

           /*
            * Lookup the method being verified in the superclass
            * 将当前类的继承结构中查找是否存在同名方法
            */
            METHOD superMethod = Cls_lookupMethod(vClass, vSuperClass, vMethod);

           /*
            * If it exists then it must not be final
            * 如果存在的话,同时该方法为final的,则抛出异常
            * 为啥,此时的情况为父类,当前类有同名方法,而父类中方法为final,则此时不符合规范的
            */
            if (superMethod != NULL && Mth_isFinal(superMethod)) {
                Vfy_throw(VE_FINAL_METHOD_OVERRIDE);
            }
        }
    }

   /*
    * Verify that all exception handlers have a reasonable exception type.
    * 3. 验证exception handler
    */
    if (handlerCount > 0) {
        int i;
        VERIFIERTYPE exceptionVerifierType;

       /*
        * Iterate through the handler table
        */
        for (i = 0; i < handlerCount; i++) {

           /*
            * Get the catch type from the exception table
            * 获得该exception handler 所catch 的异常类
            */
            POOLINDEX catchTypeIndex = Mth_getExceptionTableCatchType(vMethod, i);

           /*
            * If the catch type index is zero then this is a try/finally entry
            * and there is no  exception type, If it is not zero then it needs
            * to be checked.
            * 如果为0,则意味着exception handler 是在try/finally 结构下产生的
            */
            if (catchTypeIndex != 0) {

               /*
                * Check that the entry is there and that it is a CONSTANT_Class
                * 检查catchTypeIndex 在常量池中的类型为CONSTANT_Class
                */
                Pol_checkTagIsClass(vPool, catchTypeIndex);

               /*
                * Get the class key 获得该类所对应的key
                */
                exceptionVerifierType = Vfy_toVerifierType(Pol_getClassKey(vPool, catchTypeIndex));

               /*
                * Check that it is subclass of java.lang.Throwable
                * 如果该类不是java.lang.Throwable 的子类的话,则抛出异常
                */
                if (!Vfy_isAssignable(exceptionVerifierType, Vfy_getThrowableVerifierType())) {
                    Vfy_throw(VE_EXPECT_THROWABLE);
                }
            }
        }
    }

   /*
    * Initialize the local variable type table with the method argument types
    * 4. 初始化局部变量表
    */
    Vfy_initializeLocals();

   /*
    * The main loop 主循环
    * 5. 通过循环的方式,模拟字节码的执行
    */
    while (ip < codeLength) {

        IPINDEX original_ip = ip;// 保存当前的ip

       /*
        * Used to hold the current opcode 保存当前的操作码
        */
        int opcode;

       /*
        * Used to hold IP address of the next entry in the stackmap table
        */
        IPINDEX nextStackMapIP;

       /*
        * Setup the ip used for error messages in case it is needed later
        */
        Vfy_setErrorIp(ip); //  vErrorIp = ip;

       /*
        * Output the debug trace message 打印debug 信息
        */
        Vfy_printVerifyLoopMessage(vMethod, ip);

       /*
        * Check that stackmaps are ordered according to offset and that
        * every offset in stackmaps point to the beginning to an instruction.
        * 检查stackmaps是否按照偏移量排序,stackmaps中的每个偏移量都指向指令的开头。
        *  StackMap jvm规范 P120
        */
        nextStackMapIP = Mth_getStackMapEntryIP(vMethod, currentStackMapIndex); // 计算获得下一个StackMap 的 IP
        if (nextStackMapIP == ip) {
            currentStackMapIndex++;         /* This offset is good. 修改currentStackMapIndex */
        }
        if (nextStackMapIP < ip) {
            Vfy_throw(VE_BAD_STACKMAP);     /* ip should have met offset. */
        }

       /*
        * Trace the instruction ip 打印ip
        */
        Vfy_trace1("Check instruction %ld\n", ip);

       /*
        * Merge with the next instruction (note how noControlFlow is used here).
        */
        Vfy_checkCurrentTarget(ip, noControlFlow);

       /*
        * Set noControlFlow to its default state 设置默认值
        */
        noControlFlow = FALSE;

       /*
        * Look for a possible jump target in one or more exception handlers.
        * If found the current local variable types must be the same as those
        * of the handler entry point.
        * 5.1  如果当前ip是在异常表 中处理范围内,则需要校验错误类型是否和stack map 中的一致
        *
        */
        if (handlerCount > 0) {
            int i;

           /*
            * Iterate through the handler table
            */
            for (i = 0 ; i < handlerCount ; i++) {

               /*
                * Get the start and end points of the handler entry
                */
                IPINDEX startPC = Mth_getExceptionTableStartPC(vMethod, i);
                IPINDEX endPC   = Mth_getExceptionTableEndPC(vMethod, i);

               /*
                * Check to see if the "current" ip falls in between these pointers
                * 如果ip是在捕获范围内的话
                */
                if (ip >= startPC && ip < endPC) {
                    VERIFIERTYPE exceptionVerifierType;

                   /*
                    * Get the ip offset for the exception handler and the catch type index
                    */
                    IPINDEX handlerPC        = Mth_getExceptionTableHandlerPC(vMethod, i); // 获得hander所对应的ip
                    POOLINDEX catchTypeIndex = Mth_getExceptionTableCatchType(vMethod, i); // 获得catch 类型

                   /*
                    * If the catch type index is zero then this is a try/finally entry.
                    * Unlike J2SE (1.3) there are no jsr/ret instructions in J2SE. The
                    * code in a finally block will be copied in to the end of the try
                    * block and an anonymous handler entry is make that points to another
                    * copy of the code. This will only be called if an exception is thrown
                    * in which case there will be an exception object of some kind on the
                    * stack. On the other hand if catch type index is non-zero then it
                    * will indicate a specific throwable type.
                    * 获得异常类型
                    */
                    if (catchTypeIndex != 0) {
                        exceptionVerifierType = Vfy_toVerifierType(Pol_getClassKey(vPool, catchTypeIndex));
                    } else {
                    	// 如果为0的话,则设置为throwable
                        exceptionVerifierType = Vfy_getThrowableVerifierType();
                    }

                   /*
                    * Save the current stack types somewhere and re-initialize the stack.
                    * 保存stack的状态
                    */
                    Vfy_saveStackState();

                   /*
                    * Push the exception type and check the target has just this one type
                    * on the stack along with the current local variable types.
                    * 将exception 保存在栈中
                    */
                    Vfy_push(exceptionVerifierType);
                    Vfy_checkHandlerTarget(handlerPC);

                   /*
                    * Restore the old stack types 恢复stack的状态
                    */
                    Vfy_restoreStackState();
                }
            }
        }

       /*
        * Get the next bytecode 获得字节码
        */
        opcode = Vfy_getOpcode(ip);
         // 模拟执行字节码
        switch (opcode) {

            case NOP: {
                ip++;
                break;

            }
            // 删去其他代码
             default: {
                Vfy_throw(VE_BAD_INSTR);
                break;
            }
        } /* End of switch statement */

        /* Make sure that there is no exception handler whose start or
         * end is between original_ip and ip, because that would be 
         * an illegal value
         * 验证在original_ip 和 ip 之前没有hander覆盖
         */
        if (handlerCount > 0 && ip > original_ip + 1) {
            int i;
            for (i = 0 ; i < handlerCount ; i++) {
                IPINDEX startPC = Mth_getExceptionTableStartPC(vMethod, i);
                IPINDEX endPC   = Mth_getExceptionTableEndPC(vMethod, i);
                if (   (startPC > original_ip && startPC < ip)
                    || (endPC > original_ip && endPC < ip)) {
                    Vfy_throw(VE_BAD_EXCEPTION_HANDLER_RANGE); 
                }
            }
        }
    }

    /* Make sure that the bytecode offset within 
     * stackmaps does not exceed code size
     * 检查StackMapIndex 没有超过 StackMap
     */

    if (Mth_checkStackMapOffset(vMethod, currentStackMapIndex) == 0) {
        Vfy_throw(VE_BAD_STACKMAP);
    }

    // 如果ip 超过了 code 的长度,则抛出异常

    if (ip > codeLength) {
        Vfy_throw(VE_MIDDLE_OF_BYTE_CODE);
    }

    // 验证 在方法结束的时候不存在控制流,如果存在,则抛出异常
    if (!noControlFlow) {
        Vfy_throw(VE_FALL_THROUGH);
    }

    /* All done */
}
            

其步骤如下:

  1. 获取变量,并初始化一些变量

  2. 如果该方法不是静态的,则检查是否存在重写final方法的情况

  3. 验证exception handler,如果其捕获的异常不是java.lang.Throwable 的子类,则抛出异常

  4. 初始化局部变量表

  5. 通过循环的方式,模拟字节码的执行

    1. 如果当前ip是在异常表 中处理范围内,则需要校验错误类型是否和stack map 中的一致
    2. 模拟执行字节码
    3. 验证hander 不是在original_ip 和 ip 之间 开始或结束的,如果是的话,则是一个错误值.
  6. 在模拟完字节码后,进行如下检查:

    1. 检查StackMapIndex 没有超过 StackMap,否则抛出异常
    2. 如果ip 超过了 code 的长度,则抛出异常
    3. 验证 在方法结束的时候不存在控制流,如果存在,则抛出异常

这里面有几个比较重要的方法,下文一一介绍.

Cls_lookupMethod

这个方法是在父类中查找是否存在同名方法。此处是一个宏,定义如下:

#define Cls_lookupMethod(vClassContext, vClassTarget, vMethod) \
    lookupMethod(vClassTarget, vMethod->nameTypeKey, (INSTANCE_CLASS)vClassContext)

此处会最终调用lookupMethod方法,代码如下:

METHOD lookupMethod(CLASS thisClass, NameTypeKey key, 
                    INSTANCE_CLASS currentClass)
{
    INSTANCE_CLASS thisInstanceClass = 
        IS_ARRAY_CLASS(thisClass) ? JavaLangObject : (INSTANCE_CLASS)thisClass;
    // 1. 沿着继承体系不断向上查找,是否存在同名方法
    do {
        METHODTABLE methodTable = thisInstanceClass->methodTable;
        FOR_EACH_METHOD(thisMethod, methodTable) 
            if (thisMethod->nameTypeKey.i == key.i) { 
                if (   currentClass == NULL  // currentClass 为null
                    || currentClass == thisInstanceClass
                    || ((ACC_PUBLIC|ACC_PROTECTED) & thisMethod->accessFlags) // 该方法的修饰符为ACC_PUBLIC,ACC_PROTECTED
                    || ( ((thisMethod->accessFlags & ACC_PRIVATE) == 0) // 该方法的修饰符不是private,并且currentClass,thisInstanceClass 在同一个包中
                    && thisInstanceClass->clazz.packageName == 
                           currentClass->clazz.packageName)
                ) { 
                    return thisMethod;
                }
            }
        END_FOR_EACH_METHOD
            /*  If the class has a superclass, look its methods as well */
            thisInstanceClass = thisInstanceClass->superClass;
    } while (thisInstanceClass != NULL); // 不断的向上查找

    /* Before we give up on an interface class, check any interfaces that
     * it implements 2. 如果thisClass为接口的话,则检查该接口所继承的接口,是否存在同名方法*/
    if ((thisClass->accessFlags & ACC_INTERFACE) 
        && (((INSTANCE_CLASS)thisClass)->ifaceTable != NULL)) {
        CONSTANTPOOL constantPool = ((INSTANCE_CLASS)thisClass)->constPool;
        unsigned short *ifaceTable = ((INSTANCE_CLASS)thisClass)->ifaceTable;
        int tableLength = ifaceTable[0];
        int i;
        for (i = 1; i <= tableLength; i++) {
            int cpIndex = ifaceTable[i];
            CLASS intf = constantPool->entries[cpIndex].clazz;
            // 这里递归调用该方法
            METHOD temp = lookupMethod(intf, key, currentClass);
            if (temp != NULL) { 
                return temp;
            }
        }
    }
    return NULL;
}

这部分的代码比较简单,看注释就可以了.不再展开.

初始化局部变量表

该功能是通过Vfy_initializeLocals实现的,其代码如下:

void Vfy_initializeLocals() {
    /* Fill in the initial derived stack map with argument types. 获得此时正在验证方法的签名 */
    unsigned char *sig = (unsigned char*) change_Key_to_Name(methodBeingVerified->nameTypeKey.nt.typeKey, NULL);

    int nargs = *sig++;
    int i;
    int n;

    n = 0;
    vNeedInitialization = FALSE;
    // 1.如果该方法不是静态的话,则需要添加this指针
    if (!Mth_isStatic(methodBeingVerified)) {
        /* add one extra argument for instance methods */
        n++; // 添加this
        //  如果该方法不是JavaLangObject的<init>方法的话,则在vLocals[0] 中存放ITEM_InitObject,否则,存放当前方法所对应类
        // 也就是local[0] 为this指针
        if (methodBeingVerified->nameTypeKey.nt.nameKey == initNameAndType.nt.nameKey &&
            methodBeingVerified->ofClass->clazz.key != Vfy_getObjectVerifierType()) {
            vLocals[0] = ITEM_InitObject;
            vNeedInitialization = TRUE;
        } else {
            vLocals[0] = methodBeingVerified->ofClass->clazz.key;
        }
    }
    // 2.复制局部变量表
    for (i = 0; i < nargs; i++) {
        n += change_Arg_to_StackType(&sig, &vLocals[n]);
    }

    returnSig = sig;
}

同样,这部分的代码比较简单,不再展开.

Vfy_checkCurrentTarget

检查在指定ip的位置是否可以合并stack map.此处定义的是一个宏.定义如下:

#define Vfy_checkCurrentTarget(current_ip, noControlFlow) {                                             \
        Vfy_matchStackMap(                                                                              \
                           current_ip,                                       /* target ip */            \
                           SM_MERGE | (noControlFlow ? SM_EXIST : SM_CHECK), /* flags */                \
                           VE_SEQ_BAD_TYPE                          /* Vfy_throw code if not matched */ \
                         );                                                                             \
}


#define Vfy_matchStackMap(targetIP, flags, throwCode) {                                                 \
    if (!matchStackMap(methodBeingVerified,  targetIP, flags)) {                                        \
        Vfy_throw(throwCode);                                                                           \
    }                                                                                                   \
}

因此,最终会调用matchStackMap方法.在kvm内部定义了如下3个宏,用于控制如何比较两个堆栈映射的标志:

#define SM_CHECK 1           /* 检查派生类型是否与记录的类型匹配 */
#define SM_MERGE 2           /* 将记录的类型合并到派生类型中 */
#define SM_EXIST 4           /*  记录的类型属性必须存在*/
  • 当noControlFlow为true时,传递的参数为 SM_MERGE | SM_EXIST

  • 当noControlFlow为false时,传递的参数为 SM_MERGE | SM_CHECK

matchStackMap 方法如下:

bool_t matchStackMap(METHOD thisMethod, IPINDEX target_ip, int flags) {
    bool_t result = TRUE;  /* Assume result is TRUE */
    bool_t target_needs_initialization;
    unsigned short nstack;
    unsigned short nlocals;
    unsigned short i;

    /* Following is volatile, and will disappear at first GC 1. 获得当前ip所对应的stackMap*/
    unsigned short *stackMapBase = getStackMap(thisMethod, target_ip);

#if INCLUDEDEBUGCODE
    if (traceverifier) {
        fprintf(stdout, "Match stack maps (this=%ld, target=%ld)%s%s%s\n",
                (long)vErrorIp, (long)target_ip,
            (flags & SM_CHECK) ? " CHECK" : "",
            (flags & SM_MERGE) ? " MERGE" : "",
            (flags & SM_EXIST) ? " EXIST" : "");
    }
#endif

    //  如果不存在的话,则直接返回!(flags & SM_EXIST)
    if (stackMapBase == NULL) {
#if INCLUDEDEBUGCODE
        if (traceverifier) {
            fprintf(stdout, "No recorded stack map at %ld\n", (long)target_ip);
        }
#endif
        return !(flags & SM_EXIST);
    }

  START_TEMPORARY_ROOTS
   /*
    * unsigned short* stackMap = (                              \
                                           \
        stackMap = stackMapBase,                                                 \
       TemporaryRoots[TemporaryRootsLength].cell = -1,                   \
       TemporaryRoots[TemporaryRootsLength+1].cellpp = (cell **)&stackMap,  \
       TemporaryRoots[TemporaryRootsLength+2].cellp = (cell *)stackMapBase,    \
       TemporaryRootsLength = TemporaryRootsLength + 3,                  \
      stackMap);
    */
    DECLARE_TEMPORARY_ROOT_FROM_BASE(unsigned short*, stackMap,
                                     stackMapBase, stackMapBase);

#if INCLUDEDEBUGCODE
    if (traceverifier) {
        static void printStackMap(METHOD thisMethod, IPINDEX ip);
        printStackMap(thisMethod, target_ip);
    }
#endif

    nlocals = *stackMap++; // 获得local的数量
    /* We implicitly need to still perform initialization if at least
     * one local or stack variable contains ITEM_InitObject */
    target_needs_initialization = FALSE;

    for (i = 0; i < nlocals; i++) {
        unsigned short ty = *stackMap++;
        unsigned short mergedType = ty;
        if (ty == ITEM_InitObject) { 
            target_needs_initialization = TRUE;
        }
        // 如果vLocals[i] 不是mergedType 的子类,其flags为必须存在,则直接返回false
        if ((SM_CHECK & flags) && !vIsAssignable(vLocals[i], ty, &mergedType)) {
            result = FALSE;
            goto done;
        }
        // 如果flags 为SM_MERGE,则直接使用mergedType就可以了
        if (SM_MERGE & flags) {
            vLocals[i] = mergedType;
        }
    }
    // 如果flags为SM_MERGE的话,则在nlocals-vFrameSize填充0. 栈帧的结构是有方法参数,局部变量表,操作数栈组成的
    if (SM_MERGE & flags) {
        for (i = nlocals; i < vFrameSize; i++) {
            vLocals[i] = ITEM_Bogus;
        }
    }

    // 验证栈
    nstack = *stackMap++;
    if ((SM_CHECK & flags) && nstack != vSP) {
        result = FALSE;
        goto done;
    }
    if (SM_MERGE & flags) {
        vSP = nstack;
    }
    for (i = 0; i < nstack; i++) {
        unsigned short ty = *stackMap++;
        unsigned short mergedType = ty;
        if (ty == ITEM_InitObject) { 
            target_needs_initialization = TRUE;
        }
        if ((SM_CHECK & flags) && !vIsAssignable(vStack[i], ty, &mergedType)) {
            result = FALSE;
            goto done;
        }
        if (SM_MERGE & flags) {
            vStack[i] = mergedType;
        }
    }

    // 如果现在处理的方法为<init>方法
    if (methodBeingVerified->nameTypeKey.nt.nameKey 
                                     == initNameAndType.nt.nameKey) {
        if (SM_CHECK & flags) { 
            if (vNeedInitialization && !target_needs_initialization) { 
                /* We still need to perform initialization, but we are
                 * merging into a location that doesn't.
                 */
                result = FALSE;
                goto done;
            }
        }

        if (SM_MERGE & flags) { 
            vNeedInitialization = target_needs_initialization;
        }
    }

 done:
  END_TEMPORARY_ROOTS
  return result;
}

这里有两部分,1部分是验证局部变量表,1部分是验证操作数栈.其验证就是根据stackMap检查局部变量表,操作数栈的类型是否和记录中的一致,如不一致,就抛出异常.

Vfy_saveStackState,Vfy_restoreStackState

Vfy_saveStackState是保存操作数栈的状态,是需要和Vfy_restoreStackState一起连用的.其代码如下:

void Vfy_saveStackState() {
    vSP_bak = vSP;
    vStack0_bak = vStack[0];
    vSP = 0;
}

void Vfy_restoreStackState() {
    vStack[0] = vStack0_bak;
    vSP = vSP_bak;
}

Vfy_checkHandlerTarget

该方法是为了hander 处,其栈帧是否正确.代码如下:

#define Vfy_checkHandlerTarget(target_ip) {                                                             \
    Vfy_trace1("Check exception handler at %ld:\n", (long)target_ip);                                   \
    Vfy_matchStackMap(target_ip, SM_CHECK | SM_EXIST, VE_TARGET_BAD_TYPE);                              \
}

其最终也是调用了上文所介绍的Vfy_matchStackMap.

Vfy_push

验证最重要的方式是通过模拟操作数栈,模拟字节码的执行.这时就需要一个方法,执行压栈过程. Vfy_push就是做如此操作的。

void Vfy_push(VERIFIERTYPE typeKey) {
    if (vSP >= vMaxStack) {
        Vfy_throw(VE_STACK_OVERFLOW);
    }
    vStack[vSP++] = typeKey;
}

关于字节码模拟,在后续文章中讲述.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值