diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common_throws.cc | 80 | ||||
-rw-r--r-- | src/common_throws.h | 5 | ||||
-rw-r--r-- | src/compiler/dex/dex_to_dex_compiler.cc | 258 | ||||
-rw-r--r-- | src/compiler/driver/compiler_driver.cc | 68 | ||||
-rw-r--r-- | src/compiler/driver/compiler_driver.h | 5 | ||||
-rw-r--r-- | src/dex_file.cc | 22 | ||||
-rw-r--r-- | src/dex_file.h | 17 | ||||
-rw-r--r-- | src/dex_instruction.cc | 36 | ||||
-rw-r--r-- | src/dex_instruction.h | 24 | ||||
-rw-r--r-- | src/dex_instruction_list.h | 16 | ||||
-rw-r--r-- | src/interpreter/interpreter.cc | 204 | ||||
-rw-r--r-- | src/mem_map.cc | 22 | ||||
-rw-r--r-- | src/mem_map.h | 2 | ||||
-rw-r--r-- | src/verifier/method_verifier.cc | 420 | ||||
-rw-r--r-- | src/verifier/method_verifier.h | 41 |
15 files changed, 1172 insertions, 48 deletions
diff --git a/src/common_throws.cc b/src/common_throws.cc index dc3627a0b2..1e114bb6dd 100644 --- a/src/common_throws.cc +++ b/src/common_throws.cc @@ -27,6 +27,7 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "thread.h" +#include "verifier/method_verifier.h" #include <sstream> @@ -283,16 +284,34 @@ void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); } -void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, - InvokeType type) { - mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); - const DexFile& dex_file = *dex_cache->GetDexFile(); +static void ThrowNullPointerExceptionForMethodAccessImpl(const ThrowLocation& throw_location, + uint32_t method_idx, + const DexFile& dex_file, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::ostringstream msg; msg << "Attempt to invoke " << type << " method '" << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference"; ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); } +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, + InvokeType type) { + mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method_idx, + dex_file, type); +} + +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + mirror::AbstractMethod* method, + InvokeType type) { + mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method->GetDexMethodIndex(), + dex_file, type); +} + void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem(); uint32_t throw_dex_pc = throw_location.GetDexPc(); @@ -317,6 +336,23 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { case Instruction::INVOKE_INTERFACE_RANGE: ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kInterface); break; + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + // Since we replaced the method index, we ask the verifier to tell us which + // method is invoked at this location. + mirror::AbstractMethod* method = + verifier::MethodVerifier::FindInvokedMethodAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (method != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForMethodAccess(throw_location, method, kVirtual); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to invoke a virtual method on a null object reference"); + } + break; + } case Instruction::IGET: case Instruction::IGET_WIDE: case Instruction::IGET_OBJECT: @@ -330,6 +366,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); break; } + case Instruction::IGET_QUICK: + case Instruction::IGET_WIDE_QUICK: + case Instruction::IGET_OBJECT_QUICK: { + // Since we replaced the field index, we ask the verifier to tell us which + // field is accessed at this location. + mirror::Field* field = + verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (field != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to read from a field on a null object reference"); + } + break; + } case Instruction::IPUT: case Instruction::IPUT_WIDE: case Instruction::IPUT_OBJECT: @@ -343,6 +397,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); break; } + case Instruction::IPUT_QUICK: + case Instruction::IPUT_WIDE_QUICK: + case Instruction::IPUT_OBJECT_QUICK: { + // Since we replaced the field index, we ask the verifier to tell us which + // field is accessed at this location. + mirror::Field* field = + verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (field != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to write to a field on a null object reference"); + } + break; + } case Instruction::AGET: case Instruction::AGET_WIDE: case Instruction::AGET_OBJECT: diff --git a/src/common_throws.h b/src/common_throws.h index 5555435051..00d89f45f6 100644 --- a/src/common_throws.h +++ b/src/common_throws.h @@ -153,6 +153,11 @@ void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_locatio InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + mirror::AbstractMethod* method, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc new file mode 100644 index 0000000000..afb29f4163 --- /dev/null +++ b/src/compiler/dex/dex_to_dex_compiler.cc @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2011 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. + */ + +#include "base/logging.h" +#include "base/mutex.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler/driver/dex_compilation_unit.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/field-inl.h" + +namespace art { +namespace optimizer { + +// Controls quickening activation. +const bool kEnableQuickening = true; +// Controls logging. +const bool kEnableLogging = false; + +class DexCompiler { + public: + DexCompiler(art::CompilerDriver& compiler, + const DexCompilationUnit& unit) + : driver_(compiler), + unit_(unit) {}; + + ~DexCompiler() {}; + + void Compile(); + + private: + const DexFile& GetDexFile() const { + return *unit_.GetDexFile(); + } + + // TODO: since the whole compilation pipeline uses a "const DexFile", we need + // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile. + DexFile& GetModifiableDexFile() { + return *const_cast<DexFile*>(unit_.GetDexFile()); + } + + void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_put); + void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_range); + + CompilerDriver& driver_; + const DexCompilationUnit& unit_; + + DISALLOW_COPY_AND_ASSIGN(DexCompiler); +}; + +// Ensures write access to a part of DEX file. +// +// If a DEX file is read-only, it modifies its protection (mprotect) so it allows +// write access to the part of DEX file defined by an address and a length. +// In this case, it also takes the DexFile::modification_lock to prevent from +// concurrent protection modification from a parallel DEX-to-DEX compilation on +// the same DEX file. +// When the instance is destroyed, it recovers original protection and releases +// the lock. +// TODO: as this scoped class is similar to a MutexLock we should use annotalysis +// to capture the locking behavior. +class ScopedDexWriteAccess { + public: + ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst, + size_t length) + : dex_file_(dex_file), + address_(reinterpret_cast<uint8_t*>(inst)), + length_(length), + is_read_only_(dex_file_.IsReadOnly()) { + if (is_read_only_) { + // We need to enable DEX write access. To avoid concurrent DEX write access + // modification, we take the DexFile::modification_lock before. + dex_file_.GetModificationLock().ExclusiveLock(Thread::Current()); + bool success = dex_file_.EnableWrite(address_, length_); + DCHECK(success) << "Failed to enable DEX write access"; + } + } + + ~ScopedDexWriteAccess() { + DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly()); + if (is_read_only_) { + bool success = dex_file_.DisableWrite(address_, length_); + DCHECK(success) << "Failed to disable DEX write access"; + // Now we recovered original read-only protection, we can release the + // DexFile::modification_lock. + dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current()); + } + } + + private: + DexFile& dex_file_; + // TODO: make address_ const. + uint8_t* address_; + const size_t length_; + const bool is_read_only_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess); +}; + +void DexCompiler::Compile() { + const DexFile::CodeItem* code_item = unit_.GetCodeItem(); + const uint16_t* insns = code_item->insns_; + const uint32_t insns_size = code_item->insns_size_in_code_units_; + Instruction* inst = const_cast<Instruction*>(Instruction::At(insns)); + + for (uint32_t dex_pc = 0; dex_pc < insns_size; + inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) { + switch (inst->Opcode()) { + case Instruction::IGET: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false); + break; + case Instruction::IGET_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false); + break; + + case Instruction::IGET_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false); + break; + + case Instruction::IPUT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: + // These opcodes have the same implementation in interpreter so group + // them under IPUT_QUICK. + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true); + break; + + case Instruction::IPUT_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true); + break; + + case Instruction::IPUT_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true); + break; + + case Instruction::INVOKE_VIRTUAL: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false); + break; + + case Instruction::INVOKE_VIRTUAL_RANGE: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true); + break; + + default: + // No optimization. + break; + } + } +} + +void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_put) { + if (!kEnableQuickening) { + return; + } + uint32_t field_idx = inst->VRegC_22c(); + int field_offset; + bool is_volatile; + bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset, + is_volatile, is_put); + if (fast_path && !is_volatile && IsUint(16, field_offset)) { + // TODO: use VLOG ? + if (kEnableLogging) { + LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode()) + << " to " << Instruction::Name(new_opcode) + << " by replacing field index " << field_idx + << " by field offset " << field_offset + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + } + // We are modifying 4 consecutive bytes. + ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u); + inst->SetOpcode(new_opcode); + // Replace field index by field offset. + inst->SetVRegC_22c(static_cast<uint16_t>(field_offset)); + } +} + +void DexCompiler::CompileInvokeVirtual(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_range) { + if (!kEnableQuickening) { + return; + } + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + CompilerDriver::MethodReference target_method(&GetDexFile(), method_idx); + InvokeType invoke_type = kVirtual; + InvokeType original_invoke_type = invoke_type; + int vtable_idx; + uintptr_t direct_code; + uintptr_t direct_method; + bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type, + target_method, vtable_idx, + direct_code, direct_method, + false); + // TODO: support devirtualization. + if (fast_path && original_invoke_type == invoke_type) { + if (vtable_idx >= 0 && IsUint(16, vtable_idx)) { + // TODO: use VLOG ? + if (kEnableLogging) { + LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode()) + << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")" + << " to " << Instruction::Name(new_opcode) + << " by replacing method index " << method_idx + << " by vtable index " << vtable_idx + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + } + // We are modifying 4 consecutive bytes. + ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u); + inst->SetOpcode(new_opcode); + // Replace method index by vtable index. + if (is_range) { + inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx)); + } else { + inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx)); + } + } + } +} + +} // namespace optimizer +} // namespace art + +extern "C" art::CompiledMethod* + ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, + uint32_t access_flags, art::InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + const art::DexFile& dex_file) { + art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), + dex_file, code_item, class_def_idx, method_idx, access_flags); + art::optimizer::DexCompiler dex_compiler(compiler, unit); + dex_compiler.Compile(); + return NULL; +} diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 6050108d48..4a6eb962e3 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -372,6 +372,8 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtQuickCompileMethod"); } + dex_to_dex_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtCompileDEX"); + init_compiler_context(*this); if (compiler_backend_ == kPortable) { @@ -531,10 +533,33 @@ void CompilerDriver::CompileAll(jobject class_loader, } } +static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the + // verifier from passing on "quick" instruction at compilation time. It must + // only pass on quick instructions at runtime. + if (class_loader == NULL) { + return false; + } + const char* descriptor = dex_file.GetClassDescriptor(class_def); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); + if (klass == NULL) { + Thread* self = Thread::Current(); + CHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + // DEX-to-DEX compilation is only allowed on preverified classes. + return klass->IsVerified(); +} + void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { DCHECK(!Runtime::Current()->IsStarted()); Thread* self = Thread::Current(); - jobject class_loader; + jobject jclass_loader; const DexFile* dex_file; uint32_t class_def_idx; { @@ -542,7 +567,7 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { ScopedLocalRef<jobject> local_class_loader(soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader())); - class_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); + jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); // Find the dex_file MethodHelper mh(method); dex_file = &mh.GetDexFile(); @@ -555,14 +580,22 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { UniquePtr<ThreadPool> thread_pool(new ThreadPool(1U)); TimingLogger timings("CompileOne", false); - PreCompile(class_loader, dex_files, *thread_pool.get(), timings); + PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings); uint32_t method_idx = method->GetDexMethodIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); + // Can we run DEX-to-DEX compiler on this class ? + bool allow_dex_compilation; + { + ScopedObjectAccess soa(Thread::Current()); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def); + } CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(), - class_def_idx, method_idx, class_loader, *dex_file); + class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation); - self->GetJniEnv()->DeleteGlobalRef(class_loader); + self->GetJniEnv()->DeleteGlobalRef(jclass_loader); self->TransitionFromSuspendedToRunnable(); } @@ -2015,12 +2048,12 @@ void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFi } void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) { - jobject class_loader = manager->GetClassLoader(); + jobject jclass_loader = manager->GetClassLoader(); const DexFile& dex_file = *manager->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); { ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); if (SkipClass(class_loader, dex_file, class_def)) { return; } @@ -2035,6 +2068,13 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz // empty class, probably a marker interface return; } + // Can we run DEX-to-DEX compiler on this class ? + bool allow_dex_compilation; + { + ScopedObjectAccess soa(Thread::Current()); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def); + } ClassDataItemIterator it(dex_file, class_data); // Skip fields while (it.HasNextStaticField()) { @@ -2056,7 +2096,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_direct_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, class_loader, dex_file); + method_idx, jclass_loader, dex_file, allow_dex_compilation); it.Next(); } // Compile virtual methods @@ -2072,7 +2112,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_virtual_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, class_loader, dex_file); + method_idx, jclass_loader, dex_file, allow_dex_compilation); it.Next(); } DCHECK(!it.HasNext()); @@ -2088,7 +2128,8 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, - const DexFile& dex_file) { + const DexFile& dex_file, + bool allow_dex_to_dex_compilation) { CompiledMethod* compiled_method = NULL; uint64_t start_ns = NanoTime(); @@ -2113,6 +2154,13 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); + } else if (allow_dex_to_dex_compilation) { + // TODO: add a mode to disable DEX-to-DEX compilation ? + compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags, + invoke_type, class_def_idx, + method_idx, class_loader, dex_file); + // No native code is generated. + CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file); } } uint64_t duration_ns = NanoTime() - start_ns; diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index fbfcadb8dd..fdd2149f7f 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -349,7 +349,8 @@ class CompilerDriver { LOCKS_EXCLUDED(Locks::mutator_lock_); void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, - jobject class_loader, const DexFile& dex_file) + jobject class_loader, const DexFile& dex_file, + bool allow_dex_to_dex_compilation) LOCKS_EXCLUDED(compiled_methods_lock_); static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index) @@ -404,6 +405,8 @@ class CompilerDriver { jobject class_loader, const DexFile& dex_file); CompilerFn compiler_; + CompilerFn dex_to_dex_compiler_; + void* compiler_context_; typedef CompiledMethod* (*JniCompilerFn)(CompilerDriver& driver, diff --git a/src/dex_file.cc b/src/dex_file.cc index dad083cc3f..80465f2fed 100644 --- a/src/dex_file.cc +++ b/src/dex_file.cc @@ -108,6 +108,28 @@ int DexFile::GetPermissions() const { } } +bool DexFile::IsReadOnly() const { + return GetPermissions() == PROT_READ; +} + +bool DexFile::EnableWrite(uint8_t* addr, size_t length) const { + CHECK(IsReadOnly()); + if (mem_map_.get() == NULL) { + return false; + } else { + return mem_map_->ProtectRegion(addr, length, PROT_READ | PROT_WRITE); + } +} + +bool DexFile::DisableWrite(uint8_t* addr, size_t length) const { + CHECK(!IsReadOnly()); + if (mem_map_.get() == NULL) { + return false; + } else { + return mem_map_->ProtectRegion(addr, length, PROT_READ); + } +} + const DexFile* DexFile::OpenFile(const std::string& filename, const std::string& location, bool verify) { diff --git a/src/dex_file.h b/src/dex_file.h index ecc985fb3a..e09270e018 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -21,6 +21,7 @@ #include <vector> #include "base/logging.h" +#include "base/mutex.h" #include "base/stringpiece.h" #include "globals.h" #include "invoke_type.h" @@ -384,6 +385,10 @@ class DexFile { return *header_; } + Mutex& GetModificationLock() { + return modification_lock; + } + // Decode the dex magic version uint32_t GetVersion() const; @@ -798,6 +803,12 @@ class DexFile { int GetPermissions() const; + bool IsReadOnly() const; + + bool EnableWrite(uint8_t* addr, size_t size) const; + + bool DisableWrite(uint8_t* addr, size_t size) const; + private: // Opens a .dex file static const DexFile* OpenFile(const std::string& filename, @@ -830,6 +841,7 @@ class DexFile { location_checksum_(location_checksum), mem_map_(mem_map), dex_object_(NULL), + modification_lock("DEX modification lock"), header_(0), string_ids_(0), type_ids_(0), @@ -890,6 +902,11 @@ class DexFile { // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar. mutable jobject dex_object_; + // The DEX-to-DEX compiler uses this lock to ensure thread safety when + // enabling write access to a read-only DEX file. + // TODO: move to Locks::dex_file_modification_lock. + Mutex modification_lock; + // Points to the header section. const Header* header_; diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc index b18b4d0226..e00272796c 100644 --- a/src/dex_instruction.cc +++ b/src/dex_instruction.cc @@ -354,6 +354,14 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through + case IGET_QUICK: + case IGET_OBJECT_QUICK: + if (file != NULL) { + uint32_t field_idx = VRegC_22c(); + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " + << "// offset@" << field_idx; + break; + } // else fall-through case IPUT: case IPUT_WIDE: case IPUT_OBJECT: @@ -367,6 +375,14 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through + case IPUT_QUICK: + case IPUT_OBJECT_QUICK: + if (file != NULL) { + uint32_t field_idx = VRegC_22c(); + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " + << "// offset@" << field_idx; + break; + } // else fall-through case INSTANCE_OF: if (file != NULL) { uint32_t type_idx = VRegC_22c(); @@ -413,6 +429,19 @@ std::string Instruction::DumpString(const DexFile* file) const { os << "}, " << PrettyMethod(method_idx, *file) << " // method@" << method_idx; break; } // else fall-through + case INVOKE_VIRTUAL_QUICK: + if (file != NULL) { + os << opcode << " {"; + uint32_t method_idx = VRegB_35c(); + for (size_t i = 0; i < VRegA_35c(); ++i) { + if (i != 0) { + os << ", "; + } + os << "v" << arg[i]; + } + os << "}, // vtable@" << method_idx; + break; + } // else fall-through default: os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2] << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c(); @@ -433,6 +462,13 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyMethod(method_idx, *file) << " // method@" << method_idx; break; } // else fall-through + case INVOKE_VIRTUAL_RANGE_QUICK: + if (file != NULL) { + uint32_t method_idx = VRegB_3rc(); + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1)) + << "// vtable@" << method_idx; + break; + } // else fall-through default: os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc()); diff --git a/src/dex_instruction.h b/src/dex_instruction.h index adaada7f87..8a49216e4d 100644 --- a/src/dex_instruction.h +++ b/src/dex_instruction.h @@ -278,6 +278,30 @@ class Instruction { return static_cast<Code>(opcode); } + void SetOpcode(Code opcode) { + DCHECK_LT(static_cast<uint16_t>(opcode), 256u); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode); + } + + void SetVRegB_3rc(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k3rc); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; + } + + void SetVRegB_35c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k35c); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; + } + + void SetVRegC_22c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k22c); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; + } + // Returns the format of the given opcode. static Format FormatOf(Code opcode) { return kInstructionFormats[opcode]; diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h index 3083d224e9..9daec61ba5 100644 --- a/src/dex_instruction_list.h +++ b/src/dex_instruction_list.h @@ -242,14 +242,14 @@ V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ - V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \ + V(0xE3, IGET_QUICK, "iget-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \ + V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE6, IPUT_QUICK, "iput-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \ + V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArg) \ + V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRange) \ V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \ V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \ V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index dd96f8dead..685ca219b2 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -470,6 +470,98 @@ static void DoInvoke(Thread* self, ShadowFrame& shadow_frame, } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<bool is_range> +static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) + NO_THREAD_SAFETY_ANALYSIS; + +template<bool is_range> +static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) { + uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* receiver = shadow_frame.GetVRegReference(vregC); + if (UNLIKELY(receiver == NULL)) { + // We lost the reference to the method index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + AbstractMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return; + } + MethodHelper mh(method); + + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + uint16_t num_regs; + uint16_t num_ins; + if (code_item != NULL) { + num_regs = code_item->registers_size_; + num_ins = code_item->ins_size_; + } else if (method->IsAbstract()) { + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;", + "abstract method \"%s\"", PrettyMethod(method).c_str()); + return; + } else { + DCHECK(method->IsNative() || method->IsProxyMethod()); + num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty()); + if (!method->IsStatic()) { + num_regs++; + num_ins++; + } + } + + void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); + ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, + method, 0, memory)); + size_t cur_reg = num_regs - num_ins; + if (receiver != NULL) { + new_shadow_frame->SetVRegReference(cur_reg, receiver); + ++cur_reg; + } + + size_t arg_offset = (receiver == NULL) ? 0 : 1; + const char* shorty = mh.GetShorty(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); + size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; + switch (shorty[shorty_pos + 1]) { + case 'L': { + Object* o = shadow_frame.GetVRegReference(arg_pos); + new_shadow_frame->SetVRegReference(cur_reg, o); + break; + } + case 'J': case 'D': { + uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) | + static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos)); + new_shadow_frame->SetVRegLong(cur_reg, wide_value); + cur_reg++; + arg_offset++; + break; + } + default: + new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); + break; + } + } + + if (LIKELY(Runtime::Current()->IsStarted())) { + (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); + } else { + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); + } +} + // We use template functions to optimize compiler inlining process. Otherwise, // some parts of the code (like a switch statement) which depend on a constant // parameter would not be inlined while it should be. These constant parameters @@ -535,6 +627,41 @@ static inline void DoFieldGet(Thread* self, ShadowFrame& shadow_frame, // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. +template<Primitive::Type field_type> +static void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template<Primitive::Type field_type> +static inline void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iget-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile))); + break; + case Primitive::kPrimLong: + shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile))); + break; + case Primitive::kPrimNot: + shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> static void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst) @@ -591,6 +718,41 @@ static inline void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template<Primitive::Type field_type> +static void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template<Primitive::Type field_type> +static inline void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iput-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); + break; + case Primitive::kPrimLong: + obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile); + break; + case Primitive::kPrimNot: + obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } +} + static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Class* java_lang_string_class = String::GetJavaLangString(); @@ -1770,6 +1932,21 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; + case Instruction::IGET_QUICK: + PREAMBLE(); + DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_WIDE_QUICK: + PREAMBLE(); + DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_OBJECT_QUICK: + PREAMBLE(); + DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; case Instruction::SGET_BOOLEAN: PREAMBLE(); DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst); @@ -1840,6 +2017,21 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; + case Instruction::IPUT_QUICK: + PREAMBLE(); + DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_WIDE_QUICK: + PREAMBLE(); + DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_OBJECT_QUICK: + PREAMBLE(); + DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; case Instruction::SPUT_BOOLEAN: PREAMBLE(); DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst); @@ -1925,6 +2117,16 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; + case Instruction::INVOKE_VIRTUAL_QUICK: + PREAMBLE(); + DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); + break; + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + PREAMBLE(); + DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); + break; case Instruction::NEG_INT: PREAMBLE(); shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); @@ -2715,7 +2917,7 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte inst = inst->Next_2xx(); break; case Instruction::UNUSED_3E ... Instruction::UNUSED_43: - case Instruction::UNUSED_E3 ... Instruction::UNUSED_FF: + case Instruction::UNUSED_EB ... Instruction::UNUSED_FF: case Instruction::UNUSED_73: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: diff --git a/src/mem_map.cc b/src/mem_map.cc index fb19424c48..c75dffa63c 100644 --- a/src/mem_map.cc +++ b/src/mem_map.cc @@ -183,4 +183,26 @@ bool MemMap::Protect(int prot) { return false; } +bool MemMap::ProtectRegion(uint8_t* addr, size_t length, int prot) { + CHECK_GE(addr, base_begin_); + CHECK_LT(addr + length, reinterpret_cast<const uint8_t*>(base_begin_) + base_size_); + + /* + * Align "addr" to a page boundary and adjust "length" appropriately. + * (The address must be page-aligned, the length doesn't need to be, + * but we do need to ensure we cover the same range.) + */ + uint8_t* alignAddr = (uint8_t*) ((uintptr_t) addr & ~(kPageSize-1)); + size_t alignLength = length + (addr - alignAddr); + + if (mprotect(alignAddr, alignLength, prot) == 0) { + prot_ = prot; + return true; + } + + PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(alignAddr) << ", " << alignLength << ", " + << prot << ") failed"; + return false; +} + } // namespace art diff --git a/src/mem_map.h b/src/mem_map.h index 7310f78ddf..2eb7772705 100644 --- a/src/mem_map.h +++ b/src/mem_map.h @@ -61,6 +61,8 @@ class MemMap { bool Protect(int prot); + bool ProtectRegion(uint8_t* addr, size_t length, int prot); + int GetProtect() const { return prot_; } diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index 021e984001..87cc3286b3 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -278,6 +278,8 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca declaring_class_(NULL), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(NULL), + accessed_field(NULL), + invoked_method(NULL), have_pending_hard_failure_(false), have_pending_runtime_throw_failure_(false), new_instance_count_(0), @@ -308,6 +310,54 @@ void MethodVerifier::FindLocksAtDexPc() { Verify(); } +mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) { + MethodHelper mh(m); + MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + m, m->GetAccessFlags(), false, true); + mirror::Field* field = NULL; + verifier.interesting_dex_pc_ = dex_pc; + verifier.accessed_field = &field; + verifier.FindAccessedFieldAtDexPc(); + return field; +} + +void MethodVerifier::FindAccessedFieldAtDexPc() { + CHECK(accessed_field != NULL); + CHECK(code_item_ != NULL); // This only makes sense for methods with code. + + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + Verify(); +} + +mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) { + MethodHelper mh(m); + MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + m, m->GetAccessFlags(), false, true); + mirror::AbstractMethod* method = NULL; + verifier.interesting_dex_pc_ = dex_pc; + verifier.invoked_method = &method; + verifier.FindInvokedMethodAtDexPc(); + return method; +} + +void MethodVerifier::FindInvokedMethodAtDexPc() { + CHECK(invoked_method != NULL); + CHECK(code_item_ != NULL); // This only makes sense for methods with code. + + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + Verify(); +} + bool MethodVerifier::Verify() { // If there aren't any instructions, make sure that's expected, then exit successfully. if (code_item_ == NULL) { @@ -2361,10 +2411,63 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false); break; + // Special instructions. + // + // Note: the following instructions encode offsets derived from class linking. + // As such they use Class*/Field*/AbstractMethod* as these offsets only have + // meaning if the class linking and resolution were successful. + case Instruction::IGET_QUICK: + VerifyIGetQuick(inst, reg_types_.Integer(), true); + break; + case Instruction::IGET_WIDE_QUICK: + VerifyIGetQuick(inst, reg_types_.LongLo(), true); + break; + case Instruction::IGET_OBJECT_QUICK: + VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false); + break; + case Instruction::IPUT_QUICK: + VerifyIPutQuick(inst, reg_types_.Integer(), true); + break; + case Instruction::IPUT_WIDE_QUICK: + VerifyIPutQuick(inst, reg_types_.LongLo(), true); + break; + case Instruction::IPUT_OBJECT_QUICK: + VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false); + break; + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); + mirror::AbstractMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range); + if (called_method != NULL) { + const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + if (!return_type.IsLowHalf()) { + work_line_->SetResultRegisterType(return_type); + } else { + work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); + } + just_set_result = true; + } + break; + } + /* These should never appear during verification. */ + case Instruction::UNUSED_3E: + case Instruction::UNUSED_3F: + case Instruction::UNUSED_40: + case Instruction::UNUSED_41: + case Instruction::UNUSED_42: + case Instruction::UNUSED_43: + case Instruction::UNUSED_73: + case Instruction::UNUSED_79: + case Instruction::UNUSED_7A: + case Instruction::UNUSED_EB: + case Instruction::UNUSED_EC: case Instruction::UNUSED_ED: case Instruction::UNUSED_EE: case Instruction::UNUSED_EF: + case Instruction::UNUSED_F0: + case Instruction::UNUSED_F1: case Instruction::UNUSED_F2: case Instruction::UNUSED_F3: case Instruction::UNUSED_F4: @@ -2375,30 +2478,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::UNUSED_F9: case Instruction::UNUSED_FA: case Instruction::UNUSED_FB: - case Instruction::UNUSED_F0: - case Instruction::UNUSED_F1: - case Instruction::UNUSED_E3: - case Instruction::UNUSED_E8: - case Instruction::UNUSED_E7: - case Instruction::UNUSED_E4: - case Instruction::UNUSED_E9: case Instruction::UNUSED_FC: - case Instruction::UNUSED_E5: - case Instruction::UNUSED_EA: case Instruction::UNUSED_FD: - case Instruction::UNUSED_E6: - case Instruction::UNUSED_EB: case Instruction::UNUSED_FE: - case Instruction::UNUSED_3E: - case Instruction::UNUSED_3F: - case Instruction::UNUSED_40: - case Instruction::UNUSED_41: - case Instruction::UNUSED_42: - case Instruction::UNUSED_43: - case Instruction::UNUSED_73: - case Instruction::UNUSED_79: - case Instruction::UNUSED_7A: - case Instruction::UNUSED_EC: case Instruction::UNUSED_FF: Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); break; @@ -2802,7 +2884,7 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* } } // We use vAA as our expected arg count, rather than res_method->insSize, because we need to - // match the call to the signature. Also, we might might be calling through an abstract method + // match the call to the signature. Also, we might be calling through an abstract method // definition (which doesn't have register count values). const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); /* caught by static verifier */ @@ -2882,6 +2964,121 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* } } +mirror::AbstractMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, + bool is_range) { + DCHECK(Runtime::Current()->IsStarted()); + CHECK(invoked_method != NULL || accessed_field != NULL) + << "We should not be verifying " << inst->Name(); + const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range); + if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. + return NULL; + } + mirror::Class* this_class = NULL; + if (!actual_arg_type.IsUnresolvedTypes()) { + this_class = actual_arg_type.GetClass(); + } else { + const std::string& descriptor(actual_arg_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + this_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (this_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + this_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + CHECK(this_class != NULL) << "Cannot get Class* for type " << actual_arg_type; + mirror::ObjectArray<mirror::AbstractMethod>* vtable = this_class->GetVTable(); + CHECK(vtable != NULL); + uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + CHECK(vtable_index < vtable->GetLength()); + mirror::AbstractMethod* res_method = vtable->Get(vtable_index); + CHECK(!Thread::Current()->IsExceptionPending()); + // TODO: we should move the code below to FindInvokedMethodAtDexPc. Once the + // method is verified, we could access the information we need from register + // lines for the dex pc we are looking for. + if (invoked_method != NULL && work_insn_idx_ == interesting_dex_pc_) { + // We've been requested to tell which method is invoked at this dex pc. + *invoked_method = res_method; + } + if (res_method == NULL) { + return NULL; + } + CHECK(!res_method->IsDirect() && !res_method->IsStatic()); + + // We use vAA as our expected arg count, rather than res_method->insSize, because we need to + // match the call to the signature. Also, we might be calling through an abstract method + // definition (which doesn't have register count values). + const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); + /* caught by static verifier */ + DCHECK(is_range || expected_args <= 5); + if (expected_args > code_item_->outs_size_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args + << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + return NULL; + } + + /* + * Check the "this" argument, which must be an instance of the class that declared the method. + * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a + * rigorous check here (which is okay since we have to do it at runtime). + */ + if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; + return NULL; + } + if (!actual_arg_type.IsZero()) { + mirror::Class* klass = res_method->GetDeclaringClass(); + const RegType& res_method_class = + reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass, + klass->CannotBeAssignedFromOtherTypes()); + if (!res_method_class.IsAssignableFrom(actual_arg_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type + << "' not instance of '" << res_method_class << "'"; + return NULL; + } + } + /* + * Process the target method's signature. This signature may or may not + * have been verified, so we can't assume it's properly formed. + */ + MethodHelper mh(res_method); + const DexFile::TypeList* params = mh.GetParameterTypeList(); + size_t params_size = params == NULL ? 0 : params->Size(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + size_t actual_args = 1; + for (size_t param_index = 0; param_index < params_size; param_index++) { + if (actual_args >= expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method) + << "'. Expected " << expected_args << " arguments, processing argument " << actual_args + << " (where longs/doubles count twice)."; + return NULL; + } + const char* descriptor = + mh.GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_); + if (descriptor == NULL) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method) + << " missing signature component"; + return NULL; + } + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; + if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { + return res_method; + } + actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1; + } + if (actual_args != expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method) + << " expected " << expected_args << " arguments, found " << actual_args; + return NULL; + } else { + return res_method; + } +} + void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) { uint32_t type_idx; if (!is_filled) { @@ -3244,6 +3441,185 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty } } +// Look for an instance field with this offset. +// TODO: we may speed up the search if offsets are sorted by doing a quick search. +static mirror::Field* FindInstanceFieldWithOffset(mirror::Class* klass, + uint32_t field_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ObjectArray<mirror::Field>* instance_fields = klass->GetIFields(); + if (instance_fields != NULL) { + for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) { + mirror::Field* field = instance_fields->Get(i); + if (field->GetOffset().Uint32Value() == field_offset) { + return field; + } + } + } + if (klass->GetSuperClass() != NULL) { + return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset); + } else { + return NULL; + } +} + +void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) { + DCHECK(Runtime::Current()->IsStarted()); + CHECK(accessed_field != NULL || invoked_method != NULL) + << "We should not be verifying " << inst->Name(); + const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); + mirror::Class* object_class = NULL; + if (!object_type.IsUnresolvedTypes()) { + object_class = object_type.GetClass(); + } else { + // We need to resolve the class from its descriptor. + const std::string& descriptor(object_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (object_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + object_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type; + uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c()); + mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset); + CHECK(field != NULL); + // TODO: we should move the code below to FindAccessedFieldAtDexPc. Once the + // method is verified, we could access the information we need from register + // lines for the dex pc we are looking for. + if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) { + // We've been requested to tell which field is accessed at this dex pc. + *accessed_field = field; + } + const char* descriptor = FieldHelper(field).GetTypeDescriptor(); + mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + const uint32_t vregA = inst->VRegA_22c(); + if (is_primitive) { + if (field_type.Equals(insn_type) || + (field_type.IsFloat() && insn_type.IsIntegralTypes()) || + (field_type.IsDouble() && insn_type.IsLongTypes())) { + // expected that read is of the correct primitive type or that int reads are reading + // floats or long reads are reading doubles + } else { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type << "' in get"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in get-object"; + work_line_->SetRegisterType(vregA, reg_types_.Conflict()); + return; + } + } + if (!field_type.IsLowHalf()) { + work_line_->SetRegisterType(vregA, field_type); + } else { + work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_)); + } +} + +void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) { + DCHECK(Runtime::Current()->IsStarted()); + CHECK(accessed_field != NULL || invoked_method != NULL) + << "We should not be verifying " << inst->Name(); + const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); + mirror::Class* object_class = NULL; + if (!object_type.IsUnresolvedTypes()) { + object_class = object_type.GetClass(); + } else { + // We need to resolve the class from its descriptor. + const std::string& descriptor(object_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (object_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + object_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type; + uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c()); + mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset); + CHECK(field != NULL); + // TODO: like VerifyIGetQuick, we should move the code below to + // FindAccessedFieldAtDexPc. + if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) { + // We've been requested to tell which field is accessed at this dex pc. + *accessed_field = field; + } + const char* descriptor = FieldHelper(field).GetTypeDescriptor(); + mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + if (field != NULL) { + if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) + << " from other class " << GetDeclaringClass(); + return; + } + } + const uint32_t vregA = inst->VRegA_22c(); + if (is_primitive) { + // Primitive field assignability rules are weaker than regular assignability rules + bool instruction_compatible; + bool value_compatible; + const RegType& value_type = work_line_->GetRegisterType(vregA); + if (field_type.IsIntegralTypes()) { + instruction_compatible = insn_type.IsIntegralTypes(); + value_compatible = value_type.IsIntegralTypes(); + } else if (field_type.IsFloat()) { + instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int + value_compatible = value_type.IsFloatTypes(); + } else if (field_type.IsLong()) { + instruction_compatible = insn_type.IsLong(); + value_compatible = value_type.IsLongTypes(); + } else if (field_type.IsDouble()) { + instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long + value_compatible = value_type.IsDoubleTypes(); + } else { + instruction_compatible = false; // reference field with primitive store + value_compatible = false; // unused + } + if (!instruction_compatible) { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type + << "' in put"; + return; + } + if (!value_compatible) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA + << " of type " << value_type + << " but expected " << field_type + << " for store to " << PrettyField(field) << " in put"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in put-object"; + return; + } + work_line_->VerifyRegisterType(vregA, field_type); + } +} + bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) { if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-exception"; diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h index 198d8cb17b..da6497926d 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -197,11 +197,23 @@ class MethodVerifier { LOCKS_EXCLUDED(safecast_map_lock_); // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding - // to the locks held at 'dex_pc' in 'm'. + // to the locks held at 'dex_pc' in method 'm'. static void FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns the accessed field corresponding to the quick instruction's field + // offset at 'dex_pc' in method 'm'. + static mirror::Field* FindAccessedFieldAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Returns the invoked method corresponding to the quick instruction's vtable + // index at 'dex_pc' in method 'm'. + static mirror::AbstractMethod* FindInvokedMethodAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void Shutdown(); @@ -254,6 +266,10 @@ class MethodVerifier { void FindLocksAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FindAccessedFieldAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void FindInvokedMethodAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + /* * Compute the width of the instruction at each address in the instruction stream, and store it in * insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch @@ -484,6 +500,16 @@ class MethodVerifier { bool is_primitive, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Perform verification of an iget-quick instruction. + void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Perform verification of an iput-quick instruction. + void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // 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) @@ -532,6 +558,10 @@ class MethodVerifier { bool is_range, bool is_super) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::AbstractMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst, + bool is_range) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + /* * Verify that the target instruction is not "move-exception". It's important that the only way * to execute a move-exception is as the first instruction of an exception handler. @@ -646,11 +676,18 @@ class MethodVerifier { const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. // Instruction widths and flags, one entry per code unit. UniquePtr<InstructionFlags[]> insn_flags_; - // The dex PC of a FindLocksAtDexPc request, -1 otherwise. + // The dex PC of a FindLocksAtDexPc, FindAccessedFieldAtDexPc or + // FindInvokedMethodAtDexPc request, -1 otherwise. uint32_t interesting_dex_pc_; // The container into which FindLocksAtDexPc should write the registers containing held locks, // NULL if we're not doing FindLocksAtDexPc. std::vector<uint32_t>* monitor_enter_dex_pcs_; + // The pointer into which FindAccessedFieldAtDexPc should write the accessed field, + // NULL if we're not doing FindAccessedFieldAtDexPc. + mirror::Field** accessed_field; + // The pointer into which FindInvokedMethodAtDexPc should write the invoked method, + // NULL if we're not doing FindInvokedMethodAtDexPc. + mirror::AbstractMethod** invoked_method; // The types of any error that occurs. std::vector<VerifyError> failures_; |