summaryrefslogtreecommitdiffstats
path: root/src/verifier/method_verifier.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/verifier/method_verifier.cc')
-rw-r--r--src/verifier/method_verifier.cc4126
1 files changed, 0 insertions, 4126 deletions
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
deleted file mode 100644
index 5a70f2a696..0000000000
--- a/src/verifier/method_verifier.cc
+++ /dev/null
@@ -1,4126 +0,0 @@
-/*
- * 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 "method_verifier.h"
-
-#include <iostream>
-
-#include "base/logging.h"
-#include "base/mutex-inl.h"
-#include "base/stringpiece.h"
-#include "class_linker.h"
-#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
-#include "dex_instruction_visitor.h"
-#include "gc/accounting/card_table-inl.h"
-#include "indenter.h"
-#include "intern_table.h"
-#include "leb128.h"
-#include "mirror/abstract_method-inl.h"
-#include "mirror/class.h"
-#include "mirror/class-inl.h"
-#include "mirror/dex_cache-inl.h"
-#include "mirror/field-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "object_utils.h"
-#include "register_line-inl.h"
-#include "runtime.h"
-#include "verifier/dex_gc_map.h"
-
-namespace art {
-namespace verifier {
-
-static const bool gDebugVerify = false;
-
-void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* flags,
- uint32_t insns_size, uint16_t registers_size,
- MethodVerifier* verifier) {
- DCHECK_GT(insns_size, 0U);
-
- for (uint32_t i = 0; i < insns_size; i++) {
- bool interesting = false;
- switch (mode) {
- case kTrackRegsAll:
- interesting = flags[i].IsOpcode();
- break;
- case kTrackCompilerInterestPoints:
- interesting = flags[i].IsCompileTimeInfoPoint() || flags[i].IsBranchTarget() ;
- break;
- case kTrackRegsBranches:
- interesting = flags[i].IsBranchTarget();
- break;
- default:
- break;
- }
- if (interesting) {
- pc_to_register_line_.Put(i, new RegisterLine(registers_size, verifier));
- }
- }
-}
-
-MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* klass,
- std::string& error,
- bool allow_soft_failures) {
- if (klass->IsVerified()) {
- return kNoFailure;
- }
- mirror::Class* super = klass->GetSuperClass();
- if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") {
- error = "Verifier rejected class ";
- error += PrettyDescriptor(klass);
- error += " that has no super class";
- return kHardFailure;
- }
- if (super != NULL && super->IsFinal()) {
- error = "Verifier rejected class ";
- error += PrettyDescriptor(klass);
- error += " that attempts to sub-class final class ";
- error += PrettyDescriptor(super);
- return kHardFailure;
- }
- ClassHelper kh(klass);
- const DexFile& dex_file = kh.GetDexFile();
- uint32_t class_def_idx;
- if (!dex_file.FindClassDefIndex(kh.GetDescriptor(), class_def_idx)) {
- error = "Verifier rejected class ";
- error += PrettyDescriptor(klass);
- error += " that isn't present in dex file ";
- error += dex_file.GetLocation();
- return kHardFailure;
- }
- return VerifyClass(&dex_file, kh.GetDexCache(), klass->GetClassLoader(), class_def_idx, error, allow_soft_failures);
-}
-
-MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file,
- mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader,
- uint32_t class_def_idx,
- std::string& error,
- bool allow_soft_failures) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
- const byte* class_data = dex_file->GetClassData(class_def);
- if (class_data == NULL) {
- // empty class, probably a marker interface
- return kNoFailure;
- }
- ClassDataItemIterator it(*dex_file, class_data);
- while (it.HasNextStaticField() || it.HasNextInstanceField()) {
- it.Next();
- }
- size_t error_count = 0;
- bool hard_fail = false;
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- int64_t previous_direct_method_idx = -1;
- while (it.HasNextDirectMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_direct_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
- it.Next();
- continue;
- }
- previous_direct_method_idx = method_idx;
- InvokeType type = it.GetMethodInvokeType(class_def);
- mirror::AbstractMethod* method =
- linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, NULL, type);
- if (method == NULL) {
- DCHECK(Thread::Current()->IsExceptionPending());
- // We couldn't resolve the method, but continue regardless.
- Thread::Current()->ClearException();
- }
- MethodVerifier::FailureKind result = VerifyMethod(method_idx, dex_file, dex_cache, class_loader,
- class_def_idx, it.GetMethodCodeItem(), method, it.GetMemberAccessFlags(), allow_soft_failures);
- if (result != kNoFailure) {
- if (result == kHardFailure) {
- hard_fail = true;
- if (error_count > 0) {
- error += "\n";
- }
- error = "Verifier rejected class ";
- error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
- error += " due to bad method ";
- error += PrettyMethod(method_idx, *dex_file);
- }
- ++error_count;
- }
- it.Next();
- }
- int64_t previous_virtual_method_idx = -1;
- while (it.HasNextVirtualMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_virtual_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
- it.Next();
- continue;
- }
- previous_virtual_method_idx = method_idx;
- InvokeType type = it.GetMethodInvokeType(class_def);
- mirror::AbstractMethod* method =
- linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, NULL, type);
- if (method == NULL) {
- DCHECK(Thread::Current()->IsExceptionPending());
- // We couldn't resolve the method, but continue regardless.
- Thread::Current()->ClearException();
- }
- MethodVerifier::FailureKind result = VerifyMethod(method_idx, dex_file, dex_cache, class_loader,
- class_def_idx, it.GetMethodCodeItem(), method, it.GetMemberAccessFlags(), allow_soft_failures);
- if (result != kNoFailure) {
- if (result == kHardFailure) {
- hard_fail = true;
- if (error_count > 0) {
- error += "\n";
- }
- error = "Verifier rejected class ";
- error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
- error += " due to bad method ";
- error += PrettyMethod(method_idx, *dex_file);
- }
- ++error_count;
- }
- it.Next();
- }
- if (error_count == 0) {
- return kNoFailure;
- } else {
- return hard_fail ? kHardFailure : kSoftFailure;
- }
-}
-
-MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx,
- const DexFile* dex_file,
- mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader,
- uint32_t class_def_idx,
- const DexFile::CodeItem* code_item,
- mirror::AbstractMethod* method,
- uint32_t method_access_flags,
- bool allow_soft_failures) {
- MethodVerifier::FailureKind result = kNoFailure;
- uint64_t start_ns = NanoTime();
-
- MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item, method_idx,
- method, method_access_flags, true, allow_soft_failures);
- if (verifier.Verify()) {
- // Verification completed, however failures may be pending that didn't cause the verification
- // to hard fail.
- CHECK(!verifier.have_pending_hard_failure_);
- if (verifier.failures_.size() != 0) {
- verifier.DumpFailures(LOG(INFO) << "Soft verification failures in "
- << PrettyMethod(method_idx, *dex_file) << "\n");
- result = kSoftFailure;
- }
- } else {
- // Bad method data.
- CHECK_NE(verifier.failures_.size(), 0U);
- CHECK(verifier.have_pending_hard_failure_);
- verifier.DumpFailures(LOG(INFO) << "Verification error in "
- << PrettyMethod(method_idx, *dex_file) << "\n");
- if (gDebugVerify) {
- std::cout << "\n" << verifier.info_messages_.str();
- verifier.Dump(std::cout);
- }
- result = kHardFailure;
- }
- uint64_t duration_ns = NanoTime() - start_ns;
- if (duration_ns > MsToNs(100)) {
- LOG(WARNING) << "Verification of " << PrettyMethod(method_idx, *dex_file)
- << " took " << PrettyDuration(duration_ns);
- }
- return result;
-}
-
-void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx,
- const DexFile* dex_file, mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader, uint32_t class_def_idx,
- const DexFile::CodeItem* code_item,
- mirror::AbstractMethod* method,
- uint32_t method_access_flags) {
- MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item,
- dex_method_idx, method, method_access_flags, true, true);
- verifier.Verify();
- verifier.DumpFailures(os);
- os << verifier.info_messages_.str();
- verifier.Dump(os);
-}
-
-MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader, uint32_t class_def_idx,
- const DexFile::CodeItem* code_item,
- uint32_t dex_method_idx, mirror::AbstractMethod* method,
- uint32_t method_access_flags, bool can_load_classes,
- bool allow_soft_failures)
- : reg_types_(can_load_classes),
- work_insn_idx_(-1),
- dex_method_idx_(dex_method_idx),
- mirror_method_(method),
- method_access_flags_(method_access_flags),
- dex_file_(dex_file),
- dex_cache_(dex_cache),
- class_loader_(class_loader),
- class_def_idx_(class_def_idx),
- code_item_(code_item),
- declaring_class_(NULL),
- interesting_dex_pc_(-1),
- monitor_enter_dex_pcs_(NULL),
- have_pending_hard_failure_(false),
- have_pending_runtime_throw_failure_(false),
- new_instance_count_(0),
- monitor_enter_count_(0),
- can_load_classes_(can_load_classes),
- allow_soft_failures_(allow_soft_failures) {
-}
-
-void MethodVerifier::FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc,
- std::vector<uint32_t>& monitor_enter_dex_pcs) {
- MethodHelper mh(m);
- MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
- mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
- m, m->GetAccessFlags(), false, true);
- verifier.interesting_dex_pc_ = dex_pc;
- verifier.monitor_enter_dex_pcs_ = &monitor_enter_dex_pcs;
- verifier.FindLocksAtDexPc();
-}
-
-void MethodVerifier::FindLocksAtDexPc() {
- CHECK(monitor_enter_dex_pcs_ != 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::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);
- return verifier.FindAccessedFieldAtDexPc(dex_pc);
-}
-
-mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) {
- 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.
- bool success = Verify();
- if (!success) {
- return NULL;
- }
- RegisterLine* register_line = reg_table_.GetLine(dex_pc);
- if (register_line == NULL) {
- return NULL;
- }
- const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
- return GetQuickFieldAccess(inst, register_line);
-}
-
-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);
- return verifier.FindInvokedMethodAtDexPc(dex_pc);
-}
-
-mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) {
- 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.
- bool success = Verify();
- if (!success) {
- return NULL;
- }
- RegisterLine* register_line = reg_table_.GetLine(dex_pc);
- if (register_line == NULL) {
- return NULL;
- }
- const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
- const bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
- return GetQuickInvokedMethod(inst, register_line, is_range);
-}
-
-bool MethodVerifier::Verify() {
- // If there aren't any instructions, make sure that's expected, then exit successfully.
- if (code_item_ == NULL) {
- if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
- return false;
- } else {
- return true;
- }
- }
- // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers.
- if (code_item_->ins_size_ > code_item_->registers_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_
- << " regs=" << code_item_->registers_size_;
- return false;
- }
- // Allocate and initialize an array to hold instruction data.
- insn_flags_.reset(new InstructionFlags[code_item_->insns_size_in_code_units_]());
- // Run through the instructions and see if the width checks out.
- bool result = ComputeWidthsAndCountOps();
- // Flag instructions guarded by a "try" block and check exception handlers.
- result = result && ScanTryCatchBlocks();
- // Perform static instruction verification.
- result = result && VerifyInstructions();
- // Perform code-flow analysis and return.
- return result && VerifyCodeFlow();
-}
-
-std::ostream& MethodVerifier::Fail(VerifyError error) {
- switch (error) {
- case VERIFY_ERROR_NO_CLASS:
- case VERIFY_ERROR_NO_FIELD:
- case VERIFY_ERROR_NO_METHOD:
- case VERIFY_ERROR_ACCESS_CLASS:
- case VERIFY_ERROR_ACCESS_FIELD:
- case VERIFY_ERROR_ACCESS_METHOD:
- case VERIFY_ERROR_INSTANTIATION:
- case VERIFY_ERROR_CLASS_CHANGE:
- if (Runtime::Current()->IsCompiler() || !can_load_classes_) {
- // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
- // class change and instantiation errors into soft verification errors so that we re-verify
- // at runtime. We may fail to find or to agree on access because of not yet available class
- // loaders, or class loaders that will differ at runtime. In these cases, we don't want to
- // affect the soundness of the code being compiled. Instead, the generated code runs "slow
- // paths" that dynamically perform the verification and cause the behavior to be that akin
- // to an interpreter.
- error = VERIFY_ERROR_BAD_CLASS_SOFT;
- } else {
- have_pending_runtime_throw_failure_ = true;
- }
- break;
- // Indication that verification should be retried at runtime.
- case VERIFY_ERROR_BAD_CLASS_SOFT:
- if (!allow_soft_failures_) {
- have_pending_hard_failure_ = true;
- }
- break;
- // Hard verification failures at compile time will still fail at runtime, so the class is
- // marked as rejected to prevent it from being compiled.
- case VERIFY_ERROR_BAD_CLASS_HARD: {
- if (Runtime::Current()->IsCompiler()) {
- ClassReference ref(dex_file_, class_def_idx_);
- AddRejectedClass(ref);
- }
- have_pending_hard_failure_ = true;
- break;
- }
- }
- failures_.push_back(error);
- std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
- work_insn_idx_));
- std::ostringstream* failure_message = new std::ostringstream(location);
- failure_messages_.push_back(failure_message);
- return *failure_message;
-}
-
-void MethodVerifier::PrependToLastFailMessage(std::string prepend) {
- size_t failure_num = failure_messages_.size();
- DCHECK_NE(failure_num, 0U);
- std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
- prepend += last_fail_message->str();
- failure_messages_[failure_num - 1] = new std::ostringstream(prepend);
- delete last_fail_message;
-}
-
-void MethodVerifier::AppendToLastFailMessage(std::string append) {
- size_t failure_num = failure_messages_.size();
- DCHECK_NE(failure_num, 0U);
- std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
- (*last_fail_message) << append;
-}
-
-bool MethodVerifier::ComputeWidthsAndCountOps() {
- const uint16_t* insns = code_item_->insns_;
- size_t insns_size = code_item_->insns_size_in_code_units_;
- const Instruction* inst = Instruction::At(insns);
- size_t new_instance_count = 0;
- size_t monitor_enter_count = 0;
- size_t dex_pc = 0;
-
- while (dex_pc < insns_size) {
- Instruction::Code opcode = inst->Opcode();
- if (opcode == Instruction::NEW_INSTANCE) {
- new_instance_count++;
- } else if (opcode == Instruction::MONITOR_ENTER) {
- monitor_enter_count++;
- }
- size_t inst_size = inst->SizeInCodeUnits();
- insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
- dex_pc += inst_size;
- inst = inst->Next();
- }
-
- if (dex_pc != insns_size) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code did not end where expected ("
- << dex_pc << " vs. " << insns_size << ")";
- return false;
- }
-
- new_instance_count_ = new_instance_count;
- monitor_enter_count_ = monitor_enter_count;
- return true;
-}
-
-bool MethodVerifier::ScanTryCatchBlocks() {
- uint32_t tries_size = code_item_->tries_size_;
- if (tries_size == 0) {
- return true;
- }
- uint32_t insns_size = code_item_->insns_size_in_code_units_;
- const DexFile::TryItem* tries = DexFile::GetTryItems(*code_item_, 0);
-
- for (uint32_t idx = 0; idx < tries_size; idx++) {
- const DexFile::TryItem* try_item = &tries[idx];
- uint32_t start = try_item->start_addr_;
- uint32_t end = start + try_item->insn_count_;
- if ((start >= end) || (start >= insns_size) || (end > insns_size)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad exception entry: startAddr=" << start
- << " endAddr=" << end << " (size=" << insns_size << ")";
- return false;
- }
- if (!insn_flags_[start].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'try' block starts inside an instruction (" << start << ")";
- return false;
- }
- for (uint32_t dex_pc = start; dex_pc < end;
- dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
- insn_flags_[dex_pc].SetInTry();
- }
- }
- // Iterate over each of the handlers to verify target addresses.
- const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
- uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- for (uint32_t idx = 0; idx < handlers_size; idx++) {
- CatchHandlerIterator iterator(handlers_ptr);
- for (; iterator.HasNext(); iterator.Next()) {
- uint32_t dex_pc= iterator.GetHandlerAddress();
- if (!insn_flags_[dex_pc].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler starts at bad address (" << dex_pc << ")";
- return false;
- }
- const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
- if (inst->Opcode() != Instruction::MOVE_EXCEPTION) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler doesn't start with move-exception ("
- << dex_pc << ")";
- return false;
- }
- insn_flags_[dex_pc].SetBranchTarget();
- // Ensure exception types are resolved so that they don't need resolution to be delivered,
- // unresolved exception types will be ignored by exception delivery
- if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) {
- mirror::Class* exception_type = linker->ResolveType(*dex_file_,
- iterator.GetHandlerTypeIndex(),
- dex_cache_, class_loader_);
- if (exception_type == NULL) {
- DCHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
- }
- }
- }
- handlers_ptr = iterator.EndDataPointer();
- }
- return true;
-}
-
-bool MethodVerifier::VerifyInstructions() {
- const Instruction* inst = Instruction::At(code_item_->insns_);
-
- /* Flag the start of the method as a branch target, and a GC point due to stack overflow errors */
- insn_flags_[0].SetBranchTarget();
- insn_flags_[0].SetCompileTimeInfoPoint();
-
- uint32_t insns_size = code_item_->insns_size_in_code_units_;
- for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
- if (!VerifyInstruction(inst, dex_pc)) {
- DCHECK_NE(failures_.size(), 0U);
- return false;
- }
- /* Flag instructions that are garbage collection points */
- // All invoke points are marked as "Throw" points already.
- // We are relying on this to also count all the invokes as interesting.
- if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() || inst->IsReturn()) {
- insn_flags_[dex_pc].SetCompileTimeInfoPoint();
- }
- dex_pc += inst->SizeInCodeUnits();
- inst = inst->Next();
- }
- return true;
-}
-
-bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_offset) {
- DecodedInstruction dec_insn(inst);
- bool result = true;
- switch (inst->GetVerifyTypeArgumentA()) {
- case Instruction::kVerifyRegA:
- result = result && CheckRegisterIndex(dec_insn.vA);
- break;
- case Instruction::kVerifyRegAWide:
- result = result && CheckWideRegisterIndex(dec_insn.vA);
- break;
- }
- switch (inst->GetVerifyTypeArgumentB()) {
- case Instruction::kVerifyRegB:
- result = result && CheckRegisterIndex(dec_insn.vB);
- break;
- case Instruction::kVerifyRegBField:
- result = result && CheckFieldIndex(dec_insn.vB);
- break;
- case Instruction::kVerifyRegBMethod:
- result = result && CheckMethodIndex(dec_insn.vB);
- break;
- case Instruction::kVerifyRegBNewInstance:
- result = result && CheckNewInstance(dec_insn.vB);
- break;
- case Instruction::kVerifyRegBString:
- result = result && CheckStringIndex(dec_insn.vB);
- break;
- case Instruction::kVerifyRegBType:
- result = result && CheckTypeIndex(dec_insn.vB);
- break;
- case Instruction::kVerifyRegBWide:
- result = result && CheckWideRegisterIndex(dec_insn.vB);
- break;
- }
- switch (inst->GetVerifyTypeArgumentC()) {
- case Instruction::kVerifyRegC:
- result = result && CheckRegisterIndex(dec_insn.vC);
- break;
- case Instruction::kVerifyRegCField:
- result = result && CheckFieldIndex(dec_insn.vC);
- break;
- case Instruction::kVerifyRegCNewArray:
- result = result && CheckNewArray(dec_insn.vC);
- break;
- case Instruction::kVerifyRegCType:
- result = result && CheckTypeIndex(dec_insn.vC);
- break;
- case Instruction::kVerifyRegCWide:
- result = result && CheckWideRegisterIndex(dec_insn.vC);
- break;
- }
- switch (inst->GetVerifyExtraFlags()) {
- case Instruction::kVerifyArrayData:
- result = result && CheckArrayData(code_offset);
- break;
- case Instruction::kVerifyBranchTarget:
- result = result && CheckBranchTarget(code_offset);
- break;
- case Instruction::kVerifySwitchTargets:
- result = result && CheckSwitchTargets(code_offset);
- break;
- case Instruction::kVerifyVarArg:
- result = result && CheckVarArgRegs(dec_insn.vA, dec_insn.arg);
- break;
- case Instruction::kVerifyVarArgRange:
- result = result && CheckVarArgRangeRegs(dec_insn.vA, dec_insn.vC);
- break;
- case Instruction::kVerifyError:
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected opcode " << inst->Name();
- result = false;
- break;
- }
- return result;
-}
-
-bool MethodVerifier::CheckRegisterIndex(uint32_t idx) {
- if (idx >= code_item_->registers_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register index out of range (" << idx << " >= "
- << code_item_->registers_size_ << ")";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) {
- if (idx + 1 >= code_item_->registers_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register index out of range (" << idx
- << "+1 >= " << code_item_->registers_size_ << ")";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckFieldIndex(uint32_t idx) {
- if (idx >= dex_file_->GetHeader().field_ids_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad field index " << idx << " (max "
- << dex_file_->GetHeader().field_ids_size_ << ")";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckMethodIndex(uint32_t idx) {
- if (idx >= dex_file_->GetHeader().method_ids_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad method index " << idx << " (max "
- << dex_file_->GetHeader().method_ids_size_ << ")";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckNewInstance(uint32_t idx) {
- if (idx >= dex_file_->GetHeader().type_ids_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
- << dex_file_->GetHeader().type_ids_size_ << ")";
- return false;
- }
- // We don't need the actual class, just a pointer to the class name.
- const char* descriptor = dex_file_->StringByTypeIdx(idx);
- if (descriptor[0] != 'L') {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't call new-instance on type '" << descriptor << "'";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckStringIndex(uint32_t idx) {
- if (idx >= dex_file_->GetHeader().string_ids_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad string index " << idx << " (max "
- << dex_file_->GetHeader().string_ids_size_ << ")";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckTypeIndex(uint32_t idx) {
- if (idx >= dex_file_->GetHeader().type_ids_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
- << dex_file_->GetHeader().type_ids_size_ << ")";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckNewArray(uint32_t idx) {
- if (idx >= dex_file_->GetHeader().type_ids_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
- << dex_file_->GetHeader().type_ids_size_ << ")";
- return false;
- }
- int bracket_count = 0;
- const char* descriptor = dex_file_->StringByTypeIdx(idx);
- const char* cp = descriptor;
- while (*cp++ == '[') {
- bracket_count++;
- }
- if (bracket_count == 0) {
- /* The given class must be an array type. */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't new-array class '" << descriptor << "' (not an array)";
- return false;
- } else if (bracket_count > 255) {
- /* It is illegal to create an array of more than 255 dimensions. */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't new-array class '" << descriptor << "' (exceeds limit)";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckArrayData(uint32_t cur_offset) {
- const uint32_t insn_count = code_item_->insns_size_in_code_units_;
- const uint16_t* insns = code_item_->insns_ + cur_offset;
- const uint16_t* array_data;
- int32_t array_data_offset;
-
- DCHECK_LT(cur_offset, insn_count);
- /* make sure the start of the array data table is in range */
- array_data_offset = insns[1] | (((int32_t) insns[2]) << 16);
- if ((int32_t) cur_offset + array_data_offset < 0 ||
- cur_offset + array_data_offset + 2 >= insn_count) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid array data start: at " << cur_offset
- << ", data offset " << array_data_offset << ", count " << insn_count;
- return false;
- }
- /* offset to array data table is a relative branch-style offset */
- array_data = insns + array_data_offset;
- /* make sure the table is 32-bit aligned */
- if ((((uint32_t) array_data) & 0x03) != 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unaligned array data table: at " << cur_offset
- << ", data offset " << array_data_offset;
- return false;
- }
- uint32_t value_width = array_data[1];
- uint32_t value_count = *reinterpret_cast<const uint32_t*>(&array_data[2]);
- uint32_t table_size = 4 + (value_width * value_count + 1) / 2;
- /* make sure the end of the switch is in range */
- if (cur_offset + array_data_offset + table_size > insn_count) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid array data end: at " << cur_offset
- << ", data offset " << array_data_offset << ", end "
- << cur_offset + array_data_offset + table_size
- << ", count " << insn_count;
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::CheckBranchTarget(uint32_t cur_offset) {
- int32_t offset;
- bool isConditional, selfOkay;
- if (!GetBranchOffset(cur_offset, &offset, &isConditional, &selfOkay)) {
- return false;
- }
- if (!selfOkay && offset == 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch offset of zero not allowed at" << reinterpret_cast<void*>(cur_offset);
- return false;
- }
- // Check for 32-bit overflow. This isn't strictly necessary if we can depend on the runtime
- // to have identical "wrap-around" behavior, but it's unwise to depend on that.
- if (((int64_t) cur_offset + (int64_t) offset) != (int64_t) (cur_offset + offset)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch target overflow " << reinterpret_cast<void*>(cur_offset) << " +" << offset;
- return false;
- }
- const uint32_t insn_count = code_item_->insns_size_in_code_units_;
- int32_t abs_offset = cur_offset + offset;
- if (abs_offset < 0 || (uint32_t) abs_offset >= insn_count || !insn_flags_[abs_offset].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid branch target " << offset << " (-> "
- << reinterpret_cast<void*>(abs_offset) << ") at "
- << reinterpret_cast<void*>(cur_offset);
- return false;
- }
- insn_flags_[abs_offset].SetBranchTarget();
- return true;
-}
-
-bool MethodVerifier::GetBranchOffset(uint32_t cur_offset, int32_t* pOffset, bool* pConditional,
- bool* selfOkay) {
- const uint16_t* insns = code_item_->insns_ + cur_offset;
- *pConditional = false;
- *selfOkay = false;
- switch (*insns & 0xff) {
- case Instruction::GOTO:
- *pOffset = ((int16_t) *insns) >> 8;
- break;
- case Instruction::GOTO_32:
- *pOffset = insns[1] | (((uint32_t) insns[2]) << 16);
- *selfOkay = true;
- break;
- case Instruction::GOTO_16:
- *pOffset = (int16_t) insns[1];
- break;
- case Instruction::IF_EQ:
- case Instruction::IF_NE:
- case Instruction::IF_LT:
- case Instruction::IF_GE:
- case Instruction::IF_GT:
- case Instruction::IF_LE:
- case Instruction::IF_EQZ:
- case Instruction::IF_NEZ:
- case Instruction::IF_LTZ:
- case Instruction::IF_GEZ:
- case Instruction::IF_GTZ:
- case Instruction::IF_LEZ:
- *pOffset = (int16_t) insns[1];
- *pConditional = true;
- break;
- default:
- return false;
- break;
- }
- return true;
-}
-
-bool MethodVerifier::CheckSwitchTargets(uint32_t cur_offset) {
- const uint32_t insn_count = code_item_->insns_size_in_code_units_;
- DCHECK_LT(cur_offset, insn_count);
- const uint16_t* insns = code_item_->insns_ + cur_offset;
- /* make sure the start of the switch is in range */
- int32_t switch_offset = insns[1] | ((int32_t) insns[2]) << 16;
- if ((int32_t) cur_offset + switch_offset < 0 || cur_offset + switch_offset + 2 >= insn_count) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch start: at " << cur_offset
- << ", switch offset " << switch_offset << ", count " << insn_count;
- return false;
- }
- /* offset to switch table is a relative branch-style offset */
- const uint16_t* switch_insns = insns + switch_offset;
- /* make sure the table is 32-bit aligned */
- if ((((uint32_t) switch_insns) & 0x03) != 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unaligned switch table: at " << cur_offset
- << ", switch offset " << switch_offset;
- return false;
- }
- uint32_t switch_count = switch_insns[1];
- int32_t keys_offset, targets_offset;
- uint16_t expected_signature;
- if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
- /* 0=sig, 1=count, 2/3=firstKey */
- targets_offset = 4;
- keys_offset = -1;
- expected_signature = Instruction::kPackedSwitchSignature;
- } else {
- /* 0=sig, 1=count, 2..count*2 = keys */
- keys_offset = 2;
- targets_offset = 2 + 2 * switch_count;
- expected_signature = Instruction::kSparseSwitchSignature;
- }
- uint32_t table_size = targets_offset + switch_count * 2;
- if (switch_insns[0] != expected_signature) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << StringPrintf("wrong signature for switch table (%x, wanted %x)",
- switch_insns[0], expected_signature);
- return false;
- }
- /* make sure the end of the switch is in range */
- if (cur_offset + switch_offset + table_size > (uint32_t) insn_count) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch end: at " << cur_offset << ", switch offset "
- << switch_offset << ", end "
- << (cur_offset + switch_offset + table_size)
- << ", count " << insn_count;
- return false;
- }
- /* for a sparse switch, verify the keys are in ascending order */
- if (keys_offset > 0 && switch_count > 1) {
- int32_t last_key = switch_insns[keys_offset] | (switch_insns[keys_offset + 1] << 16);
- for (uint32_t targ = 1; targ < switch_count; targ++) {
- int32_t key = (int32_t) switch_insns[keys_offset + targ * 2] |
- (int32_t) (switch_insns[keys_offset + targ * 2 + 1] << 16);
- if (key <= last_key) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid packed switch: last key=" << last_key
- << ", this=" << key;
- return false;
- }
- last_key = key;
- }
- }
- /* verify each switch target */
- for (uint32_t targ = 0; targ < switch_count; targ++) {
- int32_t offset = (int32_t) switch_insns[targets_offset + targ * 2] |
- (int32_t) (switch_insns[targets_offset + targ * 2 + 1] << 16);
- int32_t abs_offset = cur_offset + offset;
- if (abs_offset < 0 || abs_offset >= (int32_t) insn_count || !insn_flags_[abs_offset].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch target " << offset << " (-> "
- << reinterpret_cast<void*>(abs_offset) << ") at "
- << reinterpret_cast<void*>(cur_offset) << "[" << targ << "]";
- return false;
- }
- insn_flags_[abs_offset].SetBranchTarget();
- }
- return true;
-}
-
-bool MethodVerifier::CheckVarArgRegs(uint32_t vA, uint32_t arg[]) {
- if (vA > 5) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid arg count (" << vA << ") in non-range invoke)";
- return false;
- }
- uint16_t registers_size = code_item_->registers_size_;
- for (uint32_t idx = 0; idx < vA; idx++) {
- if (arg[idx] >= registers_size) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index (" << arg[idx]
- << ") in non-range invoke (>= " << registers_size << ")";
- return false;
- }
- }
-
- return true;
-}
-
-bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) {
- uint16_t registers_size = code_item_->registers_size_;
- // vA/vC are unsigned 8-bit/16-bit quantities for /range instructions, so there's no risk of
- // integer overflow when adding them here.
- if (vA + vC > registers_size) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index " << vA << "+" << vC << " in range invoke (> "
- << registers_size << ")";
- return false;
- }
- return true;
-}
-
-static const std::vector<uint8_t>* CreateLengthPrefixedDexGcMap(const std::vector<uint8_t>& gc_map) {
- std::vector<uint8_t>* length_prefixed_gc_map = new std::vector<uint8_t>;
- length_prefixed_gc_map->reserve(gc_map.size() + 4);
- length_prefixed_gc_map->push_back((gc_map.size() & 0xff000000) >> 24);
- length_prefixed_gc_map->push_back((gc_map.size() & 0x00ff0000) >> 16);
- length_prefixed_gc_map->push_back((gc_map.size() & 0x0000ff00) >> 8);
- length_prefixed_gc_map->push_back((gc_map.size() & 0x000000ff) >> 0);
- length_prefixed_gc_map->insert(length_prefixed_gc_map->end(),
- gc_map.begin(),
- gc_map.end());
- DCHECK_EQ(gc_map.size() + 4, length_prefixed_gc_map->size());
- DCHECK_EQ(gc_map.size(),
- static_cast<size_t>((length_prefixed_gc_map->at(0) << 24) |
- (length_prefixed_gc_map->at(1) << 16) |
- (length_prefixed_gc_map->at(2) << 8) |
- (length_prefixed_gc_map->at(3) << 0)));
- return length_prefixed_gc_map;
-}
-
-bool MethodVerifier::VerifyCodeFlow() {
- uint16_t registers_size = code_item_->registers_size_;
- uint32_t insns_size = code_item_->insns_size_in_code_units_;
-
- if (registers_size * insns_size > 4*1024*1024) {
- LOG(WARNING) << "warning: method is huge (regs=" << registers_size
- << " insns_size=" << insns_size << ")";
- }
- /* Create and initialize table holding register status */
- reg_table_.Init(kTrackCompilerInterestPoints, insn_flags_.get(), insns_size, registers_size, this);
-
-
- work_line_.reset(new RegisterLine(registers_size, this));
- saved_line_.reset(new RegisterLine(registers_size, this));
-
- /* Initialize register types of method arguments. */
- if (!SetTypesFromSignature()) {
- DCHECK_NE(failures_.size(), 0U);
- std::string prepend("Bad signature in ");
- prepend += PrettyMethod(dex_method_idx_, *dex_file_);
- PrependToLastFailMessage(prepend);
- return false;
- }
- /* Perform code flow verification. */
- if (!CodeFlowVerifyMethod()) {
- DCHECK_NE(failures_.size(), 0U);
- return false;
- }
-
- /* Generate a register map and add it to the method. */
- UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
- if (map.get() == NULL) {
- DCHECK_NE(failures_.size(), 0U);
- return false; // Not a real failure, but a failure to encode
- }
- if (kIsDebugBuild) {
- VerifyGcMap(*map);
- }
- MethodReference ref(dex_file_, dex_method_idx_);
- const std::vector<uint8_t>* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get()));
- verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map);
-
- MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
- if(method_to_safe_casts != NULL ) {
- SetSafeCastMap(ref, method_to_safe_casts);
- }
-
- MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
- if(pc_to_concrete_method != NULL ) {
- SetDevirtMap(ref, pc_to_concrete_method);
- }
- return true;
-}
-
-std::ostream& MethodVerifier::DumpFailures(std::ostream& os) {
- DCHECK_EQ(failures_.size(), failure_messages_.size());
- for (size_t i = 0; i < failures_.size(); ++i) {
- os << failure_messages_[i]->str() << "\n";
- }
- return os;
-}
-
-extern "C" void MethodVerifierGdbDump(MethodVerifier* v)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- v->Dump(std::cerr);
-}
-
-void MethodVerifier::Dump(std::ostream& os) {
- if (code_item_ == NULL) {
- os << "Native method\n";
- return;
- }
- {
- os << "Register Types:\n";
- Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
- std::ostream indent_os(&indent_filter);
- reg_types_.Dump(indent_os);
- }
- os << "Dumping instructions and register lines:\n";
- Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
- std::ostream indent_os(&indent_filter);
- const Instruction* inst = Instruction::At(code_item_->insns_);
- for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
- dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
- RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
- if (reg_line != NULL) {
- indent_os << reg_line->Dump() << "\n";
- }
- indent_os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].ToString() << " ";
- const bool kDumpHexOfInstruction = false;
- if (kDumpHexOfInstruction) {
- indent_os << inst->DumpHex(5) << " ";
- }
- indent_os << inst->DumpString(dex_file_) << "\n";
- inst = inst->Next();
- }
-}
-
-static bool IsPrimitiveDescriptor(char descriptor) {
- switch (descriptor) {
- case 'I':
- case 'C':
- case 'S':
- case 'B':
- case 'Z':
- case 'F':
- case 'D':
- case 'J':
- return true;
- default:
- return false;
- }
-}
-
-bool MethodVerifier::SetTypesFromSignature() {
- RegisterLine* reg_line = reg_table_.GetLine(0);
- int arg_start = code_item_->registers_size_ - code_item_->ins_size_;
- size_t expected_args = code_item_->ins_size_; /* long/double count as two */
-
- DCHECK_GE(arg_start, 0); /* should have been verified earlier */
- //Include the "this" pointer.
- size_t cur_arg = 0;
- if (!IsStatic()) {
- // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
- // argument as uninitialized. This restricts field access until the superclass constructor is
- // called.
- const RegType& declaring_class = GetDeclaringClass();
- if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
- reg_line->SetRegisterType(arg_start + cur_arg,
- reg_types_.UninitializedThisArgument(declaring_class));
- } else {
- reg_line->SetRegisterType(arg_start + cur_arg, declaring_class);
- }
- cur_arg++;
- }
-
- const DexFile::ProtoId& proto_id =
- dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
- DexFileParameterIterator iterator(*dex_file_, proto_id);
-
- for (; iterator.HasNext(); iterator.Next()) {
- const char* descriptor = iterator.GetDescriptor();
- if (descriptor == NULL) {
- LOG(FATAL) << "Null descriptor";
- }
- if (cur_arg >= expected_args) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args
- << " args, found more (" << descriptor << ")";
- return false;
- }
- switch (descriptor[0]) {
- case 'L':
- case '[':
- // We assume that reference arguments are initialized. The only way it could be otherwise
- // (assuming the caller was verified) is if the current method is <init>, but in that case
- // it's effectively considered initialized the instant we reach here (in the sense that we
- // can return without doing anything or call virtual methods).
- {
- const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
- reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
- }
- break;
- case 'Z':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Boolean());
- break;
- case 'C':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Char());
- break;
- case 'B':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Byte());
- break;
- case 'I':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Integer());
- break;
- case 'S':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Short());
- break;
- case 'F':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Float());
- break;
- case 'J':
- case 'D': {
- const RegType& lo_half = descriptor[0] == 'J' ? reg_types_.LongLo() : reg_types_.DoubleLo();
- const RegType& hi_half = descriptor[0] == 'J' ? reg_types_.LongHi() : reg_types_.DoubleHi();
- reg_line->SetRegisterTypeWide(arg_start + cur_arg, lo_half, hi_half);
- cur_arg++;
- break;
- }
- default:
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected signature type char '" << descriptor << "'";
- return false;
- }
- cur_arg++;
- }
- if (cur_arg != expected_args) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args << " arguments, found " << cur_arg;
- return false;
- }
- const char* descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
- // Validate return type. We don't do the type lookup; just want to make sure that it has the right
- // format. Only major difference from the method argument format is that 'V' is supported.
- bool result;
- if (IsPrimitiveDescriptor(descriptor[0]) || descriptor[0] == 'V') {
- result = descriptor[1] == '\0';
- } else if (descriptor[0] == '[') { // single/multi-dimensional array of object/primitive
- size_t i = 0;
- do {
- i++;
- } while (descriptor[i] == '['); // process leading [
- if (descriptor[i] == 'L') { // object array
- do {
- i++; // find closing ;
- } while (descriptor[i] != ';' && descriptor[i] != '\0');
- result = descriptor[i] == ';';
- } else { // primitive array
- result = IsPrimitiveDescriptor(descriptor[i]) && descriptor[i + 1] == '\0';
- }
- } else if (descriptor[0] == 'L') {
- // could be more thorough here, but shouldn't be required
- size_t i = 0;
- do {
- i++;
- } while (descriptor[i] != ';' && descriptor[i] != '\0');
- result = descriptor[i] == ';';
- } else {
- result = false;
- }
- if (!result) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected char in return type descriptor '"
- << descriptor << "'";
- }
- return result;
-}
-
-bool MethodVerifier::CodeFlowVerifyMethod() {
- const uint16_t* insns = code_item_->insns_;
- const uint32_t insns_size = code_item_->insns_size_in_code_units_;
-
- /* Begin by marking the first instruction as "changed". */
- insn_flags_[0].SetChanged();
- uint32_t start_guess = 0;
-
- /* Continue until no instructions are marked "changed". */
- while (true) {
- // Find the first marked one. Use "start_guess" as a way to find one quickly.
- uint32_t insn_idx = start_guess;
- for (; insn_idx < insns_size; insn_idx++) {
- if (insn_flags_[insn_idx].IsChanged())
- break;
- }
- if (insn_idx == insns_size) {
- if (start_guess != 0) {
- /* try again, starting from the top */
- start_guess = 0;
- continue;
- } else {
- /* all flags are clear */
- break;
- }
- }
- // We carry the working set of registers from instruction to instruction. If this address can
- // be the target of a branch (or throw) instruction, or if we're skipping around chasing
- // "changed" flags, we need to load the set of registers from the table.
- // Because we always prefer to continue on to the next instruction, we should never have a
- // situation where we have a stray "changed" flag set on an instruction that isn't a branch
- // target.
- work_insn_idx_ = insn_idx;
- if (insn_flags_[insn_idx].IsBranchTarget()) {
- work_line_->CopyFromLine(reg_table_.GetLine(insn_idx));
- } else {
-#ifndef NDEBUG
- /*
- * Sanity check: retrieve the stored register line (assuming
- * a full table) and make sure it actually matches.
- */
- RegisterLine* register_line = reg_table_.GetLine(insn_idx);
- if (register_line != NULL) {
- if (work_line_->CompareLine(register_line) != 0) {
- Dump(std::cout);
- std::cout << info_messages_.str();
- LOG(FATAL) << "work_line diverged in " << PrettyMethod(dex_method_idx_, *dex_file_)
- << "@" << reinterpret_cast<void*>(work_insn_idx_) << "\n"
- << " work_line=" << *work_line_ << "\n"
- << " expected=" << *register_line;
- }
- }
-#endif
- }
- if (!CodeFlowVerifyInstruction(&start_guess)) {
- std::string prepend(PrettyMethod(dex_method_idx_, *dex_file_));
- prepend += " failed to verify: ";
- PrependToLastFailMessage(prepend);
- return false;
- }
- /* Clear "changed" and mark as visited. */
- insn_flags_[insn_idx].SetVisited();
- insn_flags_[insn_idx].ClearChanged();
- }
-
- if (gDebugVerify) {
- /*
- * Scan for dead code. There's nothing "evil" about dead code
- * (besides the wasted space), but it indicates a flaw somewhere
- * down the line, possibly in the verifier.
- *
- * If we've substituted "always throw" instructions into the stream,
- * we are almost certainly going to have some dead code.
- */
- int dead_start = -1;
- uint32_t insn_idx = 0;
- for (; insn_idx < insns_size; insn_idx += insn_flags_[insn_idx].GetLengthInCodeUnits()) {
- /*
- * Switch-statement data doesn't get "visited" by scanner. It
- * may or may not be preceded by a padding NOP (for alignment).
- */
- if (insns[insn_idx] == Instruction::kPackedSwitchSignature ||
- insns[insn_idx] == Instruction::kSparseSwitchSignature ||
- insns[insn_idx] == Instruction::kArrayDataSignature ||
- (insns[insn_idx] == Instruction::NOP && (insn_idx + 1 < insns_size) &&
- (insns[insn_idx + 1] == Instruction::kPackedSwitchSignature ||
- insns[insn_idx + 1] == Instruction::kSparseSwitchSignature ||
- insns[insn_idx + 1] == Instruction::kArrayDataSignature))) {
- insn_flags_[insn_idx].SetVisited();
- }
-
- if (!insn_flags_[insn_idx].IsVisited()) {
- if (dead_start < 0)
- dead_start = insn_idx;
- } else if (dead_start >= 0) {
- LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start) << "-" << reinterpret_cast<void*>(insn_idx - 1);
- dead_start = -1;
- }
- }
- if (dead_start >= 0) {
- LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start) << "-" << reinterpret_cast<void*>(insn_idx - 1);
- }
- // To dump the state of the verify after a method, do something like:
- // if (PrettyMethod(dex_method_idx_, *dex_file_) ==
- // "boolean java.lang.String.equals(java.lang.Object)") {
- // LOG(INFO) << info_messages_.str();
- // }
- }
- return true;
-}
-
-bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
- // If we're doing FindLocksAtDexPc, check whether we're at the dex pc we care about.
- // We want the state _before_ the instruction, for the case where the dex pc we're
- // interested in is itself a monitor-enter instruction (which is a likely place
- // for a thread to be suspended).
- if (monitor_enter_dex_pcs_ != NULL && work_insn_idx_ == interesting_dex_pc_) {
- monitor_enter_dex_pcs_->clear(); // The new work line is more accurate than the previous one.
- for (size_t i = 0; i < work_line_->GetMonitorEnterCount(); ++i) {
- monitor_enter_dex_pcs_->push_back(work_line_->GetMonitorEnterDexPc(i));
- }
- }
-
- /*
- * Once we finish decoding the instruction, we need to figure out where
- * we can go from here. There are three possible ways to transfer
- * control to another statement:
- *
- * (1) Continue to the next instruction. Applies to all but
- * unconditional branches, method returns, and exception throws.
- * (2) Branch to one or more possible locations. Applies to branches
- * and switch statements.
- * (3) Exception handlers. Applies to any instruction that can
- * throw an exception that is handled by an encompassing "try"
- * block.
- *
- * We can also return, in which case there is no successor instruction
- * from this point.
- *
- * The behavior can be determined from the opcode flags.
- */
- const uint16_t* insns = code_item_->insns_ + work_insn_idx_;
- const Instruction* inst = Instruction::At(insns);
- int opcode_flags = Instruction::FlagsOf(inst->Opcode());
-
- int32_t branch_target = 0;
- bool just_set_result = false;
- if (gDebugVerify) {
- // Generate processing back trace to debug verifier
- LogVerifyInfo() << "Processing " << inst->DumpString(dex_file_) << "\n"
- << *work_line_.get() << "\n";
- }
-
- /*
- * Make a copy of the previous register state. If the instruction
- * can throw an exception, we will copy/merge this into the "catch"
- * address rather than work_line, because we don't want the result
- * from the "successful" code path (e.g. a check-cast that "improves"
- * a type) to be visible to the exception handler.
- */
- if ((opcode_flags & Instruction::kThrow) != 0 && CurrentInsnFlags()->IsInTry()) {
- saved_line_->CopyFromLine(work_line_.get());
- } else {
-#ifndef NDEBUG
- saved_line_->FillWithGarbage();
-#endif
- }
-
-
- // We need to ensure the work line is consistent while performing validation. When we spot a
- // peephole pattern we compute a new line for either the fallthrough instruction or the
- // branch target.
- UniquePtr<RegisterLine> branch_line;
- UniquePtr<RegisterLine> fallthrough_line;
-
- switch (inst->Opcode()) {
- case Instruction::NOP:
- /*
- * A "pure" NOP has no effect on anything. Data tables start with
- * a signature that looks like a NOP; if we see one of these in
- * the course of executing code then we have a problem.
- */
- if (inst->VRegA_10x() != 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "encountered data table in instruction stream";
- }
- break;
-
- case Instruction::MOVE:
- work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategory1nr);
- break;
- case Instruction::MOVE_FROM16:
- work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategory1nr);
- break;
- case Instruction::MOVE_16:
- work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategory1nr);
- break;
- case Instruction::MOVE_WIDE:
- work_line_->CopyRegister2(inst->VRegA_12x(), inst->VRegB_12x());
- break;
- case Instruction::MOVE_WIDE_FROM16:
- work_line_->CopyRegister2(inst->VRegA_22x(), inst->VRegB_22x());
- break;
- case Instruction::MOVE_WIDE_16:
- work_line_->CopyRegister2(inst->VRegA_32x(), inst->VRegB_32x());
- break;
- case Instruction::MOVE_OBJECT:
- work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategoryRef);
- break;
- case Instruction::MOVE_OBJECT_FROM16:
- work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategoryRef);
- break;
- case Instruction::MOVE_OBJECT_16:
- work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategoryRef);
- break;
-
- /*
- * The move-result instructions copy data out of a "pseudo-register"
- * with the results from the last method invocation. In practice we
- * might want to hold the result in an actual CPU register, so the
- * Dalvik spec requires that these only appear immediately after an
- * invoke or filled-new-array.
- *
- * These calls invalidate the "result" register. (This is now
- * redundant with the reset done below, but it can make the debug info
- * easier to read in some cases.)
- */
- case Instruction::MOVE_RESULT:
- work_line_->CopyResultRegister1(inst->VRegA_11x(), false);
- break;
- case Instruction::MOVE_RESULT_WIDE:
- work_line_->CopyResultRegister2(inst->VRegA_11x());
- break;
- case Instruction::MOVE_RESULT_OBJECT:
- work_line_->CopyResultRegister1(inst->VRegA_11x(), true);
- break;
-
- case Instruction::MOVE_EXCEPTION: {
- /*
- * This statement can only appear as the first instruction in an exception handler. We verify
- * that as part of extracting the exception type from the catch block list.
- */
- const RegType& res_type = GetCaughtExceptionType();
- work_line_->SetRegisterType(inst->VRegA_11x(), res_type);
- break;
- }
- case Instruction::RETURN_VOID:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
- if (!GetMethodReturnType().IsConflict()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected";
- }
- }
- break;
- case Instruction::RETURN:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
- /* check the method signature */
- const RegType& return_type = GetMethodReturnType();
- if (!return_type.IsCategory1Types()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected non-category 1 return type " << return_type;
- } else {
- // Compilers may generate synthetic functions that write byte values into boolean fields.
- // Also, it may use integer values for boolean, byte, short, and character return types.
- const uint32_t vregA = inst->VRegA_11x();
- const RegType& src_type = work_line_->GetRegisterType(vregA);
- bool use_src = ((return_type.IsBoolean() && src_type.IsByte()) ||
- ((return_type.IsBoolean() || return_type.IsByte() ||
- return_type.IsShort() || return_type.IsChar()) &&
- src_type.IsInteger()));
- /* check the register contents */
- bool success =
- work_line_->VerifyRegisterType(vregA, use_src ? src_type : return_type);
- if (!success) {
- AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", vregA));
- }
- }
- }
- break;
- case Instruction::RETURN_WIDE:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
- /* check the method signature */
- const RegType& return_type = GetMethodReturnType();
- if (!return_type.IsCategory2Types()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected";
- } else {
- /* check the register contents */
- const uint32_t vregA = inst->VRegA_11x();
- bool success = work_line_->VerifyRegisterType(vregA, return_type);
- if (!success) {
- AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", vregA));
- }
- }
- }
- break;
- case Instruction::RETURN_OBJECT:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
- const RegType& return_type = GetMethodReturnType();
- if (!return_type.IsReferenceTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected";
- } else {
- /* return_type is the *expected* return type, not register value */
- DCHECK(!return_type.IsZero());
- DCHECK(!return_type.IsUninitializedReference());
- const uint32_t vregA = inst->VRegA_11x();
- const RegType& reg_type = work_line_->GetRegisterType(vregA);
- // Disallow returning uninitialized values and verify that the reference in vAA is an
- // instance of the "return_type"
- if (reg_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '" << reg_type << "'";
- } else if (!return_type.IsAssignableFrom(reg_type)) {
- Fail(reg_type.IsUnresolvedTypes() ? VERIFY_ERROR_BAD_CLASS_SOFT : VERIFY_ERROR_BAD_CLASS_HARD)
- << "returning '" << reg_type << "', but expected from declaration '" << return_type << "'";
- }
- }
- }
- break;
-
- /* could be boolean, int, float, or a null reference */
- case Instruction::CONST_4: {
- int32_t val = static_cast<int32_t>(inst->VRegB_11n() << 28) >> 28;
- work_line_->SetRegisterType(inst->VRegA_11n(), reg_types_.FromCat1Const(val, true));
- break;
- }
- case Instruction::CONST_16: {
- int16_t val = static_cast<int16_t>(inst->VRegB_21s());
- work_line_->SetRegisterType(inst->VRegA_21s(), reg_types_.FromCat1Const(val, true));
- break;
- }
- case Instruction::CONST:
- work_line_->SetRegisterType(inst->VRegA_31i(),
- reg_types_.FromCat1Const(inst->VRegB_31i(), true));
- break;
- case Instruction::CONST_HIGH16:
- work_line_->SetRegisterType(inst->VRegA_21h(),
- reg_types_.FromCat1Const(inst->VRegB_21h() << 16, true));
- break;
- /* could be long or double; resolved upon use */
- case Instruction::CONST_WIDE_16: {
- int64_t val = static_cast<int16_t>(inst->VRegB_21s());
- const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_21s(), lo, hi);
- break;
- }
- case Instruction::CONST_WIDE_32: {
- int64_t val = static_cast<int32_t>(inst->VRegB_31i());
- const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_31i(), lo, hi);
- break;
- }
- case Instruction::CONST_WIDE: {
- int64_t val = inst->VRegB_51l();
- const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_51l(), lo, hi);
- break;
- }
- case Instruction::CONST_WIDE_HIGH16: {
- int64_t val = static_cast<uint64_t>(inst->VRegB_21h()) << 48;
- const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_21h(), lo, hi);
- break;
- }
- case Instruction::CONST_STRING:
- work_line_->SetRegisterType(inst->VRegA_21c(), reg_types_.JavaLangString());
- break;
- case Instruction::CONST_STRING_JUMBO:
- work_line_->SetRegisterType(inst->VRegA_31c(), reg_types_.JavaLangString());
- break;
- case Instruction::CONST_CLASS: {
- // Get type from instruction if unresolved then we need an access check
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
- // Register holds class, ie its type is class, on error it will hold Conflict.
- work_line_->SetRegisterType(inst->VRegA_21c(),
- res_type.IsConflict() ? res_type
- : reg_types_.JavaLangClass(true));
- break;
- }
- case Instruction::MONITOR_ENTER:
- work_line_->PushMonitor(inst->VRegA_11x(), work_insn_idx_);
- break;
- case Instruction::MONITOR_EXIT:
- /*
- * monitor-exit instructions are odd. They can throw exceptions,
- * but when they do they act as if they succeeded and the PC is
- * pointing to the following instruction. (This behavior goes back
- * to the need to handle asynchronous exceptions, a now-deprecated
- * feature that Dalvik doesn't support.)
- *
- * In practice we don't need to worry about this. The only
- * exceptions that can be thrown from monitor-exit are for a
- * null reference and -exit without a matching -enter. If the
- * structured locking checks are working, the former would have
- * failed on the -enter instruction, and the latter is impossible.
- *
- * This is fortunate, because issue 3221411 prevents us from
- * chasing the "can throw" path when monitor verification is
- * enabled. If we can fully verify the locking we can ignore
- * some catch blocks (which will show up as "dead" code when
- * we skip them here); if we can't, then the code path could be
- * "live" so we still need to check it.
- */
- opcode_flags &= ~Instruction::kThrow;
- work_line_->PopMonitor(inst->VRegA_11x());
- break;
-
- case Instruction::CHECK_CAST:
- case Instruction::INSTANCE_OF: {
- /*
- * If this instruction succeeds, we will "downcast" register vA to the type in vB. (This
- * could be a "upcast" -- not expected, so we don't try to address it.)
- *
- * If it fails, an exception is thrown, which we deal with later by ignoring the update to
- * dec_insn.vA when branching to a handler.
- */
- const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST);
- const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c();
- const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
- if (res_type.IsConflict()) {
- DCHECK_NE(failures_.size(), 0U);
- if (!is_checkcast) {
- work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
- }
- break; // bad class
- }
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- uint32_t orig_type_reg = (is_checkcast) ? inst->VRegA_21c() : inst->VRegB_22c();
- const RegType& orig_type = work_line_->GetRegisterType(orig_type_reg);
- if (!res_type.IsNonZeroReferenceTypes()) {
- if (is_checkcast) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on unexpected class " << res_type;
- } else {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on unexpected class " << res_type;
- }
- } else if (!orig_type.IsReferenceTypes()) {
- if (is_checkcast) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on non-reference in v" << orig_type_reg;
- } else {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on non-reference in v" << orig_type_reg;
- }
- } else {
- if (is_checkcast) {
- work_line_->SetRegisterType(inst->VRegA_21c(), res_type);
- } else {
- work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
- }
- }
- break;
- }
- case Instruction::ARRAY_LENGTH: {
- const RegType& res_type = work_line_->GetRegisterType(inst->VRegB_12x());
- if (res_type.IsReferenceTypes()) {
- if (!res_type.IsArrayTypes() && !res_type.IsZero()) { // ie not an array or null
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
- } else {
- work_line_->SetRegisterType(inst->VRegA_12x(), reg_types_.Integer());
- }
- }
- break;
- }
- case Instruction::NEW_INSTANCE: {
- const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
- if (res_type.IsConflict()) {
- DCHECK_NE(failures_.size(), 0U);
- break; // bad class
- }
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- // can't create an instance of an interface or abstract class */
- if (!res_type.IsInstantiableTypes()) {
- Fail(VERIFY_ERROR_INSTANTIATION)
- << "new-instance on primitive, interface or abstract class" << res_type;
- // Soft failure so carry on to set register type.
- }
- const RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
- // Any registers holding previous allocations from this address that have not yet been
- // initialized must be marked invalid.
- work_line_->MarkUninitRefsAsInvalid(uninit_type);
- // add the new uninitialized reference to the register state
- work_line_->SetRegisterType(inst->VRegA_21c(), uninit_type);
- break;
- }
- case Instruction::NEW_ARRAY:
- VerifyNewArray(inst, false, false);
- break;
- case Instruction::FILLED_NEW_ARRAY:
- VerifyNewArray(inst, true, false);
- just_set_result = true; // Filled new array sets result register
- break;
- case Instruction::FILLED_NEW_ARRAY_RANGE:
- VerifyNewArray(inst, true, true);
- just_set_result = true; // Filled new array range sets result register
- break;
- case Instruction::CMPL_FLOAT:
- case Instruction::CMPG_FLOAT:
- if (!work_line_->VerifyRegisterType(inst->VRegB_23x(), reg_types_.Float())) {
- break;
- }
- if (!work_line_->VerifyRegisterType(inst->VRegC_23x(), reg_types_.Float())) {
- break;
- }
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
- break;
- case Instruction::CMPL_DOUBLE:
- case Instruction::CMPG_DOUBLE:
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.DoubleLo(),
- reg_types_.DoubleHi())) {
- break;
- }
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.DoubleLo(),
- reg_types_.DoubleHi())) {
- break;
- }
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
- break;
- case Instruction::CMP_LONG:
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.LongLo(),
- reg_types_.LongHi())) {
- break;
- }
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.LongLo(),
- reg_types_.LongHi())) {
- break;
- }
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
- break;
- case Instruction::THROW: {
- const RegType& res_type = work_line_->GetRegisterType(inst->VRegA_11x());
- if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type << " not instanceof Throwable";
- }
- break;
- }
- case Instruction::GOTO:
- case Instruction::GOTO_16:
- case Instruction::GOTO_32:
- /* no effect on or use of registers */
- break;
-
- case Instruction::PACKED_SWITCH:
- case Instruction::SPARSE_SWITCH:
- /* verify that vAA is an integer, or can be converted to one */
- work_line_->VerifyRegisterType(inst->VRegA_31t(), reg_types_.Integer());
- break;
-
- case Instruction::FILL_ARRAY_DATA: {
- /* Similar to the verification done for APUT */
- const RegType& array_type = work_line_->GetRegisterType(inst->VRegA_31t());
- /* array_type can be null if the reg type is Zero */
- if (!array_type.IsZero()) {
- if (!array_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type;
- } else {
- const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
- DCHECK(!component_type.IsConflict());
- if (component_type.IsNonZeroReferenceTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type "
- << component_type;
- } else {
- // Now verify if the element width in the table matches the element width declared in
- // the array
- const uint16_t* array_data = insns + (insns[1] | (((int32_t) insns[2]) << 16));
- if (array_data[0] != Instruction::kArrayDataSignature) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid magic for array-data";
- } else {
- size_t elem_width = Primitive::ComponentSize(component_type.GetPrimitiveType());
- // Since we don't compress the data in Dex, expect to see equal width of data stored
- // in the table and expected from the array class.
- if (array_data[1] != elem_width) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-data size mismatch (" << array_data[1]
- << " vs " << elem_width << ")";
- }
- }
- }
- }
- }
- break;
- }
- case Instruction::IF_EQ:
- case Instruction::IF_NE: {
- const RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t());
- const RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t());
- bool mismatch = false;
- if (reg_type1.IsZero()) { // zero then integral or reference expected
- mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes();
- } else if (reg_type1.IsReferenceTypes()) { // both references?
- mismatch = !reg_type2.IsReferenceTypes();
- } else { // both integral?
- mismatch = !reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes();
- }
- if (mismatch) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to if-eq/if-ne (" << reg_type1 << "," << reg_type2
- << ") must both be references or integral";
- }
- break;
- }
- case Instruction::IF_LT:
- case Instruction::IF_GE:
- case Instruction::IF_GT:
- case Instruction::IF_LE: {
- const RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t());
- const RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t());
- if (!reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to 'if' (" << reg_type1 << ","
- << reg_type2 << ") must be integral";
- }
- break;
- }
- case Instruction::IF_EQZ:
- case Instruction::IF_NEZ: {
- const RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
- if (!reg_type.IsReferenceTypes() && !reg_type.IsIntegralTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type << " unexpected as arg to if-eqz/if-nez";
- }
-
- // Find previous instruction - its existence is a precondition to peephole optimization.
- uint32_t instance_of_idx = 0;
- if (0 != work_insn_idx_) {
- instance_of_idx = work_insn_idx_ - 1;
- while(0 != instance_of_idx && !insn_flags_[instance_of_idx].IsOpcode()) {
- instance_of_idx--;
- }
- CHECK(insn_flags_[instance_of_idx].IsOpcode());
- } else {
- break;
- }
-
- const Instruction* instance_of_inst = Instruction::At(code_item_->insns_ + instance_of_idx);
-
- /* Check for peep-hole pattern of:
- * ...;
- * instance-of vX, vY, T;
- * ifXXX vX, label ;
- * ...;
- * label:
- * ...;
- * and sharpen the type of vY to be type T.
- * Note, this pattern can't be if:
- * - if there are other branches to this branch,
- * - when vX == vY.
- */
- if (!CurrentInsnFlags()->IsBranchTarget() &&
- (Instruction::INSTANCE_OF == instance_of_inst->Opcode()) &&
- (inst->VRegA_21t() == instance_of_inst->VRegA_22c()) &&
- (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) {
- // Check that the we are not attempting conversion to interface types,
- // which is not done because of the multiple inheritance implications.
- const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
-
- if(!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface()) {
- RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this);
- if (inst->Opcode() == Instruction::IF_EQZ) {
- fallthrough_line.reset(update_line);
- } else {
- branch_line.reset(update_line);
- }
- update_line->CopyFromLine(work_line_.get());
- update_line->SetRegisterType(instance_of_inst->VRegB_22c(), cast_type);
- if (!insn_flags_[instance_of_idx].IsBranchTarget() && 0 != instance_of_idx) {
- // See if instance-of was preceded by a move-object operation, common due to the small
- // register encoding space of instance-of, and propagate type information to the source
- // of the move-object.
- uint32_t move_idx = instance_of_idx - 1;
- while(0 != move_idx && !insn_flags_[move_idx].IsOpcode()) {
- move_idx--;
- }
- CHECK(insn_flags_[move_idx].IsOpcode());
- const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx);
- switch (move_inst->Opcode()) {
- case Instruction::MOVE_OBJECT:
- if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
- update_line->SetRegisterType(move_inst->VRegB_12x(), cast_type);
- }
- break;
- case Instruction::MOVE_OBJECT_FROM16:
- if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) {
- update_line->SetRegisterType(move_inst->VRegB_22x(), cast_type);
- }
- break;
- case Instruction::MOVE_OBJECT_16:
- if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) {
- update_line->SetRegisterType(move_inst->VRegB_32x(), cast_type);
- }
- break;
- default:
- break;
- }
- }
- }
- }
-
- break;
- }
- case Instruction::IF_LTZ:
- case Instruction::IF_GEZ:
- case Instruction::IF_GTZ:
- case Instruction::IF_LEZ: {
- const RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
- if (!reg_type.IsIntegralTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
- << " unexpected as arg to if-ltz/if-gez/if-gtz/if-lez";
- }
- break;
- }
- case Instruction::AGET_BOOLEAN:
- VerifyAGet(inst, reg_types_.Boolean(), true);
- break;
- case Instruction::AGET_BYTE:
- VerifyAGet(inst, reg_types_.Byte(), true);
- break;
- case Instruction::AGET_CHAR:
- VerifyAGet(inst, reg_types_.Char(), true);
- break;
- case Instruction::AGET_SHORT:
- VerifyAGet(inst, reg_types_.Short(), true);
- break;
- case Instruction::AGET:
- VerifyAGet(inst, reg_types_.Integer(), true);
- break;
- case Instruction::AGET_WIDE:
- VerifyAGet(inst, reg_types_.LongLo(), true);
- break;
- case Instruction::AGET_OBJECT:
- VerifyAGet(inst, reg_types_.JavaLangObject(false), false);
- break;
-
- case Instruction::APUT_BOOLEAN:
- VerifyAPut(inst, reg_types_.Boolean(), true);
- break;
- case Instruction::APUT_BYTE:
- VerifyAPut(inst, reg_types_.Byte(), true);
- break;
- case Instruction::APUT_CHAR:
- VerifyAPut(inst, reg_types_.Char(), true);
- break;
- case Instruction::APUT_SHORT:
- VerifyAPut(inst, reg_types_.Short(), true);
- break;
- case Instruction::APUT:
- VerifyAPut(inst, reg_types_.Integer(), true);
- break;
- case Instruction::APUT_WIDE:
- VerifyAPut(inst, reg_types_.LongLo(), true);
- break;
- case Instruction::APUT_OBJECT:
- VerifyAPut(inst, reg_types_.JavaLangObject(false), false);
- break;
-
- case Instruction::IGET_BOOLEAN:
- VerifyISGet(inst, reg_types_.Boolean(), true, false);
- break;
- case Instruction::IGET_BYTE:
- VerifyISGet(inst, reg_types_.Byte(), true, false);
- break;
- case Instruction::IGET_CHAR:
- VerifyISGet(inst, reg_types_.Char(), true, false);
- break;
- case Instruction::IGET_SHORT:
- VerifyISGet(inst, reg_types_.Short(), true, false);
- break;
- case Instruction::IGET:
- VerifyISGet(inst, reg_types_.Integer(), true, false);
- break;
- case Instruction::IGET_WIDE:
- VerifyISGet(inst, reg_types_.LongLo(), true, false);
- break;
- case Instruction::IGET_OBJECT:
- VerifyISGet(inst, reg_types_.JavaLangObject(false), false, false);
- break;
-
- case Instruction::IPUT_BOOLEAN:
- VerifyISPut(inst, reg_types_.Boolean(), true, false);
- break;
- case Instruction::IPUT_BYTE:
- VerifyISPut(inst, reg_types_.Byte(), true, false);
- break;
- case Instruction::IPUT_CHAR:
- VerifyISPut(inst, reg_types_.Char(), true, false);
- break;
- case Instruction::IPUT_SHORT:
- VerifyISPut(inst, reg_types_.Short(), true, false);
- break;
- case Instruction::IPUT:
- VerifyISPut(inst, reg_types_.Integer(), true, false);
- break;
- case Instruction::IPUT_WIDE:
- VerifyISPut(inst, reg_types_.LongLo(), true, false);
- break;
- case Instruction::IPUT_OBJECT:
- VerifyISPut(inst, reg_types_.JavaLangObject(false), false, false);
- break;
-
- case Instruction::SGET_BOOLEAN:
- VerifyISGet(inst, reg_types_.Boolean(), true, true);
- break;
- case Instruction::SGET_BYTE:
- VerifyISGet(inst, reg_types_.Byte(), true, true);
- break;
- case Instruction::SGET_CHAR:
- VerifyISGet(inst, reg_types_.Char(), true, true);
- break;
- case Instruction::SGET_SHORT:
- VerifyISGet(inst, reg_types_.Short(), true, true);
- break;
- case Instruction::SGET:
- VerifyISGet(inst, reg_types_.Integer(), true, true);
- break;
- case Instruction::SGET_WIDE:
- VerifyISGet(inst, reg_types_.LongLo(), true, true);
- break;
- case Instruction::SGET_OBJECT:
- VerifyISGet(inst, reg_types_.JavaLangObject(false), false, true);
- break;
-
- case Instruction::SPUT_BOOLEAN:
- VerifyISPut(inst, reg_types_.Boolean(), true, true);
- break;
- case Instruction::SPUT_BYTE:
- VerifyISPut(inst, reg_types_.Byte(), true, true);
- break;
- case Instruction::SPUT_CHAR:
- VerifyISPut(inst, reg_types_.Char(), true, true);
- break;
- case Instruction::SPUT_SHORT:
- VerifyISPut(inst, reg_types_.Short(), true, true);
- break;
- case Instruction::SPUT:
- VerifyISPut(inst, reg_types_.Integer(), true, true);
- break;
- case Instruction::SPUT_WIDE:
- VerifyISPut(inst, reg_types_.LongLo(), true, true);
- break;
- case Instruction::SPUT_OBJECT:
- VerifyISPut(inst, reg_types_.JavaLangObject(false), false, true);
- break;
-
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_SUPER:
- case Instruction::INVOKE_SUPER_RANGE: {
- bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
- inst->Opcode() == Instruction::INVOKE_SUPER_RANGE);
- bool is_super = (inst->Opcode() == Instruction::INVOKE_SUPER ||
- inst->Opcode() == Instruction::INVOKE_SUPER_RANGE);
- mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL,
- is_range, is_super);
- const char* descriptor;
- if (called_method == NULL) {
- uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- 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(&reg_types_));
- }
- just_set_result = true;
- break;
- }
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE: {
- bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
- mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_DIRECT,
- is_range, false);
- const char* return_type_descriptor;
- bool is_constructor;
- if (called_method == NULL) {
- uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- is_constructor = StringPiece(dex_file_->GetMethodName(method_id)) == "<init>";
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- return_type_descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- is_constructor = called_method->IsConstructor();
- return_type_descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
- }
- if (is_constructor) {
- /*
- * Some additional checks when calling a constructor. We know from the invocation arg check
- * that the "this" argument is an instance of called_method->klass. Now we further restrict
- * that to require that called_method->klass is the same as this->klass or this->super,
- * allowing the latter only if the "this" argument is the same as the "this" argument to
- * this method (which implies that we're in a constructor ourselves).
- */
- const RegType& this_type = work_line_->GetInvocationThis(inst, is_range);
- if (this_type.IsConflict()) // failure.
- break;
-
- /* no null refs allowed (?) */
- if (this_type.IsZero()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unable to initialize null ref";
- break;
- }
-
- /* must be in same class or in superclass */
- // const RegType& this_super_klass = this_type.GetSuperClass(&reg_types_);
- // TODO: re-enable constructor type verification
- // if (this_super_klass.IsConflict()) {
- // Unknown super class, fail so we re-check at runtime.
- // Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "super class unknown for '" << this_type << "'";
- // break;
- // }
-
- /* arg must be an uninitialized reference */
- if (!this_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected initialization on uninitialized reference "
- << this_type;
- break;
- }
-
- /*
- * Replace the uninitialized reference with an initialized one. We need to do this for all
- * registers that have the same object instance in them, not just the "this" register.
- */
- work_line_->MarkRefsAsInitialized(this_type);
- }
- const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor,
- false);
- if (!return_type.IsLowHalf()) {
- work_line_->SetResultRegisterType(return_type);
- } else {
- work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
- }
- just_set_result = true;
- break;
- }
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_STATIC_RANGE: {
- bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
- mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range, false);
- const char* descriptor;
- if (called_method == NULL) {
- uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- 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(&reg_types_));
- }
- just_set_result = true;
- }
- break;
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_INTERFACE_RANGE: {
- bool is_range = (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
- mirror::AbstractMethod* abs_method = VerifyInvocationArgs(inst, METHOD_INTERFACE, is_range, false);
- if (abs_method != NULL) {
- mirror::Class* called_interface = abs_method->GetDeclaringClass();
- if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
- << PrettyMethod(abs_method) << "'";
- break;
- }
- }
- /* Get the type of the "this" arg, which should either be a sub-interface of called
- * interface or Object (see comments in RegType::JoinClass).
- */
- const RegType& this_type = work_line_->GetInvocationThis(inst, is_range);
- if (this_type.IsZero()) {
- /* null pointer always passes (and always fails at runtime) */
- } else {
- if (this_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object "
- << this_type;
- break;
- }
- // In the past we have tried to assert that "called_interface" is assignable
- // from "this_type.GetClass()", however, as we do an imprecise Join
- // (RegType::JoinClass) we don't have full information on what interfaces are
- // implemented by "this_type". For example, two classes may implement the same
- // interfaces and have a common parent that doesn't implement the interface. The
- // join will set "this_type" to the parent class and a test that this implements
- // the interface will incorrectly fail.
- }
- /*
- * We don't have an object instance, so we can't find the concrete method. However, all of
- * the type information is in the abstract method, so we're good.
- */
- const char* descriptor;
- if (abs_method == NULL) {
- uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- descriptor = MethodHelper(abs_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(&reg_types_));
- }
- just_set_result = true;
- break;
- }
- case Instruction::NEG_INT:
- case Instruction::NOT_INT:
- work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Integer());
- break;
- case Instruction::NEG_LONG:
- case Instruction::NOT_LONG:
- work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
- break;
- case Instruction::NEG_FLOAT:
- work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Float());
- break;
- case Instruction::NEG_DOUBLE:
- work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
- break;
- case Instruction::INT_TO_LONG:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.Integer());
- break;
- case Instruction::INT_TO_FLOAT:
- work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Integer());
- break;
- case Instruction::INT_TO_DOUBLE:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.Integer());
- break;
- case Instruction::LONG_TO_INT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(),
- reg_types_.LongLo(), reg_types_.LongHi());
- break;
- case Instruction::LONG_TO_FLOAT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(),
- reg_types_.LongLo(), reg_types_.LongHi());
- break;
- case Instruction::LONG_TO_DOUBLE:
- work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
- break;
- case Instruction::FLOAT_TO_INT:
- work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Float());
- break;
- case Instruction::FLOAT_TO_LONG:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.Float());
- break;
- case Instruction::FLOAT_TO_DOUBLE:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.Float());
- break;
- case Instruction::DOUBLE_TO_INT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
- break;
- case Instruction::DOUBLE_TO_LONG:
- work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
- break;
- case Instruction::DOUBLE_TO_FLOAT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
- break;
- case Instruction::INT_TO_BYTE:
- work_line_->CheckUnaryOp(inst, reg_types_.Byte(), reg_types_.Integer());
- break;
- case Instruction::INT_TO_CHAR:
- work_line_->CheckUnaryOp(inst, reg_types_.Char(), reg_types_.Integer());
- break;
- case Instruction::INT_TO_SHORT:
- work_line_->CheckUnaryOp(inst, reg_types_.Short(), reg_types_.Integer());
- break;
-
- case Instruction::ADD_INT:
- case Instruction::SUB_INT:
- case Instruction::MUL_INT:
- case Instruction::REM_INT:
- case Instruction::DIV_INT:
- case Instruction::SHL_INT:
- case Instruction::SHR_INT:
- case Instruction::USHR_INT:
- work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(),
- reg_types_.Integer(), false);
- break;
- case Instruction::AND_INT:
- case Instruction::OR_INT:
- case Instruction::XOR_INT:
- work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(),
- reg_types_.Integer(), true);
- break;
- case Instruction::ADD_LONG:
- case Instruction::SUB_LONG:
- case Instruction::MUL_LONG:
- case Instruction::DIV_LONG:
- case Instruction::REM_LONG:
- case Instruction::AND_LONG:
- case Instruction::OR_LONG:
- case Instruction::XOR_LONG:
- work_line_->CheckBinaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
- break;
- case Instruction::SHL_LONG:
- case Instruction::SHR_LONG:
- case Instruction::USHR_LONG:
- /* shift distance is Int, making these different from other binary operations */
- work_line_->CheckBinaryOpWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.Integer());
- break;
- case Instruction::ADD_FLOAT:
- case Instruction::SUB_FLOAT:
- case Instruction::MUL_FLOAT:
- case Instruction::DIV_FLOAT:
- case Instruction::REM_FLOAT:
- work_line_->CheckBinaryOp(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false);
- break;
- case Instruction::ADD_DOUBLE:
- case Instruction::SUB_DOUBLE:
- case Instruction::MUL_DOUBLE:
- case Instruction::DIV_DOUBLE:
- case Instruction::REM_DOUBLE:
- work_line_->CheckBinaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
- break;
- case Instruction::ADD_INT_2ADDR:
- case Instruction::SUB_INT_2ADDR:
- case Instruction::MUL_INT_2ADDR:
- case Instruction::REM_INT_2ADDR:
- case Instruction::SHL_INT_2ADDR:
- case Instruction::SHR_INT_2ADDR:
- case Instruction::USHR_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
- break;
- case Instruction::AND_INT_2ADDR:
- case Instruction::OR_INT_2ADDR:
- case Instruction::XOR_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true);
- break;
- case Instruction::DIV_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
- break;
- case Instruction::ADD_LONG_2ADDR:
- case Instruction::SUB_LONG_2ADDR:
- case Instruction::MUL_LONG_2ADDR:
- case Instruction::DIV_LONG_2ADDR:
- case Instruction::REM_LONG_2ADDR:
- case Instruction::AND_LONG_2ADDR:
- case Instruction::OR_LONG_2ADDR:
- case Instruction::XOR_LONG_2ADDR:
- work_line_->CheckBinaryOp2addrWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.LongLo(), reg_types_.LongHi());
- break;
- case Instruction::SHL_LONG_2ADDR:
- case Instruction::SHR_LONG_2ADDR:
- case Instruction::USHR_LONG_2ADDR:
- work_line_->CheckBinaryOp2addrWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(),
- reg_types_.Integer());
- break;
- case Instruction::ADD_FLOAT_2ADDR:
- case Instruction::SUB_FLOAT_2ADDR:
- case Instruction::MUL_FLOAT_2ADDR:
- case Instruction::DIV_FLOAT_2ADDR:
- case Instruction::REM_FLOAT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false);
- break;
- case Instruction::ADD_DOUBLE_2ADDR:
- case Instruction::SUB_DOUBLE_2ADDR:
- case Instruction::MUL_DOUBLE_2ADDR:
- case Instruction::DIV_DOUBLE_2ADDR:
- case Instruction::REM_DOUBLE_2ADDR:
- work_line_->CheckBinaryOp2addrWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi(),
- reg_types_.DoubleLo(), reg_types_.DoubleHi());
- break;
- case Instruction::ADD_INT_LIT16:
- case Instruction::RSUB_INT:
- case Instruction::MUL_INT_LIT16:
- case Instruction::DIV_INT_LIT16:
- case Instruction::REM_INT_LIT16:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, true);
- break;
- case Instruction::AND_INT_LIT16:
- case Instruction::OR_INT_LIT16:
- case Instruction::XOR_INT_LIT16:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, true);
- break;
- case Instruction::ADD_INT_LIT8:
- case Instruction::RSUB_INT_LIT8:
- case Instruction::MUL_INT_LIT8:
- case Instruction::DIV_INT_LIT8:
- case Instruction::REM_INT_LIT8:
- case Instruction::SHL_INT_LIT8:
- case Instruction::SHR_INT_LIT8:
- case Instruction::USHR_INT_LIT8:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, false);
- break;
- case Instruction::AND_INT_LIT8:
- case Instruction::OR_INT_LIT8:
- case Instruction::XOR_INT_LIT8:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false);
- break;
-
- // Special instructions.
- case Instruction::RETURN_VOID_BARRIER:
- DCHECK(Runtime::Current()->IsStarted());
- if (!IsConstructor()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void-barrier not expected";
- }
- break;
- // 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(&reg_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_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:
- case Instruction::UNUSED_F5:
- case Instruction::UNUSED_F6:
- case Instruction::UNUSED_F7:
- case Instruction::UNUSED_F8:
- case Instruction::UNUSED_F9:
- case Instruction::UNUSED_FA:
- case Instruction::UNUSED_FB:
- case Instruction::UNUSED_FC:
- case Instruction::UNUSED_FD:
- case Instruction::UNUSED_FE:
- case Instruction::UNUSED_FF:
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
- break;
-
- /*
- * DO NOT add a "default" clause here. Without it the compiler will
- * complain if an instruction is missing (which is desirable).
- */
- } // end - switch (dec_insn.opcode)
-
- if (have_pending_hard_failure_) {
- if (Runtime::Current()->IsCompiler()) {
- /* When compiling, check that the last failure is a hard failure */
- CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD);
- }
- /* immediate failure, reject class */
- info_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_);
- return false;
- } else if (have_pending_runtime_throw_failure_) {
- /* slow path will throw, mark following code as unreachable */
- opcode_flags = Instruction::kThrow;
- }
- /*
- * If we didn't just set the result register, clear it out. This ensures that you can only use
- * "move-result" immediately after the result is set. (We could check this statically, but it's
- * not expensive and it makes our debugging output cleaner.)
- */
- if (!just_set_result) {
- work_line_->SetResultTypeToUnknown();
- }
-
-
-
- /*
- * Handle "branch". Tag the branch target.
- *
- * NOTE: instructions like Instruction::EQZ provide information about the
- * state of the register when the branch is taken or not taken. For example,
- * somebody could get a reference field, check it for zero, and if the
- * branch is taken immediately store that register in a boolean field
- * since the value is known to be zero. We do not currently account for
- * that, and will reject the code.
- *
- * TODO: avoid re-fetching the branch target
- */
- if ((opcode_flags & Instruction::kBranch) != 0) {
- bool isConditional, selfOkay;
- if (!GetBranchOffset(work_insn_idx_, &branch_target, &isConditional, &selfOkay)) {
- /* should never happen after static verification */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad branch";
- return false;
- }
- DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0);
- if (!CheckNotMoveException(code_item_->insns_, work_insn_idx_ + branch_target)) {
- return false;
- }
- /* update branch target, set "changed" if appropriate */
- if (NULL != branch_line.get()) {
- if (!UpdateRegisters(work_insn_idx_ + branch_target, branch_line.get())) {
- return false;
- }
- } else {
- if (!UpdateRegisters(work_insn_idx_ + branch_target, work_line_.get())) {
- return false;
- }
- }
- }
-
- /*
- * Handle "switch". Tag all possible branch targets.
- *
- * We've already verified that the table is structurally sound, so we
- * just need to walk through and tag the targets.
- */
- if ((opcode_flags & Instruction::kSwitch) != 0) {
- int offset_to_switch = insns[1] | (((int32_t) insns[2]) << 16);
- const uint16_t* switch_insns = insns + offset_to_switch;
- int switch_count = switch_insns[1];
- int offset_to_targets, targ;
-
- if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
- /* 0 = sig, 1 = count, 2/3 = first key */
- offset_to_targets = 4;
- } else {
- /* 0 = sig, 1 = count, 2..count * 2 = keys */
- DCHECK((*insns & 0xff) == Instruction::SPARSE_SWITCH);
- offset_to_targets = 2 + 2 * switch_count;
- }
-
- /* verify each switch target */
- for (targ = 0; targ < switch_count; targ++) {
- int offset;
- uint32_t abs_offset;
-
- /* offsets are 32-bit, and only partly endian-swapped */
- offset = switch_insns[offset_to_targets + targ * 2] |
- (((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
- abs_offset = work_insn_idx_ + offset;
- DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_);
- if (!CheckNotMoveException(code_item_->insns_, abs_offset)) {
- return false;
- }
- if (!UpdateRegisters(abs_offset, work_line_.get()))
- return false;
- }
- }
-
- /*
- * Handle instructions that can throw and that are sitting in a "try" block. (If they're not in a
- * "try" block when they throw, control transfers out of the method.)
- */
- if ((opcode_flags & Instruction::kThrow) != 0 && insn_flags_[work_insn_idx_].IsInTry()) {
- bool within_catch_all = false;
- CatchHandlerIterator iterator(*code_item_, work_insn_idx_);
-
- for (; iterator.HasNext(); iterator.Next()) {
- if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
- within_catch_all = true;
- }
- /*
- * Merge registers into the "catch" block. We want to use the "savedRegs" rather than
- * "work_regs", because at runtime the exception will be thrown before the instruction
- * modifies any registers.
- */
- if (!UpdateRegisters(iterator.GetHandlerAddress(), saved_line_.get())) {
- return false;
- }
- }
-
- /*
- * If the monitor stack depth is nonzero, there must be a "catch all" handler for this
- * instruction. This does apply to monitor-exit because of async exception handling.
- */
- if (work_line_->MonitorStackDepth() > 0 && !within_catch_all) {
- /*
- * The state in work_line reflects the post-execution state. If the current instruction is a
- * monitor-enter and the monitor stack was empty, we don't need a catch-all (if it throws,
- * it will do so before grabbing the lock).
- */
- if (inst->Opcode() != Instruction::MONITOR_ENTER || work_line_->MonitorStackDepth() != 1) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "expected to be within a catch-all for an instruction where a monitor is held";
- return false;
- }
- }
- }
-
- /* Handle "continue". Tag the next consecutive instruction.
- * Note: Keep the code handling "continue" case below the "branch" and "switch" cases,
- * because it changes work_line_ when performing peephole optimization
- * and this change should not be used in those cases.
- */
- if ((opcode_flags & Instruction::kContinue) != 0) {
- uint32_t next_insn_idx = work_insn_idx_ + CurrentInsnFlags()->GetLengthInCodeUnits();
- if (next_insn_idx >= code_item_->insns_size_in_code_units_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area";
- return false;
- }
- // The only way to get to a move-exception instruction is to get thrown there. Make sure the
- // next instruction isn't one.
- if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) {
- return false;
- }
- if (NULL != fallthrough_line.get()) {
- // Make workline consistent with fallthrough computed from peephole optimization.
- work_line_->CopyFromLine(fallthrough_line.get());
- }
- RegisterLine* next_line = reg_table_.GetLine(next_insn_idx);
- if (next_line != NULL) {
- // Merge registers into what we have for the next instruction,
- // and set the "changed" flag if needed.
- if (!UpdateRegisters(next_insn_idx, work_line_.get())) {
- return false;
- }
- } else {
- /*
- * We're not recording register data for the next instruction, so we don't know what the
- * prior state was. We have to assume that something has changed and re-evaluate it.
- */
- insn_flags_[next_insn_idx].SetChanged();
- }
- }
-
- /* If we're returning from the method, make sure monitor stack is empty. */
- if ((opcode_flags & Instruction::kReturn) != 0) {
- if (!work_line_->VerifyMonitorStackEmpty()) {
- return false;
- }
- }
-
- /*
- * Update start_guess. Advance to the next instruction of that's
- * possible, otherwise use the branch target if one was found. If
- * neither of those exists we're in a return or throw; leave start_guess
- * alone and let the caller sort it out.
- */
- if ((opcode_flags & Instruction::kContinue) != 0) {
- *start_guess = work_insn_idx_ + insn_flags_[work_insn_idx_].GetLengthInCodeUnits();
- } else if ((opcode_flags & Instruction::kBranch) != 0) {
- /* we're still okay if branch_target is zero */
- *start_guess = work_insn_idx_ + branch_target;
- }
-
- DCHECK_LT(*start_guess, code_item_->insns_size_in_code_units_);
- DCHECK(insn_flags_[*start_guess].IsOpcode());
-
- return true;
-}
-
-const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
- const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
- const RegType& referrer = GetDeclaringClass();
- mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
- const RegType& result =
- klass != NULL ? reg_types_.FromClass(descriptor, klass,
- klass->CannotBeAssignedFromOtherTypes())
- : reg_types_.FromDescriptor(class_loader_, descriptor, false);
- if (result.IsConflict()) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
- << "' in " << referrer;
- return result;
- }
- if (klass == NULL && !result.IsUnresolvedTypes()) {
- dex_cache_->SetResolvedType(class_idx, result.GetClass());
- }
- // Check if access is allowed. Unresolved types use xxxWithAccessCheck to
- // check at runtime if access is allowed and so pass here.
- if (!result.IsUnresolvedTypes() && !referrer.IsUnresolvedTypes() && !referrer.CanAccess(result)) {
- Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
- << referrer << "' -> '" << result << "'";
- }
- return result;
-}
-
-const RegType& MethodVerifier::GetCaughtExceptionType() {
- const RegType* common_super = NULL;
- if (code_item_->tries_size_ != 0) {
- const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
- uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
- for (uint32_t i = 0; i < handlers_size; i++) {
- CatchHandlerIterator iterator(handlers_ptr);
- for (; iterator.HasNext(); iterator.Next()) {
- if (iterator.GetHandlerAddress() == (uint32_t) work_insn_idx_) {
- if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
- common_super = &reg_types_.JavaLangThrowable(false);
- } else {
- const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
- if (common_super == NULL) {
- // Unconditionally assign for the first handler. We don't assert this is a Throwable
- // as that is caught at runtime
- common_super = &exception;
- } else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
- // We don't know enough about the type and the common path merge will result in
- // Conflict. Fail here knowing the correct thing can be done at runtime.
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
- return reg_types_.Conflict();
- } else if (common_super->Equals(exception)) {
- // odd case, but nothing to do
- } else {
- common_super = &common_super->Merge(exception, &reg_types_);
- CHECK(reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super));
- }
- }
- }
- }
- handlers_ptr = iterator.EndDataPointer();
- }
- }
- if (common_super == NULL) {
- /* no catch blocks, or no catches with classes we can find */
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unable to find exception handler";
- return reg_types_.Conflict();
- }
- return *common_super;
-}
-
-mirror::AbstractMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx,
- MethodType method_type) {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
- const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
- if (klass_type.IsConflict()) {
- std::string append(" in attempt to access method ");
- append += dex_file_->GetMethodName(method_id);
- AppendToLastFailMessage(append);
- return NULL;
- }
- if (klass_type.IsUnresolvedTypes()) {
- return NULL; // Can't resolve Class so no more to do here
- }
- mirror::Class* klass = klass_type.GetClass();
- const RegType& referrer = GetDeclaringClass();
- mirror::AbstractMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
- if (res_method == NULL) {
- const char* name = dex_file_->GetMethodName(method_id);
- std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL));
-
- if (method_type == METHOD_DIRECT || method_type == METHOD_STATIC) {
- res_method = klass->FindDirectMethod(name, signature);
- } else if (method_type == METHOD_INTERFACE) {
- res_method = klass->FindInterfaceMethod(name, signature);
- } else {
- res_method = klass->FindVirtualMethod(name, signature);
- }
- if (res_method != NULL) {
- dex_cache_->SetResolvedMethod(dex_method_idx, res_method);
- } else {
- // If a virtual or interface method wasn't found with the expected type, look in
- // the direct methods. This can happen when the wrong invoke type is used or when
- // a class has changed, and will be flagged as an error in later checks.
- if (method_type == METHOD_INTERFACE || method_type == METHOD_VIRTUAL) {
- res_method = klass->FindDirectMethod(name, signature);
- }
- if (res_method == NULL) {
- Fail(VERIFY_ERROR_NO_METHOD) << "couldn't find method "
- << PrettyDescriptor(klass) << "." << name
- << " " << signature;
- return NULL;
- }
- }
- }
- // Make sure calls to constructors are "direct". There are additional restrictions but we don't
- // enforce them here.
- if (res_method->IsConstructor() && method_type != METHOD_DIRECT) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "rejecting non-direct call to constructor "
- << PrettyMethod(res_method);
- return NULL;
- }
- // Disallow any calls to class initializers.
- if (MethodHelper(res_method).IsClassInitializer()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "rejecting call to class initializer "
- << PrettyMethod(res_method);
- return NULL;
- }
- // Check if access is allowed.
- if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
- Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " << PrettyMethod(res_method)
- << " from " << referrer << ")";
- return res_method;
- }
- // Check that invoke-virtual and invoke-super are not used on private methods of the same class.
- if (res_method->IsPrivate() && method_type == METHOD_VIRTUAL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-super/virtual can't be used on private method "
- << PrettyMethod(res_method);
- return NULL;
- }
- // Check that interface methods match interface classes.
- if (klass->IsInterface() && method_type != METHOD_INTERFACE) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "non-interface method " << PrettyMethod(res_method)
- << " is in an interface class " << PrettyClass(klass);
- return NULL;
- } else if (!klass->IsInterface() && method_type == METHOD_INTERFACE) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "interface method " << PrettyMethod(res_method)
- << " is in a non-interface class " << PrettyClass(klass);
- return NULL;
- }
- // See if the method type implied by the invoke instruction matches the access flags for the
- // target method.
- if ((method_type == METHOD_DIRECT && !res_method->IsDirect()) ||
- (method_type == METHOD_STATIC && !res_method->IsStatic()) ||
- ((method_type == METHOD_VIRTUAL || method_type == METHOD_INTERFACE) && res_method->IsDirect())
- ) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "invoke type (" << method_type << ") does not match method "
- " type of " << PrettyMethod(res_method);
- return NULL;
- }
- return res_method;
-}
-
-mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst,
- MethodType method_type,
- bool is_range,
- bool is_super) {
- // Resolve the method. This could be an abstract or concrete method depending on what sort of call
- // we're making.
- const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- mirror::AbstractMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type);
- if (res_method == NULL) { // error or class is unresolved
- return NULL;
- }
-
- // If we're using invoke-super(method), make sure that the executing method's class' superclass
- // has a vtable entry for the target method.
- if (is_super) {
- DCHECK(method_type == METHOD_VIRTUAL);
- const RegType& super = GetDeclaringClass().GetSuperClass(&reg_types_);
- if (super.IsUnresolvedTypes()) {
- Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from "
- << PrettyMethod(dex_method_idx_, *dex_file_)
- << " to super " << PrettyMethod(res_method);
- return NULL;
- }
- mirror::Class* super_klass = super.GetClass();
- if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
- MethodHelper mh(res_method);
- Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
- << PrettyMethod(dex_method_idx_, *dex_file_)
- << " to super " << super
- << "." << mh.GetName()
- << mh.GetSignature();
- return NULL;
- }
- }
- // 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).
- */
- size_t actual_args = 0;
- if (!res_method->IsStatic()) {
- const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
- if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
- return NULL;
- }
- if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
- return NULL;
- }
- if (method_type != METHOD_INTERFACE && !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;
- }
- }
- actual_args++;
- }
- /*
- * 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);
- }
- 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;
- }
-}
-
-mirror::AbstractMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst,
- RegisterLine* reg_line,
- bool is_range) {
- DCHECK(inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK ||
- inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
- const RegType& actual_arg_type = reg_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);
- }
- }
- if (this_class == NULL) {
- return NULL;
- }
- 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());
- return res_method;
-}
-
-mirror::AbstractMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst,
- bool is_range) {
- DCHECK(Runtime::Current()->IsStarted());
- mirror::AbstractMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(),
- is_range);
- if (res_method == NULL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name();
- 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 RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
- if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
- return NULL;
- }
- 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) {
- DCHECK_EQ(inst->Opcode(), Instruction::NEW_ARRAY);
- type_idx = inst->VRegC_22c();
- } else if (!is_range) {
- DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY);
- type_idx = inst->VRegB_35c();
- } else {
- DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE);
- type_idx = inst->VRegB_3rc();
- }
- const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
- if (res_type.IsConflict()) { // bad class
- DCHECK_NE(failures_.size(), 0U);
- } else {
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- if (!res_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type;
- } else if (!is_filled) {
- /* make sure "size" register is valid type */
- work_line_->VerifyRegisterType(inst->VRegB_22c(), reg_types_.Integer());
- /* set register type to array class */
- const RegType& precise_type = reg_types_.FromUninitialized(res_type);
- work_line_->SetRegisterType(inst->VRegA_22c(), precise_type);
- } else {
- // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of
- // the list and fail. It's legal, if silly, for arg_count to be zero.
- const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_);
- uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetArgs(arg);
- }
- for (size_t ui = 0; ui < arg_count; ui++) {
- uint32_t get_reg = is_range ? inst->VRegC_3rc() + ui : arg[ui];
- if (!work_line_->VerifyRegisterType(get_reg, expected_type)) {
- work_line_->SetResultRegisterType(reg_types_.Conflict());
- return;
- }
- }
- // filled-array result goes into "result" register
- const RegType& precise_type = reg_types_.FromUninitialized(res_type);
- work_line_->SetResultRegisterType(precise_type);
- }
- }
-}
-
-void MethodVerifier::VerifyAGet(const Instruction* inst,
- const RegType& insn_type, bool is_primitive) {
- const RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
- if (!index_type.IsArrayIndexTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
- } else {
- const RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x());
- if (array_type.IsZero()) {
- // Null array class; this code path will fail at runtime. Infer a merge-able type from the
- // instruction type. TODO: have a proper notion of bottom here.
- if (!is_primitive || insn_type.IsCategory1Types()) {
- // Reference or category 1
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Zero());
- } else {
- // Category 2
- work_line_->SetRegisterTypeWide(inst->VRegA_23x(), reg_types_.FromCat2ConstLo(0, false),
- reg_types_.FromCat2ConstHi(0, false));
- }
- } else if (!array_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
- } else {
- /* verify the class */
- const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
- if (!component_type.IsReferenceTypes() && !is_primitive) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
- << " source for aget-object";
- } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
- << " source for category 1 aget";
- } else if (is_primitive && !insn_type.Equals(component_type) &&
- !((insn_type.IsInteger() && component_type.IsFloat()) ||
- (insn_type.IsLong() && component_type.IsDouble()))) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
- << " incompatible with aget of type " << insn_type;
- } else {
- // Use knowledge of the field type which is stronger than the type inferred from the
- // instruction, which can't differentiate object types and ints from floats, longs from
- // doubles.
- if (!component_type.IsLowHalf()) {
- work_line_->SetRegisterType(inst->VRegA_23x(), component_type);
- } else {
- work_line_->SetRegisterTypeWide(inst->VRegA_23x(), component_type,
- component_type.HighHalf(&reg_types_));
- }
- }
- }
- }
-}
-
-void MethodVerifier::VerifyAPut(const Instruction* inst,
- const RegType& insn_type, bool is_primitive) {
- const RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
- if (!index_type.IsArrayIndexTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
- } else {
- const RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x());
- if (array_type.IsZero()) {
- // Null array type; this code path will fail at runtime. Infer a merge-able type from the
- // instruction type.
- } else if (!array_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
- } else {
- /* verify the class */
- const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
- if (!component_type.IsReferenceTypes() && !is_primitive) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
- << " source for aput-object";
- } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
- << " source for category 1 aput";
- } else if (is_primitive && !insn_type.Equals(component_type) &&
- !((insn_type.IsInteger() && component_type.IsFloat()) ||
- (insn_type.IsLong() && component_type.IsDouble()))) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
- << " incompatible with aput of type " << insn_type;
- } else {
- // The instruction agrees with the type of array, confirm the value to be stored does too
- // Note: we use the instruction type (rather than the component type) for aput-object as
- // incompatible classes will be caught at runtime as an array store exception
- work_line_->VerifyRegisterType(inst->VRegA_23x(), is_primitive ? component_type : insn_type);
- }
- }
- }
-}
-
-mirror::Field* MethodVerifier::GetStaticField(int field_idx) {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- // Check access to class
- const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
- if (klass_type.IsConflict()) { // bad class
- AppendToLastFailMessage(StringPrintf(" in attempt to access static field %d (%s) in %s",
- field_idx, dex_file_->GetFieldName(field_id),
- dex_file_->GetFieldDeclaringClassDescriptor(field_id)));
- return NULL;
- }
- if (klass_type.IsUnresolvedTypes()) {
- return NULL; // Can't resolve Class so no more to do here, will do checking at runtime.
- }
- mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
- dex_cache_, class_loader_);
- if (field == NULL) {
- LOG(INFO) << "Unable to resolve static field " << field_idx << " ("
- << dex_file_->GetFieldName(field_id) << ") in "
- << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
- DCHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
- return NULL;
- } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
- field->GetAccessFlags())) {
- Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access static field " << PrettyField(field)
- << " from " << GetDeclaringClass();
- return NULL;
- } else if (!field->IsStatic()) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) << " to be static";
- return NULL;
- } else {
- return field;
- }
-}
-
-mirror::Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- // Check access to class
- const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
- if (klass_type.IsConflict()) {
- AppendToLastFailMessage(StringPrintf(" in attempt to access instance field %d (%s) in %s",
- field_idx, dex_file_->GetFieldName(field_id),
- dex_file_->GetFieldDeclaringClassDescriptor(field_id)));
- return NULL;
- }
- if (klass_type.IsUnresolvedTypes()) {
- return NULL; // Can't resolve Class so no more to do here
- }
- mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
- dex_cache_, class_loader_);
- if (field == NULL) {
- LOG(INFO) << "Unable to resolve instance field " << field_idx << " ("
- << dex_file_->GetFieldName(field_id) << ") in "
- << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
- DCHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
- return NULL;
- } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
- field->GetAccessFlags())) {
- Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access instance field " << PrettyField(field)
- << " from " << GetDeclaringClass();
- return NULL;
- } else if (field->IsStatic()) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field)
- << " to not be static";
- return NULL;
- } else if (obj_type.IsZero()) {
- // Cannot infer and check type, however, access will cause null pointer exception
- return field;
- } else {
- mirror::Class* klass = field->GetDeclaringClass();
- const RegType& field_klass =
- reg_types_.FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id),
- klass, klass->CannotBeAssignedFromOtherTypes());
- if (obj_type.IsUninitializedTypes() &&
- (!IsConstructor() || GetDeclaringClass().Equals(obj_type) ||
- !field_klass.Equals(GetDeclaringClass()))) {
- // Field accesses through uninitialized references are only allowable for constructors where
- // the field is declared in this class
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
- << " of a not fully initialized object within the context of "
- << PrettyMethod(dex_method_idx_, *dex_file_);
- return NULL;
- } else if (!field_klass.IsAssignableFrom(obj_type)) {
- // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
- // of C1. For resolution to occur the declared class of the field must be compatible with
- // obj_type, we've discovered this wasn't so, so report the field didn't exist.
- Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field)
- << " from object of type " << obj_type;
- return NULL;
- } else {
- return field;
- }
- }
-}
-
-void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_type,
- bool is_primitive, bool is_static) {
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- mirror::Field* field;
- if (is_static) {
- field = GetStaticField(field_idx);
- } else {
- const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
- field = GetInstanceField(object_type, field_idx);
- }
- const char* descriptor;
- mirror::ClassLoader* loader;
- if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = class_loader_;
- }
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
- const uint32_t vregA = (is_static) ? inst->VRegA_21c() : 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(&reg_types_));
- }
-}
-
-void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_type,
- bool is_primitive, bool is_static) {
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- mirror::Field* field;
- if (is_static) {
- field = GetStaticField(field_idx);
- } else {
- const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
- field = GetInstanceField(object_type, field_idx);
- }
- const char* descriptor;
- mirror::ClassLoader* loader;
- if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = class_loader_;
- }
- 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 = (is_static) ? inst->VRegA_21c() : 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);
- }
-}
-
-// 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(const mirror::Class* klass,
- uint32_t field_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- const 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;
- }
- }
- }
- // We did not find field in class: look into superclass.
- if (klass->GetSuperClass() != NULL) {
- return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset);
- } else {
- return NULL;
- }
-}
-
-// Returns the access field of a quick field access (iget/iput-quick) or NULL
-// if it cannot be found.
-mirror::Field* MethodVerifier::GetQuickFieldAccess(const Instruction* inst,
- RegisterLine* reg_line) {
- DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
- inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
- inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
- inst->Opcode() == Instruction::IPUT_QUICK ||
- inst->Opcode() == Instruction::IPUT_WIDE_QUICK ||
- inst->Opcode() == Instruction::IPUT_OBJECT_QUICK);
- const RegType& object_type = reg_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);
- }
- }
- if (object_class == NULL) {
- // Failed to get the Class* from reg type.
- LOG(WARNING) << "Failed to get Class* from " << object_type;
- return NULL;
- }
- uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
- return FindInstanceFieldWithOffset(object_class, field_offset);
-}
-
-void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
- bool is_primitive) {
- DCHECK(Runtime::Current()->IsStarted());
- mirror::Field* field = GetQuickFieldAccess(inst, work_line_.get());
- if (field == NULL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name();
- return;
- }
- 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(&reg_types_));
- }
-}
-
-void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
- bool is_primitive) {
- DCHECK(Runtime::Current()->IsStarted());
- mirror::Field* field = GetQuickFieldAccess(inst, work_line_.get());
- if (field == NULL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name();
- return;
- }
- 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";
- return false;
- }
- return true;
-}
-
-bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* merge_line) {
- bool changed = true;
- RegisterLine* target_line = reg_table_.GetLine(next_insn);
- if (!insn_flags_[next_insn].IsVisitedOrChanged()) {
- /*
- * We haven't processed this instruction before, and we haven't touched the registers here, so
- * there's nothing to "merge". Copy the registers over and mark it as changed. (This is the
- * only way a register can transition out of "unknown", so this is not just an optimization.)
- */
- target_line->CopyFromLine(merge_line);
- } else {
- UniquePtr<RegisterLine> copy(gDebugVerify ? new RegisterLine(target_line->NumRegs(), this) : NULL);
- if (gDebugVerify) {
- copy->CopyFromLine(target_line);
- }
- changed = target_line->MergeRegisters(merge_line);
- if (have_pending_hard_failure_) {
- return false;
- }
- if (gDebugVerify && changed) {
- LogVerifyInfo() << "Merging at [" << reinterpret_cast<void*>(work_insn_idx_) << "]"
- << " to [" << reinterpret_cast<void*>(next_insn) << "]: " << "\n"
- << *copy.get() << " MERGE\n"
- << *merge_line << " ==\n"
- << *target_line << "\n";
- }
- }
- if (changed) {
- insn_flags_[next_insn].SetChanged();
- }
- return true;
-}
-
-InstructionFlags* MethodVerifier::CurrentInsnFlags() {
- return &insn_flags_[work_insn_idx_];
-}
-
-const RegType& MethodVerifier::GetMethodReturnType() {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
- uint16_t return_type_idx = proto_id.return_type_idx_;
- const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
- return reg_types_.FromDescriptor(class_loader_, descriptor, false);
-}
-
-const RegType& MethodVerifier::GetDeclaringClass() {
- if (declaring_class_ == NULL) {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
- if (mirror_method_ != NULL) {
- mirror::Class* klass = mirror_method_->GetDeclaringClass();
- declaring_class_ = &reg_types_.FromClass(descriptor, klass,
- klass->CannotBeAssignedFromOtherTypes());
- } else {
- declaring_class_ = &reg_types_.FromDescriptor(class_loader_, descriptor, false);
- }
- }
- return *declaring_class_;
-}
-
-void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits,
- size_t* log2_max_gc_pc) {
- size_t local_gc_points = 0;
- size_t max_insn = 0;
- size_t max_ref_reg = -1;
- for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
- if (insn_flags_[i].IsCompileTimeInfoPoint()) {
- local_gc_points++;
- max_insn = i;
- RegisterLine* line = reg_table_.GetLine(i);
- max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
- }
- }
- *gc_points = local_gc_points;
- *ref_bitmap_bits = max_ref_reg + 1; // if max register is 0 we need 1 bit to encode (ie +1)
- size_t i = 0;
- while ((1U << i) <= max_insn) {
- i++;
- }
- *log2_max_gc_pc = i;
-}
-
-MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() {
- /*
- * Walks over the method code and adds any cast instructions in which
- * the type cast is implicit to a set, which is used in the code generation
- * to elide these casts.
- */
- if (!failure_messages_.empty()) {
- return NULL;
- }
- UniquePtr<MethodSafeCastSet> mscs;
- const Instruction* inst = Instruction::At(code_item_->insns_);
- const Instruction* end = Instruction::At(code_item_->insns_ +
- code_item_->insns_size_in_code_units_);
-
- for (; inst < end; inst = inst->Next()) {
- if (Instruction::CHECK_CAST != inst->Opcode()) {
- continue;
- }
- uint32_t dex_pc = inst->GetDexPc(code_item_->insns_);
- RegisterLine* line = reg_table_.GetLine(dex_pc);
- const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
- const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
- if (cast_type.IsStrictlyAssignableFrom(reg_type)) {
- if (mscs.get() == NULL) {
- mscs.reset(new MethodSafeCastSet());
- }
- mscs->insert(dex_pc);
- }
- }
- return mscs.release();
-}
-
-MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() {
-
- // It is risky to rely on reg_types for sharpening in cases of soft
- // verification, we might end up sharpening to a wrong implementation. Just abort.
- if (!failure_messages_.empty()) {
- return NULL;
- }
-
- UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map;
- const uint16_t* insns = code_item_->insns_ ;
- const Instruction* inst = Instruction::At(insns);
- const Instruction* end = Instruction::At(insns + code_item_->insns_size_in_code_units_);
-
- for (; inst < end; inst = inst->Next()) {
- bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
- (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
- bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
-
- if(!is_interface && !is_virtual) {
- continue;
- }
- // Get reg type for register holding the reference to the object that will be dispatched upon.
- uint32_t dex_pc = inst->GetDexPc(insns);
- RegisterLine* line = reg_table_.GetLine(dex_pc);
- bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
- const RegType&
- reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
-
- if (!reg_type.HasClass()) {
- // We will compute devirtualization information only when we know the Class of the reg type.
- continue;
- }
- mirror::Class* reg_class = reg_type.GetClass();
- if (reg_class->IsInterface()) {
- // We can't devirtualize when the known type of the register is an interface.
- continue;
- }
- if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
- // We can't devirtualize abstract classes except on arrays of abstract classes.
- continue;
- }
- mirror::AbstractMethod* abstract_method =
- dex_cache_->GetResolvedMethod(is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
- if(abstract_method == NULL) {
- // If the method is not found in the cache this means that it was never found
- // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
- continue;
- }
- // Find the concrete method.
- mirror::AbstractMethod* concrete_method = NULL;
- if (is_interface) {
- concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
- }
- if (is_virtual) {
- concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
- }
- if (concrete_method == NULL || concrete_method->IsAbstract()) {
- // In cases where concrete_method is not found, or is abstract, continue to the next invoke.
- continue;
- }
- if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
- concrete_method->GetDeclaringClass()->IsFinal()) {
- // If we knew exactly the class being dispatched upon, or if the target method cannot be
- // overridden record the target to be used in the compiler driver.
- if (pc_to_concrete_method_map.get() == NULL) {
- pc_to_concrete_method_map.reset(new PcToConcreteMethodMap());
- }
- MethodReference concrete_ref(
- concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
- concrete_method->GetDexMethodIndex());
- pc_to_concrete_method_map->Put(dex_pc, concrete_ref);
- }
- }
- return pc_to_concrete_method_map.release();
-}
-
-const std::vector<uint8_t>* MethodVerifier::GenerateGcMap() {
- size_t num_entries, ref_bitmap_bits, pc_bits;
- ComputeGcMapSizes(&num_entries, &ref_bitmap_bits, &pc_bits);
- // There's a single byte to encode the size of each bitmap
- if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
- // TODO: either a better GC map format or per method failures
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
- << ref_bitmap_bits << " registers";
- return NULL;
- }
- size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
- // There are 2 bytes to encode the number of entries
- if (num_entries >= 65536) {
- // TODO: either a better GC map format or per method failures
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
- << num_entries << " entries";
- return NULL;
- }
- size_t pc_bytes;
- RegisterMapFormat format;
- if (pc_bits <= 8) {
- format = kRegMapFormatCompact8;
- pc_bytes = 1;
- } else if (pc_bits <= 16) {
- format = kRegMapFormatCompact16;
- pc_bytes = 2;
- } else {
- // TODO: either a better GC map format or per method failures
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
- << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
- return NULL;
- }
- size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
- std::vector<uint8_t>* table = new std::vector<uint8_t>;
- if (table == NULL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")";
- return NULL;
- }
- table->reserve(table_size);
- // Write table header
- table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) &
- ~DexPcToReferenceMap::kRegMapFormatMask));
- table->push_back(ref_bitmap_bytes & 0xFF);
- table->push_back(num_entries & 0xFF);
- table->push_back((num_entries >> 8) & 0xFF);
- // Write table data
- for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
- if (insn_flags_[i].IsCompileTimeInfoPoint()) {
- table->push_back(i & 0xFF);
- if (pc_bytes == 2) {
- table->push_back((i >> 8) & 0xFF);
- }
- RegisterLine* line = reg_table_.GetLine(i);
- line->WriteReferenceBitMap(*table, ref_bitmap_bytes);
- }
- }
- DCHECK_EQ(table->size(), table_size);
- return table;
-}
-
-void MethodVerifier::VerifyGcMap(const std::vector<uint8_t>& data) {
- // Check that for every GC point there is a map entry, there aren't entries for non-GC points,
- // that the table data is well formed and all references are marked (or not) in the bitmap
- DexPcToReferenceMap map(&data[0], data.size());
- size_t map_index = 0;
- for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
- const uint8_t* reg_bitmap = map.FindBitMap(i, false);
- if (insn_flags_[i].IsCompileTimeInfoPoint()) {
- CHECK_LT(map_index, map.NumEntries());
- CHECK_EQ(map.GetDexPc(map_index), i);
- CHECK_EQ(map.GetBitMap(map_index), reg_bitmap);
- map_index++;
- RegisterLine* line = reg_table_.GetLine(i);
- for (size_t j = 0; j < code_item_->registers_size_; j++) {
- if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
- CHECK_LT(j / 8, map.RegWidth());
- CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
- } else if ((j / 8) < map.RegWidth()) {
- CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
- } else {
- // If a register doesn't contain a reference then the bitmap may be shorter than the line
- }
- }
- } else {
- CHECK(reg_bitmap == NULL);
- }
- }
-}
-
-void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>& gc_map) {
- {
- WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
- DexGcMapTable::iterator it = dex_gc_maps_->find(ref);
- if (it != dex_gc_maps_->end()) {
- delete it->second;
- dex_gc_maps_->erase(it);
- }
- dex_gc_maps_->Put(ref, &gc_map);
- }
- DCHECK(GetDexGcMap(ref) != NULL);
-}
-
-
-void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
- MutexLock mu(Thread::Current(), *safecast_map_lock_);
- SafeCastMap::iterator it = safecast_map_->find(ref);
- if (it != safecast_map_->end()) {
- delete it->second;
- safecast_map_->erase(it);
- }
-
- safecast_map_->Put(ref, cast_set);
- CHECK(safecast_map_->find(ref) != safecast_map_->end());
-}
-
-bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) {
- MutexLock mu(Thread::Current(), *safecast_map_lock_);
- SafeCastMap::const_iterator it = safecast_map_->find(ref);
- if (it == safecast_map_->end()) {
- return false;
- }
-
- // Look up the cast address in the set of safe casts
- MethodVerifier::MethodSafeCastSet::const_iterator cast_it = it->second->find(pc);
- return cast_it != it->second->end();
-}
-
-const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(MethodReference ref) {
- ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
- DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref);
- if (it == dex_gc_maps_->end()) {
- LOG(WARNING) << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file);
- return NULL;
- }
- CHECK(it->second != NULL);
- return it->second;
-}
-
-void MethodVerifier::SetDevirtMap(MethodReference ref,
- const PcToConcreteMethodMap* devirt_map) {
- WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_);
- DevirtualizationMapTable::iterator it = devirt_maps_->find(ref);
- if (it != devirt_maps_->end()) {
- delete it->second;
- devirt_maps_->erase(it);
- }
-
- devirt_maps_->Put(ref, devirt_map);
- CHECK(devirt_maps_->find(ref) != devirt_maps_->end());
-}
-
-const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref,
- uint32_t dex_pc) {
- ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_);
- DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref);
- if (it == devirt_maps_->end()) {
- return NULL;
- }
-
- // Look up the PC in the map, get the concrete method to execute and return its reference.
- MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc);
- if(pc_to_concrete_method != it->second->end()) {
- return &(pc_to_concrete_method->second);
- } else {
- return NULL;
- }
-}
-
-std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_pc) {
- RegisterLine* line = reg_table_.GetLine(dex_pc);
- std::vector<int32_t> result;
- for (size_t i = 0; i < line->NumRegs(); ++i) {
- const RegType& type = line->GetRegisterType(i);
- if (type.IsConstant()) {
- result.push_back(type.IsPreciseConstant() ? kConstant : kImpreciseConstant);
- result.push_back(type.ConstantValue());
- } else if (type.IsConstantLo()) {
- result.push_back(type.IsPreciseConstantLo() ? kConstant : kImpreciseConstant);
- result.push_back(type.ConstantValueLo());
- } else if (type.IsConstantHi()) {
- result.push_back(type.IsPreciseConstantHi() ? kConstant : kImpreciseConstant);
- result.push_back(type.ConstantValueHi());
- } else if (type.IsIntegralTypes()) {
- result.push_back(kIntVReg);
- result.push_back(0);
- } else if (type.IsFloat()) {
- result.push_back(kFloatVReg);
- result.push_back(0);
- } else if (type.IsLong()) {
- result.push_back(kLongLoVReg);
- result.push_back(0);
- result.push_back(kLongHiVReg);
- result.push_back(0);
- ++i;
- } else if (type.IsDouble()) {
- result.push_back(kDoubleLoVReg);
- result.push_back(0);
- result.push_back(kDoubleHiVReg);
- result.push_back(0);
- ++i;
- } else if (type.IsUndefined() || type.IsConflict() || type.IsHighHalf()) {
- result.push_back(kUndefined);
- result.push_back(0);
- } else {
- CHECK(type.IsNonZeroReferenceTypes());
- result.push_back(kReferenceVReg);
- result.push_back(0);
- }
- }
- return result;
-}
-
-ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
-MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
-
-Mutex* MethodVerifier::safecast_map_lock_ = NULL;
-MethodVerifier::SafeCastMap* MethodVerifier::safecast_map_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::devirt_maps_lock_ = NULL;
-MethodVerifier::DevirtualizationMapTable* MethodVerifier::devirt_maps_ = NULL;
-
-Mutex* MethodVerifier::rejected_classes_lock_ = NULL;
-MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL;
-
-void MethodVerifier::Init() {
- dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
- }
-
- safecast_map_lock_ = new Mutex("verifier Cast Elision lock");
- {
- MutexLock mu(self, *safecast_map_lock_);
- safecast_map_ = new MethodVerifier::SafeCastMap();
- }
-
- devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
-
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
- }
-
- rejected_classes_lock_ = new Mutex("verifier rejected classes lock");
- {
- MutexLock mu(self, *rejected_classes_lock_);
- rejected_classes_ = new MethodVerifier::RejectedClassesTable;
- }
- art::verifier::RegTypeCache::Init();
-}
-
-void MethodVerifier::Shutdown() {
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- STLDeleteValues(dex_gc_maps_);
- delete dex_gc_maps_;
- dex_gc_maps_ = NULL;
- }
- delete dex_gc_maps_lock_;
- dex_gc_maps_lock_ = NULL;
-
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- STLDeleteValues(devirt_maps_);
- delete devirt_maps_;
- devirt_maps_ = NULL;
- }
- delete devirt_maps_lock_;
- devirt_maps_lock_ = NULL;
-
- {
- MutexLock mu(self, *rejected_classes_lock_);
- delete rejected_classes_;
- rejected_classes_ = NULL;
- }
- delete rejected_classes_lock_;
- rejected_classes_lock_ = NULL;
- verifier::RegTypeCache::ShutDown();
-}
-
-void MethodVerifier::AddRejectedClass(ClassReference ref) {
- {
- MutexLock mu(Thread::Current(), *rejected_classes_lock_);
- rejected_classes_->insert(ref);
- }
- CHECK(IsClassRejected(ref));
-}
-
-bool MethodVerifier::IsClassRejected(ClassReference ref) {
- MutexLock mu(Thread::Current(), *rejected_classes_lock_);
- return (rejected_classes_->find(ref) != rejected_classes_->end());
-}
-
-} // namespace verifier
-} // namespace art