diff options
| -rw-r--r-- | build/art.go | 4 | ||||
| -rw-r--r-- | compiler/oat_test.cc | 30 | ||||
| -rw-r--r-- | compiler/oat_writer.cc | 2 | ||||
| -rw-r--r-- | runtime/base/arena_allocator.cc | 2 | ||||
| -rw-r--r-- | runtime/base/mutex.cc | 5 | ||||
| -rw-r--r-- | runtime/base/mutex.h | 4 | ||||
| -rw-r--r-- | runtime/mem_map.cc | 39 | ||||
| -rw-r--r-- | runtime/mem_map.h | 28 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.cc | 62 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.h | 7 | ||||
| -rw-r--r-- | test/921-hello-failure/expected.txt | 3 | ||||
| -rw-r--r-- | test/921-hello-failure/src/Main.java | 1 | ||||
| -rw-r--r-- | test/921-hello-failure/src/Verification.java | 82 |
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"); + } +} |