summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/art.go4
-rw-r--r--compiler/oat_test.cc30
-rw-r--r--compiler/oat_writer.cc2
-rw-r--r--runtime/base/arena_allocator.cc2
-rw-r--r--runtime/base/mutex.cc5
-rw-r--r--runtime/base/mutex.h4
-rw-r--r--runtime/mem_map.cc39
-rw-r--r--runtime/mem_map.h28
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc62
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h7
-rw-r--r--test/921-hello-failure/expected.txt3
-rw-r--r--test/921-hello-failure/src/Main.java1
-rw-r--r--test/921-hello-failure/src/Verification.java82
13 files changed, 209 insertions, 60 deletions
diff --git a/build/art.go b/build/art.go
index baa6e59b55..e7f7e2121e 100644
--- a/build/art.go
+++ b/build/art.go
@@ -68,10 +68,6 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) {
asflags = append(asflags,
"-DART_USE_READ_BARRIER=1",
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
-
- // Temporarily override -fstack-protector-strong with -fstack-protector to avoid a major
- // slowdown with the read barrier config. b/26744236.
- cflags = append(cflags, "-fstack-protector")
}
if envTrue(ctx, "ART_USE_VIXL_ARM_BACKEND") {
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index d5842a8c9d..66111f6e23 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -149,11 +149,10 @@ class OatTest : public CommonCompilerTest {
File* oat_file,
const std::vector<const char*>& dex_filenames,
SafeMap<std::string, std::string>& key_value_store,
- bool verify) {
+ bool verify,
+ ProfileCompilationInfo* profile_compilation_info) {
TimingLogger timings("WriteElf", false, false);
- OatWriter oat_writer(/*compiling_boot_image*/false,
- &timings,
- /*profile_compilation_info*/nullptr);
+ OatWriter oat_writer(/*compiling_boot_image*/false, &timings, profile_compilation_info);
for (const char* dex_filename : dex_filenames) {
if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) {
return false;
@@ -264,7 +263,7 @@ class OatTest : public CommonCompilerTest {
return true;
}
- void TestDexFileInput(bool verify, bool low_4gb);
+ void TestDexFileInput(bool verify, bool low_4gb, bool use_profile);
void TestZipFileInput(bool verify);
std::unique_ptr<const InstructionSetFeatures> insn_features_;
@@ -568,7 +567,7 @@ static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>
}
}
-void OatTest::TestDexFileInput(bool verify, bool low_4gb) {
+void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) {
TimingLogger timings("OatTest::DexFileInput", false, false);
std::vector<const char*> input_filenames;
@@ -606,11 +605,14 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb) {
ScratchFile oat_file, vdex_file(oat_file, ".vdex");
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
+ std::unique_ptr<ProfileCompilationInfo>
+ profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
success = WriteElf(vdex_file.GetFile(),
oat_file.GetFile(),
input_filenames,
key_value_store,
- verify);
+ verify,
+ profile_compilation_info.get());
// In verify mode, we expect failure.
if (verify) {
@@ -654,15 +656,19 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb) {
}
TEST_F(OatTest, DexFileInputCheckOutput) {
- TestDexFileInput(false, /*low_4gb*/false);
+ TestDexFileInput(/*verify*/false, /*low_4gb*/false, /*use_profile*/false);
}
TEST_F(OatTest, DexFileInputCheckOutputLow4GB) {
- TestDexFileInput(false, /*low_4gb*/true);
+ TestDexFileInput(/*verify*/false, /*low_4gb*/true, /*use_profile*/false);
}
TEST_F(OatTest, DexFileInputCheckVerifier) {
- TestDexFileInput(true, /*low_4gb*/false);
+ TestDexFileInput(/*verify*/true, /*low_4gb*/false, /*use_profile*/false);
+}
+
+TEST_F(OatTest, DexFileFailsVerifierWithLayout) {
+ TestDexFileInput(/*verify*/true, /*low_4gb*/false, /*use_profile*/true);
}
void OatTest::TestZipFileInput(bool verify) {
@@ -717,8 +723,8 @@ void OatTest::TestZipFileInput(bool verify) {
std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4]
ScratchFile oat_file, vdex_file(oat_file, ".vdex");
- success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(),
- input_filenames, key_value_store, verify);
+ success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames,
+ key_value_store, verify, /*profile_compilation_info*/nullptr);
if (verify) {
ASSERT_FALSE(success);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index a16a34b299..41468bae59 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -2267,7 +2267,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil
dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg);
}
if (dex_file == nullptr) {
- LOG(ERROR) << "Failed to open dex file for layout:" << error_msg;
+ LOG(ERROR) << "Failed to open dex file for layout: " << error_msg;
return false;
}
Options options;
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 9fdb0cc9d0..db433194d3 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -146,7 +146,9 @@ void ArenaAllocatorStatsImpl<kCount>::Dump(std::ostream& os, const Arena* first,
}
#pragma GCC diagnostic push
+#if __clang_major__ >= 4
#pragma GCC diagnostic ignored "-Winstantiation-after-specialization"
+#endif
// Explicitly instantiate the used implementation.
template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations>;
#pragma GCC diagnostic pop
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index e05a85a116..6e102be1a1 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -49,7 +49,6 @@ Mutex* Locks::intern_table_lock_ = nullptr;
Mutex* Locks::jni_function_table_lock_ = nullptr;
Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
-Mutex* Locks::mem_maps_lock_ = nullptr;
Mutex* Locks::modify_ldt_lock_ = nullptr;
MutatorMutex* Locks::mutator_lock_ = nullptr;
Mutex* Locks::profiler_lock_ = nullptr;
@@ -1116,10 +1115,6 @@ void Locks::Init() {
DCHECK(unexpected_signal_lock_ == nullptr);
unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true);
- UPDATE_CURRENT_LOCK_LEVEL(kMemMapsLock);
- DCHECK(mem_maps_lock_ == nullptr);
- mem_maps_lock_ = new Mutex("mem maps lock", current_lock_level);
-
UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock);
DCHECK(logging_lock_ == nullptr);
logging_lock_ = new Mutex("logging lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 21dd437711..ffe18c6a50 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -55,7 +55,6 @@ class Thread;
// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
enum LockLevel {
kLoggingLock = 0,
- kMemMapsLock,
kSwapMutexesLock,
kUnexpectedSignalLock,
kThreadSuspendCountLock,
@@ -712,9 +711,6 @@ class Locks {
// One unexpected signal at a time lock.
static Mutex* unexpected_signal_lock_ ACQUIRED_AFTER(thread_suspend_count_lock_);
- // Guards the maps in mem_map.
- static Mutex* mem_maps_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
-
// Have an exclusive logging thread.
static Mutex* logging_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
};
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 19a65bb27e..dce56b3c58 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -70,6 +70,7 @@ std::ostream& operator<<(std::ostream& os, const MemMap::Maps& mem_maps) {
return os;
}
+std::mutex* MemMap::mem_maps_lock_ = nullptr;
MemMap::Maps* MemMap::maps_ = nullptr;
#if USE_ART_LOW_4G_ALLOCATOR
@@ -139,7 +140,7 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string*
// There is a suspicion that BacktraceMap::Create is occasionally missing maps. TODO: Investigate
// further.
{
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
for (auto& pair : *maps_) {
MemMap* const map = pair.second;
if (begin >= reinterpret_cast<uintptr_t>(map->Begin()) &&
@@ -490,7 +491,7 @@ MemMap::~MemMap() {
}
// Remove it from maps_.
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
bool found = false;
DCHECK(maps_ != nullptr);
for (auto it = maps_->lower_bound(base_begin_), end = maps_->end();
@@ -518,7 +519,7 @@ MemMap::MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_
CHECK_NE(base_size_, 0U);
// Add it to maps_.
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
DCHECK(maps_ != nullptr);
maps_->insert(std::make_pair(base_begin_, this));
}
@@ -637,7 +638,7 @@ bool MemMap::Protect(int prot) {
}
bool MemMap::CheckNoGaps(MemMap* begin_map, MemMap* end_map) {
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
CHECK(begin_map != nullptr);
CHECK(end_map != nullptr);
CHECK(HasMemMap(begin_map));
@@ -656,7 +657,7 @@ bool MemMap::CheckNoGaps(MemMap* begin_map, MemMap* end_map) {
}
void MemMap::DumpMaps(std::ostream& os, bool terse) {
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
DumpMapsLocked(os, terse);
}
@@ -747,17 +748,31 @@ MemMap* MemMap::GetLargestMemMapAt(void* address) {
}
void MemMap::Init() {
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
- if (maps_ == nullptr) {
+ if (mem_maps_lock_ != nullptr) {
// dex2oat calls MemMap::Init twice since its needed before the runtime is created.
- maps_ = new Maps;
+ return;
}
+ mem_maps_lock_ = new std::mutex();
+ // Not for thread safety, but for the annotation that maps_ is GUARDED_BY(mem_maps_lock_).
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+ DCHECK(maps_ == nullptr);
+ maps_ = new Maps;
}
void MemMap::Shutdown() {
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
- delete maps_;
- maps_ = nullptr;
+ if (mem_maps_lock_ == nullptr) {
+ // If MemMap::Shutdown is called more than once, there is no effect.
+ return;
+ }
+ {
+ // Not for thread safety, but for the annotation that maps_ is GUARDED_BY(mem_maps_lock_).
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+ DCHECK(maps_ != nullptr);
+ delete maps_;
+ maps_ = nullptr;
+ }
+ delete mem_maps_lock_;
+ mem_maps_lock_ = nullptr;
}
void MemMap::SetSize(size_t new_size) {
@@ -813,7 +828,7 @@ void* MemMap::MapInternal(void* addr,
if (low_4gb && addr == nullptr) {
bool first_run = true;
- MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
for (uintptr_t ptr = next_mem_pos_; ptr < 4 * GB; ptr += kPageSize) {
// Use maps_ as an optimization to skip over large maps.
// Find the first map which is address > ptr.
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 0fea1a52c9..71db3f7014 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -21,6 +21,7 @@
#include <string>
#include <map>
+#include <mutex>
#include <stddef.h>
#include <sys/mman.h> // For the PROT_* and MAP_* constants.
@@ -120,7 +121,7 @@ class MemMap {
std::string* error_msg);
// Releases the memory mapping.
- ~MemMap() REQUIRES(!Locks::mem_maps_lock_);
+ ~MemMap() REQUIRES(!MemMap::mem_maps_lock_);
const std::string& GetName() const {
return name_;
@@ -175,14 +176,17 @@ class MemMap {
bool use_ashmem = true);
static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)
- REQUIRES(!Locks::mem_maps_lock_);
+ REQUIRES(!MemMap::mem_maps_lock_);
static void DumpMaps(std::ostream& os, bool terse = false)
- REQUIRES(!Locks::mem_maps_lock_);
+ REQUIRES(!MemMap::mem_maps_lock_);
typedef AllocationTrackingMultiMap<void*, MemMap*, kAllocatorTagMaps> Maps;
- static void Init() REQUIRES(!Locks::mem_maps_lock_);
- static void Shutdown() REQUIRES(!Locks::mem_maps_lock_);
+ // Init and Shutdown are NOT thread safe.
+ // Both may be called multiple times and MemMap objects may be created any
+ // time after the first call to Init and before the first call to Shutodwn.
+ static void Init() REQUIRES(!MemMap::mem_maps_lock_);
+ static void Shutdown() REQUIRES(!MemMap::mem_maps_lock_);
// If the map is PROT_READ, try to read each page of the map to check it is in fact readable (not
// faulting). This is used to diagnose a bug b/19894268 where mprotect doesn't seem to be working
@@ -197,16 +201,16 @@ class MemMap {
size_t base_size,
int prot,
bool reuse,
- size_t redzone_size = 0) REQUIRES(!Locks::mem_maps_lock_);
+ size_t redzone_size = 0) REQUIRES(!MemMap::mem_maps_lock_);
static void DumpMapsLocked(std::ostream& os, bool terse)
- REQUIRES(Locks::mem_maps_lock_);
+ REQUIRES(MemMap::mem_maps_lock_);
static bool HasMemMap(MemMap* map)
- REQUIRES(Locks::mem_maps_lock_);
+ REQUIRES(MemMap::mem_maps_lock_);
static MemMap* GetLargestMemMapAt(void* address)
- REQUIRES(Locks::mem_maps_lock_);
+ REQUIRES(MemMap::mem_maps_lock_);
static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* error_msg)
- REQUIRES(!Locks::mem_maps_lock_);
+ REQUIRES(!MemMap::mem_maps_lock_);
// Internal version of mmap that supports low 4gb emulation.
static void* MapInternal(void* addr,
@@ -236,8 +240,10 @@ class MemMap {
static uintptr_t next_mem_pos_; // Next memory location to check for low_4g extent.
#endif
+ static std::mutex* mem_maps_lock_;
+
// All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()).
- static Maps* maps_ GUARDED_BY(Locks::mem_maps_lock_);
+ static Maps* maps_ GUARDED_BY(MemMap::mem_maps_lock_);
friend class MemMapTest; // To allow access to base_begin_ and base_size_.
};
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 058b93a042..59cb8763de 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -56,6 +56,8 @@
#include "ScopedLocalRef.h"
#include "ti_class_loader.h"
#include "transform.h"
+#include "verifier/method_verifier.h"
+#include "verifier/verifier_log_mode.h"
namespace openjdkjvmti {
@@ -703,7 +705,6 @@ bool Redefiner::ClassRedefinition::CheckClass() {
}
}
LOG(WARNING) << "No verification is done on annotations of redefined classes.";
- LOG(WARNING) << "Bytecodes of redefinitions are not verified.";
return true;
}
@@ -766,26 +767,28 @@ class RedefinitionDataHolder {
}
// TODO Maybe make an iterable view type to simplify using this.
- art::mirror::ClassLoader* GetSourceClassLoader(jint klass_index)
+ art::mirror::ClassLoader* GetSourceClassLoader(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::ClassLoader*>(GetSlot(klass_index, kSlotSourceClassLoader));
}
- art::mirror::Object* GetJavaDexFile(jint klass_index) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::mirror::Object* GetJavaDexFile(jint klass_index) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
return GetSlot(klass_index, kSlotJavaDexFile);
}
- art::mirror::LongArray* GetNewDexFileCookie(jint klass_index)
+ art::mirror::LongArray* GetNewDexFileCookie(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::LongArray*>(GetSlot(klass_index, kSlotNewDexFileCookie));
}
- art::mirror::DexCache* GetNewDexCache(jint klass_index)
+ art::mirror::DexCache* GetNewDexCache(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::DexCache*>(GetSlot(klass_index, kSlotNewDexCache));
}
- art::mirror::Class* GetMirrorClass(jint klass_index) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::mirror::Class* GetMirrorClass(jint klass_index) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass));
}
- art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index)
+ art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile));
}
@@ -815,15 +818,15 @@ class RedefinitionDataHolder {
SetSlot(klass_index, kSlotOrigDexFile, bytes);
}
- int32_t Length() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ int32_t Length() const REQUIRES_SHARED(art::Locks::mutator_lock_) {
return arr_->GetLength() / kNumSlots;
}
private:
- art::Handle<art::mirror::ObjectArray<art::mirror::Object>> arr_;
+ mutable art::Handle<art::mirror::ObjectArray<art::mirror::Object>> arr_;
art::mirror::Object* GetSlot(jint klass_index,
- DataSlot slot) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ DataSlot slot) const REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_LT(klass_index, Length());
return arr_->Get((kNumSlots * klass_index) + slot);
}
@@ -839,6 +842,31 @@ class RedefinitionDataHolder {
DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder);
};
+// TODO Stash and update soft failure state
+bool Redefiner::ClassRedefinition::CheckVerification(int32_t klass_index,
+ const RedefinitionDataHolder& holder) {
+ DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+ art::StackHandleScope<2> hs(driver_->self_);
+ std::string error;
+ // TODO Make verification log level lower
+ art::verifier::MethodVerifier::FailureKind failure =
+ art::verifier::MethodVerifier::VerifyClass(driver_->self_,
+ dex_file_.get(),
+ hs.NewHandle(holder.GetNewDexCache(klass_index)),
+ hs.NewHandle(GetClassLoader()),
+ dex_file_->GetClassDef(0), /*class_def*/
+ nullptr, /*compiler_callbacks*/
+ false, /*allow_soft_failures*/
+ /*log_level*/
+ art::verifier::HardFailLogMode::kLogWarning,
+ &error);
+ bool passes = failure == art::verifier::MethodVerifier::kNoFailure;
+ if (!passes) {
+ RecordFailure(ERR(FAILS_VERIFICATION), "Failed to verify class. Error was: " + error);
+ }
+ return passes;
+}
+
// Looks through the previously allocated cookies to see if we need to update them with another new
// dexfile. This is so that even if multiple classes with the same classloader are redefined at
// once they are all added to the classloader.
@@ -978,6 +1006,17 @@ void Redefiner::ReleaseAllDexFiles() {
}
}
+bool Redefiner::CheckAllClassesAreVerified(const RedefinitionDataHolder& holder) {
+ int32_t cnt = 0;
+ for (Redefiner::ClassRedefinition& redef : redefinitions_) {
+ if (!redef.CheckVerification(cnt, holder)) {
+ return false;
+ }
+ cnt++;
+ }
+ return true;
+}
+
jvmtiError Redefiner::Run() {
art::StackHandleScope<1> hs(self_);
// Allocate an array to hold onto all java temporary objects associated with this redefinition.
@@ -997,7 +1036,8 @@ jvmtiError Redefiner::Run() {
// try loop.
if (!CheckAllRedefinitionAreValid() ||
!EnsureAllClassAllocationsFinished() ||
- !FinishAllRemainingAllocations(holder)) {
+ !FinishAllRemainingAllocations(holder) ||
+ !CheckAllClassesAreVerified(holder)) {
// TODO Null out the ClassExt fields we allocated (if possible, might be racing with another
// redefineclass call which made it even bigger. Leak shouldn't be huge (2x array of size
// declared_methods_.length) but would be good to get rid of. All other allocations should be
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 3209abbe64..421d22ef4c 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -165,6 +165,11 @@ class Redefiner {
// data has not been modified in an incompatible manner.
bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // Checks that the contained class can be successfully verified.
+ bool CheckVerification(int32_t klass_index,
+ const RedefinitionDataHolder& holder)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
// Preallocates all needed allocations in klass so that we can pause execution safely.
// TODO We should be able to free the arrays if they end up not being used. Investigate doing
// this in the future. For now we will just take the memory hit.
@@ -239,6 +244,8 @@ class Redefiner {
jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_);
bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool CheckAllClassesAreVerified(const RedefinitionDataHolder& holder)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
bool EnsureAllClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/test/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt
index e9b6a20cd6..a5dc10d59c 100644
--- a/test/921-hello-failure/expected.txt
+++ b/test/921-hello-failure/expected.txt
@@ -1,3 +1,6 @@
+hello - Verification
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_FAILS_VERIFICATION)
+hello - Verification
hello - NewName
Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
hello - NewName
diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java
index 61d69e7396..5bbe2b5479 100644
--- a/test/921-hello-failure/src/Main.java
+++ b/test/921-hello-failure/src/Main.java
@@ -18,6 +18,7 @@ import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
+ Verification.doTest(new Transform());
NewName.doTest(new Transform());
DifferentAccess.doTest(new Transform());
NewInterface.doTest(new Transform2());
diff --git a/test/921-hello-failure/src/Verification.java b/test/921-hello-failure/src/Verification.java
new file mode 100644
index 0000000000..242b5d2b44
--- /dev/null
+++ b/test/921-hello-failure/src/Verification.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class Verification {
+ // Jasmin program:
+ //
+ // .source Transform.java
+ // .class Transform
+ // .super java/lang/Object
+ // .method <init>()V
+ // .limit stack 1
+ // .limit locals 1
+ // aload_0
+ // invokespecial java/lang/Object/<init>()V
+ // return
+ // .end method
+ // .method sayHi(Ljava/lang/String;)V
+ // .limit stack 1
+ // .limit locals 2
+ // aload_1
+ // areturn
+ // .end method
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgADAC0ADgoADQAHBwAIAQAQamF2YS9sYW5nL09iamVjdAEAClNvdXJjZUZpbGUBAAY8aW5p" +
+ "dD4BAAVzYXlIaQwABQAKAQAJVHJhbnNmb3JtAQAEQ29kZQEAAygpVgEADlRyYW5zZm9ybS5qYXZh" +
+ "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWBwADACAAAgANAAAAAAACAAAABQAKAAEACQAAABEAAQAB" +
+ "AAAABSq3AAGxAAAAAAABAAYADAABAAkAAAAOAAEAAgAAAAIrsAAAAAAAAQAEAAAAAgAL");
+
+ // Smali program:
+ //
+ // .class LTransform;
+ // .super Ljava/lang/Object;
+ // .source "Transform.java"
+ // # direct methods
+ // .method constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ // # virtual methods
+ // .method public sayHi(Ljava/lang/String;)V
+ // .registers 2
+ // return-object p1
+ // .end method
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQClOAc4ZDMXaHMezhYcqZxcjUeVCWRYUkooAgAAcAAAAHhWNBIAAAAAAAAAAJQBAAAI" +
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAA4AQAA8AAAAPAA" +
+ "AAD4AAAABQEAABkBAAAtAQAAPQEAAEABAABEAQAAAQAAAAIAAAADAAAABQAAAAUAAAADAAAAAAAA" +
+ "AAYAAAADAAAATAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAA" +
+ "AAAAAIYBAAAAAAAABjxpbml0PgALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGph" +
+ "dmEvbGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgACVkwABXNheUhpAAABAAAAAgAAAAAA" +
+ "AAAAAAAAAQABAAEAAAAAAAAABAAAAHAQAgAAAA4AAgACAAAAAAAAAAAAAQAAABEBAAABAQCAgATc" +
+ "AgEB9AIMAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAAAwAAAAIAAACgAAAA" +
+ "BQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABMAQAAAxAAAAIAAABU" +
+ "AQAAASAAAAIAAABcAQAAACAAAAEAAACGAQAAABAAAAEAAACUAQAA");
+
+ public static void doTest(Transform t) {
+ t.sayHi("Verification");
+ try {
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("Verification");
+ }
+}