summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2020-06-05 18:34:49 +0100
committer Nicolas Geoffray <ngeoffray@google.com> 2020-06-09 12:28:42 +0000
commit1715efa0b46d57d587237829d1c0695aaca2c344 (patch)
tree61e7013808a8fa9c45384fa8d84bd7f3eb1eaf34
parent9922f00cf68aac69209216a0726a45eb6338763c (diff)
Add a new class status for verified with access checks.
At runtime, we won't run the verifier for those classes, but run with access checks enabled in the interpreter. Bug: 157265232 Test: test.py Change-Id: Ia087c3b6f9fcbd295307333e524945d844ef54dc
-rw-r--r--dex2oat/driver/compiler_driver.cc16
-rw-r--r--dex2oat/driver/compiler_driver_test.cc3
-rw-r--r--dex2oat/linker/image_writer.cc5
-rw-r--r--dex2oat/linker/oat_writer.cc5
-rw-r--r--openjdkjvmti/ti_redefine.cc1
-rw-r--r--runtime/aot_class_linker.cc3
-rw-r--r--runtime/class_linker.cc94
-rw-r--r--runtime/class_status.h12
-rw-r--r--runtime/mirror/class.cc32
-rw-r--r--runtime/mirror/class.h16
-rw-r--r--runtime/oat.h4
-rw-r--r--runtime/verifier/method_verifier.cc39
-rw-r--r--runtime/verifier/verifier_enums.h1
13 files changed, 149 insertions, 82 deletions
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index 1b29e3b77a..cb4cb274ba 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -831,7 +831,10 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader,
if (cls == nullptr) {
soa.Self()->ClearException();
} else if (&cls->GetDexFile() == dex_file) {
- DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->ShouldVerifyAtRuntime())
+ DCHECK(cls->IsErroneous() ||
+ cls->IsVerified() ||
+ cls->ShouldVerifyAtRuntime() ||
+ cls->IsVerifiedNeedsAccessChecks())
<< cls->PrettyClass()
<< " " << cls->GetStatus();
}
@@ -2014,7 +2017,8 @@ class VerifyClassVisitor : public CompilationVisitor {
// Force a soft failure for the VerifierDeps. This is a sanity measure, as
// the vdex file already records that the class hasn't been resolved. It avoids
// trying to do future verification optimizations when processing the vdex file.
- DCHECK(failure_kind == verifier::FailureKind::kNoFailure) << failure_kind;
+ DCHECK(failure_kind == verifier::FailureKind::kNoFailure ||
+ failure_kind == verifier::FailureKind::kAccessChecksFailure) << failure_kind;
failure_kind = verifier::FailureKind::kSoftFailure;
}
} else if (&klass->GetDexFile() != &dex_file) {
@@ -2043,7 +2047,10 @@ class VerifyClassVisitor : public CompilationVisitor {
manager_->GetCompiler()->AddSoftVerifierFailure();
}
- CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerified() || klass->IsErroneous())
+ CHECK(klass->ShouldVerifyAtRuntime() ||
+ klass->IsVerifiedNeedsAccessChecks() ||
+ klass->IsVerified() ||
+ klass->IsErroneous())
<< klass->PrettyDescriptor() << ": state=" << klass->GetStatus();
// Class has a meaningful status for the compiler now, record it.
@@ -2073,6 +2080,8 @@ class VerifyClassVisitor : public CompilationVisitor {
}
if (klass->IsVerified()) {
DCHECK_EQ(failure_kind, verifier::FailureKind::kNoFailure);
+ } else if (klass->IsVerifiedNeedsAccessChecks()) {
+ DCHECK_EQ(failure_kind, verifier::FailureKind::kAccessChecksFailure);
} else if (klass->ShouldVerifyAtRuntime()) {
DCHECK_EQ(failure_kind, verifier::FailureKind::kSoftFailure);
} else {
@@ -2875,6 +2884,7 @@ void CompilerDriver::RecordClassStatus(const ClassReference& ref, ClassStatus st
case ClassStatus::kNotReady:
case ClassStatus::kResolved:
case ClassStatus::kRetryVerificationAtRuntime:
+ case ClassStatus::kVerifiedNeedsAccessChecks:
case ClassStatus::kVerified:
case ClassStatus::kSuperclassValidated:
case ClassStatus::kVisiblyInitialized:
diff --git a/dex2oat/driver/compiler_driver_test.cc b/dex2oat/driver/compiler_driver_test.cc
index 50cb292a4d..3096fc31b9 100644
--- a/dex2oat/driver/compiler_driver_test.cc
+++ b/dex2oat/driver/compiler_driver_test.cc
@@ -353,8 +353,7 @@ TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) {
++i) {
const ClassStatus expected_status = enum_cast<ClassStatus>(i);
// Skip unsupported status that are not supposed to be ever recorded.
- if (expected_status == ClassStatus::kVerifyingAtRuntime ||
- expected_status == ClassStatus::kInitializing ||
+ if (expected_status == ClassStatus::kInitializing ||
expected_status == ClassStatus::kInitialized) {
continue;
}
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index d56fea997e..c8f36ccf63 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3167,6 +3167,11 @@ void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
// Remove the clinitThreadId. This is required for image determinism.
copy->SetClinitThreadId(static_cast<pid_t>(0));
+ // We never emit kRetryVerificationAtRuntime, instead we mark the class as
+ // resolved and the class will therefore be re-verified at runtime.
+ if (orig->ShouldVerifyAtRuntime()) {
+ copy->SetStatusInternal(ClassStatus::kResolved);
+ }
}
void ImageWriter::FixupObject(Object* orig, Object* copy) {
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index c005cc23a8..30b61873f9 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -1008,6 +1008,11 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
status = ClassStatus::kNotReady;
}
}
+ // We never emit kRetryVerificationAtRuntime, instead we mark the class as
+ // resolved and the class will therefore be re-verified at runtime.
+ if (status == ClassStatus::kRetryVerificationAtRuntime) {
+ status = ClassStatus::kResolved;
+ }
writer_->oat_class_headers_.emplace_back(offset_,
compiled_methods_with_code_,
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 707c7009f7..367751f912 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1632,6 +1632,7 @@ bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter&
// were fixed. It would be nice to reflect this in the new implementations.
return true;
case art::verifier::FailureKind::kSoftFailure:
+ case art::verifier::FailureKind::kAccessChecksFailure:
// Soft failures might require interpreter on some methods. It won't prevent redefinition but
// it does mean we need to run the verifier again and potentially update method flags after
// performing the swap.
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 6625b8dbd2..a2f450b784 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -115,6 +115,9 @@ verifier::FailureKind AotClassLinker::PerformClassVerification(Thread* self,
if (old_status >= ClassStatus::kVerified) {
return verifier::FailureKind::kNoFailure;
}
+ if (old_status >= ClassStatus::kVerifiedNeedsAccessChecks) {
+ return verifier::FailureKind::kAccessChecksFailure;
+ }
// Does it need to be verified at runtime? Report soft failure.
if (old_status >= ClassStatus::kRetryVerificationAtRuntime) {
// Error messages from here are only reported through -verbose:class. It is not worth it to
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 4b8a94f291..f24c5f48de 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4574,7 +4574,9 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
VerifyClass(self, supertype);
}
- if (supertype->IsVerified() || supertype->ShouldVerifyAtRuntime()) {
+ if (supertype->IsVerified()
+ || supertype->ShouldVerifyAtRuntime()
+ || supertype->IsVerifiedNeedsAccessChecks()) {
// The supertype is either verified, or we soft failed at AOT time.
DCHECK(supertype->IsVerified() || Runtime::Current()->IsAotCompiler());
return true;
@@ -4614,8 +4616,7 @@ verifier::FailureKind ClassLinker::VerifyClass(
// Is somebody verifying this now?
ClassStatus old_status = klass->GetStatus();
- while (old_status == ClassStatus::kVerifying ||
- old_status == ClassStatus::kVerifyingAtRuntime) {
+ while (old_status == ClassStatus::kVerifying) {
lock.WaitIgnoringInterrupts();
// WaitIgnoringInterrupts can still receive an interrupt and return early, in this
// case we may see the same status again. b/62912904. This is why the check is
@@ -4640,20 +4641,25 @@ verifier::FailureKind ClassLinker::VerifyClass(
return verifier::FailureKind::kNoFailure;
}
+ if (klass->IsVerifiedNeedsAccessChecks()) {
+ if (!Runtime::Current()->IsAotCompiler()) {
+ // Mark the class as having a verification attempt to avoid re-running
+ // the verifier and avoid calling EnsureSkipAccessChecksMethods.
+ klass->SetVerificationAttempted();
+ mirror::Class::SetStatus(klass, ClassStatus::kVerified, self);
+ }
+ return verifier::FailureKind::kAccessChecksFailure;
+ }
+
// For AOT, don't attempt to re-verify if we have already found we should
// verify at runtime.
- if (Runtime::Current()->IsAotCompiler() && klass->ShouldVerifyAtRuntime()) {
+ if (klass->ShouldVerifyAtRuntime()) {
+ CHECK(Runtime::Current()->IsAotCompiler());
return verifier::FailureKind::kSoftFailure;
}
- if (klass->GetStatus() == ClassStatus::kResolved) {
- mirror::Class::SetStatus(klass, ClassStatus::kVerifying, self);
- } else {
- CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime)
- << klass->PrettyClass();
- CHECK(!Runtime::Current()->IsAotCompiler());
- mirror::Class::SetStatus(klass, ClassStatus::kVerifyingAtRuntime, self);
- }
+ DCHECK_EQ(klass->GetStatus(), ClassStatus::kResolved);
+ mirror::Class::SetStatus(klass, ClassStatus::kVerifying, self);
// Skip verification if disabled.
if (!Runtime::Current()->IsVerificationEnabled()) {
@@ -4726,7 +4732,8 @@ verifier::FailureKind ClassLinker::VerifyClass(
<< klass->PrettyDescriptor()
<< " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
<< ": "
- << preverified;
+ << preverified
+ << "( " << oat_file_class_status << ")";
// If the oat file says the class had an error, re-run the verifier. That way we will get a
// precise error message. To ensure a rerun, test:
@@ -4755,21 +4762,29 @@ verifier::FailureKind ClassLinker::VerifyClass(
if (verifier_failure == verifier::FailureKind::kNoFailure) {
// Even though there were no verifier failures we need to respect whether the super-class and
// super-default-interfaces were verified or requiring runtime reverification.
- if (supertype == nullptr || supertype->IsVerified()) {
+ if (supertype == nullptr
+ || supertype->IsVerified()
+ || supertype->IsVerifiedNeedsAccessChecks()) {
mirror::Class::SetStatus(klass, ClassStatus::kVerified, self);
} else {
+ CHECK(Runtime::Current()->IsAotCompiler());
CHECK_EQ(supertype->GetStatus(), ClassStatus::kRetryVerificationAtRuntime);
mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self);
// Pretend a soft failure occurred so that we don't consider the class verified below.
verifier_failure = verifier::FailureKind::kSoftFailure;
}
} else {
- CHECK_EQ(verifier_failure, verifier::FailureKind::kSoftFailure);
+ CHECK(verifier_failure == verifier::FailureKind::kSoftFailure ||
+ verifier_failure == verifier::FailureKind::kAccessChecksFailure);
// Soft failures at compile time should be retried at runtime. Soft
// failures at runtime will be handled by slow paths in the generated
// code. Set status accordingly.
if (Runtime::Current()->IsAotCompiler()) {
- mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self);
+ if (verifier_failure == verifier::FailureKind::kSoftFailure) {
+ mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self);
+ } else {
+ mirror::Class::SetStatus(klass, ClassStatus::kVerifiedNeedsAccessChecks, self);
+ }
} else {
mirror::Class::SetStatus(klass, ClassStatus::kVerified, self);
// As this is a fake verified status, make sure the methods are _not_ marked
@@ -4786,18 +4801,18 @@ verifier::FailureKind ClassLinker::VerifyClass(
mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self);
}
if (preverified || verifier_failure == verifier::FailureKind::kNoFailure) {
- // Class is verified so we don't need to do any access check on its methods.
- // Let the interpreter know it by setting the kAccSkipAccessChecks flag onto each
- // method.
- // Note: we're going here during compilation and at runtime. When we set the
- // kAccSkipAccessChecks flag when compiling image classes, the flag is recorded
- // in the image and is set when loading the image.
-
- if (UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) {
+ if (oat_file_class_status == ClassStatus::kVerifiedNeedsAccessChecks ||
+ UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) {
// Never skip access checks if the verification soft fail is forced.
// Mark the class as having a verification attempt to avoid re-running the verifier.
klass->SetVerificationAttempted();
} else {
+ // Class is verified so we don't need to do any access check on its methods.
+ // Let the interpreter know it by setting the kAccSkipAccessChecks flag onto each
+ // method.
+ // Note: we're going here during compilation and at runtime. When we set the
+ // kAccSkipAccessChecks flag when compiling image classes, the flag is recorded
+ // in the image and is set when loading the image.
EnsureSkipAccessChecksMethods(klass, image_pointer_size_);
}
}
@@ -4850,31 +4865,20 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
if (oat_file_class_status >= ClassStatus::kVerified) {
return true;
}
+ if (oat_file_class_status >= ClassStatus::kVerifiedNeedsAccessChecks) {
+ // We return that the clas has already been verified, and the caller should
+ // check the class status to ensure we run with access checks.
+ return true;
+ }
// If we only verified a subset of the classes at compile time, we can end up with classes that
// were resolved by the verifier.
if (oat_file_class_status == ClassStatus::kResolved) {
return false;
}
- if (oat_file_class_status == ClassStatus::kRetryVerificationAtRuntime) {
- // Compile time verification failed with a soft error. Compile time verification can fail
- // because we have incomplete type information. Consider the following:
- // class ... {
- // Foo x;
- // .... () {
- // if (...) {
- // v1 gets assigned a type of resolved class Foo
- // } else {
- // v1 gets assigned a type of unresolved class Bar
- // }
- // iput x = v1
- // } }
- // when we merge v1 following the if-the-else it results in Conflict
- // (see verifier::RegType::Merge) as we can't know the type of Bar and we could possibly be
- // allowing an unsafe assignment to the field x in the iput (javac may have compiled this as
- // it knew Bar was a sub-class of Foo, but for us this may have been moved into a separate apk
- // at compile time).
- return false;
- }
+ // We never expect a .oat file to have kRetryVerificationAtRuntime statuses.
+ CHECK_NE(oat_file_class_status, ClassStatus::kRetryVerificationAtRuntime)
+ << klass->PrettyClass() << " " << dex_file.GetLocation();
+
if (mirror::Class::IsErroneous(oat_file_class_status)) {
// Compile time verification failed with a hard error. This is caused by invalid instructions
// in the class. These errors are unrecoverable.
@@ -5342,7 +5346,7 @@ bool ClassLinker::InitializeClass(Thread* self,
VlogClassInitializationFailure(klass);
} else {
CHECK(Runtime::Current()->IsAotCompiler());
- CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime);
+ CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerifiedNeedsAccessChecks());
self->AssertNoPendingException();
self->SetException(Runtime::Current()->GetPreAllocatedNoClassDefFoundError());
}
diff --git a/runtime/class_status.h b/runtime/class_status.h
index de64ab7617..b194ffa861 100644
--- a/runtime/class_status.h
+++ b/runtime/class_status.h
@@ -68,7 +68,15 @@ namespace art {
// this state if it encounters a soft failure at compile time. This
// often happens when there are unresolved classes in other dex
// files, and this status marks a class as needing to be verified
-// again at runtime.
+// again at runtime. This status is only set and seen during AOT
+// compilation, and the compiler will mark the class as resolved in the
+// image and/or oat file.
+//
+// kVerifiedNeedsAccessChecks: The verifier sets a class to
+// this state if it encounters access-checks only soft failure at compile
+// time. This happens when there are unresolved classes in other dex
+// files, and this status marks a class as verified but that will need to run
+// with access checks enabled in the interpreter.
//
// TODO: Explain the other states
enum class ClassStatus : uint8_t {
@@ -82,7 +90,7 @@ enum class ClassStatus : uint8_t {
kResolved = 7, // Part of linking.
kVerifying = 8, // In the process of being verified.
kRetryVerificationAtRuntime = 9, // Compile time verification failed, retry at runtime.
- kVerifyingAtRuntime = 10, // Retrying verification at runtime.
+ kVerifiedNeedsAccessChecks = 10, // Compile time verification only failed for access checks.
kVerified = 11, // Logically part of linking; done pre-init.
kSuperclassValidated = 12, // Superclass validation part of init done.
kInitializing = 13, // Class init in progress.
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 33ce785a65..6915eb052b 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -218,9 +218,7 @@ static void CheckSetStatus(Thread* self, T thiz, ClassStatus new_status, ClassSt
}
}
-void Class::SetStatusLocked(ClassStatus new_status) {
- ClassStatus old_status = GetStatus();
- CheckSetStatus(Thread::Current(), this, new_status, old_status);
+void Class::SetStatusInternal(ClassStatus new_status) {
if (kBitstringSubtypeCheckEnabled) {
// FIXME: This looks broken with respect to aborted transactions.
SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(this, new_status);
@@ -228,11 +226,20 @@ void Class::SetStatusLocked(ClassStatus new_status) {
// The ClassStatus is always in the 4 most-significant bits of status_.
static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32");
uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize);
- DCHECK(!Runtime::Current()->IsActiveTransaction());
- SetField32Volatile<false>(StatusOffset(), new_status_value);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetField32Volatile<true>(StatusOffset(), new_status_value);
+ } else {
+ SetField32Volatile<false>(StatusOffset(), new_status_value);
+ }
}
}
+void Class::SetStatusLocked(ClassStatus new_status) {
+ ClassStatus old_status = GetStatus();
+ CheckSetStatus(Thread::Current(), this, new_status, old_status);
+ SetStatusInternal(new_status);
+}
+
void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self) {
ClassStatus old_status = h_this->GetStatus();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -263,20 +270,7 @@ void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self
self->AssertPendingException();
}
- if (kBitstringSubtypeCheckEnabled) {
- // FIXME: This looks broken with respect to aborted transactions.
- ObjPtr<mirror::Class> h_this_ptr = h_this.Get();
- SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(h_this_ptr, new_status);
- } else {
- // The ClassStatus is always in the 4 most-significant bits of status_.
- static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32");
- uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize);
- if (Runtime::Current()->IsActiveTransaction()) {
- h_this->SetField32Volatile<true>(StatusOffset(), new_status_value);
- } else {
- h_this->SetField32Volatile<false>(StatusOffset(), new_status_value);
- }
- }
+ h_this->SetStatusInternal(new_status);
// Setting the object size alloc fast path needs to be after the status write so that if the
// alloc path sees a valid object size, we would know that it's initialized as long as it has a
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 9b6390e02d..11a5ce528c 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -45,6 +45,10 @@ namespace hiddenapi {
class AccessContext;
} // namespace hiddenapi
+namespace linker {
+class ImageWriter;
+} // namespace linker
+
template<typename T> class ArraySlice;
class ArtField;
class ArtMethod;
@@ -169,6 +173,13 @@ class MANAGED Class final : public Object {
return GetStatus<kVerifyFlags>() == ClassStatus::kRetryVerificationAtRuntime;
}
+ // Returns true if the class has been verified at compile time, but should be
+ // executed with access checks.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsVerifiedNeedsAccessChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() >= ClassStatus::kVerifiedNeedsAccessChecks;
+ }
+
// Returns true if the class has been verified.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsVerified() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1397,6 +1408,10 @@ class MANAGED Class final : public Object {
void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Helper to set the status without any validity cheks.
+ void SetStatusInternal(ClassStatus new_status)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
// 'Class' Object Fields
// Order governed by java field ordering. See art::ClassLinker::LinkFields.
@@ -1551,6 +1566,7 @@ class MANAGED Class final : public Object {
ART_FRIEND_TEST(DexCacheTest, TestResolvedFieldAccess); // For ResolvedFieldAccessTest
friend struct art::ClassOffsets; // for verifying offset information
friend class Object; // For VisitReferences
+ friend class linker::ImageWriter; // For SetStatusInternal
DISALLOW_IMPLICIT_CONSTRUCTORS(Class);
};
diff --git a/runtime/oat.h b/runtime/oat.h
index 0645e4c566..d37927d4bc 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Add requires-image flag.
- static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '2', '\0' } };
+ // Last oat version changed reason: Change ClassStatus bits with kVerifiedNeedsAccessChecks.
+ static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '3', '\0' } };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index c4593f2d0d..59ade97aba 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -5157,6 +5157,19 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
}
}
+// Return whether the runtime knows how to execute a method without needing to
+// re-verify it at runtime (and therefore save on first use of the class). We
+// currently only support it for access checks, where the runtime will mark the
+// methods as needing access checks and have the interpreter execute with them.
+// The AOT/JIT compiled code is not affected.
+static inline bool CanRuntimeHandleVerificationFailure(uint32_t encountered_failure_types) {
+ constexpr uint32_t unresolved_mask =
+ verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS |
+ verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD |
+ verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
+ return (encountered_failure_types & (~unresolved_mask)) == 0;
+}
+
template <bool kVerifierDebug>
MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
ClassLinker* class_linker,
@@ -5219,7 +5232,11 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
LOG(INFO) << verifier.info_messages_.str();
verifier.Dump(LOG_STREAM(INFO));
}
- result.kind = FailureKind::kSoftFailure;
+ if (CanRuntimeHandleVerificationFailure(verifier.encountered_failure_types_)) {
+ result.kind = FailureKind::kAccessChecksFailure;
+ } else {
+ result.kind = FailureKind::kSoftFailure;
+ }
if (method != nullptr &&
!CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) {
set_dont_compile = true;
@@ -5512,14 +5529,18 @@ std::ostream& MethodVerifier::Fail(VerifyError error, bool pending_exc) {
case VERIFY_ERROR_FORCE_INTERPRETER:
case VERIFY_ERROR_LOCKING:
if (IsAotMode() || !can_load_classes_) {
- // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
- // class change and instantiation errors into soft verification errors so that we
- // re-verify at runtime. We may fail to find or to agree on access because of not yet
- // available class loaders, or class loaders that will differ at runtime. In these cases,
- // we don't want to affect the soundness of the code being compiled. Instead, the
- // generated code runs "slow paths" that dynamically perform the verification and cause
- // the behavior to be that akin to an interpreter.
- error = VERIFY_ERROR_BAD_CLASS_SOFT;
+ if (error != VERIFY_ERROR_ACCESS_CLASS &&
+ error != VERIFY_ERROR_ACCESS_FIELD &&
+ error != VERIFY_ERROR_ACCESS_METHOD) {
+ // If we're optimistically running verification at compile time, turn NO_xxx,
+ // class change and instantiation errors into soft verification errors so that we
+ // re-verify at runtime. We may fail to find or to agree on access because of not yet
+ // available class loaders, or class loaders that will differ at runtime. In these
+ // cases, we don't want to affect the soundness of the code being compiled. Instead, the
+ // generated code runs "slow paths" that dynamically perform the verification and cause
+ // the behavior to be that akin to an interpreter.
+ error = VERIFY_ERROR_BAD_CLASS_SOFT;
+ }
} else {
// If we fail again at runtime, mark that this instruction would throw and force this
// method to be executed using the interpreter with checks.
diff --git a/runtime/verifier/verifier_enums.h b/runtime/verifier/verifier_enums.h
index b445e944a9..33eca4d868 100644
--- a/runtime/verifier/verifier_enums.h
+++ b/runtime/verifier/verifier_enums.h
@@ -32,6 +32,7 @@ enum class VerifyMode : int8_t {
// The outcome of verification.
enum class FailureKind {
kNoFailure,
+ kAccessChecksFailure,
kSoftFailure,
kHardFailure,
};