/* * public native int preloadClasses() */ static void Dalvik_dalvik_system_VMRuntime_preloadClasses(const u4* args, JValue* pResult) { ClassObject* caller = dvmGetCallerClass(dvmThreadSelf()->curFrame); Object* loader; int count = 0; unsigned int index; UNUSED_PARAMETER(args); if (caller == NULL) RETURN_INT(0); loader = (Object*)caller->classLoader; for (index = 0; index < sizeof(preloadClassesTable)/sizeof(char*); index ++) { ClassObject* clazz = dvmFindClassByCstrName(preloadClassesTable[index], loader); if (clazz == NULL) { dvmLogExceptionStackTrace(); dvmClearException(dvmThreadSelf()); continue; } count ++; } RETURN_INT(count); }
/* * 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; }
static void callMethod(Thread *self, Object *obj, Method *method) { JValue unused; /* Keep track of the method we're about to call and * the current time so that other threads can detect * when this thread wedges and provide useful information. */ gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec(); gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec(); gDvm.gcHeap->heapWorkerCurrentMethod = method; gDvm.gcHeap->heapWorkerCurrentObject = obj; /* Call the method. * * Don't hold the lock when executing interpreted * code. It may suspend, and the GC needs to grab * heapWorkerLock. */ dvmUnlockMutex(&gDvm.heapWorkerLock); if (false) { /* Log entry/exit; this will likely flood the log enough to * cause "logcat" to drop entries. */ char tmpTag[16]; sprintf(tmpTag, "HW%d", self->systemTid); LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor); dvmCallMethod(self, method, obj, &unused); LOG(LOG_DEBUG, tmpTag, " done\n"); } else { dvmCallMethod(self, method, obj, &unused); } /* * Reacquire the heap worker lock in a suspend-friendly way. */ lockMutex(&gDvm.heapWorkerLock); gDvm.gcHeap->heapWorkerCurrentObject = NULL; gDvm.gcHeap->heapWorkerCurrentMethod = NULL; gDvm.gcHeap->heapWorkerInterpStartTime = 0LL; /* Exceptions thrown during these calls interrupt * the method, but are otherwise ignored. */ if (dvmCheckException(self)) { #if DVM_SHOW_EXCEPTION >= 1 LOGI("Uncaught exception thrown by finalizer (will be discarded):\n"); dvmLogExceptionStackTrace(); #endif dvmClearException(self); } }
/* * 从指定的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); }
Method* dvmFindInlinableMethod(const char* classDescriptor, const char* methodName, const char* methodSignature) { /* * Find the class. */ ClassObject* clazz = dvmFindClassNoInit(classDescriptor, NULL); if (clazz == NULL) { ALOGE("dvmFindInlinableMethod: can't find class '%s'", classDescriptor); dvmClearException(dvmThreadSelf()); return NULL; } /* * Method could be virtual or direct. Try both. Don't use * the "hier" versions. */ Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, methodSignature); if (method == NULL) { method = dvmFindVirtualMethodByDescriptor(clazz, methodName, methodSignature); } if (method == NULL) { ALOGE("dvmFindInlinableMethod: can't find method %s.%s %s", clazz->descriptor, methodName, methodSignature); return NULL; } /* * Check that the method is appropriate for inlining. */ if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) { ALOGE("dvmFindInlinableMethod: can't inline non-final method %s.%s", clazz->descriptor, method->name); return NULL; } if (dvmIsSynchronizedMethod(method) || dvmIsDeclaredSynchronizedMethod(method)) { ALOGE("dvmFindInlinableMethod: can't inline synchronized method %s.%s", clazz->descriptor, method->name); return NULL; } return method; }
/* * 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); }
/* * Broadcast an event to all handlers. */ static void broadcast(int event) { ClassObject* ddmServerClass; Method* bcast; ddmServerClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL); if (ddmServerClass == NULL) { LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n"); goto bail; } bcast = dvmFindDirectMethodByDescriptor(ddmServerClass, "broadcast", "(I)V"); if (bcast == NULL) { LOGW("Unable to find DdmServer.broadcast\n"); goto bail; } Thread* self = dvmThreadSelf(); if (self->status != THREAD_RUNNING) { LOGE("ERROR: DDM broadcast with thread status=%d\n", self->status); /* try anyway? */ } JValue unused; dvmCallMethod(self, bcast, NULL, &unused, event); if (dvmCheckException(self)) { LOGI("Exception thrown by broadcast(%d)\n", event); dvmLogExceptionStackTrace(); dvmClearException(self); goto bail; } bail: ; }
/* * 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); }
/* * "buf" contains a full JDWP packet, possibly with multiple chunks. We * need to process each, accumulate the replies, and ship the whole thing * back. * * Returns "true" if we have a reply. The reply buffer is newly allocated, * and includes the chunk type/length, followed by the data. * * TODO: we currently assume that the request and reply include a single * chunk. If this becomes inconvenient we will need to adapt. */ bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf, int* pReplyLen) { Thread* self = dvmThreadSelf(); const int kChunkHdrLen = 8; ArrayObject* dataArray = NULL; bool result = false; assert(dataLen >= 0); /* * Prep DdmServer. We could throw this in gDvm. */ ClassObject* ddmServerClass; Method* dispatch; ddmServerClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL); if (ddmServerClass == NULL) { LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n"); goto bail; } dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;"); if (dispatch == NULL) { LOGW("Unable to find DdmServer.dispatch\n"); goto bail; } /* * Prep Chunk. */ int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff; ClassObject* chunkClass; chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL); if (chunkClass == NULL) { LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n"); goto bail; } chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I"); chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B"); chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I"); chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I"); if (chunkTypeOff < 0 || chunkDataOff < 0 || chunkOffsetOff < 0 || chunkLengthOff < 0) { LOGW("Unable to find all chunk fields\n"); goto bail; } /* * The chunk handlers are written in the Java programming language, so * we need to convert the buffer to a byte array. */ dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT); if (dataArray == NULL) { LOGW("array alloc failed (%d)\n", dataLen); dvmClearException(self); goto bail; } memcpy(dataArray->contents, buf, dataLen); /* * Run through and find all chunks. [Currently just find the first.] */ unsigned int offset, length, type; type = get4BE((u1*)dataArray->contents + 0); length = get4BE((u1*)dataArray->contents + 4); offset = kChunkHdrLen; if (offset+length > (unsigned int) dataLen) { LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen); goto bail; } /* * Call the handler. */ JValue callRes; dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset, length); if (dvmCheckException(self)) { LOGI("Exception thrown by dispatcher for 0x%08x\n", type); dvmLogExceptionStackTrace(); dvmClearException(self); goto bail; } Object* chunk; ArrayObject* replyData; chunk = (Object*) callRes.l; if (chunk == NULL) goto bail; /* * Pull the pieces out of the chunk. We copy the results into a * newly-allocated buffer that the caller can free. We don't want to * continue using the Chunk object because nothing has a reference to it. * (If we do an alloc in here, we need to dvmAddTrackedAlloc it.) * * We could avoid this by returning type/data/offset/length and having * the caller be aware of the object lifetime issues, but that * integrates the JDWP code more tightly into the VM, and doesn't work * if we have responses for multiple chunks. * * So we're pretty much stuck with copying data around multiple times. */ type = dvmGetFieldInt(chunk, chunkTypeOff); replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff); offset = dvmGetFieldInt(chunk, chunkOffsetOff); length = dvmGetFieldInt(chunk, chunkLengthOff); LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n", type, replyData, offset, length); if (length == 0 || replyData == NULL) goto bail; if (offset + length > replyData->length) { LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n", offset, length, replyData->length); goto bail; } u1* reply; reply = (u1*) malloc(length + kChunkHdrLen); if (reply == NULL) { LOGW("malloc %d failed\n", length+kChunkHdrLen); goto bail; } set4BE(reply + 0, type); set4BE(reply + 4, length); memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length); *pReplyBuf = reply; *pReplyLen = length + kChunkHdrLen; result = true; LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n", (char*) reply, reply, length); bail: dvmReleaseTrackedAlloc((Object*) dataArray, NULL); return result; }
/* * Search the method's list of exceptions for a match. * * Returns the offset of the catch block on success, or -1 on failure. */ static int findCatchInMethod(Thread* self, const Method* method, int relPc, ClassObject* excepClass) { /* * Need to clear the exception before entry. Otherwise, dvmResolveClass * might think somebody threw an exception while it was loading a class. */ assert(!dvmCheckException(self)); assert(!dvmIsNativeMethod(method)); LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n", method->clazz->descriptor, method->name, excepClass->descriptor, dvmComputeExactFrameDepth(self->curFrame)); DvmDex* pDvmDex = method->clazz->pDvmDex; const DexCode* pCode = dvmGetMethodCode(method); DexCatchIterator iterator; if (dexFindCatchHandler(&iterator, pCode, relPc)) { for (;;) { DexCatchHandler* handler = dexCatchIteratorNext(&iterator); if (handler == NULL) { break; } if (handler->typeIdx == kDexNoIndex) { /* catch-all */ LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n", relPc, method->clazz->descriptor, method->name, excepClass->descriptor); return handler->address; } ClassObject* throwable = dvmDexGetResolvedClass(pDvmDex, handler->typeIdx); if (throwable == NULL) { /* * TODO: this behaves badly if we run off the stack * while trying to throw an exception. The problem is * that, if we're in a class loaded by a class loader, * the call to dvmResolveClass has to ask the class * loader for help resolving any previously-unresolved * classes. If this particular class loader hasn't * resolved StackOverflowError, it will call into * interpreted code, and blow up. * * We currently replace the previous exception with * the StackOverflowError, which means they won't be * catching it *unless* they explicitly catch * StackOverflowError, in which case we'll be unable * to resolve the class referred to by the "catch" * block. * * We end up getting a huge pile of warnings if we do * a simple synthetic test, because this method gets * called on every stack frame up the tree, and it * fails every time. * * This eventually bails out, effectively becoming an * uncatchable exception, so other than the flurry of * warnings it's not really a problem. Still, we could * probably handle this better. */ throwable = dvmResolveClass(method->clazz, handler->typeIdx, true); if (throwable == NULL) { /* * We couldn't find the exception they wanted in * our class files (or, perhaps, the stack blew up * while we were querying a class loader). Cough * up a warning, then move on to the next entry. * Keep the exception status clear. */ LOGW("Could not resolve class ref'ed in exception " "catch list (class index %d, exception %s)\n", handler->typeIdx, (self->exception != NULL) ? self->exception->clazz->descriptor : "(none)"); dvmClearException(self); continue; } } //LOGD("ADDR MATCH, check %s instanceof %s\n", // excepClass->descriptor, pEntry->excepClass->descriptor); if (dvmInstanceof(excepClass, throwable)) { LOGV("Match on catch block at 0x%02x in %s.%s for %s\n", relPc, method->clazz->descriptor, method->name, excepClass->descriptor); return handler->address; } } } LOGV("No matching catch block at 0x%02x in %s for %s\n", relPc, method->name, excepClass->descriptor); return -1; }