/* * Mark all debugger-visible locals as live. * * The "locals" table describes the positions of the various locals in the * stack frame based on the current execution address. If the debugger * wants to display one, it issues a request by "slot number". We need * to ensure that references in stack slots that might be queried by the * debugger aren't GCed. * * (If the GC had some way to mark the slot as invalid we wouldn't have * to do this. We could also have the debugger interface check the * register map and simply refuse to return a "dead" value, but that's * potentially confusing since the referred-to object might actually be * alive, and being able to see it without having to hunt around for a * "live" stack frame is useful.) */ static bool markDebugLocals(VerifierData* vdata) { const Method* meth = vdata->method; dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile, dvmGetMethodCode(meth), meth->clazz->descriptor, meth->prototype.protoIdx, meth->accessFlags, NULL, markLocalsCb, vdata); return true; }
/* * Determine the source file line number based on the program counter. * "pc" is an offset, in 16-bit units, from the start of the method's code. * * Returns -1 if no match was found (possibly because the source files were * compiled without "-g", so no line number information is present). * Returns -2 for native methods (as expected in exception traces). */ int dvmLineNumFromPC(const Method* method, u4 relPc) { const DexCode* pDexCode = dvmGetMethodCode(method); if (pDexCode == NULL) { if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method)) return -2; return -1; /* can happen for abstract method stub */ } LineNumFromPcContext context; memset(&context, 0, sizeof(context)); context.address = relPc; // A method with no line number info should return -1 context.lineNum = -1; dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode, method->clazz->descriptor, method->prototype.protoIdx, method->accessFlags, lineNumForPcCb, NULL, &context); return context.lineNum; }
/* Set the "in try" flags for all instructions protected by "try" statements. Also sets the "branch target" flags for exception handlers. Call this after widths have been set in "insnFlags". Returns "false" if something in the exception table looks fishy, but we're expecting the exception table to be somewhat sane. 对被“try”声明保护的所有指令的设置"in try"标识。同时为异常句柄设置“branch target(分支目标)” 标识。 在宽度被设置“inFlags”后调用。 返回“false”,如果异常表中出现可疑的东西,我们期望它能有点理智。 */ static bool scanTryCatchBlocks(const Method* meth, InsnFlags* insnFlags) { u4 insnsSize = dvmGetMethodInsnsSize(meth); const DexCode* pCode = dvmGetMethodCode(meth); u4 triesSize = pCode->triesSize; const DexTry* pTries; u4 idx; if (triesSize == 0) { return true; } pTries = dexGetTries(pCode); for (idx = 0; idx < triesSize; idx++) { const DexTry* pTry = &pTries[idx]; u4 start = pTry->startAddr; u4 end = start + pTry->insnCount; u4 addr; if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) { LOG_VFY_METH(meth, "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)", start, end, insnsSize); return false; } if (dvmInsnGetWidth(insnFlags, start) == 0) { LOG_VFY_METH(meth, "VFY: 'try' block starts inside an instruction (%d)", start); return false; } for (addr = start; addr < end; addr += dvmInsnGetWidth(insnFlags, addr)) { assert(dvmInsnGetWidth(insnFlags, addr) != 0); dvmInsnSetInTry(insnFlags, addr, true); } } /* Iterate over each of the handlers to verify target addresses. */ u4 handlersSize = dexGetHandlersSize(pCode); u4 offset = dexGetFirstHandlerOffset(pCode); for (idx = 0; idx < handlersSize; idx++) { DexCatchIterator iterator; dexCatchIteratorInit(&iterator, pCode, offset); for (;;) { DexCatchHandler* handler = dexCatchIteratorNext(&iterator); u4 addr; if (handler == NULL) { break; } addr = handler->address; if (dvmInsnGetWidth(insnFlags, addr) == 0) { LOG_VFY_METH(meth, "VFY: exception handler starts at bad address (%d)", addr); return false; } dvmInsnSetBranchTarget(insnFlags, addr, true); } offset = dexCatchIteratorGetEndOffset(&iterator, pCode); } return true; }
/* * 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; }
/* * Get the size of the insns associated with a method. This returns 0 * for non-bytecode methods. */ INLINE u4 dvmGetMethodInsnsSize(const Method* meth) { const DexCode* pCode = dvmGetMethodCode(meth); return (pCode == NULL) ? 0 : pCode->insnsSize; }