/* * Dump a bytecode disassembly. */ void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod) { const DexCode* pCode = dexGetCode(pDexFile, pDexMethod); const u2* insns; int insnIdx; FieldMethodInfo methInfo; int startAddr; char* className = NULL; assert(pCode->insnsSize > 0); insns = pCode->insns; getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo); startAddr = ((u1*)pCode - pDexFile->baseAddr); className = descriptorToDot(methInfo.classDescriptor); printf("%06x: |[%06x] %s.%s:%s\n", startAddr, startAddr, className, methInfo.name, methInfo.signature); insnIdx = 0; while (insnIdx < (int) pCode->insnsSize) { int insnWidth; OpCode opCode; DecodedInstruction decInsn; u2 instr; instr = get2LE((const u1*)insns); if (instr == kPackedSwitchSignature) { insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2; } else if (instr == kSparseSwitchSignature) { insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4; } else if (instr == kArrayDataSignature) { int width = get2LE((const u1*)(insns+1)); int size = get2LE((const u1*)(insns+2)) | (get2LE((const u1*)(insns+3))<<16); // The plus 1 is to round up for odd size and width insnWidth = 4 + ((size * width) + 1) / 2; } else { opCode = instr & 0xff; insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode); if (insnWidth == 0) { fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx); break; } } dexDecodeInstruction(gInstrFormat, insns, &decInsn); dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn); insns += insnWidth; insnIdx += insnWidth; } free(className); }
/* * For monitor unlock, we don't have to use ldrex/strex. Once * we've determined that the lock is thin and that we own it with * a zero recursion count, it's safe to punch it back to the * initial, unlock thin state with a store word. */ static void genMonitorExit(CompilationUnit *cUnit, MIR *mir) { RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); ArmLIR *target; ArmLIR *branch; ArmLIR *hopTarget; ArmLIR *hopBranch; assert(LW_SHAPE_THIN == 0); loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj dvmCompilerLockAllTemps(cUnit); // Prepare for explicit register usage dvmCompilerFreeTemp(cUnit, r4PC); // Free up r4 for general use loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL); loadWordDisp(cUnit, r1, offsetof(Object, lock), r2); // Get object->lock loadWordDisp(cUnit, r0, offsetof(Thread, threadId), r3); // Get threadId // Is lock unheld on lock or held by us (==threadId) on unlock? opRegRegImm(cUnit, kOpAnd, r7, r2, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1); opRegReg(cUnit, kOpSub, r2, r3); hopBranch = opCondBranch(cUnit, kArmCondNe); storeWordDisp(cUnit, r1, offsetof(Object, lock), r7); branch = opNone(cUnit, kOpUncondBr); hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); hopTarget->defMask = ENCODE_ALL; hopBranch->generic.target = (LIR *)hopTarget; // Export PC (part 1) loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset)); LOAD_FUNC_ADDR(cUnit, r7, (int)dvmUnlockObject); // Export PC (part 2) newLIR3(cUnit, kThumb2StrRRI8Predec, r3, rFP, sizeof(StackSaveArea) - offsetof(StackSaveArea, xtra.currentPc)); opReg(cUnit, kOpBlx, r7); opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe); loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset + dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_EXIT))); genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); // Resume here target = newLIR0(cUnit, kArmPseudoTargetLabel); target->defMask = ENCODE_ALL; branch->generic.target = (LIR *)target; branchOver->generic.target = (LIR *) target; }
/* * Handle simple case (thin lock) inline. If it's complicated, bail * out to the heavyweight lock/unlock routines. We'll use dedicated * registers here in order to be in the right position in case we * to bail to dvm[Lock/Unlock]Object(self, object) * * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object * r1 -> object [arg1 for dvm[Lock/Unlock]Object * r2 -> intial contents of object->lock, later result of strex * r3 -> self->threadId * r7 -> temp to hold new lock value [unlock only] * r4 -> allow to be used by utilities as general temp * * The result of the strex is 0 if we acquire the lock. * * See comments in Sync.c for the layout of the lock word. * Of particular interest to this code is the test for the * simple case - which we handle inline. For monitor enter, the * simple case is thin lock, held by no-one. For monitor exit, * the simple case is thin lock, held by the unlocking thread with * a recurse count of 0. * * A minor complication is that there is a field in the lock word * unrelated to locking: the hash state. This field must be ignored, but * preserved. * */ static void genMonitorEnter(CompilationUnit *cUnit, MIR *mir) { RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); bool enter = (mir->dalvikInsn.opCode == OP_MONITOR_ENTER); ArmLIR *target; ArmLIR *hopTarget; ArmLIR *branch; ArmLIR *hopBranch; assert(LW_SHAPE_THIN == 0); loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj dvmCompilerLockAllTemps(cUnit); // Prepare for explicit register usage dvmCompilerFreeTemp(cUnit, r4PC); // Free up r4 for general use loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL); loadWordDisp(cUnit, r0, offsetof(Thread, threadId), r3); // Get threadId newLIR3(cUnit, kThumb2Ldrex, r2, r1, offsetof(Object, lock) >> 2); // Get object->lock opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner // Is lock unheld on lock or held by us (==threadId) on unlock? newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1); newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1); hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0); newLIR4(cUnit, kThumb2Strex, r2, r3, r1, offsetof(Object, lock) >> 2); branch = newLIR2(cUnit, kThumb2Cbz, r2, 0); hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); hopTarget->defMask = ENCODE_ALL; hopBranch->generic.target = (LIR *)hopTarget; // Clear the lock ArmLIR *inst = newLIR0(cUnit, kThumb2Clrex); // ...and make it a scheduling barrier inst->defMask = ENCODE_ALL; // Export PC (part 1) loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset)); /* Get dPC of next insn */ loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset + dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_ENTER))); // Export PC (part 2) newLIR3(cUnit, kThumb2StrRRI8Predec, r3, rFP, sizeof(StackSaveArea) - offsetof(StackSaveArea, xtra.currentPc)); /* Call template, and don't return */ genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER); // Resume here target = newLIR0(cUnit, kArmPseudoTargetLabel); target->defMask = ENCODE_ALL; branch->generic.target = (LIR *)target; }
/* * Compute the width of the instruction at each address in the instruction * stream. Addresses that are in the middle of an instruction, or that * are part of switch table data, are not set (so the caller should probably * initialize "insnFlags" to zero). * * If "pNewInstanceCount" is not NULL, it will be set to the number of * new-instance instructions in the method. * * Logs an error and returns "false" on failure. */ bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags, int* pNewInstanceCount) { const int insnCount = dvmGetMethodInsnsSize(meth); const u2* insns = meth->insns; bool result = false; int newInstanceCount = 0; int i; for (i = 0; i < insnCount; /**/) { int width; /* * Switch tables and array data tables are identified with * "extended NOP" opcodes. They contain no executable code, * so we can just skip past them. */ if (*insns == kPackedSwitchSignature) { width = 4 + insns[1] * 2; } else if (*insns == kSparseSwitchSignature) { width = 2 + insns[1] * 4; } else if (*insns == kArrayDataSignature) { u4 size = insns[2] | (((u4)insns[3]) << 16); width = 4 + (insns[1] * size + 1) / 2; } else { int instr = *insns & 0xff; width = dexGetInstrWidthAbs(gDvm.instrWidth, instr); if (width == 0) { LOG_VFY_METH(meth, "VFY: invalid post-opt instruction (0x%x)\n", instr); LOGI("### instr=%d width=%d table=%d\n", instr, width, dexGetInstrWidthAbs(gDvm.instrWidth, instr)); goto bail; } if (width < 0 || width > 5) { LOGE("VFY: bizarre width value %d\n", width); dvmAbort(); } if (instr == OP_NEW_INSTANCE) newInstanceCount++; } if (width > 65535) { LOG_VFY_METH(meth, "VFY: insane width %d\n", width); goto bail; } insnFlags[i] |= width; i += width; insns += width; } if (i != (int) dvmGetMethodInsnsSize(meth)) { LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n", i, dvmGetMethodInsnsSize(meth)); goto bail; } result = true; if (pNewInstanceCount != NULL) *pNewInstanceCount = newInstanceCount; bail: return result; }