/* * Open up the reserved area and throw an exception. The reserved area * should only be needed to create and initialize the exception itself. * * If we already opened it and we're continuing to overflow, abort the VM. * * We have to leave the "reserved" area open until the "catch" handler has * finished doing its processing. This is because the catch handler may * need to resolve classes, which requires calling into the class loader if * the classes aren't already in the "initiating loader" list. */ void dvmHandleStackOverflow(Thread* self) { /* * Can we make the reserved area available? */ if (self->stackOverflowed) { /* * Already did, nothing to do but bail. */ LOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting\n", self->threadId); dvmDumpThread(self, false); dvmAbort(); } /* open it up to the full range */ LOGI("Stack overflow, expanding (%p to %p)\n", self->interpStackEnd, self->interpStackStart - self->interpStackSize); //dvmDumpThread(self, false); self->interpStackEnd = self->interpStackStart - self->interpStackSize; self->stackOverflowed = true; /* * If we were trying to throw an exception when the stack overflowed, * we will blow up when doing the class lookup on StackOverflowError * because of the pending exception. So, we clear it and make it * the cause of the SOE. */ Object* excep = dvmGetException(self); if (excep != NULL) { LOGW("Stack overflow while throwing exception\n"); dvmClearException(self); } dvmThrowChainedException("Ljava/lang/StackOverflowError;", NULL, excep); }
/* * Find a class by name, initializing it if requested. */ ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader, bool doInit) { ClassObject* clazz = NULL; char* name = NULL; char* descriptor = NULL; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); goto bail; } name = dvmCreateCstrFromString(nameObj); /* * We need to validate and convert the name (from x.y.z to x/y/z). This * is especially handy for array types, since we want to avoid * auto-generating bogus array classes. */ if (!validateClassName(name)) { LOGW("dvmFindClassByName rejecting '%s'\n", name); dvmThrowException("Ljava/lang/ClassNotFoundException;", name); goto bail; } descriptor = dvmDotToDescriptor(name); if (descriptor == NULL) { goto bail; } if (doInit) clazz = dvmFindClass(descriptor, loader); else clazz = dvmFindClassNoInit(descriptor, loader); if (clazz == NULL) { LOGVV("FAIL: load %s (%d)\n", descriptor, doInit); Thread* self = dvmThreadSelf(); Object* oldExcep = dvmGetException(self); dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */ dvmClearException(self); dvmThrowChainedException("Ljava/lang/ClassNotFoundException;", name, oldExcep); dvmReleaseTrackedAlloc(oldExcep, self); } else { LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n", descriptor, doInit, clazz, clazz->classLoader); } bail: free(name); free(descriptor); return clazz; }
/* * 从指定的DEX文件中加载一个类,使用类加载器去初始化一个类对象 */ static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args, JValue* pResult) { StringObject* nameObj = (StringObject*) args[0]; Object* loader = (Object*) args[1]; int cookie = args[2]; ClassObject* clazz = NULL; DexOrJar* pDexOrJar = (DexOrJar*) cookie; DvmDex* pDvmDex; char* name; char* descriptor; name = dvmCreateCstrFromString(nameObj); descriptor = dvmDotToDescriptor(name); ALOGV("--- Explicit class load '%s' l=%p c=0x%08x", descriptor, loader, cookie); free(name); if (!validateCookie(cookie)) RETURN_VOID(); if (pDexOrJar->isDex) pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); else pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); /* once we load something, we can't unmap the storage */ pDexOrJar->okayToFree = false; clazz = dvmDefineClass(pDvmDex, descriptor, loader); Thread* self = dvmThreadSelf(); if (dvmCheckException(self)) { /* * If we threw a "class not found" exception, stifle it, since the * contract in the higher method says we simply return null if * the class is not found. */ Object* excep = dvmGetException(self); if (strcmp(excep->clazz->descriptor, "Ljava/lang/ClassNotFoundException;") == 0 || strcmp(excep->clazz->descriptor, "Ljava/lang/NoClassDefFoundError;") == 0) { dvmClearException(self); } clazz = NULL; } free(descriptor); RETURN_PTR(clazz); }
/* * Open up the reserved area and throw an exception. The reserved area * should only be needed to create and initialize the exception itself. * * If we already opened it and we're continuing to overflow, abort the VM. * * We have to leave the "reserved" area open until the "catch" handler has * finished doing its processing. This is because the catch handler may * need to resolve classes, which requires calling into the class loader if * the classes aren't already in the "initiating loader" list. */ void dvmHandleStackOverflow(Thread* self, const Method* method) { /* * Can we make the reserved area available? */ if (self->stackOverflowed) { /* * Already did, nothing to do but bail. */ LOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting\n", self->threadId); dvmDumpThread(self, false); dvmAbort(); } /* open it up to the full range */ LOGI("threadid=%d: stack overflow on call to %s.%s:%s\n", self->threadId, method->clazz->descriptor, method->name, method->shorty); StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->curFrame); LOGI(" method requires %d+%d+%d=%d bytes, fp is %p (%d left)\n", method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4, (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea), saveArea, (u1*) saveArea - self->interpStackEnd); LOGI(" expanding stack end (%p to %p)\n", self->interpStackEnd, self->interpStackStart - self->interpStackSize); //dvmDumpThread(self, false); self->interpStackEnd = self->interpStackStart - self->interpStackSize; self->stackOverflowed = true; /* * If we were trying to throw an exception when the stack overflowed, * we will blow up when doing the class lookup on StackOverflowError * because of the pending exception. So, we clear it and make it * the cause of the SOE. */ Object* excep = dvmGetException(self); if (excep != NULL) { LOGW("Stack overflow while throwing exception\n"); dvmClearException(self); } dvmThrowChainedExceptionByClass(gDvm.classJavaLangStackOverflowError, NULL, excep); }
/* * Wrap the now-pending exception in a different exception. This is useful * for reflection stuff that wants to hand a checked exception back from a * method that doesn't declare it. * * If something fails, an (unchecked) exception related to that failure * will be pending instead. */ void dvmWrapException(const char* newExcepStr) { Thread* self = dvmThreadSelf(); Object* origExcep; ClassObject* iteClass; origExcep = dvmGetException(self); dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it dvmClearException(self); // clear before class lookup iteClass = dvmFindSystemClass(newExcepStr); if (iteClass != NULL) { Object* iteExcep; Method* initMethod; iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT); if (iteExcep != NULL) { initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>", "(Ljava/lang/Throwable;)V"); if (initMethod != NULL) { JValue unused; dvmCallMethod(self, initMethod, iteExcep, &unused, origExcep); /* if <init> succeeded, replace the old exception */ if (!dvmCheckException(self)) dvmSetException(self, iteExcep); } dvmReleaseTrackedAlloc(iteExcep, NULL); /* if initMethod doesn't exist, or failed... */ if (!dvmCheckException(self)) dvmSetException(self, origExcep); } else { /* leave OutOfMemoryError pending */ } } else { /* leave ClassNotFoundException pending */ } assert(dvmCheckException(self)); dvmReleaseTrackedAlloc(origExcep, self); }
/* * private static Class defineClass(String name, ClassLoader loader, * int cookie, ProtectionDomain pd) * * Load a class from a DEX file. This is roughly equivalent to defineClass() * in a regular VM -- it's invoked by the class loader to cause the * creation of a specific class. The difference is that the search for and * reading of the bytes is done within the VM. * * The class name is a "binary name", e.g. "java.lang.String". * * Returns a null pointer with no exception if the class was not found. * Throws an exception on other failures. */ static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args, JValue* pResult) { StringObject* nameObj = (StringObject*) args[0]; Object* loader = (Object*) args[1]; int cookie = args[2]; Object* pd = (Object*) args[3]; ClassObject* clazz = NULL; DexOrJar* pDexOrJar = (DexOrJar*) cookie; DvmDex* pDvmDex; char* name; char* descriptor; name = dvmCreateCstrFromString(nameObj); descriptor = dvmDotToDescriptor(name); LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie); free(name); if (!validateCookie(cookie)) RETURN_VOID(); if (pDexOrJar->isDex) pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); else pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); /* once we load something, we can't unmap the storage */ pDexOrJar->okayToFree = false; clazz = dvmDefineClass(pDvmDex, descriptor, loader); Thread* self = dvmThreadSelf(); if (dvmCheckException(self)) { /* * If we threw a "class not found" exception, stifle it, since the * contract in the higher method says we simply return null if * the class is not found. */ Object* excep = dvmGetException(self); if (strcmp(excep->clazz->descriptor, "Ljava/lang/ClassNotFoundException;") == 0 || strcmp(excep->clazz->descriptor, "Ljava/lang/NoClassDefFoundError;") == 0) { dvmClearException(self); } clazz = NULL; } /* * Set the ProtectionDomain -- do we need this to happen before we * link the class and make it available? If so, we need to pass it * through dvmDefineClass (and figure out some other * stuff, like where it comes from for bootstrap classes). */ if (clazz != NULL) { //LOGI("SETTING pd '%s' to %p\n", clazz->descriptor, pd); dvmSetFieldObject((Object*) clazz, gDvm.offJavaLangClass_pd, pd); } free(descriptor); RETURN_PTR(clazz); }
/* * Alternate version of dvmResolveClass for use with verification and * optimization. Performs access checks on every resolve, and refuses * to acknowledge the existence of classes defined in more than one DEX * file. * * Exceptions caused by failures are cleared before returning. * * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; /* * Check the table first. If not there, do the lookup by name. */ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); if (resClass == NULL) { const char* 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) { /* not found, exception should be raised */ ALOGV("DexOpt: class %d (%s) not found", classIdx, dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); if (pFailure != NULL) { /* dig through the wrappers to find the original failure */ Object* excep = dvmGetException(dvmThreadSelf()); while (true) { Object* cause = dvmGetExceptionCause(excep); if (cause == NULL) break; excep = cause; } if (strcmp(excep->clazz->descriptor, "Ljava/lang/IncompatibleClassChangeError;") == 0) { *pFailure = VERIFY_ERROR_CLASS_CHANGE; } else { *pFailure = VERIFY_ERROR_NO_CLASS; } } dvmClearOptException(dvmThreadSelf()); return NULL; } /* * Add it to the resolved table so we're faster on the next lookup. */ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); } /* multiple definitions? */ if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) { ALOGI("DexOpt: not resolving ambiguous class '%s'", resClass->descriptor); if (pFailure != NULL) *pFailure = VERIFY_ERROR_NO_CLASS; return NULL; } /* access allowed? */ tweakLoader(referrer, resClass); bool allowed = dvmCheckClassAccess(referrer, resClass); untweakLoader(referrer, resClass); if (!allowed) { ALOGW("DexOpt: resolve class illegal access: %s -> %s", referrer->descriptor, resClass->descriptor); if (pFailure != NULL) *pFailure = VERIFY_ERROR_ACCESS_CLASS; return NULL; } return resClass; }