您的位置:首页 > 编程语言 > Java开发

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

2017-11-02 19:47 671 查看
机型信息:android4.0系列

0x01. 崩溃堆栈

"---java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
com.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 NDEBUG
bail:
#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包发给用户,抓取用户的日志.根据日志信息验证分析结论.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐