java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

来源:互联网 发布:js比较数值大小 编辑:程序博客网 时间:2024/05/16 19:27

机型信息:android4.0系列

0x01. 崩溃堆栈

"---java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementationcom.huawei.android.pushagent.c.a.e(Unknown Source)com.huawei.android.pushagent.c.a.f(Unknown Source)com.huawei.android.pushagent.c.c.b.b(Unknown Source)com.huawei.android.pushagent.c.c.b.a(Unknown Source)com.huawei.android.pushagent.c.c.c.run(Unknown Source)android.os.Handler.handleCallback(Handler.java:605)android.os.Handler.dispatchMessage(Handler.java:92)android.os.Looper.loop(Looper.java:137)com.huawei.android.pushagent.c.c.b.run(Unknown Source)"

0x02. 崩溃来源

dalvik/vm/oo/Resolve.cpp

/* * Find the class corresponding to "classIdx", which maps to a class name * string.  It might be in the same DEX file as "referrer", in a different * DEX file, generated by a class loader, or generated by the VM (e.g. * array classes). * * Because the DexTypeId is associated with the referring class' DEX file, * we may have to resolve the same class more than once if it's referred * to from classes in multiple DEX files.  This is a necessary property for * DEX files associated with different class loaders. * * We cache a copy of the lookup in the DexFile's "resolved class" table, * so future references to "classIdx" are faster. * * Note that "referrer" may be in the process of being linked. * * Traditional VMs might do access checks here, but in Dalvik the class * "constant pool" is shared between all classes in the DEX file.  We rely * on the verifier to do the checks for us. * * Does not initialize the class. * * "fromUnverifiedConstant" should only be set if this call is the direct * result of executing a "const-class" or "instance-of" instruction, which * use class constants not resolved by the bytecode verifier. * * Returns NULL with an exception raised on failure. */ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,    bool fromUnverifiedConstant){    DvmDex* pDvmDex = referrer->pDvmDex;    ClassObject* resClass;    const char* className;    /*     * Check the table first -- this gets called from the other "resolve"     * methods.     */    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);    if (resClass != NULL)        return resClass;    LOGVV("--- resolving class %u (referrer=%s cl=%p)",        classIdx, referrer->descriptor, referrer->classLoader);    /*     * Class hasn't been loaded yet, or is in the process of being loaded     * and initialized now.  Try to get a copy.  If we find one, put the     * pointer in the DexTypeId.  There isn't a race condition here --     * 32-bit writes are guaranteed atomic on all target platforms.  Worst     * case we have two threads storing the same value.     *     * If this is an array class, we'll generate it here.     */    className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);    if (className[0] != '\0' && className[1] == '\0') {        /* primitive type */        resClass = dvmFindPrimitiveClass(className[0]);    } else {        resClass = dvmFindClassNoInit(className, referrer->classLoader); //跟踪这个实现    }    if (resClass != NULL) {        /*         * If the referrer was pre-verified, the resolved class must come         * from the same DEX or from a bootstrap class.  The pre-verifier         * makes assumptions that could be invalidated by a wacky class         * loader.  (See the notes at the top of oo/Class.c.)         *         * The verifier does *not* fail a class for using a const-class         * or instance-of instruction referring to an unresolveable class,         * because the result of the instruction is simply a Class object         * or boolean -- there's no need to resolve the class object during         * verification.  Instance field and virtual method accesses can         * break dangerously if we get the wrong class, but const-class and         * instance-of are only interesting at execution time.  So, if we         * we got here as part of executing one of the "unverified class"         * instructions, we skip the additional check.         *         * Ditto for class references from annotations and exception         * handler lists.         */        if (!fromUnverifiedConstant &&            IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))        {            ClassObject* resClassCheck = resClass;              if (dvmIsArrayClass(resClassCheck))                     resClassCheck = resClassCheck->elementClass;            if (referrer->pDvmDex != resClassCheck->pDvmDex && //resClass和referrer(入参)指向的pDvmDex对象不相等,说明这个类来自不同的dex文件.                resClassCheck->classLoader != NULL)              {                ALOGW("Class resolved by unexpected DEX:"                     " %s(%p):%p ref [%s] %s(%p):%p",                    referrer->descriptor, referrer->classLoader,                    referrer->pDvmDex,                    resClass->descriptor, resClassCheck->descriptor,                    resClassCheck->classLoader, resClassCheck->pDvmDex);                ALOGW("(%s had used a different %s during pre-verification)",                    referrer->descriptor, resClass->descriptor);                dvmThrowIllegalAccessError(                    "Class ref in pre-verified class resolved to unexpected "                    "implementation");    //这里抛出的异常                return NULL;            }        }        LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d",            resClass->descriptor, referrer->descriptor, referrer->pDvmDex,            referrer->classLoader, classIdx);        /*         * Add what we found to the list so we can skip the class search         * next time through.         *         * TODO: should we be doing this when fromUnverifiedConstant==true?         * (see comments at top of oo/Class.c)         */        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);    } else {        /* not found, exception should be raised */        LOGVV("Class not found: %s",            dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));        assert(dvmCheckException(dvmThreadSelf()));    }    return resClass;}

dalvik/vm/oo/Class.cpp

/* * Find the named class (by descriptor), using the specified * initiating ClassLoader. * * The class will be loaded if it has not already been, as will its * superclass.  It will not be initialized. * * If the class can't be found, returns NULL with an appropriate exception * raised. */ClassObject* dvmFindClassNoInit(const char* descriptor,        Object* loader){    assert(descriptor != NULL);    //assert(loader != NULL);    LOGVV("FindClassNoInit '%s' %p", descriptor, loader);    if (*descriptor == '[') {        /*         * Array class.  Find in table, generate if not found.         */        return dvmFindArrayClass(descriptor, loader);    } else {        /*         * Regular class.  Find in table, load if not found.         */        if (loader != NULL) {            return findClassFromLoaderNoInit(descriptor, loader);        } else {            return dvmFindSystemClassNoInit(descriptor);        }    }}
/* * Load the named class (by descriptor) from the specified class * loader.  This calls out to let the ClassLoader object do its thing. * * Returns with NULL and an exception raised on error. */static ClassObject* findClassFromLoaderNoInit(const char* descriptor,    Object* loader){    //ALOGI("##### findClassFromLoaderNoInit (%s,%p)",    //        descriptor, loader);    Thread* self = dvmThreadSelf();    assert(loader != NULL);    /*     * Do we already have it?     *     * The class loader code does the "is it already loaded" check as     * well.  However, this call is much faster than calling through     * interpreted code.  Doing this does mean that in the common case     * (365 out of 420 calls booting the sim) we're doing the     * lookup-by-descriptor twice.  It appears this is still a win, so     * I'm keeping it in.     */    ClassObject* clazz = dvmLookupClass(descriptor, loader, false);    if (clazz != NULL) {        LOGVV("Already loaded: %s %p", descriptor, loader);        return clazz;    } else {        LOGVV("Not already loaded: %s %p", descriptor, loader);    }    char* dotName = NULL;    StringObject* nameObj = NULL;    /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */    dotName = dvmDescriptorToDot(descriptor);    if (dotName == NULL) {        dvmThrowOutOfMemoryError(NULL);        return NULL;    }    nameObj = dvmCreateStringFromCstr(dotName);    if (nameObj == NULL) {        assert(dvmCheckException(self));        goto bail;    }    dvmMethodTraceClassPrepBegin();    /*     * Invoke loadClass().  This will probably result in a couple of     * exceptions being thrown, because the ClassLoader.loadClass()     * implementation eventually calls VMClassLoader.loadClass to see if     * the bootstrap class loader can find it before doing its own load.     */    LOGVV("--- Invoking loadClass(%s, %p)", dotName, loader);    {        const Method* loadClass =            loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];        JValue result;        dvmCallMethod(self, loadClass, loader, &result, nameObj); //dvmCallMethod        clazz = (ClassObject*) result.l;        dvmMethodTraceClassPrepEnd();        Object* excep = dvmGetException(self);        if (excep != NULL) {#if DVM_SHOW_EXCEPTION >= 2            ALOGD("NOTE: loadClass '%s' %p threw exception %s",                 dotName, loader, excep->clazz->descriptor);#endif            dvmAddTrackedAlloc(excep, self);            dvmClearException(self);            dvmThrowChainedNoClassDefFoundError(descriptor, excep);            dvmReleaseTrackedAlloc(excep, self);            clazz = NULL;            goto bail;        } else if (clazz == NULL) {            ALOGW("ClassLoader returned NULL w/o exception pending");            dvmThrowNullPointerException("ClassLoader returned null");            goto bail;        }    }    /* not adding clazz to tracked-alloc list, because it's a ClassObject */    dvmAddInitiatingLoader(clazz, loader);    LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)",        descriptor, clazz->classLoader, loader, clazz);bail:    dvmReleaseTrackedAlloc((Object*)nameObj, NULL);    free(dotName);    return clazz;}

dvmCallMethod

dalvik/vm/interp/Stack.cpp

/* * Issue a method call. * * Pass in NULL for "obj" on calls to static methods. * * (Note this can't be inlined because it takes a variable number of args.) */void dvmCallMethod(Thread* self, const Method* method, Object* obj,    JValue* pResult, ...){    va_list args;    va_start(args, pResult);    dvmCallMethodV(self, method, obj, false, pResult, args);    va_end(args);}/* * Issue a method call with a variable number of arguments.  We process * the contents of "args" by scanning the method signature. * * Pass in NULL for "obj" on calls to static methods. * * We don't need to take the class as an argument because, in Dalvik, * we don't need to worry about static synchronized methods. */void dvmCallMethodV(Thread* self, const Method* method, Object* obj,    bool fromJni, JValue* pResult, va_list args){    const char* desc = &(method->shorty[1]); // [0] is the return type.    int verifyCount = 0;    ClassObject* clazz;    u4* ins;    clazz = callPrep(self, method, obj, false);    if (clazz == NULL)        return;    /* "ins" for new frame start at frame pointer plus locals */    ins = ((u4*)self->interpSave.curFrame) +           (method->registersSize - method->insSize);    //ALOGD("  FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);    /* put "this" pointer into in0 if appropriate */    if (!dvmIsStaticMethod(method)) {#ifdef WITH_EXTRA_OBJECT_VALIDATION        assert(obj != NULL && dvmIsHeapAddress(obj));#endif        *ins++ = (u4) obj;        verifyCount++;    }    while (*desc != '\0') {        switch (*(desc++)) {            case 'D': case 'J': {                u8 val = va_arg(args, u8);                memcpy(ins, &val, 8);       // EABI prevents direct store                ins += 2;                verifyCount += 2;                break;            }            case 'F': {                /* floats were normalized to doubles; convert back */                float f = (float) va_arg(args, double);                *ins++ = dvmFloatToU4(f);                verifyCount++;                break;            }            case 'L': {     /* 'shorty' descr uses L for all refs, incl array */                void* arg = va_arg(args, void*);                assert(obj == NULL || dvmIsHeapAddress(obj));                jobject argObj = reinterpret_cast<jobject>(arg);                if (fromJni)                    *ins++ = (u4) dvmDecodeIndirectRef(self, argObj);                else                    *ins++ = (u4) argObj;                verifyCount++;                break;            }            default: {                /* Z B C S I -- all passed as 32-bit integers */                *ins++ = va_arg(args, u4);                verifyCount++;                break;            }        }    }#ifndef NDEBUG    if (verifyCount != method->insSize) {        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,            method->insSize, clazz->descriptor, method->name);        assert(false);        goto bail;    }#endif    //dvmDumpThreadStack(dvmThreadSelf());    if (dvmIsNativeMethod(method)) {        TRACE_METHOD_ENTER(self, method);        /*         * Because we leave no space for local variables, "curFrame" points         * directly at the method arguments.         */        (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,                              method, self);        TRACE_METHOD_EXIT(self, method);    } else {        dvmInterpret(self, method, pResult);//给pResult赋值    }#ifndef NDEBUGbail:#endif    dvmPopFrame(self);}

/dalvik/vm/interp/Interp.cpp

/* * Main interpreter loop entry point. * * This begins executing code at the start of "method".  On exit, "pResult" * holds the return value of the method (or, if "method" returns NULL, it * holds an undefined value). * * The interpreted stack frame, which holds the method arguments, has * already been set up. */void dvmInterpret(Thread* self, const Method* method, JValue* pResult){    InterpSaveState interpSaveState;    ExecutionSubModes savedSubModes;#if defined(WITH_JIT)    /* Target-specific save/restore */    double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];    /*     * If the previous VM left the code cache through single-stepping the     * inJitCodeCache flag will be set when the VM is re-entered (for example,     * in self-verification mode we single-step NEW_INSTANCE which may re-enter     * the VM through findClassFromLoaderNoInit). Because of that, we cannot     * assert that self->inJitCodeCache is NULL here.     */#endif    /*     * Save interpreter state from previous activation, linking     * new to last.     */    interpSaveState = self->interpSave;    self->interpSave.prev = &interpSaveState;    /*     * Strip out and save any flags that should not be inherited by     * nested interpreter activation.     */    savedSubModes = (ExecutionSubModes)(              self->interpBreak.ctl.subMode & LOCAL_SUBMODE);    if (savedSubModes != kSubModeNormal) {        dvmDisableSubMode(self, savedSubModes);    }#if defined(WITH_JIT)    dvmJitCalleeSave(calleeSave);#endif#if defined(WITH_TRACKREF_CHECKS)    self->interpSave.debugTrackedRefStart =        dvmReferenceTableEntries(&self->internalLocalRefTable);#endif    self->debugIsMethodEntry = true;#if defined(WITH_JIT)    dvmJitCalleeSave(calleeSave);    /* Initialize the state to kJitNot */    self->jitState = kJitNot;#endif    /*     * Initialize working state.     *     * No need to initialize "retval".     */    self->interpSave.method = method;    self->interpSave.curFrame = (u4*) self->interpSave.curFrame;    self->interpSave.pc = method->insns;    assert(!dvmIsNativeMethod(method));    /*     * Make sure the class is ready to go.  Shouldn't be possible to get     * here otherwise.     */    if (method->clazz->status < CLASS_INITIALIZING ||        method->clazz->status == CLASS_ERROR)    {        ALOGE("ERROR: tried to execute code in unprepared class '%s' (%d)",            method->clazz->descriptor, method->clazz->status);        dvmDumpThread(self, false);        dvmAbort();    }    typedef void (*Interpreter)(Thread*);    Interpreter stdInterp;    if (gDvm.executionMode == kExecutionModeInterpFast)        stdInterp = dvmMterpStd;#if defined(WITH_JIT)    else if (gDvm.executionMode == kExecutionModeJit)        stdInterp = dvmMterpStd;#endif    else        stdInterp = dvmInterpretPortable;    // Call the interpreter    (*stdInterp)(self);    *pResult = self->interpSave.retval;    /* Restore interpreter state from previous activation */    self->interpSave = interpSaveState;#if defined(WITH_JIT)    dvmJitCalleeRestore(calleeSave);#endif    if (savedSubModes != kSubModeNormal) {        dvmEnableSubMode(self, savedSubModes);    }}

0x03. 分析

从上面的信息来看并没有得到更多的信息,仅仅是看到了,引用某个类的时候,系统检测到当前Resolve的类和其引用的类来自不同的dex文件.

根据这个结论推断:

  • 0x01. 插件框架加载组件的dex有问题;
    导致当前需要Resolve的class和其引用的class的dex搞错了?一个是老的插件里面的,一个是新的插件里面的?

  • 0x02. 插件框架加载组建的classloader有问题;
    当前需要Resolve的class和其引用的class的classloader不是同一个,这时候class里面的同一个对象也是不相等?

目前能想到的就这两个情况.

0x04. 验证

找到崩溃用户,把Debug包发给用户,抓取用户的日志.根据日志信息验证分析结论.

阅读全文
0 0
原创粉丝点击