diff options
| author | 2011-10-27 15:19:26 -0700 | |
|---|---|---|
| committer | 2011-10-28 23:14:15 -0700 | |
| commit | 28ad40dc3ec2f09b0ffd4f6d6787bf1b532ccd5d (patch) | |
| tree | f5628e47948a0d255112a3a7a01b67db41c8d424 /src | |
| parent | e0918556e7551de638870dcad3f2023f94f85a50 (diff) | |
Support for unresolved types in new-instance during verification.
Also, ensure that classes that don't load are erroneous, warn early
about exceptions left on a thread by the verifier/compiler, factor out
slowpath checks for the compiler and fix the slowpath selector for
const-class.
This change causes more dex cache misses at runtime (more slowpath
execution). It also requires a "mm clean-oat".
Change-Id: I014b49ebdd7d8f7dd2e39cc0958fc0b708d58c4c
Diffstat (limited to 'src')
| -rw-r--r-- | src/class_linker.cc | 2 | ||||
| -rw-r--r-- | src/compiler.cc | 3 | ||||
| -rw-r--r-- | src/compiler.h | 15 | ||||
| -rw-r--r-- | src/compiler/codegen/arm/MethodCodegenDriver.cc | 19 | ||||
| -rw-r--r-- | src/compiler/codegen/arm/Thumb2/Gen.cc | 220 | ||||
| -rw-r--r-- | src/dex_verifier.cc | 520 | ||||
| -rw-r--r-- | src/dex_verifier.h | 52 | ||||
| -rw-r--r-- | src/object.cc | 14 | ||||
| -rw-r--r-- | src/object.h | 6 | ||||
| -rw-r--r-- | src/runtime_support.cc | 72 | ||||
| -rw-r--r-- | src/runtime_support.h | 3 | ||||
| -rw-r--r-- | src/runtime_support_asm.S | 27 | ||||
| -rw-r--r-- | src/thread.cc | 66 | ||||
| -rw-r--r-- | src/thread.h | 4 |
14 files changed, 597 insertions, 426 deletions
diff --git a/src/class_linker.cc b/src/class_linker.cc index f49f546aa3..8f4029a354 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -1039,6 +1039,7 @@ Class* ClassLinker::DefineClass(const std::string& descriptor, if (!LoadSuperAndInterfaces(klass, dex_file)) { // Loading failed. CHECK(self->IsExceptionPending()); + klass->SetStatus(Class::kStatusError); lock.NotifyAll(); return NULL; } @@ -1048,6 +1049,7 @@ Class* ClassLinker::DefineClass(const std::string& descriptor, if (!LinkClass(klass)) { // Linking failed. CHECK(self->IsExceptionPending()); + klass->SetStatus(Class::kStatusError); lock.NotifyAll(); return NULL; } diff --git a/src/compiler.cc b/src/compiler.cc index cbfd35cfcd..ba61b4d968 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -333,6 +333,9 @@ void Compiler::CompileMethod(const Method* method) { // TODO: this fails if we have an abstract method defined in more than one input dex file. CHECK(compiled_invoke_stubs_.find(method) == compiled_invoke_stubs_.end()) << PrettyMethod(method); compiled_invoke_stubs_[method] = compiled_invoke_stub; + + Thread* self = Thread::Current(); + CHECK(!self->IsExceptionPending()) << PrettyMethod(method); } const CompiledMethod* Compiler::GetCompiledMethod(const Method* method) const { diff --git a/src/compiler.h b/src/compiler.h index 0fc68df48b..55bab59e6b 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -55,6 +55,21 @@ class Compiler { const CompiledMethod* GetCompiledMethod(const Method* method) const; const CompiledInvokeStub* GetCompiledInvokeStub(const Method* method) const; + // Callbacks from OAT/ART compiler to see what runtime checks must be generated + bool CanAssumeTypeIsPresentInDexCache(const Method* referrer, uint32_t type_idx) const { + return IsImage() && referrer->GetDexCacheResolvedTypes()->Get(type_idx) != NULL; + } + bool CanAssumeStringIsPresentInDexCache(const Method* referrer, uint32_t string_idx) const { + return IsImage() && referrer->GetDexCacheStrings()->Get(string_idx) != NULL; + } + bool CanAccessTypeWithoutChecks(const Method* referrer, uint32_t type_idx) const { + Class* resolved_class = referrer->GetDexCacheResolvedTypes()->Get(type_idx); + // We should never ask whether a type needs access checks to raise a verification error, + // all other cases where this following test could fail should have been rewritten by the + // verifier to verification errors. + DCHECK(resolved_class == NULL || referrer->GetDeclaringClass()->CanAccess(resolved_class)); + return resolved_class != NULL; + } private: // Attempt to resolve all type, methods, fields, and strings // referenced from code in the dex file following PathClassLoader diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc index 7ea7d698c2..4326a9e2b6 100644 --- a/src/compiler/codegen/arm/MethodCodegenDriver.cc +++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc @@ -46,10 +46,19 @@ STATIC void genNewArray(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, RegLocation rlSrc) { oatFlushAllRegs(cUnit); /* Everything to home location */ - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR); + uint32_t type_idx = mir->dalvikInsn.vC; + if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) { + loadWordDisp(cUnit, rSELF, + OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR); + } else { + UNIMPLEMENTED(WARNING) << "Need to check access of '" + << PrettyMethod(cUnit->method) + << "' to unresolved type " << type_idx; + loadWordDisp(cUnit, rSELF, + OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR); + } loadCurrMethodDirect(cUnit, r1); // arg1 <- Method* - loadConstant(cUnit, r0, mir->dalvikInsn.vC); // arg0 <- type_id + loadConstant(cUnit, r0, type_idx); // arg0 <- type_id loadValueDirectFixed(cUnit, rlSrc, r2); // arg2 <- count callRuntimeHelper(cUnit, rLR); RegLocation rlResult = oatGetReturn(cUnit); @@ -70,6 +79,10 @@ STATIC void genFilledNewArray(CompilationUnit* cUnit, MIR* mir, bool isRange) oatFlushAllRegs(cUnit); /* Everything to home location */ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckAndAllocArrayFromCode), rLR); + if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, typeId)) { + UNIMPLEMENTED(WARNING) << "Need to check access of '" << PrettyMethod(cUnit->method) + << "' to unresolved type " << typeId; + } loadCurrMethodDirect(cUnit, r1); // arg1 <- Method* loadConstant(cUnit, r0, typeId); // arg0 <- type_id loadConstant(cUnit, r2, elems); // arg2 <- count diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc index 70981881f4..4e3888d9f7 100644 --- a/src/compiler/codegen/arm/Thumb2/Gen.cc +++ b/src/compiler/codegen/arm/Thumb2/Gen.cc @@ -442,7 +442,7 @@ STATIC void getFieldOffset(CompilationUnit* cUnit, MIR* mir, Field* fieldPtr) loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pFindInstanceFieldFromCode), rLR); loadConstant(cUnit, r0, fieldIdx); - callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method) + callRuntimeHelper(cUnit, rLR); // FindInstanceFieldFromCoderesolveTypeFromCode(idx, method) ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel); target->defMask = ENCODE_ALL; if (!EXERCISE_SLOWEST_FIELD_PATH) { @@ -606,42 +606,46 @@ STATIC void genIPutWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc, STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, RegLocation rlSrc) { - art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()-> - Get(mir->dalvikInsn.vB); + uint32_t type_idx = mir->dalvikInsn.vB; int mReg = loadCurrMethod(cUnit); int resReg = oatAllocTemp(cUnit); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); - loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(), - resReg); - loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() + - (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg); - if (SLOW_TYPE_PATH || (classPtr == NULL)) { - // Fast path, we're done - just store result - storeValue(cUnit, rlDest, rlResult); + if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) { + // Check we have access to type_idx and if not throw IllegalAccessError + UNIMPLEMENTED(FATAL); } else { - // Slow path. Must test at runtime - oatFlushAllRegs(cUnit); - ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, - 0); - // Resolved, store and hop over following code - storeValue(cUnit, rlDest, rlResult); - ArmLIR* branch2 = genUnconditionalBranch(cUnit,0); - // TUNING: move slow path to end & remove unconditional branch - ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel); - target1->defMask = ENCODE_ALL; - // Call out to helper, which will return resolved type in r0 - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR); - genRegCopy(cUnit, r1, mReg); - loadConstant(cUnit, r0, mir->dalvikInsn.vB); - callRuntimeHelper(cUnit, rLR); - RegLocation rlResult = oatGetReturn(cUnit); - storeValue(cUnit, rlDest, rlResult); - // Rejoin code paths - ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel); - target2->defMask = ENCODE_ALL; - branch1->generic.target = (LIR*)target1; - branch2->generic.target = (LIR*)target2; + // We're don't need access checks, load type from dex cache + int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value(); + loadWordDisp(cUnit, mReg, dex_cache_offset, resReg); + int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx); + loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg); + if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->method, type_idx) || + SLOW_TYPE_PATH) { + // Slow path, at runtime test if the type is null and if so initialize + oatFlushAllRegs(cUnit); + ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, 0); + // Resolved, store and hop over following code + storeValue(cUnit, rlDest, rlResult); + ArmLIR* branch2 = genUnconditionalBranch(cUnit,0); + // TUNING: move slow path to end & remove unconditional branch + ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel); + target1->defMask = ENCODE_ALL; + // Call out to helper, which will return resolved type in r0 + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR); + genRegCopy(cUnit, r1, mReg); + loadConstant(cUnit, r0, type_idx); + callRuntimeHelper(cUnit, rLR); + RegLocation rlResult = oatGetReturn(cUnit); + storeValue(cUnit, rlDest, rlResult); + // Rejoin code paths + ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel); + target2->defMask = ENCODE_ALL; + branch1->generic.target = (LIR*)target1; + branch2->generic.target = (LIR*)target2; + } else { + // Fast path, we're done - just store result + storeValue(cUnit, rlDest, rlResult); + } } } @@ -649,20 +653,19 @@ STATIC void genConstString(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, RegLocation rlSrc) { /* NOTE: Most strings should be available at compile time */ - const art::String* str = cUnit->method->GetDexCacheStrings()-> - Get(mir->dalvikInsn.vB); - if (SLOW_STRING_PATH || (str == NULL) || !cUnit->compiler->IsImage()) { + uint32_t string_idx = mir->dalvikInsn.vB; + int32_t offset_of_string = Array::DataOffset().Int32Value() + (sizeof(String*) * string_idx); + if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache(cUnit->method, string_idx) || + SLOW_STRING_PATH) { + // slow path, resolve string if not in dex cache oatFlushAllRegs(cUnit); oatLockCallTemps(cUnit); // Using explicit registers loadCurrMethodDirect(cUnit, r2); - loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), - r0); + loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), r0); // Might call out to helper, which will return resolved string in r0 - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR); - loadWordDisp(cUnit, r0, Array::DataOffset().Int32Value() + - (sizeof(String*) * mir->dalvikInsn.vB), r0); - loadConstant(cUnit, r1, mir->dalvikInsn.vB); + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR); + loadWordDisp(cUnit, r0, offset_of_string, r0); + loadConstant(cUnit, r1, string_idx); opRegImm(cUnit, kOpCmp, r0, 0); // Is resolved? genBarrier(cUnit); // For testing, always force through helper @@ -677,10 +680,8 @@ STATIC void genConstString(CompilationUnit* cUnit, MIR* mir, int mReg = loadCurrMethod(cUnit); int resReg = oatAllocTemp(cUnit); RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); - loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), - resReg); - loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() + - (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg); + loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), resReg); + loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg); storeValue(cUnit, rlDest, rlResult); } } @@ -693,13 +694,17 @@ STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest) { oatFlushAllRegs(cUnit); /* Everything to home location */ - art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()-> - Get(mir->dalvikInsn.vB); - loadWordDisp(cUnit, rSELF, (classPtr != NULL) - ? OFFSETOF_MEMBER(Thread, pAllocObjectFromCode) - : OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeSlowPath), rLR); - loadCurrMethodDirect(cUnit, r1); // arg1 <= Method* - loadConstant(cUnit, r0, mir->dalvikInsn.vB); // arg0 <- type_id + uint32_t type_idx = mir->dalvikInsn.vB; + // alloc will always check for resolution, do we also need to verify access because the + // verifier was unable to? + if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) { + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR); + } else { + loadWordDisp(cUnit, rSELF, + OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeWithAccessCheck), rLR); + } + loadCurrMethodDirect(cUnit, r1); // arg1 <= Method* + loadConstant(cUnit, r0, type_idx); // arg0 <- type_idx callRuntimeHelper(cUnit, rLR); RegLocation rlResult = oatGetReturn(cUnit); storeValue(cUnit, rlDest, rlResult); @@ -708,8 +713,7 @@ STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir, void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc) { oatFlushAllRegs(cUnit); - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pDeliverException), rLR); + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pDeliverException), rLR); loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception); } @@ -720,30 +724,33 @@ STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, oatFlushAllRegs(cUnit); // May generate a call - use explicit registers oatLockCallTemps(cUnit); - art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()-> - Get(mir->dalvikInsn.vC); - int classReg = r2; // Fixed usage + uint32_t type_idx = mir->dalvikInsn.vC; loadCurrMethodDirect(cUnit, r1); // r1 <= current Method* - loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */ - loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), - classReg); - loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() + - (sizeof(String*) * mir->dalvikInsn.vC), classReg); - if (classPtr == NULL) { - // Generate a runtime test - ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0); - // Not resolved - // Call out to helper, which will return resolved type in r0 - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR); - loadConstant(cUnit, r0, mir->dalvikInsn.vC); - callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method) - genRegCopy(cUnit, r2, r0); // Align usage with fast path - loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */ - // Rejoin code paths - ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); - hopTarget->defMask = ENCODE_ALL; - hopBranch->generic.target = (LIR*)hopTarget; + loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref + int classReg = r2; // r2 will hold the Class* + if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) { + // Check we have access to type_idx and if not throw IllegalAccessError + UNIMPLEMENTED(FATAL); + } else { + // Load dex cache entry into classReg (r2) + loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg); + int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx); + loadWordDisp(cUnit, classReg, offset_of_type, classReg); + if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->method, type_idx)) { + // Need to test presence of type in dex cache at runtime + ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0); + // Not resolved + // Call out to helper, which will return resolved type in r0 + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR); + loadConstant(cUnit, r0, type_idx); + callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method) + genRegCopy(cUnit, r2, r0); // Align usage with fast path + loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */ + // Rejoin code paths + ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); + hopTarget->defMask = ENCODE_ALL; + hopBranch->generic.target = (LIR*)hopTarget; + } } /* r0 is ref, r2 is class. If ref==null, use directly as bool result */ ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0); @@ -751,8 +758,7 @@ STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, DCHECK_EQ(Object::ClassOffset().Int32Value(), 0); loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1); /* r0 is ref, r1 is ref->clazz, r2 is class */ - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR); + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR); opRegReg(cUnit, kOpCmp, r1, r2); // Same? genBarrier(cUnit); genIT(cUnit, kArmCondEq, "EE"); // if-convert the test @@ -774,39 +780,41 @@ STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc) oatFlushAllRegs(cUnit); // May generate a call - use explicit registers oatLockCallTemps(cUnit); - art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()-> - Get(mir->dalvikInsn.vB); - int classReg = r2; // Fixed usage + uint32_t type_idx = mir->dalvikInsn.vB; loadCurrMethodDirect(cUnit, r1); // r1 <= current Method* - loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), - classReg); - loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() + - (sizeof(String*) * mir->dalvikInsn.vB), classReg); - if (classPtr == NULL) { - // Generate a runtime test - ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0); - // Not resolved - // Call out to helper, which will return resolved type in r0 - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR); - loadConstant(cUnit, r0, mir->dalvikInsn.vB); - callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method) - genRegCopy(cUnit, r2, r0); // Align usage with fast path - // Rejoin code paths - ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); - hopTarget->defMask = ENCODE_ALL; - hopBranch->generic.target = (LIR*)hopTarget; + int classReg = r2; // r2 will hold the Class* + if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) { + // Check we have access to type_idx and if not throw IllegalAccessError + UNIMPLEMENTED(FATAL); + } else { + // Load dex cache entry into classReg (r2) + loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg); + int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx); + loadWordDisp(cUnit, classReg, offset_of_type, classReg); + if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->method, type_idx)) { + // Need to test presence of type in dex cache at runtime + ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0); + // Not resolved + // Call out to helper, which will return resolved type in r0 + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR); + loadConstant(cUnit, r0, type_idx); + callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method) + genRegCopy(cUnit, r2, r0); // Align usage with fast path + // Rejoin code paths + ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); + hopTarget->defMask = ENCODE_ALL; + hopBranch->generic.target = (LIR*)hopTarget; + } } - // At this point, r2 has class - loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */ + // At this point, classReg (r2) has class + loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref /* Null is OK - continue */ ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0); /* load object->clazz */ DCHECK_EQ(Object::ClassOffset().Int32Value(), 0); loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1); /* r1 now contains object->clazz */ - loadWordDisp(cUnit, rSELF, - OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR); + loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR); opRegReg(cUnit, kOpCmp, r1, r2); ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */ genRegCopy(cUnit, r0, r1); diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc index 79a455e04b..79f90e7b11 100644 --- a/src/dex_verifier.cc +++ b/src/dex_verifier.cc @@ -43,7 +43,7 @@ static const char* type_strings[] = { "Unresolved Reference", "Uninitialized Reference", "Uninitialized This Reference", - "Unresolved And Uninitialized This Reference", + "Unresolved And Uninitialized Reference", "Reference", }; @@ -65,7 +65,7 @@ std::string RegType::Dump() const { result = type_strings[type_]; if (IsReferenceTypes()) { result += ": "; - if (IsUnresolvedReference()) { + if (IsUnresolvedTypes()) { result += PrettyDescriptor(GetDescriptor()); } else { result += PrettyDescriptor(GetClass()->GetDescriptor()); @@ -370,15 +370,23 @@ const RegType& RegTypeCache::From(RegType::Type type, const ClassLoader* loader, entries_.push_back(entry); return *entry; } else { + // TODO: we assume unresolved, but we may be able to do better by validating whether the + // descriptor string is valid // Unable to resolve so create unresolved register type DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); - String* string_descriptor = - Runtime::Current()->GetInternTable()->InternStrong(descriptor.c_str()); - RegType* entry = new RegType(RegType::kRegTypeUnresolvedReference, string_descriptor, 0, - entries_.size()); - entries_.push_back(entry); - return *entry; + if (IsValidDescriptor(descriptor.c_str())) { + String* string_descriptor = + Runtime::Current()->GetInternTable()->InternStrong(descriptor.c_str()); + RegType* entry = new RegType(RegType::kRegTypeUnresolvedReference, string_descriptor, 0, + entries_.size()); + entries_.push_back(entry); + return *entry; + } else { + // The descriptor is broken return the unknown type as there's nothing sensible that + // could be done at runtime + return Unknown(); + } } } } @@ -407,15 +415,58 @@ const RegType& RegTypeCache::FromClass(Class* klass) { } } -const RegType& RegTypeCache::Uninitialized(Class* klass, uint32_t allocation_pc) { - for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { - RegType* cur_entry = entries_[i]; - if (cur_entry->IsUninitializedReference() && cur_entry->GetAllocationPc() == allocation_pc && - cur_entry->GetClass() == klass) { - return *cur_entry; +const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) { + RegType* entry; + if (type.IsUnresolvedTypes()) { + String* descriptor = type.GetDescriptor(); + for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry->IsUnresolvedAndUninitializedReference() && + cur_entry->GetAllocationPc() == allocation_pc && + cur_entry->GetDescriptor() == descriptor) { + return *cur_entry; + } + } + entry = new RegType(RegType::kRegTypeUnresolvedAndUninitializedReference, + descriptor, allocation_pc, entries_.size()); + } else { + Class* klass = type.GetClass(); + for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry->IsUninitializedReference() && + cur_entry->GetAllocationPc() == allocation_pc && + cur_entry->GetClass() == klass) { + return *cur_entry; + } + } + entry = new RegType(RegType::kRegTypeUninitializedReference, + klass, allocation_pc, entries_.size()); + } + entries_.push_back(entry); + return *entry; +} + +const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { + RegType* entry; + if (uninit_type.IsUnresolvedTypes()) { + String* descriptor = uninit_type.GetDescriptor(); + for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry->IsUnresolvedReference() && cur_entry->GetDescriptor() == descriptor) { + return *cur_entry; + } } + entry = new RegType(RegType::kRegTypeUnresolvedReference, descriptor, 0, entries_.size()); + } else { + Class* klass = uninit_type.GetClass(); + for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry->IsReference() && cur_entry->GetClass() == klass) { + return *cur_entry; + } + } + entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size()); } - RegType* entry = new RegType(RegType::kRegTypeUninitializedReference, klass, allocation_pc, entries_.size()); entries_.push_back(entry); return *entry; } @@ -462,6 +513,18 @@ const RegType& RegTypeCache::FromCat1Const(int32_t value) { return *entry; } +const RegType& RegTypeCache::GetComponentType(const RegType& array, const ClassLoader* loader) { + CHECK(array.IsArrayClass()); + if (array.IsUnresolvedTypes()) { + std::string descriptor = array.GetDescriptor()->ToModifiedUtf8(); + std::string component = descriptor.substr(1, descriptor.size() - 1); + return FromDescriptor(loader, component); + } else { + return FromClass(array.GetClass()->GetComponentType()); + } +} + + bool RegisterLine::CheckConstructorReturn() const { for (size_t i = 0; i < num_regs_; i++) { if (GetRegisterType(i).IsUninitializedThisReference()) { @@ -561,20 +624,16 @@ bool RegisterLine::VerifyRegisterType(uint32_t vsrc, const RegType& check_type) } void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) { - Class* klass = uninit_type.GetClass(); - if (klass == NULL) { - verifier_->Fail(VERIFY_ERROR_GENERIC) << "Unable to find type=" << uninit_type; - } else { - const RegType& init_type = verifier_->GetRegTypeCache()->FromClass(klass); - size_t changed = 0; - for (size_t i = 0; i < num_regs_; i++) { - if (GetRegisterType(i).Equals(uninit_type)) { - line_[i] = init_type.GetId(); - changed++; - } + DCHECK(uninit_type.IsUninitializedTypes()); + const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type); + size_t changed = 0; + for (size_t i = 0; i < num_regs_; i++) { + if (GetRegisterType(i).Equals(uninit_type)) { + line_[i] = init_type.GetId(); + changed++; } - DCHECK_GT(changed, 0u); } + DCHECK_GT(changed, 0u); } void RegisterLine::MarkUninitRefsAsInvalid(const RegType& uninit_type) { @@ -895,8 +954,9 @@ void DexVerifier::VerifyMethodAndDump(Method* method) { << verifier.info_messages_.str() << Dumpable<DexVerifier>(verifier); } -DexVerifier::DexVerifier(Method* method) : java_lang_throwable_(NULL), work_insn_idx_(-1), - method_(method), failure_(VERIFY_ERROR_NONE), +DexVerifier::DexVerifier(Method* method) : work_insn_idx_(-1), method_(method), + failure_(VERIFY_ERROR_NONE), + new_instance_count_(0), monitor_enter_count_(0) { const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -994,15 +1054,26 @@ bool DexVerifier::ScanTryCatchBlocks() { /* Iterate over each of the handlers to verify target addresses. */ const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item_, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); for (uint32_t idx = 0; idx < handlers_size; idx++) { DexFile::CatchHandlerIterator iterator(handlers_ptr); for (; !iterator.HasNext(); iterator.Next()) { - uint32_t dex_pc= iterator.Get().address_; + const DexFile::CatchHandlerItem& handler = iterator.Get(); + uint32_t dex_pc= handler.address_; if (!insn_flags_[dex_pc].IsOpcode()) { Fail(VERIFY_ERROR_GENERIC) << "exception handler starts at bad address (" << dex_pc << ")"; return false; } insn_flags_[dex_pc].SetBranchTarget(); + // Ensure exception types are resolved so that they don't need resolution to be delivered, + // unresolved exception types will be ignored by exception delivery + if (handler.type_idx_ != DexFile::kDexNoIndex) { + Class* exception_type = linker->ResolveType(handler.type_idx_, method_); + if (exception_type == NULL) { + DCHECK(Thread::Current()->IsExceptionPending()); + Thread::Current()->ClearException(); + } + } } handlers_ptr = iterator.GetData(); } @@ -1814,12 +1885,8 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * all exception handlers need to have one of these). We verify that as part of extracting the * exception type from the catch block list. */ - Class* res_class = GetCaughtExceptionType(); - if (res_class == NULL) { - DCHECK(failure_ != VERIFY_ERROR_NONE); - } else { - work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class)); - } + const RegType& res_type = GetCaughtExceptionType(); + work_line_->SetRegisterType(dec_insn.vA_, res_type); break; } case Instruction::RETURN_VOID: @@ -1911,17 +1978,12 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->SetRegisterType(dec_insn.vA_, reg_types_.JavaLangString()); break; case Instruction::CONST_CLASS: { - /* make sure we can resolve the class; access check is important */ - Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_); - if (res_class == NULL) { - const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_); - fail_messages_ << "unable to resolve const-class " << dec_insn.vB_ - << " (" << bad_class_desc << ") in " - << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor()); - DCHECK(failure_ != VERIFY_ERROR_GENERIC); - } else { - work_line_->SetRegisterType(dec_insn.vA_, reg_types_.JavaLangClass()); - } + // Get type from instruction if unresolved then we need an access check + // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved + const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_); + // Register holds class, ie its type is class, but on error we keep it Unknown + work_line_->SetRegisterType(dec_insn.vA_, + res_type.IsUnknown() ? res_type : reg_types_.JavaLangClass()); break; } case Instruction::MONITOR_ENTER: @@ -1952,58 +2014,39 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->PopMonitor(dec_insn.vA_); break; - case Instruction::CHECK_CAST: { + case Instruction::CHECK_CAST: + case Instruction::INSTANCE_OF: { /* - * If this instruction succeeds, we will promote register vA to - * the type in vB. (This could be a demotion -- not expected, so - * we don't try to address it.) + * If this instruction succeeds, we will "downcast" register vA to the type in vB. (This + * could be a "upcast" -- not expected, so we don't try to address it.) * - * If it fails, an exception is thrown, which we deal with later - * by ignoring the update to dec_insn.vA_ when branching to a handler. + * If it fails, an exception is thrown, which we deal with later by ignoring the update to + * dec_insn.vA_ when branching to a handler. */ - Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_); - if (res_class == NULL) { - const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_); - fail_messages_ << "unable to resolve check-cast " << dec_insn.vB_ - << " (" << bad_class_desc << ") in " - << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor()); - DCHECK(failure_ != VERIFY_ERROR_GENERIC); + bool is_checkcast = dec_insn.opcode_ == Instruction::CHECK_CAST; + const RegType& res_type = + ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB_ : dec_insn.vC_); + // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved + const RegType& orig_type = + work_line_->GetRegisterType(is_checkcast ? dec_insn.vA_ : dec_insn.vB_); + if (!res_type.IsNonZeroReferenceTypes()) { + Fail(VERIFY_ERROR_GENERIC) << "check-cast on unexpected class " << res_type; + } else if (!orig_type.IsReferenceTypes()) { + Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA_; } else { - const RegType& orig_type = work_line_->GetRegisterType(dec_insn.vA_); - if (!orig_type.IsReferenceTypes()) { - Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA_; + if (is_checkcast) { + work_line_->SetRegisterType(dec_insn.vA_, res_type); } else { - work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class)); - } - } - break; - } - case Instruction::INSTANCE_OF: { - /* make sure we're checking a reference type */ - const RegType& tmp_type = work_line_->GetRegisterType(dec_insn.vB_); - if (!tmp_type.IsReferenceTypes()) { - Fail(VERIFY_ERROR_GENERIC) << "vB not a reference (" << tmp_type << ")"; - } else { - /* make sure we can resolve the class; access check is important */ - Class* res_class = ResolveClassAndCheckAccess(dec_insn.vC_); - if (res_class == NULL) { - const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vC_); - fail_messages_ << "unable to resolve instance of " << dec_insn.vC_ - << " (" << bad_class_desc << ") in " - << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor()); - DCHECK(failure_ != VERIFY_ERROR_GENERIC); - } else { - /* result is boolean */ work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Boolean()); } } break; } case Instruction::ARRAY_LENGTH: { - Class* res_class = work_line_->GetClassFromRegister(dec_insn.vB_); - if (failure_ == VERIFY_ERROR_NONE) { - if (res_class != NULL && !res_class->IsArrayClass()) { - Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array"; + const RegType& res_type = work_line_->GetRegisterType(dec_insn.vB_); + if (res_type.IsReferenceTypes()) { + if (!res_type.IsArrayClass()) { + Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array " << res_type; } else { work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer()); } @@ -2011,66 +2054,47 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } case Instruction::NEW_INSTANCE: { - Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_); - if (res_class == NULL) { - const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_); - fail_messages_ << "unable to resolve new-instance " << dec_insn.vB_ - << " (" << bad_class_desc << ") in " - << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor()); - DCHECK(failure_ != VERIFY_ERROR_GENERIC); + const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_); + // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved + // can't create an instance of an interface or abstract class */ + if (!res_type.IsInstantiableTypes()) { + Fail(VERIFY_ERROR_INSTANTIATION) + << "new-instance on primitive, interface or abstract class" << res_type; } else { - /* can't create an instance of an interface or abstract class */ - if (res_class->IsPrimitive() || res_class->IsAbstract() || res_class->IsInterface()) { - Fail(VERIFY_ERROR_INSTANTIATION) - << "new-instance on primitive, interface or abstract class" - << PrettyDescriptor(res_class->GetDescriptor()); - } else { - const RegType& uninit_type = reg_types_.Uninitialized(res_class, work_insn_idx_); - // Any registers holding previous allocations from this address that have not yet been - // initialized must be marked invalid. - work_line_->MarkUninitRefsAsInvalid(uninit_type); - - /* add the new uninitialized reference to the register state */ - work_line_->SetRegisterType(dec_insn.vA_, uninit_type); - } + const RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_); + // Any registers holding previous allocations from this address that have not yet been + // initialized must be marked invalid. + work_line_->MarkUninitRefsAsInvalid(uninit_type); + // add the new uninitialized reference to the register state + work_line_->SetRegisterType(dec_insn.vA_, uninit_type); } break; } case Instruction::NEW_ARRAY: { - Class* res_class = ResolveClassAndCheckAccess(dec_insn.vC_); - if (res_class == NULL) { - const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vC_); - fail_messages_ << "unable to resolve new-array " << dec_insn.vC_ - << " (" << bad_class_desc << ") in " - << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor()); - DCHECK(failure_ != VERIFY_ERROR_GENERIC); - } else if (!res_class->IsArrayClass()) { - Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class"; + const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vC_); + // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved + if (!res_type.IsArrayClass()) { + Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type; } else { /* make sure "size" register is valid type */ work_line_->VerifyRegisterType(dec_insn.vB_, reg_types_.Integer()); /* set register type to array class */ - work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class)); + work_line_->SetRegisterType(dec_insn.vA_, res_type); } break; } case Instruction::FILLED_NEW_ARRAY: case Instruction::FILLED_NEW_ARRAY_RANGE: { - Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_); - if (res_class == NULL) { - const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_); - fail_messages_ << "unable to resolve filled-array " << dec_insn.vB_ - << " (" << bad_class_desc << ") in " - << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor()); - DCHECK(failure_ != VERIFY_ERROR_GENERIC); - } else if (!res_class->IsArrayClass()) { + const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_); + // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved + if (!res_type.IsArrayClass()) { Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class"; } else { bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE); /* check the arguments to the instruction */ - VerifyFilledNewArrayRegs(dec_insn, res_class, is_range); + VerifyFilledNewArrayRegs(dec_insn, res_type, is_range); /* filled-array result goes into "result" register */ - work_line_->SetResultRegisterType(reg_types_.FromClass(res_class)); + work_line_->SetResultRegisterType(res_type); just_set_result = true; } break; @@ -2093,12 +2117,9 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer()); break; case Instruction::THROW: { - Class* res_class = work_line_->GetClassFromRegister(dec_insn.vA_); - if (failure_ == VERIFY_ERROR_NONE && res_class != NULL) { - if (!JavaLangThrowable()->IsAssignableFrom(res_class)) { - Fail(VERIFY_ERROR_GENERIC) << "thrown class " - << PrettyDescriptor(res_class->GetDescriptor()) << " not instanceof Throwable"; - } + const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA_); + if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type)) { + Fail(VERIFY_ERROR_GENERIC) << "thrown class " << res_type << " not instanceof Throwable"; } break; } @@ -2338,9 +2359,17 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE); Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, is_range, is_super); if (failure_ == VERIFY_ERROR_NONE) { + const char* descriptor; + if (called_method == NULL) { + uint32_t method_idx = dec_insn.vB_; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->dexStringByTypeIdx(return_type_idx); + } else { + descriptor = called_method->GetReturnTypeDescriptor(); + } const RegType& return_type = - reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(), - called_method->GetReturnTypeDescriptor()); + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); work_line_->SetResultRegisterType(return_type); just_set_result = true; } @@ -2358,7 +2387,16 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * allowing the latter only if the "this" argument is the same as the "this" argument to * this method (which implies that we're in a constructor ourselves). */ - if (called_method->IsConstructor()) { + bool is_constructor; + if (called_method != NULL) { + is_constructor = called_method->IsConstructor(); + } else { + uint32_t method_idx = dec_insn.vB_; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + const char* name = dex_file_->GetMethodName(method_id); + is_constructor = strcmp(name, "<init>") == 0; + } + if (is_constructor) { const RegType& this_type = work_line_->GetInvocationThis(dec_insn); if (failure_ != VERIFY_ERROR_NONE) break; @@ -2368,19 +2406,20 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_GENERIC) << "unable to initialize null ref"; break; } - Class* this_class = this_type.GetClass(); - DCHECK(this_class != NULL); - - /* must be in same class or in superclass */ - if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) { - if (this_class != method_->GetDeclaringClass()) { - Fail(VERIFY_ERROR_GENERIC) - << "invoke-direct <init> on super only allowed for 'this' in <init>"; + if (called_method != NULL) { + Class* this_class = this_type.GetClass(); + DCHECK(this_class != NULL); + /* must be in same class or in superclass */ + if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) { + if (this_class != method_->GetDeclaringClass()) { + Fail(VERIFY_ERROR_GENERIC) + << "invoke-direct <init> on super only allowed for 'this' in <init>"; + break; + } + } else if (called_method->GetDeclaringClass() != this_class) { + Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super"; break; } - } else if (called_method->GetDeclaringClass() != this_class) { - Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super"; - break; } /* arg must be an uninitialized reference */ @@ -2398,9 +2437,17 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (failure_ != VERIFY_ERROR_NONE) break; } + const char* descriptor; + if (called_method == NULL) { + uint32_t method_idx = dec_insn.vB_; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->dexStringByTypeIdx(return_type_idx); + } else { + descriptor = called_method->GetReturnTypeDescriptor(); + } const RegType& return_type = - reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(), - called_method->GetReturnTypeDescriptor()); + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); work_line_->SetResultRegisterType(return_type); just_set_result = true; } @@ -2411,9 +2458,17 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { bool is_range = (dec_insn.opcode_ == Instruction::INVOKE_STATIC_RANGE); Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false); if (failure_ == VERIFY_ERROR_NONE) { + const char* descriptor; + if (called_method == NULL) { + uint32_t method_idx = dec_insn.vB_; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->dexStringByTypeIdx(return_type_idx); + } else { + descriptor = called_method->GetReturnTypeDescriptor(); + } const RegType& return_type = - reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(), - called_method->GetReturnTypeDescriptor()); + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); work_line_->SetResultRegisterType(return_type); just_set_result = true; } @@ -2424,42 +2479,52 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { bool is_range = (dec_insn.opcode_ == Instruction::INVOKE_INTERFACE_RANGE); Method* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false); if (failure_ == VERIFY_ERROR_NONE) { - Class* called_interface = abs_method->GetDeclaringClass(); - if (!called_interface->IsInterface()) { - Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '" - << PrettyMethod(abs_method) << "'"; - break; - } else { - /* Get the type of the "this" arg, which should either be a sub-interface of called - * interface or Object (see comments in RegType::JoinClass). - */ - const RegType& this_type = work_line_->GetInvocationThis(dec_insn); - if (failure_ == VERIFY_ERROR_NONE) { - if (this_type.IsZero()) { - /* null pointer always passes (and always fails at runtime) */ - } else { - if (this_type.IsUninitializedTypes()) { - Fail(VERIFY_ERROR_GENERIC) << "interface call on uninitialized object " - << this_type; - break; - } - // In the past we have tried to assert that "called_interface" is assignable - // from "this_type.GetClass()", however, as we do an imprecise Join - // (RegType::JoinClass) we don't have full information on what interfaces are - // implemented by "this_type". For example, two classes may implement the same - // interfaces and have a common parent that doesn't implement the interface. The - // join will set "this_type" to the parent class and a test that this implements - // the interface will incorrectly fail. + if (abs_method != NULL) { + Class* called_interface = abs_method->GetDeclaringClass(); + if (!called_interface->IsInterface()) { + Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '" + << PrettyMethod(abs_method) << "'"; + break; + } + } + /* Get the type of the "this" arg, which should either be a sub-interface of called + * interface or Object (see comments in RegType::JoinClass). + */ + const RegType& this_type = work_line_->GetInvocationThis(dec_insn); + if (failure_ == VERIFY_ERROR_NONE) { + if (this_type.IsZero()) { + /* null pointer always passes (and always fails at runtime) */ + } else { + if (this_type.IsUninitializedTypes()) { + Fail(VERIFY_ERROR_GENERIC) << "interface call on uninitialized object " + << this_type; + break; } + // In the past we have tried to assert that "called_interface" is assignable + // from "this_type.GetClass()", however, as we do an imprecise Join + // (RegType::JoinClass) we don't have full information on what interfaces are + // implemented by "this_type". For example, two classes may implement the same + // interfaces and have a common parent that doesn't implement the interface. The + // join will set "this_type" to the parent class and a test that this implements + // the interface will incorrectly fail. } } /* * We don't have an object instance, so we can't find the concrete method. However, all of * the type information is in the abstract method, so we're good. */ + const char* descriptor; + if (abs_method == NULL) { + uint32_t method_idx = dec_insn.vB_; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->dexStringByTypeIdx(return_type_idx); + } else { + descriptor = abs_method->GetReturnTypeDescriptor(); + } const RegType& return_type = - reg_types_.FromDescriptor(abs_method->GetDeclaringClass()->GetClassLoader(), - abs_method->GetReturnTypeDescriptor()); + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); + work_line_->SetResultRegisterType(return_type); work_line_->SetResultRegisterType(return_type); just_set_result = true; } @@ -2887,24 +2952,30 @@ bool DexVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { return true; } -Class* DexVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { - const Class* referrer = method_->GetDeclaringClass(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Class* res_class = class_linker->ResolveType(*dex_file_, class_idx, referrer); - - if (res_class == NULL) { - Thread::Current()->ClearException(); - Fail(VERIFY_ERROR_NO_CLASS) << "can't find class with index " << (void*) class_idx; - } else if (!referrer->CanAccess(res_class)) { /* Check if access is allowed. */ - Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: " - << referrer->GetDescriptor()->ToModifiedUtf8() << " -> " - << res_class->GetDescriptor()->ToModifiedUtf8(); +const RegType& DexVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { + const char* descriptor = dex_file_->dexStringByTypeIdx(class_idx); + Class* referrer = method_->GetDeclaringClass(); + Class* klass = method_->GetDexCacheResolvedTypes()->Get(class_idx); + const RegType& result = + klass != NULL ? reg_types_.FromClass(klass) + : reg_types_.FromDescriptor(referrer->GetClassLoader(), descriptor); + if (klass == NULL && !result.IsUnresolvedTypes()) { + method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass()); + } + // Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to + // check at runtime if access is allowed and so pass here. + if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) { + Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '" + << PrettyDescriptor(referrer->GetDescriptor()) << "' -> '" + << result << "'"; + return reg_types_.Unknown(); + } else { + return result; } - return res_class; } -Class* DexVerifier::GetCaughtExceptionType() { - Class* common_super = NULL; +const RegType& DexVerifier::GetCaughtExceptionType() { + const RegType* common_super = NULL; if (code_item_->tries_size_ != 0) { const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item_, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); @@ -2914,25 +2985,23 @@ Class* DexVerifier::GetCaughtExceptionType() { DexFile::CatchHandlerItem handler = iterator.Get(); if (handler.address_ == (uint32_t) work_insn_idx_) { if (handler.type_idx_ == DexFile::kDexNoIndex) { - common_super = JavaLangThrowable(); + common_super = ®_types_.JavaLangThrowable(); } else { - Class* klass = ResolveClassAndCheckAccess(handler.type_idx_); + const RegType& exception = ResolveClassAndCheckAccess(handler.type_idx_); /* TODO: on error do we want to keep going? If we don't fail this we run the risk of * having a non-Throwable introduced at runtime. However, that won't pass an instanceof * test, so is essentially harmless. */ - if (klass == NULL) { - Fail(VERIFY_ERROR_GENERIC) << "unable to resolve exception class " - << handler.type_idx_ << " (" - << dex_file_->dexStringByTypeIdx(handler.type_idx_) << ")"; - return NULL; - } else if(!JavaLangThrowable()->IsAssignableFrom(klass)) { - Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << PrettyClass(klass); - return NULL; + if(!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) { + Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << exception; + return reg_types_.Unknown(); } else if (common_super == NULL) { - common_super = klass; + common_super = &exception; + } else if (common_super->Equals(exception)) { + // nothing to do } else { - common_super = RegType::ClassJoin(common_super, klass); + common_super = &common_super->Merge(exception, ®_types_); + CHECK(reg_types_.JavaLangThrowable().IsAssignableFrom(*common_super)); } } } @@ -2944,7 +3013,7 @@ Class* DexVerifier::GetCaughtExceptionType() { /* no catch blocks, or no catches with classes we can find */ Fail(VERIFY_ERROR_GENERIC) << "unable to find exception handler"; } - return common_super; + return *common_super; } Method* DexVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, bool is_direct) { @@ -2953,11 +3022,11 @@ Method* DexVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, bool is_di Method* res_method = dex_cache->GetResolvedMethod(method_idx); if (res_method == NULL) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - Class* klass = ResolveClassAndCheckAccess(method_id.class_idx_); - if (klass == NULL) { - DCHECK(failure_ != VERIFY_ERROR_NONE); - return NULL; + const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_); + if(klass_type.IsUnresolvedTypes()) { + return NULL; // Can't resolve Class so no more to do here } + Class* klass = klass_type.GetClass(); const char* name = dex_file_->GetMethodName(method_id); std::string signature(dex_file_->CreateMethodDescriptor(method_id.proto_idx_, NULL)); if (is_direct) { @@ -2991,14 +3060,7 @@ Method* DexVerifier::VerifyInvocationArgs(const Instruction::DecodedInstruction& // we're making. Method* res_method = ResolveMethodAndCheckAccess(dec_insn.vB_, (method_type == METHOD_DIRECT || method_type == METHOD_STATIC)); - if (res_method == NULL) { - const DexFile::MethodId& method_id = dex_file_->GetMethodId(dec_insn.vB_); - const char* method_name = dex_file_->GetMethodName(method_id); - std::string method_signature = dex_file_->GetMethodSignature(method_id); - const char* class_descriptor = dex_file_->GetMethodDeclaringClassDescriptor(method_id); - DCHECK_NE(failure_, VERIFY_ERROR_NONE); - fail_messages_ << "unable to resolve method " << dec_insn.vB_ << ": " - << class_descriptor << "." << method_name << " " << method_signature; + if (res_method == NULL) { // error or class is unresolved return NULL; } // Make sure calls to constructors are "direct". There are additional restrictions but we don't @@ -3410,13 +3472,14 @@ bool DexVerifier::CheckMoveException(const uint16_t* insns, int insn_idx) { } void DexVerifier::VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn, - Class* res_class, bool is_range) { - DCHECK(res_class->IsArrayClass()) << PrettyClass(res_class); // Checked before calling. + const RegType& res_type, bool is_range) { + DCHECK(res_type.IsArrayClass()) << res_type; // Checked before calling. /* * Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of the * list and fail. It's legal, if silly, for arg_count to be zero. */ - const RegType& expected_type = reg_types_.FromClass(res_class->GetComponentType()); + const RegType& expected_type = reg_types_.GetComponentType(res_type, + method_->GetDeclaringClass()->GetClassLoader()); uint32_t arg_count = dec_insn.vA_; for (size_t ui = 0; ui < arg_count; ui++) { uint32_t get_reg; @@ -3663,15 +3726,6 @@ void DexVerifier::VerifyGcMap() { } } -Class* DexVerifier::JavaLangThrowable() { - if (java_lang_throwable_ == NULL) { - java_lang_throwable_ = - Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Throwable;"); - DCHECK(java_lang_throwable_ != NULL); - } - return java_lang_throwable_; -} - const uint8_t* PcToReferenceMap::FindBitMap(uint16_t dex_pc, bool error_if_not_present) const { size_t num_entries = NumEntries(); // Do linear or binary search? diff --git a/src/dex_verifier.h b/src/dex_verifier.h index 9041c95399..dbe92f6268 100644 --- a/src/dex_verifier.h +++ b/src/dex_verifier.h @@ -143,7 +143,7 @@ class RegType { bool IsReferenceTypes() const { return IsReference() || IsUnresolvedReference() || IsUninitializedReference() || - IsUninitializedThisReference() || IsZero(); + IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsZero(); } bool IsNonZeroReferenceTypes() const { return IsReference() || IsUnresolvedReference() || IsUninitializedReference() || @@ -182,7 +182,7 @@ class RegType { } uint32_t GetAllocationPc() const { - DCHECK(IsUninitializedReference()); + DCHECK(IsUninitializedTypes()); return allocation_pc_or_constant_; } @@ -196,12 +196,24 @@ class RegType { bool IsJavaLangObject() const { return IsReference() && GetClass()->IsObjectClass(); } + bool IsInstantiableTypes() const { + return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable()); + } String* GetDescriptor() const { - DCHECK(IsUnresolvedReference()); + DCHECK(IsUnresolvedTypes()); DCHECK(klass_or_descriptor_ != NULL); DCHECK(klass_or_descriptor_->IsString()); return down_cast<String*>(klass_or_descriptor_); } + bool IsArrayClass() const { + if (IsUnresolvedTypes()) { + return GetDescriptor()->CharAt(0) == '['; + } else if (!IsConstant()) { + return GetClass()->IsArrayClass(); + } else { + return false; + } + } uint16_t GetId() const { return cache_id_; @@ -239,12 +251,12 @@ class RegType { RegType(Type type, Object* klass_or_descriptor, uint32_t allocation_pc_or_constant, uint16_t cache_id) : type_(type), klass_or_descriptor_(klass_or_descriptor), allocation_pc_or_constant_(allocation_pc_or_constant), cache_id_(cache_id) { - DCHECK(IsConstant() || IsUninitializedReference() || allocation_pc_or_constant == 0); + DCHECK(IsConstant() || IsUninitializedTypes() || allocation_pc_or_constant == 0); if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUnknown() && !IsConflict()) { DCHECK(klass_or_descriptor != NULL); - DCHECK(IsUnresolvedReference() || klass_or_descriptor_->IsClass()); - DCHECK(!IsUnresolvedReference() || klass_or_descriptor_->IsString()); + DCHECK(IsUnresolvedTypes() || klass_or_descriptor_->IsClass()); + DCHECK(!IsUnresolvedTypes() || klass_or_descriptor_->IsString()); } } @@ -300,14 +312,16 @@ class RegTypeCache { const RegType& JavaLangClass() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Class;"); } const RegType& JavaLangObject() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Object;"); } const RegType& JavaLangString() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;"); } + const RegType& JavaLangThrowable() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;"); } const RegType& Unknown() { return FromType(RegType::kRegTypeUnknown); } const RegType& Conflict() { return FromType(RegType::kRegTypeConflict); } const RegType& ConstLo() { return FromType(RegType::kRegTypeConstLo); } const RegType& Zero() { return FromCat1Const(0); } - const RegType& Uninitialized(Class* klass, uint32_t allocation_pc); + const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc); const RegType& UninitializedThisArgument(Class* klass); + const RegType& FromUninitialized(const RegType& uninit_type); // Representatives of various constant types. When merging constants we can't infer a type, // (an int may later be used as a float) so we select these representative values meaning future @@ -315,6 +329,8 @@ class RegTypeCache { const RegType& ByteConstant() { return FromCat1Const(std::numeric_limits<jbyte>::min()); } const RegType& ShortConstant() { return FromCat1Const(std::numeric_limits<jshort>::min()); } const RegType& IntConstant() { return FromCat1Const(std::numeric_limits<jint>::max()); } + + const RegType& GetComponentType(const RegType& array, const ClassLoader* loader); private: // The allocated entries std::vector<RegType*> entries_; @@ -1073,17 +1089,12 @@ class DexVerifier { bool is_primitive, bool is_static); // Verify that the arguments in a filled-new-array instruction are valid. - // "res_class" is the class refered to by dec_insn->vB_. - void VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn, Class* res_class, - bool is_range); + void VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn, + const RegType& res_type, bool is_range); - /* - * Resolves a class based on an index and performs access checks to ensure the referrer can - * access the resolved class. - * Exceptions caused by failures are cleared before returning. - * Sets "*failure" on failure. - */ - Class* ResolveClassAndCheckAccess(uint32_t class_idx); + // Resolves a class based on an index and performs access checks to ensure the referrer can + // access the resolved class. + const RegType& ResolveClassAndCheckAccess(uint32_t class_idx); /* * For the "move-exception" instruction at "work_insn_idx_", which must be at an exception handler @@ -1091,7 +1102,7 @@ class DexVerifier { * Returns NULL if no matching exception handler can be found, or if the exception is not a * subclass of Throwable. */ - Class* GetCaughtExceptionType(); + const RegType& GetCaughtExceptionType(); /* * Resolves a method based on an index and performs access checks to ensure @@ -1169,8 +1180,6 @@ class DexVerifier { // Compute sizes for GC map data void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); - Class* JavaLangThrowable(); - InsnFlags CurrentInsnFlags() { return insn_flags_[work_insn_idx_]; } @@ -1182,9 +1191,6 @@ class DexVerifier { // Storage for the register status we're currently working on. UniquePtr<RegisterLine> work_line_; - // Lazily initialized reference to java.lang.Class<java.lang.Throwable> - Class* java_lang_throwable_; - // The address of the instruction we're currently working on, note that this is in 2 byte // quantities uint32_t work_insn_idx_; diff --git a/src/object.cc b/src/object.cc index e59e79a9a1..45fc78167c 100644 --- a/src/object.cc +++ b/src/object.cc @@ -668,7 +668,6 @@ uintptr_t Method::ToNativePC(const uint32_t dex_pc) const { uint32_t Method::FindCatchBlock(Class* exception_type, uint32_t dex_pc) const { DexCache* dex_cache = GetDeclaringClass()->GetDexCache(); - const ClassLoader* class_loader = GetDeclaringClass()->GetClassLoader(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const DexFile& dex_file = class_linker->FindDexFile(dex_cache); const DexFile::CodeItem* code_item = dex_file.GetCodeItem(GetCodeItemOffset()); @@ -681,9 +680,12 @@ uint32_t Method::FindCatchBlock(Class* exception_type, uint32_t dex_pc) const { return iter.Get().address_; } // Does this catch exception type apply? - Class* iter_exception_type = - class_linker->ResolveType(dex_file, iter_type_idx, dex_cache, class_loader); - if (iter_exception_type->IsAssignableFrom(exception_type)) { + Class* iter_exception_type = dex_cache->GetResolvedType(iter_type_idx); + if (iter_exception_type == NULL) { + // The verifier should take care of resolving all exception classes early + LOG(WARNING) << "Unresolved exception class when finding catch block: " + << dex_file.GetTypeDescriptor(dex_file.GetTypeId(iter_type_idx)); + } else if (iter_exception_type->IsAssignableFrom(exception_type)) { return iter.Get().address_; } } @@ -764,10 +766,8 @@ void Class::SetDexCache(DexCache* new_dex_cache) { } Object* Class::AllocObject() { - DCHECK(!IsAbstract()) << PrettyClass(this); - DCHECK(!IsInterface()) << PrettyClass(this); - DCHECK(!IsPrimitive()) << PrettyClass(this); DCHECK(!IsArrayClass()) << PrettyClass(this); + DCHECK(IsInstantiable()) << PrettyClass(this); DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this); DCHECK_GE(this->object_size_, sizeof(Object)); return Heap::AllocObject(this, this->object_size_); diff --git a/src/object.h b/src/object.h index 58c6ba762e..1051985f02 100644 --- a/src/object.h +++ b/src/object.h @@ -1463,6 +1463,9 @@ class MANAGED Class : public StaticStorageBase { bool IsObjectClass() const { return !IsPrimitive() && GetSuperClass() == NULL; } + bool IsInstantiable() const { + return !IsPrimitive() && !IsInterface() && !IsAbstract(); + } // Creates a raw object instance but does not invoke the default constructor. Object* AllocObject(); @@ -2022,8 +2025,7 @@ class MANAGED Class : public StaticStorageBase { Class* super_class_; // If class verify fails, we must return same error on subsequent tries. - // Update with SetVerifyErrorClass to ensure a write barrier is used. - const Class* verify_error_class_; + Class* verify_error_class_; // virtual methods defined in this class; invoked through vtable ObjectArray<Method>* virtual_methods_; diff --git a/src/runtime_support.cc b/src/runtime_support.cc index 6773338f53..8df2abe6f2 100644 --- a/src/runtime_support.cc +++ b/src/runtime_support.cc @@ -425,30 +425,6 @@ void* UnresolvedDirectMethodTrampolineFromCode(int32_t method_idx, Method** sp, return code; } -// TODO: placeholder. Helper function to type -Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) { - /* - * Should initialize & fix up method->dex_cache_resolved_types_[]. - * Returns initialized type. Does not return normally if an exception - * is thrown, but instead initiates the catch. Should be similar to - * ClassLinker::InitializeStaticStorageFromCode. - */ - UNIMPLEMENTED(FATAL); - return NULL; -} - -// TODO: placeholder. Helper function to resolve virtual method -void ResolveMethodFromCode(Method* method, uint32_t method_idx) { - /* - * Slow-path handler on invoke virtual method path in which - * base method is unresolved at compile-time. Doesn't need to - * return anything - just either ensure that - * method->dex_cache_resolved_methods_(method_idx) != NULL or - * throw and unwind. The caller will restart call sequence - * from the beginning. - */ -} - // Fast path field resolution that can't throw exceptions static Field* FindFieldFast(uint32_t field_idx, const Method* referrer) { Field* resolved_field = referrer->GetDexCacheResolvedFields()->Get(field_idx); @@ -651,11 +627,30 @@ extern "C" Object* artAllocObjectFromCode(uint32_t type_idx, Method* method, return klass->AllocObject(); } -extern "C" Object* artAllocObjectFromCodeSlowPath(uint32_t type_idx, - Method* method, - Thread* self, Method** sp) { - //TODO: Add delayed verification checks here - return artAllocObjectFromCode(type_idx, method, self, sp); +extern "C" Object* artAllocObjectFromCodeWithAccessCheck(uint32_t type_idx, Method* method, + Thread* self, Method** sp) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx); + Runtime* runtime = Runtime::Current(); + if (UNLIKELY(klass == NULL)) { + klass = runtime->GetClassLinker()->ResolveType(type_idx, method); + if (klass == NULL) { + DCHECK(self->IsExceptionPending()); + return NULL; // Failure + } + } + Class* referrer = method->GetDeclaringClass(); + if (UNLIKELY(!referrer->CanAccess(klass))) { + self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", "illegal class access: '%s' -> '%s'", + PrettyDescriptor(referrer->GetDescriptor()).c_str(), + PrettyDescriptor(klass->GetDescriptor()).c_str()); + return NULL; // Failure + } + if (!runtime->GetClassLinker()->EnsureInitialized(klass, true)) { + DCHECK(self->IsExceptionPending()); + return NULL; // Failure + } + return klass->AllocObject(); } Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count, @@ -789,6 +784,25 @@ extern "C" Class* artInitializeStaticStorageFromCode(uint32_t type_idx, const Me return InitializeStaticStorage(type_idx, referrer, self); } +extern "C" Class* artInitializeTypeFromCode(uint32_t type_idx, const Method* referrer, Thread* self, + Method** sp) { + // Called when method->dex_cache_resolved_types_[] misses + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return InitializeStaticStorage(type_idx, referrer, self); +} + +// TODO: placeholder. Helper function to resolve virtual method +void ResolveMethodFromCode(Method* method, uint32_t method_idx) { + /* + * Slow-path handler on invoke virtual method path in which + * base method is unresolved at compile-time. Doesn't need to + * return anything - just either ensure that + * method->dex_cache_resolved_methods_(method_idx) != NULL or + * throw and unwind. The caller will restart call sequence + * from the beginning. + */ +} + String* ResolveStringFromCode(const Method* referrer, uint32_t string_idx) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return class_linker->ResolveString(string_idx, referrer); diff --git a/src/runtime_support.h b/src/runtime_support.h index 4ed6455be1..90fcf2c9b2 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -59,12 +59,13 @@ extern "C" void art_proxy_invoke_handler(); extern "C" void art_unlock_object_from_code(void*); extern "C" void* art_alloc_array_from_code(uint32_t, void*, int32_t); extern "C" void* art_alloc_object_from_code(uint32_t type_idx, void* method); - extern "C" void* art_alloc_object_from_code_slow_path(uint32_t type_idx, void* method); + extern "C" void* art_alloc_object_from_code_with_access_check(uint32_t type_idx, void* method); extern "C" void* art_check_and_alloc_array_from_code(uint32_t, void*, int32_t); extern "C" void* art_find_instance_field_from_code(uint32_t, void*); extern "C" void* art_find_static_field_from_code(uint32_t, void*); extern "C" void* art_get_obj_static_from_code(uint32_t, void*); extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*); + extern "C" void* art_initialize_type_from_code(uint32_t, void*); extern "C" void* art_resolve_string_from_code(void*, uint32_t); /* Conversions */ diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S index b6d89286d3..112a1141a2 100644 --- a/src/runtime_support_asm.S +++ b/src/runtime_support_asm.S @@ -280,6 +280,22 @@ art_initialize_static_storage_from_code: bxne lr @ return on success DELIVER_PENDING_EXCEPTION + .global art_initialize_type_from_code + .extern artInitializeTypeFromCode + /* + * Entry from managed code when dex cache misses for a type_idx + */ +art_initialize_type_from_code: + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, SP) + bl artInitializeTypeFromCode + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + cmp r0, #0 @ success if result is non-null + bxne lr @ return on success + DELIVER_PENDING_EXCEPTION + .global art_find_instance_field_from_code .extern artFindInstanceFieldFromCode /* @@ -441,16 +457,17 @@ art_alloc_object_from_code: bxne lr @ return on success DELIVER_PENDING_EXCEPTION - .global art_alloc_object_from_code_slow_path - .extern artAllocObjectFromCodeSlowPath + .global art_alloc_object_from_code_with_access_check + .extern artAllocObjectFromCodeWithAccessCheck /* - * Called by managed code to allocate an object + * Called by managed code to allocate an object when the caller doesn't know whether it has + * access to the created type */ -art_alloc_object_from_code_slow_path: +art_alloc_object_from_code_with_access_check: SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP - bl artAllocObjectFromCodeSlowPath @ (uint32_t type_idx, Method* method, Thread*, SP) + bl artAllocObjectFromCodeWithAccessCheck @ (uint32_t type_idx, Method* method, Thread*, SP) RESTORE_REF_ONLY_CALLEE_SAVE_FRAME cmp r0, #0 @ success if result is non-null bxne lr @ return on success diff --git a/src/thread.cc b/src/thread.cc index 841ea9bb68..a8c58bf78b 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -91,7 +91,7 @@ void Thread::InitFunctionPointers() { pLmul = __aeabi_lmul; pAllocArrayFromCode = art_alloc_array_from_code; pAllocObjectFromCode = art_alloc_object_from_code; - pAllocObjectFromCodeSlowPath = art_alloc_object_from_code_slow_path; + pAllocObjectFromCodeWithAccessCheck = art_alloc_object_from_code_with_access_check; pCanPutArrayElementFromCode = art_can_put_array_element_from_code; pCheckAndAllocArrayFromCode = art_check_and_alloc_array_from_code; pCheckCastFromCode = art_check_cast_from_code; @@ -101,6 +101,7 @@ void Thread::InitFunctionPointers() { pGetObjStatic = art_get_obj_static_from_code; pHandleFillArrayDataFromCode = art_handle_fill_data_from_code; pInitializeStaticStorage = art_initialize_static_storage_from_code; + pInitializeTypeFromCode = art_initialize_type_from_code; pInvokeInterfaceTrampoline = art_invoke_interface_trampoline; pLockObjectFromCode = art_lock_object_from_code; pObjectInit = art_object_init_from_code; @@ -126,7 +127,6 @@ void Thread::InitFunctionPointers() { pDecodeJObjectInThread = DecodeJObjectInThread; pDeliverException = art_deliver_exception_from_code; pFindNativeMethod = FindNativeMethod; - pInitializeTypeFromCode = InitializeTypeFromCode; pInstanceofNonTrivialFromCode = IsAssignableFromCode; pResolveMethodFromCode = ResolveMethodFromCode; pThrowAbstractMethodErrorFromCode = ThrowAbstractMethodErrorFromCode; @@ -482,7 +482,8 @@ void Thread::PopNativeToManagedRecord(const NativeToManagedRecord& record) { struct StackDumpVisitor : public Thread::StackVisitor { StackDumpVisitor(std::ostream& os, const Thread* thread) - : os(os), thread(thread), frame_count(0) { + : last_method(NULL), last_line_number(0), repetition_count(0), os(os), thread(thread), + frame_count(0) { } virtual ~StackDumpVisitor() { @@ -492,28 +493,41 @@ struct StackDumpVisitor : public Thread::StackVisitor { if (!frame.HasMethod()) { return; } - + const int kMaxRepetition = 3; Method* m = frame.GetMethod(); Class* c = m->GetDeclaringClass(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const DexFile& dex_file = class_linker->FindDexFile(c->GetDexCache()); - - os << " at " << PrettyMethod(m, false); - if (m->IsNative()) { - os << "(Native method)"; + int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc)); + if (line_number == last_line_number && last_method == m) { + repetition_count++; } else { - int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc)); - SirtRef<String> source_file(c->GetSourceFile()); - os << "(" << (source_file.get() != NULL ? source_file->ToModifiedUtf8() : "unavailable") - << ":" << line_number << ")"; + if (repetition_count >= kMaxRepetition) { + os << " ... repeated " << (repetition_count - kMaxRepetition) << " times\n"; + } + repetition_count = 0; + last_line_number = line_number; + last_method = m; + } + if (repetition_count < kMaxRepetition) { + os << " at " << PrettyMethod(m, false); + if (m->IsNative()) { + os << "(Native method)"; + } else { + SirtRef<String> source_file(c->GetSourceFile()); + os << "(" << (source_file.get() != NULL ? source_file->ToModifiedUtf8() : "unavailable") + << ":" << line_number << ")"; + } + os << "\n"; } - os << "\n"; if (frame_count++ == 0) { Monitor::DescribeWait(os, thread); } } - + Method* last_method; + int last_line_number; + int repetition_count; std::ostream& os; const Thread* thread; int frame_count; @@ -1316,13 +1330,35 @@ class CatchBlockStackVisitor : public Thread::StackVisitor { }; void Thread::DeliverException() { - Throwable *exception = GetException(); // Set exception on thread + const bool kDebugExceptionDelivery = false; + Throwable *exception = GetException(); // Get exception from thread CHECK(exception != NULL); + // Don't leave exception visible while we try to find the handler, which may cause class + // resolution + ClearException(); + if (kDebugExceptionDelivery) { + DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << std::endl); + } Context* long_jump_context = GetLongJumpContext(); CatchBlockStackVisitor catch_finder(exception->GetClass(), long_jump_context); WalkStackUntilUpCall(&catch_finder, true); + if (kDebugExceptionDelivery) { + Method* handler_method = catch_finder.handler_frame_.GetMethod(); + if (handler_method == NULL) { + LOG(INFO) << "Handler is upcall"; + } else { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const DexFile& dex_file = + class_linker->FindDexFile(handler_method->GetDeclaringClass()->GetDexCache()); + int line_number = dex_file.GetLineNumFromPC(handler_method, + handler_method->ToDexPC(catch_finder.handler_pc_)); + LOG(INFO) << "Handler: " << PrettyMethod(handler_method) + << " (line: " << line_number << ")"; + } + } + SetException(exception); long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP())); long_jump_context->SetPC(catch_finder.handler_pc_); long_jump_context->DoLongJump(); diff --git a/src/thread.h b/src/thread.h index ac97ebecdf..eec9df40ad 100644 --- a/src/thread.h +++ b/src/thread.h @@ -120,7 +120,7 @@ class PACKED Thread { void (*pCheckSuspendFromCode)(Thread*); // Stub that is called when the suspend count is non-zero void (*pTestSuspendFromCode)(); // Stub that is periodically called to test the suspend count void* (*pAllocObjectFromCode)(uint32_t, void*); - void* (*pAllocObjectFromCodeSlowPath)(uint32_t, void*); + void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*); void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t); void (*pCanPutArrayElementFromCode)(void*, void*); void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t); @@ -137,7 +137,7 @@ class PACKED Thread { void* (*pInitializeStaticStorage)(uint32_t, void*); uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*); void (*pInvokeInterfaceTrampoline)(uint32_t, void*); - Class* (*pInitializeTypeFromCode)(uint32_t, Method*); + void* (*pInitializeTypeFromCode)(uint32_t, void*); void (*pLockObjectFromCode)(void*); void (*pObjectInit)(void*); void (*pResolveMethodFromCode)(Method*, uint32_t); |