diff options
Diffstat (limited to 'runtime')
162 files changed, 3589 insertions, 17631 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index db9bceaf29..1ac770fd06 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -14,83 +14,6 @@ // limitations under the License. // -cc_defaults { - name: "libdexfile_defaults", - defaults: ["art_defaults"], - host_supported: true, - srcs: [ - "dex/compact_dex_debug_info.cc", - "dex/compact_dex_file.cc", - "dex/descriptors_names.cc", - "dex/dex_file.cc", - "dex/dex_file_exception_helpers.cc", - "dex/dex_file_loader.cc", - "dex/dex_file_tracking_registrar.cc", - "dex/dex_file_verifier.cc", - "dex/dex_instruction.cc", - "dex/modifiers.cc", - "dex/standard_dex_file.cc", - "dex/utf.cc", - ], - - target: { - android: { - static_libs: [ - "libziparchive", - "libz", - "libbase", - ], - shared_libs: [ - "libutils", - ], - }, - host: { - shared_libs: [ - "libziparchive", - "libz", - ], - }, - }, - generated_sources: ["dexfile_operator_srcs"], - include_dirs: [ - "external/zlib", - ], - shared_libs: [ - "liblog", - // For common macros. - "libbase", - "libz", - ], - - // Exporting "." would shadow the system elf.h with our elf.h, - // which in turn breaks any tools that reference this library. - // export_include_dirs: ["."], -} - -gensrcs { - name: "dexfile_operator_srcs", - cmd: "$(location generate-operator-out.py) art/runtime $(in) > $(out)", - tool_files: ["generate-operator-out.py"], - srcs: [ - "dex/dex_file.h", - "dex/dex_file_layout.h", - "dex/dex_instruction.h", - "dex/dex_instruction_utils.h", - "dex/invoke_type.h", - ], - output_extension: "operator_out.cc", -} - -art_cc_library { - name: "libdexfile", - defaults: ["libdexfile_defaults"], - // Leave the symbols in the shared library so that stack unwinders can - // produce meaningful name resolution. - strip: { - keep_symbols: true, - }, -} - // Keep the __jit_debug_register_code symbol as a unique symbol during ICF for architectures where // we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when // new jit code is generated. We don't want it to be called when a different function with the same @@ -133,9 +56,9 @@ cc_defaults { "common_throws.cc", "compiler_filter.cc", "debugger.cc", + "dex/art_dex_file_loader.cc", "dex/dex_file_annotations.cc", "dex/dex_file_layout.cc", - "dex/art_dex_file_loader.cc", "dex_to_dex_decompiler.cc", "elf_file.cc", "exec_utils.cc", @@ -244,7 +167,6 @@ cc_defaults { "native/java_lang_Thread.cc", "native/java_lang_Throwable.cc", "native/java_lang_VMClassLoader.cc", - "native/java_lang_Void.cc", "native/java_lang_invoke_MethodHandleImpl.cc", "native/java_lang_ref_FinalizerReference.cc", "native/java_lang_ref_Reference.cc", @@ -487,7 +409,6 @@ cc_defaults { "jni_platform_headers", ], shared_libs: [ - "libdexfile", "libnativebridge", "libnativeloader", "libbacktrace", @@ -521,9 +442,9 @@ gensrcs { "base/callee_save_type.h", "base/enums.h", "base/mutex.h", - "debugger.h", "base/unix_file/fd_file.h", "class_status.h", + "debugger.h", "dex/dex_file_layout.h", "gc_root.h", "gc/allocator_type.h", @@ -565,6 +486,8 @@ art_cc_library { strip: { keep_symbols: true, }, + shared_libs: ["libdexfile"], + export_shared_lib_headers: ["libdexfile"], } art_cc_library { @@ -573,6 +496,8 @@ art_cc_library { "art_debug_defaults", "libart_defaults", ], + shared_libs: ["libdexfiled"], + export_shared_lib_headers: ["libdexfiled"], } art_cc_library { @@ -637,13 +562,7 @@ art_cc_test { "class_loader_context_test.cc", "class_table_test.cc", "compiler_filter_test.cc", - "dex/code_item_accessors_test.cc", - "dex/compact_dex_debug_info_test.cc", - "dex/compact_dex_file_test.cc", - "dex/dex_file_test.cc", - "dex/dex_file_verifier_test.cc", - "dex/dex_instruction_test.cc", - "dex/utf_test.cc", + "dex/art_dex_file_loader_test.cc", "entrypoints/math_entrypoints_test.cc", "entrypoints/quick/quick_trampoline_entrypoints_test.cc", "entrypoints_order_test.cc", diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc index 2f3639c4b1..37aad21a40 100644 --- a/runtime/arch/memcmp16_test.cc +++ b/runtime/arch/memcmp16_test.cc @@ -100,7 +100,7 @@ static void CheckSeparate(size_t max_length, size_t min_length) { } size_t min = count1 < count2 ? count1 : count2; - bool fill_same = r.next() % 1 == 1; + bool fill_same = r.next() % 2 == 1; if (fill_same) { for (size_t i = 0; i < min; ++i) { diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index e6e35c89c9..8b48aa27f9 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -328,6 +328,7 @@ inline mirror::DexCache* ArtMethod::GetDexCache() { } inline bool ArtMethod::IsProxyMethod() { + DCHECK(!IsRuntimeMethod()) << "ArtMethod::IsProxyMethod called on a runtime method"; // Avoid read barrier since the from-space version of the class will have the correct proxy class // flags since they are constant for the lifetime of the class. return GetDeclaringClass<kWithoutReadBarrier>()->IsProxyClass(); diff --git a/runtime/art_method.h b/runtime/art_method.h index cec2ec4df2..21ee8f19e5 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -551,7 +551,7 @@ class ArtMethod FINAL { // Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal // conventions for a method of managed code. Returns false for Proxy methods. ALWAYS_INLINE bool IsRuntimeMethod() { - return dex_method_index_ == kRuntimeMethodDexMethodIndex;; + return dex_method_index_ == kRuntimeMethodDexMethodIndex; } // Is this a hand crafted method used for something like describing callee saves? diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc index 5e97a63c0d..c706c7ebf2 100644 --- a/runtime/base/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -223,7 +223,7 @@ void BitVector::Subtract(const BitVector *src) { // Difference until max, we know both accept it: // There is no need to do more: // If we are bigger than src, the upper bits are unchanged. - // If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted. + // If we are smaller than src, the nonexistent upper bits are 0 and thus can't get subtracted. for (uint32_t idx = 0; idx < min_size; idx++) { storage_[idx] &= (~(src->GetRawStorageWord(idx))); } diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc index 672431cf9d..aea4e74bb1 100644 --- a/runtime/base/stringpiece.cc +++ b/runtime/base/stringpiece.cc @@ -23,13 +23,6 @@ namespace art { -#if !defined(NDEBUG) -char StringPiece::operator[](size_type i) const { - CHECK_LT(i, length_); - return ptr_[i]; -} -#endif - void StringPiece::CopyToString(std::string* target) const { target->assign(ptr_, length_); } diff --git a/runtime/base/stringpiece.h b/runtime/base/stringpiece.h index 46743e9643..e7109dc18a 100644 --- a/runtime/base/stringpiece.h +++ b/runtime/base/stringpiece.h @@ -20,6 +20,8 @@ #include <string.h> #include <string> +#include <android-base/logging.h> + namespace art { // A string-like object that points to a sized piece of memory. @@ -84,13 +86,10 @@ class StringPiece { length_ = len; } -#if defined(NDEBUG) char operator[](size_type i) const { + DCHECK_LT(i, length_); return ptr_[i]; } -#else - char operator[](size_type i) const; -#endif void remove_prefix(size_type n) { ptr_ += n; diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 5549122c34..05f099f3b2 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -46,6 +46,7 @@ #include "well_known_classes.h" namespace art { +namespace { using android::base::StringAppendF; using android::base::StringPrintf; @@ -1211,7 +1212,7 @@ class ScopedCheck { // this particular instance of JNIEnv. if (env != threadEnv) { // Get the thread owning the JNIEnv that's being used. - Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->self_; + Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->GetSelf(); AbortF("thread %s using JNIEnv* from thread %s", ToStr<Thread>(*self).c_str(), ToStr<Thread>(*envThread).c_str()); return false; @@ -1223,7 +1224,7 @@ class ScopedCheck { case kFlag_CritOkay: // okay to call this method break; case kFlag_CritBad: // not okay to call - if (threadEnv->critical_ > 0) { + if (threadEnv->GetCritical() > 0) { AbortF("thread %s using JNI after critical get", ToStr<Thread>(*self).c_str()); return false; @@ -1231,25 +1232,25 @@ class ScopedCheck { break; case kFlag_CritGet: // this is a "get" call // Don't check here; we allow nested gets. - if (threadEnv->critical_ == 0) { - threadEnv->critical_start_us_ = self->GetCpuMicroTime(); + if (threadEnv->GetCritical() == 0) { + threadEnv->SetCriticalStartUs(self->GetCpuMicroTime()); } - threadEnv->critical_++; + threadEnv->SetCritical(threadEnv->GetCritical() + 1); break; case kFlag_CritRelease: // this is a "release" call - if (threadEnv->critical_ == 0) { + if (threadEnv->GetCritical() == 0) { AbortF("thread %s called too many critical releases", ToStr<Thread>(*self).c_str()); return false; - } else if (threadEnv->critical_ == 1) { + } else if (threadEnv->GetCritical() == 1) { // Leaving the critical region, possibly warn about long critical regions. - uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->critical_start_us_; + uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->GetCriticalStartUs(); if (critical_duration_us > kCriticalWarnTimeUs) { LOG(WARNING) << "JNI critical lock held for " << PrettyDuration(UsToNs(critical_duration_us)) << " on " << *self; } } - threadEnv->critical_--; + threadEnv->SetCritical(threadEnv->GetCritical() - 1); break; default: LOG(FATAL) << "Bad flags (internal error): " << flags_; @@ -2621,7 +2622,7 @@ class CheckJNI { } static const JNINativeInterface* baseEnv(JNIEnv* env) { - return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions_; + return reinterpret_cast<JNIEnvExt*>(env)->GetUncheckedFunctions(); } static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) { @@ -3847,10 +3848,6 @@ const JNINativeInterface gCheckNativeInterface = { CheckJNI::GetObjectRefType, }; -const JNINativeInterface* GetCheckJniNativeInterface() { - return &gCheckNativeInterface; -} - class CheckJII { public: static jint DestroyJavaVM(JavaVM* vm) { @@ -3922,6 +3919,12 @@ const JNIInvokeInterface gCheckInvokeInterface = { CheckJII::AttachCurrentThreadAsDaemon }; +} // anonymous namespace + +const JNINativeInterface* GetCheckJniNativeInterface() { + return &gCheckNativeInterface; +} + const JNIInvokeInterface* GetCheckJniInvokeInterface() { return &gCheckInvokeInterface; } diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index cd6e8d59e8..ae06f8f9bc 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -226,14 +226,7 @@ inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx, const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); ObjPtr<mirror::Class> klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); if (klass != nullptr) { - if (klass->IsInterface()) { - resolved = klass->FindInterfaceMethod(dex_cache, method_idx, pointer_size); - } else { - resolved = klass->FindClassMethod(dex_cache, method_idx, pointer_size); - } - if (resolved != nullptr) { - dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size); - } + resolved = FindResolvedMethod(klass, dex_cache, class_loader, method_idx); } } return resolved; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 800427d6ab..62627287b0 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -457,7 +457,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b VoidFunctor())); // Initialize the SubtypeCheck bitstring for java.lang.Object and java.lang.Class. - { + if (kBitstringSubtypeCheckEnabled) { // It might seem the lock here is unnecessary, however all the SubtypeCheck // functions are annotated to require locks all the way down. // @@ -1856,7 +1856,7 @@ bool ClassLinker::AddImageSpace( visitor(root.Read()); } - { + if (kBitstringSubtypeCheckEnabled) { // Every class in the app image has initially SubtypeCheckInfo in the // Uninitialized state. // @@ -4484,6 +4484,14 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(temp_klass, klass); + // SubtypeCheckInfo::Initialized must happen-before any new-instance for that type. + // See also ClassLinker::EnsureInitialized(). + if (kBitstringSubtypeCheckEnabled) { + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(klass.Get()); + // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck for j.l.r.Proxy is already assigned. + } + { // Lock on klass is released. Lock new class object. ObjectLock<mirror::Class> initialization_lock(self, klass); @@ -5231,7 +5239,7 @@ bool ClassLinker::EnsureInitialized(Thread* self, // can be used as a source for the IsSubClass check, and that all ancestors // of the class are Assigned (can be used as a target for IsSubClass check) // or Overflowed (can be used as a source for IsSubClass check). - { + if (kBitstringSubtypeCheckEnabled) { MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(c.Get()); // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck is already initialized. @@ -7931,6 +7939,38 @@ std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* clas return oss.str(); } +ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader, + uint32_t method_idx) { + // Search for the method using dex_cache and method_idx. The Class::Find*Method() + // functions can optimize the search if the dex_cache is the same as the DexCache + // of the class, with fall-back to name and signature search otherwise. + ArtMethod* resolved = nullptr; + if (klass->IsInterface()) { + resolved = klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_); + } else { + resolved = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); + } + DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); + if (resolved != nullptr) { + // In case of jmvti, the dex file gets verified before being registered, so first + // check if it's registered before checking class tables. + const DexFile& dex_file = *dex_cache->GetDexFile(); + CHECK(!IsDexFileRegistered(Thread::Current(), dex_file) || + FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader)) + << "DexFile referrer: " << dex_file.GetLocation() + << " ClassLoader: " << DescribeLoaders(class_loader, ""); + // Be a good citizen and update the dex cache to speed subsequent calls. + dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_); + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + CHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr) + << "Class: " << klass->PrettyClass() << ", " + << "DexFile referrer: " << dex_file.GetLocation(); + } + return resolved; +} + template <ClassLinker::ResolveMode kResolveMode> ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, @@ -7963,6 +8003,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, << resolved->PrettyMethod() << ";" << resolved << "/0x" << std::hex << resolved->GetAccessFlags() << " ReferencedClass: " << descriptor + << " DexFile referrer: " << dex_file.GetLocation() << " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor); } } else { @@ -7983,19 +8024,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, } if (!valid_dex_cache_method) { - // Search for the method using dex_cache and method_idx. The Class::Find*Method() - // functions can optimize the search if the dex_cache is the same as the DexCache - // of the class, with fall-back to name and signature search otherwise. - if (klass->IsInterface()) { - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size); - } else { - resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); - } - DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); - if (resolved != nullptr) { - // Be a good citizen and update the dex cache to speed subsequent calls. - dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size); - } + resolved = FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); } // Note: We can check for IllegalAccessError only if we have a referrer. diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 16fa1ce801..6bb924fc7d 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -312,6 +312,13 @@ class ClassLinker { ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + // Find a method with the given index from class `klass`, and update the dex cache. + ArtMethod* FindResolvedMethod(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader, + uint32_t method_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a method with a given ID from the DexFile associated with the given DexCache // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are // used as in ResolveType. What is unique is the method type argument which is used to diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 1b6f317053..8f65c66441 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -53,6 +53,11 @@ static void AddReferrerLocation(std::ostream& os, ObjPtr<mirror::Class> referrer } } +static void ThrowException(const char* exception_descriptor) REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + self->ThrowNewException(exception_descriptor, nullptr); +} + static void ThrowException(const char* exception_descriptor, ObjPtr<mirror::Class> referrer, const char* fmt, @@ -243,6 +248,11 @@ void ThrowIllegalArgumentException(const char* msg) { ThrowException("Ljava/lang/IllegalArgumentException;", nullptr, msg); } +// IllegalStateException + +void ThrowIllegalStateException(const char* msg) { + ThrowException("Ljava/lang/IllegalStateException;", nullptr, msg); +} // IncompatibleClassChangeError @@ -314,6 +324,13 @@ void ThrowIncompatibleClassChangeErrorForMethodConflict(ArtMethod* method) { ArtMethod::PrettyMethod(method).c_str()).c_str()); } +// IndexOutOfBoundsException + +void ThrowIndexOutOfBoundsException(int index, int length) { + ThrowException("Ljava/lang/IndexOutOfBoundsException;", nullptr, + StringPrintf("length=%d; index=%d", length, index).c_str()); +} + // InternalError void ThrowInternalError(const char* fmt, ...) { @@ -721,6 +738,12 @@ void ThrowNullPointerException(const char* msg) { ThrowException("Ljava/lang/NullPointerException;", nullptr, msg); } +// ReadOnlyBufferException + +void ThrowReadOnlyBufferException() { + Thread::Current()->ThrowNewException("Ljava/nio/ReadOnlyBufferException;", nullptr); +} + // RuntimeException void ThrowRuntimeException(const char* fmt, ...) { @@ -844,6 +867,12 @@ void ThrowStringIndexOutOfBoundsException(int index, int length) { StringPrintf("length=%d; index=%d", length, index).c_str()); } +// UnsupportedOperationException + +void ThrowUnsupportedOperationException() { + ThrowException("Ljava/lang/UnsupportedOperationException;"); +} + // VerifyError void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) { @@ -855,13 +884,13 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) { // WrongMethodTypeException -void ThrowWrongMethodTypeException(mirror::MethodType* callee_type, - mirror::MethodType* callsite_type) { +void ThrowWrongMethodTypeException(mirror::MethodType* expected_type, + mirror::MethodType* actual_type) { ThrowException("Ljava/lang/invoke/WrongMethodTypeException;", nullptr, StringPrintf("Expected %s but was %s", - callee_type->PrettyDescriptor().c_str(), - callsite_type->PrettyDescriptor().c_str()).c_str()); + expected_type->PrettyDescriptor().c_str(), + actual_type->PrettyDescriptor().c_str()).c_str()); } } // namespace art diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 3512b2b5bf..e9baa4fef0 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -120,6 +120,11 @@ void ThrowIllegalAccessException(const char* msg) void ThrowIllegalArgumentException(const char* msg) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +// IllegalAccessException + +void ThrowIllegalStateException(const char* msg) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + // IncompatibleClassChangeError void ThrowIncompatibleClassChangeError(InvokeType expected_type, @@ -151,6 +156,11 @@ void ThrowIncompatibleClassChangeError(ObjPtr<mirror::Class> referrer, const cha void ThrowIncompatibleClassChangeErrorForMethodConflict(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +// IndexOutOfBoundsException + +void ThrowIndexOutOfBoundsException(int index, int length) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + // InternalError void ThrowInternalError(const char* fmt, ...) @@ -223,6 +233,10 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address = false, uintptr_t ad void ThrowNullPointerException(const char* msg) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +// ReadOnlyBufferException + +void ThrowReadOnlyBufferException() REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + // RuntimeException void ThrowRuntimeException(const char* fmt, ...) @@ -244,6 +258,10 @@ void ThrowStackOverflowError(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) void ThrowStringIndexOutOfBoundsException(int index, int length) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +// UnsupportedOperationException + +void ThrowUnsupportedOperationException() REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + // VerifyError void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) @@ -251,6 +269,7 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; // WrongMethodTypeException + void ThrowWrongMethodTypeException(mirror::MethodType* callee_type, mirror::MethodType* callsite_type) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index 08cf30d5bf..0817cb4b6a 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -25,10 +25,10 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" -#include "compact_dex_file.h" -#include "dex_file.h" -#include "dex_file_verifier.h" -#include "standard_dex_file.h" +#include "dex/compact_dex_file.h" +#include "dex/dex_file.h" +#include "dex/dex_file_verifier.h" +#include "dex/standard_dex_file.h" #include "zip_archive.h" namespace art { diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h index b31d1e94e0..3585381f9b 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/runtime/dex/art_dex_file_loader.h @@ -22,8 +22,8 @@ #include <string> #include <vector> -#include "dex_file_loader.h" #include "base/macros.h" +#include "dex/dex_file_loader.h" namespace art { diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc new file mode 100644 index 0000000000..25d4dd0875 --- /dev/null +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -0,0 +1,307 @@ +/* + * 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 <sys/mman.h> + +#include <memory> + +#include "art_dex_file_loader.h" +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" +#include "common_runtime_test.h" +#include "dex/base64_test_util.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/descriptors_names.h" +#include "dex/dex_file.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "mem_map.h" +#include "os.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +class ArtDexFileLoaderTest : public CommonRuntimeTest {}; + +// TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and +// the tests that depend upon them should be moved to dex_file_loader_test.cc + +TEST_F(ArtDexFileLoaderTest, Open) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested")); + ASSERT_TRUE(dex.get() != nullptr); +} + +TEST_F(ArtDexFileLoaderTest, GetLocationChecksum) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main")); + EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum()); +} + +TEST_F(ArtDexFileLoaderTest, GetChecksum) { + std::vector<uint32_t> checksums; + ScopedObjectAccess soa(Thread::Current()); + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), + &checksums, + &error_msg)) + << error_msg; + ASSERT_EQ(1U, checksums.size()); + EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]); +} + +TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksums) { + std::string error_msg; + std::vector<uint32_t> checksums; + std::string multidex_file = GetTestDexFileName("MultiDex"); + const ArtDexFileLoader dex_file_loader; + EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(multidex_file.c_str(), + &checksums, + &error_msg)) << error_msg; + + std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex"); + ASSERT_EQ(2U, dexes.size()); + ASSERT_EQ(2U, checksums.size()); + + EXPECT_EQ(dexes[0]->GetLocation(), DexFileLoader::GetMultiDexLocation(0, multidex_file.c_str())); + EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]); + + EXPECT_EQ(dexes[1]->GetLocation(), DexFileLoader::GetMultiDexLocation(1, multidex_file.c_str())); + EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]); +} + +TEST_F(ArtDexFileLoaderTest, ClassDefs) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested")); + ASSERT_TRUE(raw.get() != nullptr); + EXPECT_EQ(3U, raw->NumClassDefs()); + + const DexFile::ClassDef& c0 = raw->GetClassDef(0); + EXPECT_STREQ("LNested$1;", raw->GetClassDescriptor(c0)); + + const DexFile::ClassDef& c1 = raw->GetClassDef(1); + EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c1)); + + const DexFile::ClassDef& c2 = raw->GetClassDef(2); + EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c2)); +} + +TEST_F(ArtDexFileLoaderTest, GetMethodSignature) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature")); + ASSERT_TRUE(raw.get() != nullptr); + EXPECT_EQ(1U, raw->NumClassDefs()); + + const DexFile::ClassDef& class_def = raw->GetClassDef(0); + ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def)); + + const uint8_t* class_data = raw->GetClassData(class_def); + ASSERT_TRUE(class_data != nullptr); + ClassDataItemIterator it(*raw, class_data); + + EXPECT_EQ(1u, it.NumDirectMethods()); + + // Check the signature for the static initializer. + { + ASSERT_EQ(1U, it.NumDirectMethods()); + const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); + const char* name = raw->StringDataByIdx(method_id.name_idx_); + ASSERT_STREQ("<init>", name); + std::string signature(raw->GetMethodSignature(method_id).ToString()); + ASSERT_EQ("()V", signature); + } + + // Check all virtual methods. + struct Result { + const char* name; + const char* signature; + const char* pretty_method; + }; + static const Result results[] = { + { + "m1", + "(IDJLjava/lang/Object;)Ljava/lang/Float;", + "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)" + }, + { + "m2", + "(ZSC)LGetMethodSignature;", + "GetMethodSignature GetMethodSignature.m2(boolean, short, char)" + }, + { + "m3", + "()V", + "void GetMethodSignature.m3()" + }, + { + "m4", + "(I)V", + "void GetMethodSignature.m4(int)" + }, + { + "m5", + "(II)V", + "void GetMethodSignature.m5(int, int)" + }, + { + "m6", + "(II[[I)V", + "void GetMethodSignature.m6(int, int, int[][])" + }, + { + "m7", + "(II[[ILjava/lang/Object;)V", + "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)" + }, + { + "m8", + "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V", + "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])" + }, + { + "m9", + "()I", + "int GetMethodSignature.m9()" + }, + { + "mA", + "()[[I", + "int[][] GetMethodSignature.mA()" + }, + { + "mB", + "()[[Ljava/lang/Object;", + "java.lang.Object[][] GetMethodSignature.mB()" + }, + }; + ASSERT_EQ(arraysize(results), it.NumVirtualMethods()); + for (const Result& r : results) { + it.Next(); + const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); + + const char* name = raw->StringDataByIdx(method_id.name_idx_); + ASSERT_STREQ(r.name, name); + + std::string signature(raw->GetMethodSignature(method_id).ToString()); + ASSERT_EQ(r.signature, signature); + + std::string plain_method = std::string("GetMethodSignature.") + r.name; + ASSERT_EQ(plain_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ false)); + ASSERT_EQ(r.pretty_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ true)); + } +} + +TEST_F(ArtDexFileLoaderTest, FindStringId) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature")); + ASSERT_TRUE(raw.get() != nullptr); + EXPECT_EQ(1U, raw->NumClassDefs()); + + const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;", + "D", "I", "J", nullptr }; + for (size_t i = 0; strings[i] != nullptr; i++) { + const char* str = strings[i]; + const DexFile::StringId* str_id = raw->FindStringId(str); + const char* dex_str = raw->GetStringData(*str_id); + EXPECT_STREQ(dex_str, str); + } +} + +TEST_F(ArtDexFileLoaderTest, FindTypeId) { + for (size_t i = 0; i < java_lang_dex_file_->NumTypeIds(); i++) { + const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i)); + const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str); + ASSERT_TRUE(type_str_id != nullptr); + dex::StringIndex type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id); + const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx); + ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str)); + ASSERT_TRUE(type_id != nullptr); + EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id).index_, i); + } +} + +TEST_F(ArtDexFileLoaderTest, FindProtoId) { + for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) { + const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i); + const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find); + std::vector<dex::TypeIndex> to_find_types; + if (to_find_tl != nullptr) { + for (size_t j = 0; j < to_find_tl->Size(); j++) { + to_find_types.push_back(to_find_tl->GetTypeItem(j).type_idx_); + } + } + const DexFile::ProtoId* found = + java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types); + ASSERT_TRUE(found != nullptr); + EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i); + } +} + +TEST_F(ArtDexFileLoaderTest, FindMethodId) { + for (size_t i = 0; i < java_lang_dex_file_->NumMethodIds(); i++) { + const DexFile::MethodId& to_find = java_lang_dex_file_->GetMethodId(i); + const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_); + const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_); + const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_); + const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature); + ASSERT_TRUE(found != nullptr) << "Didn't find method " << i << ": " + << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "." + << java_lang_dex_file_->GetStringData(name) + << java_lang_dex_file_->GetMethodSignature(to_find); + EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i); + } +} + +TEST_F(ArtDexFileLoaderTest, FindFieldId) { + for (size_t i = 0; i < java_lang_dex_file_->NumFieldIds(); i++) { + const DexFile::FieldId& to_find = java_lang_dex_file_->GetFieldId(i); + const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_); + const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_); + const DexFile::TypeId& type = java_lang_dex_file_->GetTypeId(to_find.type_idx_); + const DexFile::FieldId* found = java_lang_dex_file_->FindFieldId(klass, name, type); + ASSERT_TRUE(found != nullptr) << "Didn't find field " << i << ": " + << java_lang_dex_file_->StringByTypeIdx(to_find.type_idx_) << " " + << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "." + << java_lang_dex_file_->GetStringData(name); + EXPECT_EQ(java_lang_dex_file_->GetIndexForFieldId(*found), i); + } +} + +TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { + ScratchFile file; + UniqueCPtr<const char[]> dex_location_real(realpath(file.GetFilename().c_str(), nullptr)); + std::string dex_location(dex_location_real.get()); + + ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location.c_str())); + std::string multidex_location = DexFileLoader::GetMultiDexLocation(1, dex_location.c_str()); + ASSERT_EQ(multidex_location, DexFileLoader::GetDexCanonicalLocation(multidex_location.c_str())); + + std::string dex_location_sym = dex_location + "symlink"; + ASSERT_EQ(0, symlink(dex_location.c_str(), dex_location_sym.c_str())); + + ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location_sym.c_str())); + + std::string multidex_location_sym = DexFileLoader::GetMultiDexLocation( + 1, dex_location_sym.c_str()); + ASSERT_EQ(multidex_location, + DexFileLoader::GetDexCanonicalLocation(multidex_location_sym.c_str())); + + ASSERT_EQ(0, unlink(dex_location_sym.c_str())); +} + +} // namespace art diff --git a/runtime/dex/base64_test_util.h b/runtime/dex/base64_test_util.h deleted file mode 100644 index 0657f9fd01..0000000000 --- a/runtime/dex/base64_test_util.h +++ /dev/null @@ -1,99 +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. - */ - -#ifndef ART_RUNTIME_DEX_BASE64_TEST_UTIL_H_ -#define ART_RUNTIME_DEX_BASE64_TEST_UTIL_H_ - -#include <base/logging.h> -#include <stdint.h> -#include <stdlib.h> - -#include <memory> -#include <vector> - -namespace art { - -static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) { - static const uint8_t kBase64Map[256] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, - 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255 - }; - - CHECK(dst_size != nullptr); - std::vector<uint8_t> tmp; - uint32_t t = 0, y = 0; - int g = 3; - for (size_t i = 0; src[i] != '\0'; ++i) { - uint8_t c = kBase64Map[src[i] & 0xFF]; - if (c == 255) continue; - // the final = symbols are read and used to trim the remaining bytes - if (c == 254) { - c = 0; - // prevent g < 0 which would potentially allow an overflow later - if (--g < 0) { - *dst_size = 0; - return nullptr; - } - } else if (g != 3) { - // we only allow = to be at the end - *dst_size = 0; - return nullptr; - } - t = (t << 6) | c; - if (++y == 4) { - tmp.push_back((t >> 16) & 255); - if (g > 1) { - tmp.push_back((t >> 8) & 255); - } - if (g > 2) { - tmp.push_back(t & 255); - } - y = t = 0; - } - } - if (y != 0) { - *dst_size = 0; - return nullptr; - } - std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]); - *dst_size = tmp.size(); - std::copy(tmp.begin(), tmp.end(), dst.get()); - return dst.release(); -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_BASE64_TEST_UTIL_H_ diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h deleted file mode 100644 index 9c39935d3b..0000000000 --- a/runtime/dex/code_item_accessors-inl.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_ -#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_ - -#include "code_item_accessors.h" - -#include "compact_dex_file.h" -#include "dex_file-inl.h" -#include "standard_dex_file.h" - -// The no ART version is used by binaries that don't include the whole runtime. -namespace art { - -inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units, - const uint16_t* insns) { - insns_size_in_code_units_ = insns_size_in_code_units; - insns_ = insns; -} - -inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { - uint32_t insns_size_in_code_units; - code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>( - &insns_size_in_code_units, - /*registers_size*/ nullptr, - /*ins_size*/ nullptr, - /*outs_size*/ nullptr, - /*tries_size*/ nullptr); - Init(insns_size_in_code_units, code_item.insns_); -} - -inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) { - Init(code_item.insns_size_in_code_units_, code_item.insns_); -} - -inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item != nullptr) { - DCHECK(dex_file.IsInDataSection(code_item)); - if (dex_file.IsCompactDexFile()) { - Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); - } else { - DCHECK(dex_file.IsStandardDexFile()); - Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); - } - } -} - -inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( - const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - Init(dex_file, code_item); -} - -inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { - return DexInstructionIterator(insns_, 0u); -} - -inline DexInstructionIterator CodeItemInstructionAccessor::end() const { - return DexInstructionIterator(insns_, insns_size_in_code_units_); -} - -inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom( - uint32_t start_dex_pc) const { - DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits()); - return { - DexInstructionIterator(insns_, start_dex_pc), - DexInstructionIterator(insns_, insns_size_in_code_units_) }; -} - -inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { - uint32_t insns_size_in_code_units; - code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units, - ®isters_size_, - &ins_size_, - &outs_size_, - &tries_size_); - CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_); -} - -inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) { - CodeItemInstructionAccessor::Init(code_item); - registers_size_ = code_item.registers_size_; - ins_size_ = code_item.ins_size_; - outs_size_ = code_item.outs_size_; - tries_size_ = code_item.tries_size_; -} - -inline void CodeItemDataAccessor::Init(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item != nullptr) { - if (dex_file.IsCompactDexFile()) { - CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); - } else { - DCHECK(dex_file.IsStandardDexFile()); - CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); - } - } -} - -inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - Init(dex_file, code_item); -} - -inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const { - const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); - return { - try_items, - try_items + TriesSize() }; -} - -inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const { - return DexFile::GetCatchHandlerData(end(), TriesSize(), offset); -} - -inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const { - IterationRange<const DexFile::TryItem*> try_items(TryItems()); - int32_t index = DexFile::FindTryItem(try_items.begin(), - try_items.end() - try_items.begin(), - try_dex_pc); - return index != -1 ? &try_items.begin()[index] : nullptr; -} - -inline const void* CodeItemDataAccessor::CodeItemDataEnd() const { - const uint8_t* handler_data = GetCatchHandlerData(); - - if (TriesSize() == 0 || handler_data == nullptr) { - return &end().Inst(); - } - // Get the start of the handler data. - const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data); - // Manually read each handler. - for (uint32_t i = 0; i < handlers_size; ++i) { - int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2; - if (uleb128_count <= 0) { - uleb128_count = -uleb128_count + 1; - } - for (int32_t j = 0; j < uleb128_count; ++j) { - DecodeUnsignedLeb128(&handler_data); - } - } - return reinterpret_cast<const void*>(handler_data); -} - -inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file, - const DexFile::CodeItem* code_item, - uint32_t dex_method_index) { - if (code_item == nullptr) { - return; - } - dex_file_ = &dex_file; - if (dex_file.IsCompactDexFile()) { - Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index); - } else { - DCHECK(dex_file.IsStandardDexFile()); - Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); - } -} - -inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item, - uint32_t dex_method_index) { - debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset( - dex_method_index); - CodeItemDataAccessor::Init(code_item); -} - -inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { - debug_info_offset_ = code_item.debug_info_off_; - CodeItemDataAccessor::Init(code_item); -} - -template<typename NewLocalCallback> -inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, - uint32_t method_idx, - NewLocalCallback new_local, - void* context) const { - return dex_file_->DecodeDebugLocalInfo(RegistersSize(), - InsSize(), - InsnsSizeInCodeUnits(), - DebugInfoOffset(), - is_static, - method_idx, - new_local, - context); -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h deleted file mode 100644 index 8082be3818..0000000000 --- a/runtime/dex/code_item_accessors-no_art-inl.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ -#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ - -// TODO: delete this file once system/core is updated. -#include "code_item_accessors-inl.h" - -#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ diff --git a/runtime/dex/code_item_accessors.h b/runtime/dex/code_item_accessors.h deleted file mode 100644 index beb78f6e4f..0000000000 --- a/runtime/dex/code_item_accessors.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump. - -#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_ -#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_ - -#include "base/mutex.h" -#include "compact_dex_file.h" -#include "dex_file.h" -#include "dex_instruction_iterator.h" -#include "standard_dex_file.h" - -namespace art { - -class ArtMethod; - -// Abstracts accesses to the instruction fields of code items for CompactDexFile and -// StandardDexFile. -class CodeItemInstructionAccessor { - public: - ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item); - - ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method); - - ALWAYS_INLINE DexInstructionIterator begin() const; - - ALWAYS_INLINE DexInstructionIterator end() const; - - IterationRange<DexInstructionIterator> InstructionsFrom(uint32_t start_dex_pc) const; - - uint32_t InsnsSizeInCodeUnits() const { - return insns_size_in_code_units_; - } - - const uint16_t* Insns() const { - return insns_; - } - - // Return the instruction for a dex pc. - const Instruction& InstructionAt(uint32_t dex_pc) const { - DCHECK_LT(dex_pc, InsnsSizeInCodeUnits()); - return *Instruction::At(insns_ + dex_pc); - } - - // Return true if the accessor has a code item. - bool HasCodeItem() const { - return Insns() != nullptr; - } - - protected: - CodeItemInstructionAccessor() = default; - - ALWAYS_INLINE void Init(uint32_t insns_size_in_code_units, const uint16_t* insns); - ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); - ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); - ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item); - - private: - // size of the insns array, in 2 byte code units. 0 if there is no code item. - uint32_t insns_size_in_code_units_ = 0; - - // Pointer to the instructions, null if there is no code item. - const uint16_t* insns_ = 0; -}; - -// Abstracts accesses to code item fields other than debug info for CompactDexFile and -// StandardDexFile. -class CodeItemDataAccessor : public CodeItemInstructionAccessor { - public: - ALWAYS_INLINE CodeItemDataAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item); - - uint16_t RegistersSize() const { - return registers_size_; - } - - uint16_t InsSize() const { - return ins_size_; - } - - uint16_t OutsSize() const { - return outs_size_; - } - - uint16_t TriesSize() const { - return tries_size_; - } - - IterationRange<const DexFile::TryItem*> TryItems() const; - - const uint8_t* GetCatchHandlerData(size_t offset = 0) const; - - const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const; - - inline const void* CodeItemDataEnd() const; - - protected: - CodeItemDataAccessor() = default; - - ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); - ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); - ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item); - - private: - // Fields mirrored from the dex/cdex code item. - uint16_t registers_size_; - uint16_t ins_size_; - uint16_t outs_size_; - uint16_t tries_size_; -}; - -// Abstract accesses to code item data including debug info offset. More heavy weight than the other -// helpers. -class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { - public: - CodeItemDebugInfoAccessor() = default; - - // Initialize with an existing offset. - ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item, - uint32_t dex_method_index) { - Init(dex_file, code_item, dex_method_index); - } - - ALWAYS_INLINE void Init(const DexFile& dex_file, - const DexFile::CodeItem* code_item, - uint32_t dex_method_index); - - ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); - - uint32_t DebugInfoOffset() const { - return debug_info_offset_; - } - - template<typename NewLocalCallback> - bool DecodeDebugLocalInfo(bool is_static, - uint32_t method_idx, - NewLocalCallback new_local, - void* context) const; - - protected: - ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index); - ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); - - private: - const DexFile* dex_file_ = nullptr; - uint32_t debug_info_offset_ = 0u; -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_ diff --git a/runtime/dex/code_item_accessors_test.cc b/runtime/dex/code_item_accessors_test.cc deleted file mode 100644 index 2bb4dde649..0000000000 --- a/runtime/dex/code_item_accessors_test.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "code_item_accessors-inl.h" - -#include <sys/mman.h> -#include <memory> -#include <vector> - -#include "dex_file_loader.h" -#include "gtest/gtest.h" - -namespace art { - -class CodeItemAccessorsTest : public testing::Test {}; - -std::unique_ptr<const DexFile> CreateFakeDex(bool compact_dex, std::vector<uint8_t>* data) { - data->resize(kPageSize); - if (compact_dex) { - CompactDexFile::Header* header = - const_cast<CompactDexFile::Header*>(CompactDexFile::Header::At(data->data())); - CompactDexFile::WriteMagic(header->magic_); - CompactDexFile::WriteCurrentVersion(header->magic_); - header->data_off_ = 0; - header->data_size_ = data->size(); - } else { - StandardDexFile::WriteMagic(data->data()); - StandardDexFile::WriteCurrentVersion(data->data()); - } - const DexFileLoader dex_file_loader; - std::string error_msg; - std::unique_ptr<const DexFile> dex(dex_file_loader.Open(data->data(), - data->size(), - "location", - /*location_checksum*/ 123, - /*oat_dex_file*/nullptr, - /*verify*/false, - /*verify_checksum*/false, - &error_msg)); - CHECK(dex != nullptr) << error_msg; - return dex; -} - -TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) { - std::vector<uint8_t> standard_dex_data; - std::unique_ptr<const DexFile> standard_dex(CreateFakeDex(/*compact_dex*/false, - &standard_dex_data)); - ASSERT_TRUE(standard_dex != nullptr); - std::vector<uint8_t> compact_dex_data; - std::unique_ptr<const DexFile> compact_dex(CreateFakeDex(/*compact_dex*/true, - &compact_dex_data)); - ASSERT_TRUE(compact_dex != nullptr); - static constexpr uint16_t kRegisterSize = 2; - static constexpr uint16_t kInsSize = 1; - static constexpr uint16_t kOutsSize = 3; - static constexpr uint16_t kTriesSize = 4; - // debug_info_off_ is not accessible from the helpers yet. - static constexpr size_t kInsnsSizeInCodeUnits = 5; - - auto verify_code_item = [&](const DexFile* dex, - const DexFile::CodeItem* item, - const uint16_t* insns) { - CodeItemInstructionAccessor insns_accessor(*dex, item); - EXPECT_TRUE(insns_accessor.HasCodeItem()); - ASSERT_EQ(insns_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits); - EXPECT_EQ(insns_accessor.Insns(), insns); - - CodeItemDataAccessor data_accessor(*dex, item); - EXPECT_TRUE(data_accessor.HasCodeItem()); - EXPECT_EQ(data_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits); - EXPECT_EQ(data_accessor.Insns(), insns); - EXPECT_EQ(data_accessor.RegistersSize(), kRegisterSize); - EXPECT_EQ(data_accessor.InsSize(), kInsSize); - EXPECT_EQ(data_accessor.OutsSize(), kOutsSize); - EXPECT_EQ(data_accessor.TriesSize(), kTriesSize); - }; - - StandardDexFile::CodeItem* dex_code_item = - reinterpret_cast<StandardDexFile::CodeItem*>(const_cast<uint8_t*>(standard_dex->Begin())); - dex_code_item->registers_size_ = kRegisterSize; - dex_code_item->ins_size_ = kInsSize; - dex_code_item->outs_size_ = kOutsSize; - dex_code_item->tries_size_ = kTriesSize; - dex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits; - verify_code_item(standard_dex.get(), dex_code_item, dex_code_item->insns_); - - CompactDexFile::CodeItem* cdex_code_item = - reinterpret_cast<CompactDexFile::CodeItem*>(const_cast<uint8_t*>(compact_dex->Begin() + - CompactDexFile::CodeItem::kMaxPreHeaderSize * sizeof(uint16_t))); - std::vector<uint16_t> preheader; - cdex_code_item->Create(kRegisterSize, - kInsSize, - kOutsSize, - kTriesSize, - kInsnsSizeInCodeUnits, - cdex_code_item->GetPreHeader()); - - verify_code_item(compact_dex.get(), cdex_code_item, cdex_code_item->insns_); -} - -} // namespace art diff --git a/runtime/dex/compact_dex_debug_info.cc b/runtime/dex/compact_dex_debug_info.cc deleted file mode 100644 index 19495ca92c..0000000000 --- a/runtime/dex/compact_dex_debug_info.cc +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2018 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 "compact_dex_debug_info.h" - -#include "compact_dex_utils.h" -#include "leb128.h" - -namespace art { - -constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex; - -CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin, - uint32_t debug_info_base, - uint32_t debug_info_table_offset) - : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)), - debug_info_base_(debug_info_base), - data_begin_(data_begin) {} - -uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const { - const uint32_t offset = table_[method_idx / kElementsPerIndex]; - const size_t bit_index = method_idx % kElementsPerIndex; - - const uint8_t* block = data_begin_ + offset; - uint16_t bit_mask = *block; - ++block; - bit_mask = (bit_mask << kBitsPerByte) | *block; - ++block; - if ((bit_mask & (1 << bit_index)) == 0) { - // Bit is not set means the offset is 0 for the debug info. - return 0u; - } - // Trim off the bits above the index we want and count how many bits are set. This is how many - // lebs we need to decode. - size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index)); - DCHECK_GT(count, 0u); - uint32_t current_offset = debug_info_base_; - do { - current_offset += DecodeUnsignedLeb128(&block); - --count; - } while (count > 0); - return current_offset; -} - -void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets, - std::vector<uint8_t>* out_data, - uint32_t* out_min_offset, - uint32_t* out_table_offset) { - DCHECK(out_data != nullptr); - DCHECK(out_data->empty()); - // Calculate the base offset and return it. - *out_min_offset = std::numeric_limits<uint32_t>::max(); - for (const uint32_t offset : debug_info_offsets) { - if (offset != 0u) { - *out_min_offset = std::min(*out_min_offset, offset); - } - } - // Write the leb blocks and store the important offsets (each kElementsPerIndex elements). - size_t block_start = 0; - - std::vector<uint32_t> offset_table; - - // Write data first then the table. - while (block_start < debug_info_offsets.size()) { - // Write the offset of the block for each block. - offset_table.push_back(out_data->size()); - - // Block size of up to kElementsPerIndex - const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex); - - // Calculate bit mask since need to write that first. - uint16_t bit_mask = 0u; - for (size_t i = 0; i < block_size; ++i) { - if (debug_info_offsets[block_start + i] != 0u) { - bit_mask |= 1 << i; - } - } - // Write bit mask. - out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte)); - out_data->push_back(static_cast<uint8_t>(bit_mask)); - - // Write debug info offsets relative to the current offset. - uint32_t current_offset = *out_min_offset; - for (size_t i = 0; i < block_size; ++i) { - const uint32_t debug_info_offset = debug_info_offsets[block_start + i]; - if (debug_info_offset != 0u) { - uint32_t delta = debug_info_offset - current_offset; - EncodeUnsignedLeb128(out_data, delta); - current_offset = debug_info_offset; - } - } - - block_start += block_size; - } - - // Write the offset table. - AlignmentPadVector(out_data, alignof(uint32_t)); - *out_table_offset = out_data->size(); - out_data->insert(out_data->end(), - reinterpret_cast<const uint8_t*>(&offset_table[0]), - reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size())); -} - -} // namespace art diff --git a/runtime/dex/compact_dex_debug_info.h b/runtime/dex/compact_dex_debug_info.h deleted file mode 100644 index 1aff75879e..0000000000 --- a/runtime/dex/compact_dex_debug_info.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#ifndef ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ -#define ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ - -#include <cstdint> -#include <vector> - -namespace art { - -// Debug offset table for compact dex, aims to minimize size while still providing reasonable -// speed (10-20ns average time per lookup on host). -class CompactDexDebugInfoOffsetTable { - public: - // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the - // integer is modified. - static constexpr size_t kElementsPerIndex = 16; - - // Leb block format: - // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk. - // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset - // changes compared to the previous index. - - class Accessor { - public: - Accessor(const uint8_t* data_begin, - uint32_t debug_info_base, - uint32_t debug_info_table_offset); - - // Return the debug info for a method index (or 0 if it doesn't have one). - uint32_t GetDebugInfoOffset(uint32_t method_idx) const; - - private: - const uint32_t* const table_; - const uint32_t debug_info_base_; - const uint8_t* const data_begin_; - }; - - // Returned offsets are all relative to debug_info_offsets. - static void Build(const std::vector<uint32_t>& debug_info_offsets, - std::vector<uint8_t>* out_data, - uint32_t* out_min_offset, - uint32_t* out_table_offset); - - // 32 bit aligned for the offset table. - static constexpr size_t kAlignment = sizeof(uint32_t); -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ diff --git a/runtime/dex/compact_dex_debug_info_test.cc b/runtime/dex/compact_dex_debug_info_test.cc deleted file mode 100644 index 3267612443..0000000000 --- a/runtime/dex/compact_dex_debug_info_test.cc +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2018 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 <vector> - -#include "base/logging.h" -#include "dex/compact_dex_debug_info.h" -#include "gtest/gtest.h" - -namespace art { - -TEST(CompactDexDebugInfoTest, TestBuildAndAccess) { - const size_t kDebugInfoMinOffset = 1234567; - std::vector<uint32_t> offsets = { - 0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0, - std::numeric_limits<uint32_t>::max() - kDebugInfoMinOffset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, - }; - // Add some large offset since the debug info section will never be that close to the beginning - // of the file. - for (uint32_t& offset : offsets) { - if (offset != 0u) { - offset += kDebugInfoMinOffset; - } - } - - std::vector<uint8_t> data; - uint32_t base_offset = 0; - uint32_t table_offset = 0; - CompactDexDebugInfoOffsetTable::Build(offsets, - /*out*/ &data, - /*out*/ &base_offset, - /*out*/ &table_offset); - EXPECT_GE(base_offset, kDebugInfoMinOffset); - EXPECT_LT(table_offset, data.size()); - ASSERT_GT(data.size(), 0u); - const size_t before_size = offsets.size() * sizeof(offsets.front()); - EXPECT_LT(data.size(), before_size); - - // Note that the accessor requires the data to be aligned. Use memmap to accomplish this. - std::string error_msg; - // Leave some extra room since we don't copy the table at the start (for testing). - constexpr size_t kExtraOffset = 4 * 128; - std::vector<uint8_t> fake_dex(data.size() + kExtraOffset); - std::copy(data.begin(), data.end(), fake_dex.data() + kExtraOffset); - - CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset, - base_offset, - table_offset); - for (size_t i = 0; i < offsets.size(); ++i) { - EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i)); - } - - // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller - // for sorted increasing order. - std::sort(offsets.begin(), offsets.end()); - std::vector<uint8_t> sorted_data; - CompactDexDebugInfoOffsetTable::Build(offsets, - /*out*/ &sorted_data, - /*out*/ &base_offset, - /*out*/ &table_offset); - EXPECT_LT(sorted_data.size(), data.size()); - { - ScopedLogSeverity sls(LogSeverity::INFO); - LOG(INFO) << "raw size " << before_size - << " table size " << data.size() - << " sorted table size " << sorted_data.size(); - } -} - -} // namespace art diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc deleted file mode 100644 index ce289d4d7b..0000000000 --- a/runtime/dex/compact_dex_file.cc +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compact_dex_file.h" - -#include "code_item_accessors-inl.h" -#include "dex_file-inl.h" -#include "leb128.h" - -namespace art { - -constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize]; -constexpr uint8_t CompactDexFile::kDexMagicVersion[]; - -void CompactDexFile::WriteMagic(uint8_t* magic) { - std::copy_n(kDexMagic, kDexMagicSize, magic); -} - -void CompactDexFile::WriteCurrentVersion(uint8_t* magic) { - std::copy_n(kDexMagicVersion, kDexVersionLen, magic + kDexMagicSize); -} - -bool CompactDexFile::IsMagicValid(const uint8_t* magic) { - return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); -} - -bool CompactDexFile::IsVersionValid(const uint8_t* magic) { - const uint8_t* version = &magic[sizeof(kDexMagic)]; - return memcmp(version, kDexMagicVersion, kDexVersionLen) == 0; -} - -bool CompactDexFile::IsMagicValid() const { - return IsMagicValid(header_->magic_); -} - -bool CompactDexFile::IsVersionValid() const { - return IsVersionValid(header_->magic_); -} - -bool CompactDexFile::SupportsDefaultMethods() const { - return (GetHeader().GetFeatureFlags() & - static_cast<uint32_t>(FeatureFlags::kDefaultMethods)) != 0; -} - -uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { - DCHECK(IsInDataSection(&item)); - return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) - - reinterpret_cast<uintptr_t>(&item); -} - - -uint32_t CompactDexFile::CalculateChecksum(const uint8_t* base_begin, - size_t base_size, - const uint8_t* data_begin, - size_t data_size) { - Header temp_header(*Header::At(base_begin)); - // Zero out fields that are not included in the sum. - temp_header.checksum_ = 0u; - temp_header.data_off_ = 0u; - temp_header.data_size_ = 0u; - uint32_t checksum = ChecksumMemoryRange(reinterpret_cast<const uint8_t*>(&temp_header), - sizeof(temp_header)); - // Exclude the header since we already computed it's checksum. - checksum = (checksum * 31) ^ ChecksumMemoryRange(base_begin + sizeof(temp_header), - base_size - sizeof(temp_header)); - checksum = (checksum * 31) ^ ChecksumMemoryRange(data_begin, data_size); - return checksum; -} - -uint32_t CompactDexFile::CalculateChecksum() const { - return CalculateChecksum(Begin(), Size(), DataBegin(), DataSize()); -} - -CompactDexFile::CompactDexFile(const uint8_t* base, - size_t size, - const uint8_t* data_begin, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - DexFileContainer* container) - : DexFile(base, - size, - data_begin, - data_size, - location, - location_checksum, - oat_dex_file, - container, - /*is_compact_dex*/ true), - debug_info_offsets_(DataBegin() + GetHeader().debug_info_offsets_pos_, - GetHeader().debug_info_base_, - GetHeader().debug_info_offsets_table_offset_) {} - -} // namespace art diff --git a/runtime/dex/compact_dex_file.h b/runtime/dex/compact_dex_file.h deleted file mode 100644 index 31aeb27872..0000000000 --- a/runtime/dex/compact_dex_file.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_ -#define ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_ - -#include "base/casts.h" -#include "dex_file.h" -#include "dex/compact_dex_debug_info.h" - -namespace art { - -// CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage. -class CompactDexFile : public DexFile { - public: - static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; - static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; - - enum class FeatureFlags : uint32_t { - kDefaultMethods = 0x1, - }; - - class Header : public DexFile::Header { - public: - static const Header* At(const void* at) { - return reinterpret_cast<const Header*>(at); - } - - uint32_t GetFeatureFlags() const { - return feature_flags_; - } - - uint32_t GetDataOffset() const { - return data_off_; - } - - uint32_t GetDataSize() const { - return data_size_; - } - - private: - uint32_t feature_flags_ = 0u; - - // Position in the compact dex file for the debug info table data starts. - uint32_t debug_info_offsets_pos_ = 0u; - - // Offset into the debug info table data where the lookup table is. - uint32_t debug_info_offsets_table_offset_ = 0u; - - // Base offset of where debug info starts in the dex file. - uint32_t debug_info_base_ = 0u; - - friend class CompactDexFile; - friend class CompactDexWriter; - }; - - // Like the standard code item except without a debug info offset. Each code item may have a - // preheader to encode large methods. In 99% of cases, the preheader is not used. This enables - // smaller size with a good fast path case in the accessors. - struct CodeItem : public DexFile::CodeItem { - static constexpr size_t kAlignment = sizeof(uint16_t); - // Max preheader size in uint16_ts. - static constexpr size_t kMaxPreHeaderSize = 6; - - private: - CodeItem() = default; - - static constexpr size_t kRegistersSizeShift = 12; - static constexpr size_t kInsSizeShift = 8; - static constexpr size_t kOutsSizeShift = 4; - static constexpr size_t kTriesSizeSizeShift = 0; - static constexpr uint16_t kFlagPreHeaderRegisterSize = 0x1 << 0; - static constexpr uint16_t kFlagPreHeaderInsSize = 0x1 << 1; - static constexpr uint16_t kFlagPreHeaderOutsSize = 0x1 << 2; - static constexpr uint16_t kFlagPreHeaderTriesSize = 0x1 << 3; - static constexpr uint16_t kFlagPreHeaderInsnsSize = 0x1 << 4; - static constexpr size_t kInsnsSizeShift = 5; - static constexpr size_t kInsnsSizeBits = sizeof(uint16_t) * kBitsPerByte - kInsnsSizeShift; - - // Combined preheader flags for fast testing if we need to go slow path. - static constexpr uint16_t kFlagPreHeaderCombined = - kFlagPreHeaderRegisterSize | - kFlagPreHeaderInsSize | - kFlagPreHeaderOutsSize | - kFlagPreHeaderTriesSize | - kFlagPreHeaderInsnsSize; - - // Create a code item and associated preheader if required based on field values. - // Returns the start of the preheader. The preheader buffer must be at least as large as - // kMaxPreHeaderSize; - uint16_t* Create(uint16_t registers_size, - uint16_t ins_size, - uint16_t outs_size, - uint16_t tries_size, - uint32_t insns_size_in_code_units, - uint16_t* out_preheader) { - // Dex verification ensures that registers size > ins_size, so we can subtract the registers - // size accordingly to reduce how often we need to use the preheader. - DCHECK_GE(registers_size, ins_size); - registers_size -= ins_size; - fields_ = (registers_size & 0xF) << kRegistersSizeShift; - fields_ |= (ins_size & 0xF) << kInsSizeShift; - fields_ |= (outs_size & 0xF) << kOutsSizeShift; - fields_ |= (tries_size & 0xF) << kTriesSizeSizeShift; - registers_size &= ~0xF; - ins_size &= ~0xF; - outs_size &= ~0xF; - tries_size &= ~0xF; - insns_count_and_flags_ = 0; - const size_t masked_count = insns_size_in_code_units & ((1 << kInsnsSizeBits) - 1); - insns_count_and_flags_ |= masked_count << kInsnsSizeShift; - insns_size_in_code_units -= masked_count; - - // Since the preheader case is rare (1% of code items), use a suboptimally large but fast - // decoding format. - if (insns_size_in_code_units != 0) { - insns_count_and_flags_ |= kFlagPreHeaderInsnsSize; - --out_preheader; - *out_preheader = static_cast<uint16_t>(insns_size_in_code_units); - --out_preheader; - *out_preheader = static_cast<uint16_t>(insns_size_in_code_units >> 16); - } - auto preheader_encode = [&](uint16_t size, uint16_t flag) { - if (size != 0) { - insns_count_and_flags_ |= flag; - --out_preheader; - *out_preheader = size; - } - }; - preheader_encode(registers_size, kFlagPreHeaderRegisterSize); - preheader_encode(ins_size, kFlagPreHeaderInsSize); - preheader_encode(outs_size, kFlagPreHeaderOutsSize); - preheader_encode(tries_size, kFlagPreHeaderTriesSize); - return out_preheader; - } - - ALWAYS_INLINE bool HasPreHeader(uint16_t flag) const { - return (insns_count_and_flags_ & flag) != 0; - } - - // Return true if the code item has any preheaders. - ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) { - return (insns_count_and_flags & kFlagPreHeaderCombined) != 0; - } - - ALWAYS_INLINE uint16_t* GetPreHeader() { - return reinterpret_cast<uint16_t*>(this); - } - - ALWAYS_INLINE const uint16_t* GetPreHeader() const { - return reinterpret_cast<const uint16_t*>(this); - } - - // Decode fields and read the preheader if necessary. If kDecodeOnlyInstructionCount is - // specified then only the instruction count is decoded. - template <bool kDecodeOnlyInstructionCount> - ALWAYS_INLINE void DecodeFields(uint32_t* insns_count, - uint16_t* registers_size, - uint16_t* ins_size, - uint16_t* outs_size, - uint16_t* tries_size) const { - *insns_count = insns_count_and_flags_ >> kInsnsSizeShift; - if (!kDecodeOnlyInstructionCount) { - const uint16_t fields = fields_; - *registers_size = (fields >> kRegistersSizeShift) & 0xF; - *ins_size = (fields >> kInsSizeShift) & 0xF; - *outs_size = (fields >> kOutsSizeShift) & 0xF; - *tries_size = (fields >> kTriesSizeSizeShift) & 0xF; - } - if (UNLIKELY(HasAnyPreHeader(insns_count_and_flags_))) { - const uint16_t* preheader = GetPreHeader(); - if (HasPreHeader(kFlagPreHeaderInsnsSize)) { - --preheader; - *insns_count += static_cast<uint32_t>(*preheader); - --preheader; - *insns_count += static_cast<uint32_t>(*preheader) << 16; - } - if (!kDecodeOnlyInstructionCount) { - if (HasPreHeader(kFlagPreHeaderRegisterSize)) { - --preheader; - *registers_size += preheader[0]; - } - if (HasPreHeader(kFlagPreHeaderInsSize)) { - --preheader; - *ins_size += preheader[0]; - } - if (HasPreHeader(kFlagPreHeaderOutsSize)) { - --preheader; - *outs_size += preheader[0]; - } - if (HasPreHeader(kFlagPreHeaderTriesSize)) { - --preheader; - *tries_size += preheader[0]; - } - } - } - if (!kDecodeOnlyInstructionCount) { - *registers_size += *ins_size; - } - } - - // Packed code item data, 4 bits each: [registers_size, ins_size, outs_size, tries_size] - uint16_t fields_; - - // 5 bits for if either of the fields required preheader extension, 11 bits for the number of - // instruction code units. - uint16_t insns_count_and_flags_; - - uint16_t insns_[1]; // actual array of bytecode. - - ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); - ART_FRIEND_TEST(CompactDexFileTest, CodeItemFields); - friend class CodeItemDataAccessor; - friend class CodeItemDebugInfoAccessor; - friend class CodeItemInstructionAccessor; - friend class CompactDexFile; - friend class CompactDexWriter; - DISALLOW_COPY_AND_ASSIGN(CodeItem); - }; - - // Write the compact dex specific magic. - static void WriteMagic(uint8_t* magic); - - // Write the current version, note that the input is the address of the magic. - static void WriteCurrentVersion(uint8_t* magic); - - // Returns true if the byte string points to the magic value. - static bool IsMagicValid(const uint8_t* magic); - virtual bool IsMagicValid() const OVERRIDE; - - // Returns true if the byte string after the magic is the correct value. - static bool IsVersionValid(const uint8_t* magic); - virtual bool IsVersionValid() const OVERRIDE; - - // TODO This is completely a guess. We really need to do better. b/72402467 - // We ask for 64 megabytes which should be big enough for any realistic dex file. - virtual size_t GetDequickenedSize() const OVERRIDE { - return 64 * MB; - } - - const Header& GetHeader() const { - return down_cast<const Header&>(DexFile::GetHeader()); - } - - virtual bool SupportsDefaultMethods() const OVERRIDE; - - uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; - - uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const { - return debug_info_offsets_.GetDebugInfoOffset(dex_method_index); - } - - static uint32_t CalculateChecksum(const uint8_t* base_begin, - size_t base_size, - const uint8_t* data_begin, - size_t data_size); - virtual uint32_t CalculateChecksum() const OVERRIDE; - - private: - CompactDexFile(const uint8_t* base, - size_t size, - const uint8_t* data_begin, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - DexFileContainer* container); - - CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_; - - friend class DexFile; - friend class DexFileLoader; - DISALLOW_COPY_AND_ASSIGN(CompactDexFile); -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_ diff --git a/runtime/dex/compact_dex_file_test.cc b/runtime/dex/compact_dex_file_test.cc deleted file mode 100644 index 517c5873ed..0000000000 --- a/runtime/dex/compact_dex_file_test.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "compact_dex_file.h" -#include "dex_file_loader.h" -#include "gtest/gtest.h" - -namespace art { - -TEST(CompactDexFileTest, MagicAndVersion) { - // Test permutations of valid/invalid headers. - for (size_t i = 0; i < 2; ++i) { - for (size_t j = 0; j < 2; ++j) { - static const size_t len = CompactDexFile::kDexVersionLen + CompactDexFile::kDexMagicSize; - uint8_t header[len] = {}; - std::fill_n(header, len, 0x99); - const bool valid_magic = (i & 1) == 0; - const bool valid_version = (j & 1) == 0; - if (valid_magic) { - CompactDexFile::WriteMagic(header); - } - if (valid_version) { - CompactDexFile::WriteCurrentVersion(header); - } - EXPECT_EQ(valid_magic, CompactDexFile::IsMagicValid(header)); - EXPECT_EQ(valid_version, CompactDexFile::IsVersionValid(header)); - EXPECT_EQ(valid_magic, DexFileLoader::IsMagicValid(header)); - EXPECT_EQ(valid_magic && valid_version, DexFileLoader::IsVersionAndMagicValid(header)); - } - } -} - -TEST(CompactDexFileTest, CodeItemFields) { - auto test_and_write = [&] (uint16_t registers_size, - uint16_t ins_size, - uint16_t outs_size, - uint16_t tries_size, - uint32_t insns_size_in_code_units) { - ASSERT_GE(registers_size, ins_size); - uint16_t buffer[sizeof(CompactDexFile::CodeItem) + - CompactDexFile::CodeItem::kMaxPreHeaderSize] = {}; - CompactDexFile::CodeItem* code_item = reinterpret_cast<CompactDexFile::CodeItem*>( - &buffer[CompactDexFile::CodeItem::kMaxPreHeaderSize]); - const uint16_t* preheader_ptr = code_item->Create(registers_size, - ins_size, - outs_size, - tries_size, - insns_size_in_code_units, - code_item->GetPreHeader()); - ASSERT_GT(preheader_ptr, buffer); - - uint16_t out_registers_size; - uint16_t out_ins_size; - uint16_t out_outs_size; - uint16_t out_tries_size; - uint32_t out_insns_size_in_code_units; - code_item->DecodeFields</*kDecodeOnlyInstructionCount*/false>(&out_insns_size_in_code_units, - &out_registers_size, - &out_ins_size, - &out_outs_size, - &out_tries_size); - ASSERT_EQ(registers_size, out_registers_size); - ASSERT_EQ(ins_size, out_ins_size); - ASSERT_EQ(outs_size, out_outs_size); - ASSERT_EQ(tries_size, out_tries_size); - ASSERT_EQ(insns_size_in_code_units, out_insns_size_in_code_units); - - ++out_insns_size_in_code_units; // Force value to change. - code_item->DecodeFields</*kDecodeOnlyInstructionCount*/true>(&out_insns_size_in_code_units, - /*registers_size*/ nullptr, - /*ins_size*/ nullptr, - /*outs_size*/ nullptr, - /*tries_size*/ nullptr); - ASSERT_EQ(insns_size_in_code_units, out_insns_size_in_code_units); - }; - static constexpr uint32_t kMax32 = std::numeric_limits<uint32_t>::max(); - static constexpr uint16_t kMax16 = std::numeric_limits<uint16_t>::max(); - test_and_write(0, 0, 0, 0, 0); - test_and_write(kMax16, kMax16, kMax16, kMax16, kMax32); - test_and_write(kMax16 - 1, kMax16 - 2, kMax16 - 3, kMax16 - 4, kMax32 - 5); - test_and_write(kMax16 - 4, kMax16 - 5, kMax16 - 3, kMax16 - 2, kMax32 - 1); - test_and_write(5, 4, 3, 2, 1); - test_and_write(5, 0, 3, 2, 1); - test_and_write(kMax16, 0, kMax16 / 2, 1234, kMax32 / 4); -} - -} // namespace art diff --git a/runtime/dex/compact_dex_level.h b/runtime/dex/compact_dex_level.h deleted file mode 100644 index de9ca3c783..0000000000 --- a/runtime/dex/compact_dex_level.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_COMPACT_DEX_LEVEL_H_ -#define ART_RUNTIME_DEX_COMPACT_DEX_LEVEL_H_ - -#include <string> - -#include "base/macros.h" -#include "dex_file.h" - -namespace art { - -// Optimization level for compact dex generation. -enum class CompactDexLevel { - // Level none means not generated. - kCompactDexLevelNone, - // Level fast means optimizations that don't take many resources to perform. - kCompactDexLevelFast, -}; - -#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL -#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified. -#else -#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast -#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone - -#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \ - ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \ - ART_DEFAULT_COMPACT_DEX_LEVEL) - -static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT; -#endif - -} // namespace art - -#endif // ART_RUNTIME_DEX_COMPACT_DEX_LEVEL_H_ diff --git a/runtime/dex/compact_dex_utils.h b/runtime/dex/compact_dex_utils.h deleted file mode 100644 index 1c7e9514fd..0000000000 --- a/runtime/dex/compact_dex_utils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#ifndef ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ -#define ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ - -#include <vector> - -#include "base/bit_utils.h" - -namespace art { - -// Add padding to the end of the array until the size is aligned. -template <typename T, template<typename> class Allocator> -static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest, - size_t alignment) { - while (!IsAlignedParam(dest->size(), alignment)) { - dest->push_back(T()); - } -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ diff --git a/runtime/dex/descriptors_names.cc b/runtime/dex/descriptors_names.cc deleted file mode 100644 index 8124e7256f..0000000000 --- a/runtime/dex/descriptors_names.cc +++ /dev/null @@ -1,426 +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 "descriptors_names.h" - -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "dex/utf-inl.h" - -namespace art { - -using android::base::StringAppendF; -using android::base::StringPrintf; - -void AppendPrettyDescriptor(const char* descriptor, std::string* result) { - // Count the number of '['s to get the dimensionality. - const char* c = descriptor; - size_t dim = 0; - while (*c == '[') { - dim++; - c++; - } - - // Reference or primitive? - if (*c == 'L') { - // "[[La/b/C;" -> "a.b.C[][]". - c++; // Skip the 'L'. - } else { - // "[[B" -> "byte[][]". - // To make life easier, we make primitives look like unqualified - // reference types. - switch (*c) { - case 'B': c = "byte;"; break; - case 'C': c = "char;"; break; - case 'D': c = "double;"; break; - case 'F': c = "float;"; break; - case 'I': c = "int;"; break; - case 'J': c = "long;"; break; - case 'S': c = "short;"; break; - case 'Z': c = "boolean;"; break; - case 'V': c = "void;"; break; // Used when decoding return types. - default: result->append(descriptor); return; - } - } - - // At this point, 'c' is a string of the form "fully/qualified/Type;" - // or "primitive;". Rewrite the type with '.' instead of '/': - const char* p = c; - while (*p != ';') { - char ch = *p++; - if (ch == '/') { - ch = '.'; - } - result->push_back(ch); - } - // ...and replace the semicolon with 'dim' "[]" pairs: - for (size_t i = 0; i < dim; ++i) { - result->append("[]"); - } -} - -std::string PrettyDescriptor(const char* descriptor) { - std::string result; - AppendPrettyDescriptor(descriptor, &result); - return result; -} - -std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) { - // Remove the leading 'L' and trailing ';'... - std::string class_name(class_descriptor); - CHECK_EQ(class_name[0], 'L') << class_name; - CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name; - class_name.erase(0, 1); - class_name.erase(class_name.size() - 1, 1); - - std::string short_name; - short_name += "Java_"; - short_name += MangleForJni(class_name); - short_name += "_"; - short_name += MangleForJni(method); - return short_name; -} - -// See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules. -std::string MangleForJni(const std::string& s) { - std::string result; - size_t char_count = CountModifiedUtf8Chars(s.c_str()); - const char* cp = &s[0]; - for (size_t i = 0; i < char_count; ++i) { - uint32_t ch = GetUtf16FromUtf8(&cp); - if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) { - result.push_back(ch); - } else if (ch == '.' || ch == '/') { - result += "_"; - } else if (ch == '_') { - result += "_1"; - } else if (ch == ';') { - result += "_2"; - } else if (ch == '[') { - result += "_3"; - } else { - const uint16_t leading = GetLeadingUtf16Char(ch); - const uint32_t trailing = GetTrailingUtf16Char(ch); - - StringAppendF(&result, "_0%04x", leading); - if (trailing != 0) { - StringAppendF(&result, "_0%04x", trailing); - } - } - } - return result; -} - -std::string DotToDescriptor(const char* class_name) { - std::string descriptor(class_name); - std::replace(descriptor.begin(), descriptor.end(), '.', '/'); - if (descriptor.length() > 0 && descriptor[0] != '[') { - descriptor = "L" + descriptor + ";"; - } - return descriptor; -} - -std::string DescriptorToDot(const char* descriptor) { - size_t length = strlen(descriptor); - if (length > 1) { - if (descriptor[0] == 'L' && descriptor[length - 1] == ';') { - // Descriptors have the leading 'L' and trailing ';' stripped. - std::string result(descriptor + 1, length - 2); - std::replace(result.begin(), result.end(), '/', '.'); - return result; - } else { - // For arrays the 'L' and ';' remain intact. - std::string result(descriptor); - std::replace(result.begin(), result.end(), '/', '.'); - return result; - } - } - // Do nothing for non-class/array descriptors. - return descriptor; -} - -std::string DescriptorToName(const char* descriptor) { - size_t length = strlen(descriptor); - if (descriptor[0] == 'L' && descriptor[length - 1] == ';') { - std::string result(descriptor + 1, length - 2); - return result; - } - return descriptor; -} - -// Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii. -static uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = { - 0x00000000, // 00..1f low control characters; nothing valid - 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-' - 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_' - 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z' -}; - -// Helper for IsValidPartOfMemberNameUtf8(); do not call directly. -static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { - /* - * It's a multibyte encoded character. Decode it and analyze. We - * accept anything that isn't (a) an improperly encoded low value, - * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high - * control character, or (e) a high space, layout, or special - * character (U+00a0, U+2000..U+200f, U+2028..U+202f, - * U+fff0..U+ffff). This is all specified in the dex format - * document. - */ - - const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr); - const uint16_t leading = GetLeadingUtf16Char(pair); - - // We have a surrogate pair resulting from a valid 4 byte UTF sequence. - // No further checks are necessary because 4 byte sequences span code - // points [U+10000, U+1FFFFF], which are valid codepoints in a dex - // identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of - // the surrogate halves are valid and well formed in this instance. - if (GetTrailingUtf16Char(pair) != 0) { - return true; - } - - - // We've encountered a one, two or three byte UTF-8 sequence. The - // three byte UTF-8 sequence could be one half of a surrogate pair. - switch (leading >> 8) { - case 0x00: - // It's only valid if it's above the ISO-8859-1 high space (0xa0). - return (leading > 0x00a0); - case 0xd8: - case 0xd9: - case 0xda: - case 0xdb: - { - // We found a three byte sequence encoding one half of a surrogate. - // Look for the other half. - const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr); - const uint16_t trailing = GetLeadingUtf16Char(pair2); - - return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff); - } - case 0xdc: - case 0xdd: - case 0xde: - case 0xdf: - // It's a trailing surrogate, which is not valid at this point. - return false; - case 0x20: - case 0xff: - // It's in the range that has spaces, controls, and specials. - switch (leading & 0xfff8) { - case 0x2000: - case 0x2008: - case 0x2028: - case 0xfff0: - case 0xfff8: - return false; - } - return true; - default: - return true; - } - - UNREACHABLE(); -} - -/* Return whether the pointed-at modified-UTF-8 encoded character is - * valid as part of a member name, updating the pointer to point past - * the consumed character. This will consume two encoded UTF-16 code - * points if the character is encoded as a surrogate pair. Also, if - * this function returns false, then the given pointer may only have - * been partially advanced. - */ -static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) { - uint8_t c = (uint8_t) **pUtf8Ptr; - if (LIKELY(c <= 0x7f)) { - // It's low-ascii, so check the table. - uint32_t wordIdx = c >> 5; - uint32_t bitIdx = c & 0x1f; - (*pUtf8Ptr)++; - return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0; - } - - // It's a multibyte encoded character. Call a non-inline function - // for the heavy lifting. - return IsValidPartOfMemberNameUtf8Slow(pUtf8Ptr); -} - -bool IsValidMemberName(const char* s) { - bool angle_name = false; - - switch (*s) { - case '\0': - // The empty string is not a valid name. - return false; - case '<': - angle_name = true; - s++; - break; - } - - while (true) { - switch (*s) { - case '\0': - return !angle_name; - case '>': - return angle_name && s[1] == '\0'; - } - - if (!IsValidPartOfMemberNameUtf8(&s)) { - return false; - } - } -} - -enum ClassNameType { kName, kDescriptor }; -template<ClassNameType kType, char kSeparator> -static bool IsValidClassName(const char* s) { - int arrayCount = 0; - while (*s == '[') { - arrayCount++; - s++; - } - - if (arrayCount > 255) { - // Arrays may have no more than 255 dimensions. - return false; - } - - ClassNameType type = kType; - if (type != kDescriptor && arrayCount != 0) { - /* - * If we're looking at an array of some sort, then it doesn't - * matter if what is being asked for is a class name; the - * format looks the same as a type descriptor in that case, so - * treat it as such. - */ - type = kDescriptor; - } - - if (type == kDescriptor) { - /* - * We are looking for a descriptor. Either validate it as a - * single-character primitive type, or continue on to check the - * embedded class name (bracketed by "L" and ";"). - */ - switch (*(s++)) { - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'Z': - // These are all single-character descriptors for primitive types. - return (*s == '\0'); - case 'V': - // Non-array void is valid, but you can't have an array of void. - return (arrayCount == 0) && (*s == '\0'); - case 'L': - // Class name: Break out and continue below. - break; - default: - // Oddball descriptor character. - return false; - } - } - - /* - * We just consumed the 'L' that introduces a class name as part - * of a type descriptor, or we are looking for an unadorned class - * name. - */ - - bool sepOrFirst = true; // first character or just encountered a separator. - for (;;) { - uint8_t c = (uint8_t) *s; - switch (c) { - case '\0': - /* - * Premature end for a type descriptor, but valid for - * a class name as long as we haven't encountered an - * empty component (including the degenerate case of - * the empty string ""). - */ - return (type == kName) && !sepOrFirst; - case ';': - /* - * Invalid character for a class name, but the - * legitimate end of a type descriptor. In the latter - * case, make sure that this is the end of the string - * and that it doesn't end with an empty component - * (including the degenerate case of "L;"). - */ - return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0'); - case '/': - case '.': - if (c != kSeparator) { - // The wrong separator character. - return false; - } - if (sepOrFirst) { - // Separator at start or two separators in a row. - return false; - } - sepOrFirst = true; - s++; - break; - default: - if (!IsValidPartOfMemberNameUtf8(&s)) { - return false; - } - sepOrFirst = false; - break; - } - } -} - -bool IsValidBinaryClassName(const char* s) { - return IsValidClassName<kName, '.'>(s); -} - -bool IsValidJniClassName(const char* s) { - return IsValidClassName<kName, '/'>(s); -} - -bool IsValidDescriptor(const char* s) { - return IsValidClassName<kDescriptor, '/'>(s); -} - -void Split(const std::string& s, char separator, std::vector<std::string>* result) { - const char* p = s.data(); - const char* end = p + s.size(); - while (p != end) { - if (*p == separator) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != separator) { - // Skip to the next occurrence of the separator. - } - result->push_back(std::string(start, p - start)); - } - } -} - -std::string PrettyDescriptor(Primitive::Type type) { - return PrettyDescriptor(Primitive::Descriptor(type)); -} - -} // namespace art diff --git a/runtime/dex/descriptors_names.h b/runtime/dex/descriptors_names.h deleted file mode 100644 index 22e9573556..0000000000 --- a/runtime/dex/descriptors_names.h +++ /dev/null @@ -1,63 +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. - */ - -#ifndef ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_ -#define ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_ - -#include <string> - -#include "primitive.h" - -namespace art { - -// Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf, -// one of which is probably more useful to you. -// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int", -// "[[I" would be "int[][]", "[Ljava/lang/String;" would be -// "java.lang.String[]", and so forth. -void AppendPrettyDescriptor(const char* descriptor, std::string* result); -std::string PrettyDescriptor(const char* descriptor); -std::string PrettyDescriptor(Primitive::Type type); - -// Performs JNI name mangling as described in section 11.3 "Linking Native Methods" -// of the JNI spec. -std::string MangleForJni(const std::string& s); - -std::string GetJniShortName(const std::string& class_name, const std::string& method_name); - -// Turn "java.lang.String" into "Ljava/lang/String;". -std::string DotToDescriptor(const char* class_name); - -// Turn "Ljava/lang/String;" into "java.lang.String" using the conventions of -// java.lang.Class.getName(). -std::string DescriptorToDot(const char* descriptor); - -// Turn "Ljava/lang/String;" into "java/lang/String" using the opposite conventions of -// java.lang.Class.getName(). -std::string DescriptorToName(const char* descriptor); - -// Tests for whether 's' is a valid class name in the three common forms: -bool IsValidBinaryClassName(const char* s); // "java.lang.String" -bool IsValidJniClassName(const char* s); // "java/lang/String" -bool IsValidDescriptor(const char* s); // "Ljava/lang/String;" - -// Returns whether the given string is a valid field or method name, -// additionally allowing names that begin with '<' and end with '>'. -bool IsValidMemberName(const char* s); - -} // namespace art - -#endif // ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_ diff --git a/runtime/dex/dex_file-inl.h b/runtime/dex/dex_file-inl.h deleted file mode 100644 index aa53daac35..0000000000 --- a/runtime/dex/dex_file-inl.h +++ /dev/null @@ -1,521 +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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_INL_H_ -#define ART_RUNTIME_DEX_DEX_FILE_INL_H_ - -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/stringpiece.h" -#include "compact_dex_file.h" -#include "dex_file.h" -#include "invoke_type.h" -#include "leb128.h" -#include "standard_dex_file.h" - -namespace art { - -inline int32_t DexFile::GetStringLength(const StringId& string_id) const { - const uint8_t* ptr = DataBegin() + string_id.string_data_off_; - return DecodeUnsignedLeb128(&ptr); -} - -inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id, - uint32_t* utf16_length) const { - DCHECK(utf16_length != nullptr) << GetLocation(); - const uint8_t* ptr = DataBegin() + string_id.string_data_off_; - *utf16_length = DecodeUnsignedLeb128(&ptr); - return reinterpret_cast<const char*>(ptr); -} - -inline const char* DexFile::GetStringData(const StringId& string_id) const { - uint32_t ignored; - return GetStringDataAndUtf16Length(string_id, &ignored); -} - -inline const char* DexFile::StringDataAndUtf16LengthByIdx(dex::StringIndex idx, - uint32_t* utf16_length) const { - if (!idx.IsValid()) { - *utf16_length = 0; - return nullptr; - } - const StringId& string_id = GetStringId(idx); - return GetStringDataAndUtf16Length(string_id, utf16_length); -} - -inline const char* DexFile::StringDataByIdx(dex::StringIndex idx) const { - uint32_t unicode_length; - return StringDataAndUtf16LengthByIdx(idx, &unicode_length); -} - -inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const { - if (!idx.IsValid()) { - return nullptr; - } - const TypeId& type_id = GetTypeId(idx); - return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length); -} - -inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const { - if (!idx.IsValid()) { - return nullptr; - } - const TypeId& type_id = GetTypeId(idx); - return StringDataByIdx(type_id.descriptor_idx_); -} - -inline const char* DexFile::GetTypeDescriptor(const TypeId& type_id) const { - return StringDataByIdx(type_id.descriptor_idx_); -} - -inline const char* DexFile::GetFieldTypeDescriptor(const FieldId& field_id) const { - const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_); - return GetTypeDescriptor(type_id); -} - -inline const char* DexFile::GetFieldName(const FieldId& field_id) const { - return StringDataByIdx(field_id.name_idx_); -} - -inline const char* DexFile::GetMethodDeclaringClassDescriptor(const MethodId& method_id) const { - const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_); - return GetTypeDescriptor(type_id); -} - -inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const { - return Signature(this, GetProtoId(method_id.proto_idx_)); -} - -inline const Signature DexFile::GetProtoSignature(const ProtoId& proto_id) const { - return Signature(this, proto_id); -} - -inline const char* DexFile::GetMethodName(const MethodId& method_id) const { - return StringDataByIdx(method_id.name_idx_); -} - -inline const char* DexFile::GetMethodShorty(uint32_t idx) const { - return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_); -} - -inline const char* DexFile::GetMethodShorty(const MethodId& method_id) const { - return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); -} - -inline const char* DexFile::GetMethodShorty(const MethodId& method_id, uint32_t* length) const { - // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters. - return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); -} - -inline const char* DexFile::GetClassDescriptor(const ClassDef& class_def) const { - return StringByTypeIdx(class_def.class_idx_); -} - -inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) const { - return StringByTypeIdx(proto_id.return_type_idx_); -} - -inline const char* DexFile::GetShorty(uint32_t proto_idx) const { - const ProtoId& proto_id = GetProtoId(proto_idx); - return StringDataByIdx(proto_id.shorty_idx_); -} - -inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end, - uint32_t offset) { - return reinterpret_cast<const TryItem*> - (RoundUp(reinterpret_cast<uintptr_t>(&code_item_end.Inst()), TryItem::kAlignment)) + offset; -} - -static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1, - const DexFile* df2, dex::StringIndex sidx2) { - uint32_t s1_len; // Note: utf16 length != mutf8 length. - const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len); - uint32_t s2_len; - const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len); - return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0); -} - -inline bool Signature::operator==(const Signature& rhs) const { - if (dex_file_ == nullptr) { - return rhs.dex_file_ == nullptr; - } - if (rhs.dex_file_ == nullptr) { - return false; - } - if (dex_file_ == rhs.dex_file_) { - return proto_id_ == rhs.proto_id_; - } - uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length. - const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_, - &lhs_shorty_len); - StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len); - { - uint32_t rhs_shorty_len; - const char* rhs_shorty_data = - rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_, - &rhs_shorty_len); - StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len); - if (lhs_shorty != rhs_shorty) { - return false; // Shorty mismatch. - } - } - if (lhs_shorty[0] == 'L') { - const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_); - const DexFile::TypeId& rhs_return_type_id = - rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_); - if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_, - rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) { - return false; // Return type mismatch. - } - } - if (lhs_shorty.find('L', 1) != StringPiece::npos) { - const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); - const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_); - // We found a reference parameter in the matching shorty, so both lists must be non-empty. - DCHECK(params != nullptr); - DCHECK(rhs_params != nullptr); - uint32_t params_size = params->Size(); - DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match. - for (uint32_t i = 0; i < params_size; ++i) { - const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_); - const DexFile::TypeId& rhs_param_id = - rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_); - if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_, - rhs.dex_file_, rhs_param_id.descriptor_idx_)) { - return false; // Parameter type mismatch. - } - } - } - return true; -} - -inline -InvokeType ClassDataItemIterator::GetMethodInvokeType(const DexFile::ClassDef& class_def) const { - if (HasNextDirectMethod()) { - if ((GetRawMemberAccessFlags() & kAccStatic) != 0) { - return kStatic; - } else { - return kDirect; - } - } else { - DCHECK_EQ(GetRawMemberAccessFlags() & kAccStatic, 0U); - if ((class_def.access_flags_ & kAccInterface) != 0) { - return kInterface; - } else if ((GetRawMemberAccessFlags() & kAccConstructor) != 0) { - return kSuper; - } else { - return kVirtual; - } - } -} - -template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData> -bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, - const std::string& location, - const char* declaring_class_descriptor, - const std::vector<const char*>& arg_descriptors, - const std::string& method_name, - bool is_static, - uint16_t registers_size, - uint16_t ins_size, - uint16_t insns_size_in_code_units, - IndexToStringData index_to_string_data, - TypeIndexToStringData type_index_to_string_data, - NewLocalCallback new_local_callback, - void* context) { - if (stream == nullptr) { - return false; - } - std::vector<LocalInfo> local_in_reg(registers_size); - - uint16_t arg_reg = registers_size - ins_size; - if (!is_static) { - const char* descriptor = declaring_class_descriptor; - local_in_reg[arg_reg].name_ = "this"; - local_in_reg[arg_reg].descriptor_ = descriptor; - local_in_reg[arg_reg].signature_ = nullptr; - local_in_reg[arg_reg].start_address_ = 0; - local_in_reg[arg_reg].reg_ = arg_reg; - local_in_reg[arg_reg].is_live_ = true; - arg_reg++; - } - - DecodeUnsignedLeb128(&stream); // Line. - uint32_t parameters_size = DecodeUnsignedLeb128(&stream); - uint32_t i; - if (parameters_size != arg_descriptors.size()) { - LOG(ERROR) << "invalid stream - problem with parameter iterator in " << location - << " for method " << method_name; - return false; - } - for (i = 0; i < parameters_size && i < arg_descriptors.size(); ++i) { - if (arg_reg >= registers_size) { - LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg - << " >= " << registers_size << ") in " << location; - return false; - } - uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); - const char* descriptor = arg_descriptors[i]; - local_in_reg[arg_reg].name_ = index_to_string_data(name_idx); - local_in_reg[arg_reg].descriptor_ = descriptor; - local_in_reg[arg_reg].signature_ = nullptr; - local_in_reg[arg_reg].start_address_ = 0; - local_in_reg[arg_reg].reg_ = arg_reg; - local_in_reg[arg_reg].is_live_ = true; - switch (*descriptor) { - case 'D': - case 'J': - arg_reg += 2; - break; - default: - arg_reg += 1; - break; - } - } - - uint32_t address = 0; - for (;;) { - uint8_t opcode = *stream++; - switch (opcode) { - case DBG_END_SEQUENCE: - // Emit all variables which are still alive at the end of the method. - for (uint16_t reg = 0; reg < registers_size; reg++) { - if (local_in_reg[reg].is_live_) { - local_in_reg[reg].end_address_ = insns_size_in_code_units; - new_local_callback(context, local_in_reg[reg]); - } - } - return true; - case DBG_ADVANCE_PC: - address += DecodeUnsignedLeb128(&stream); - break; - case DBG_ADVANCE_LINE: - DecodeSignedLeb128(&stream); // Line. - break; - case DBG_START_LOCAL: - case DBG_START_LOCAL_EXTENDED: { - uint16_t reg = DecodeUnsignedLeb128(&stream); - if (reg >= registers_size) { - LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= " - << registers_size << ") in " << location; - return false; - } - - uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); - uint16_t descriptor_idx = DecodeUnsignedLeb128P1(&stream); - uint32_t signature_idx = dex::kDexNoIndex; - if (opcode == DBG_START_LOCAL_EXTENDED) { - signature_idx = DecodeUnsignedLeb128P1(&stream); - } - - // Emit what was previously there, if anything - if (local_in_reg[reg].is_live_) { - local_in_reg[reg].end_address_ = address; - new_local_callback(context, local_in_reg[reg]); - } - - local_in_reg[reg].name_ = index_to_string_data(name_idx); - local_in_reg[reg].descriptor_ = type_index_to_string_data(descriptor_idx);; - local_in_reg[reg].signature_ = index_to_string_data(signature_idx); - local_in_reg[reg].start_address_ = address; - local_in_reg[reg].reg_ = reg; - local_in_reg[reg].is_live_ = true; - break; - } - case DBG_END_LOCAL: { - uint16_t reg = DecodeUnsignedLeb128(&stream); - if (reg >= registers_size) { - LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= " - << registers_size << ") in " << location; - return false; - } - // If the register is live, close it properly. Otherwise, closing an already - // closed register is sloppy, but harmless if no further action is taken. - if (local_in_reg[reg].is_live_) { - local_in_reg[reg].end_address_ = address; - new_local_callback(context, local_in_reg[reg]); - local_in_reg[reg].is_live_ = false; - } - break; - } - case DBG_RESTART_LOCAL: { - uint16_t reg = DecodeUnsignedLeb128(&stream); - if (reg >= registers_size) { - LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= " - << registers_size << ") in " << location; - return false; - } - // If the register is live, the "restart" is superfluous, - // and we don't want to mess with the existing start address. - if (!local_in_reg[reg].is_live_) { - local_in_reg[reg].start_address_ = address; - local_in_reg[reg].is_live_ = true; - } - break; - } - case DBG_SET_PROLOGUE_END: - case DBG_SET_EPILOGUE_BEGIN: - break; - case DBG_SET_FILE: - DecodeUnsignedLeb128P1(&stream); // name. - break; - default: - address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE; - break; - } - } -} - -template<typename NewLocalCallback> -bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, - uint32_t ins_size, - uint32_t insns_size_in_code_units, - uint32_t debug_info_offset, - bool is_static, - uint32_t method_idx, - NewLocalCallback new_local_callback, - void* context) const { - const uint8_t* const stream = GetDebugInfoStream(debug_info_offset); - if (stream == nullptr) { - return false; - } - std::vector<const char*> arg_descriptors; - DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx))); - for (; it.HasNext(); it.Next()) { - arg_descriptors.push_back(it.GetDescriptor()); - } - return DecodeDebugLocalInfo(stream, - GetLocation(), - GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)), - arg_descriptors, - this->PrettyMethod(method_idx), - is_static, - registers_size, - ins_size, - insns_size_in_code_units, - [this](uint32_t idx) { - return StringDataByIdx(dex::StringIndex(idx)); - }, - [this](uint32_t idx) { - return StringByTypeIdx(dex::TypeIndex( - dchecked_integral_cast<uint16_t>(idx))); - }, - new_local_callback, - context); -} - -template<typename DexDebugNewPosition, typename IndexToStringData> -bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, - IndexToStringData index_to_string_data, - DexDebugNewPosition position_functor, - void* context) { - if (stream == nullptr) { - return false; - } - - PositionInfo entry = PositionInfo(); - entry.line_ = DecodeUnsignedLeb128(&stream); - uint32_t parameters_size = DecodeUnsignedLeb128(&stream); - for (uint32_t i = 0; i < parameters_size; ++i) { - DecodeUnsignedLeb128P1(&stream); // Parameter name. - } - - for (;;) { - uint8_t opcode = *stream++; - switch (opcode) { - case DBG_END_SEQUENCE: - return true; // end of stream. - case DBG_ADVANCE_PC: - entry.address_ += DecodeUnsignedLeb128(&stream); - break; - case DBG_ADVANCE_LINE: - entry.line_ += DecodeSignedLeb128(&stream); - break; - case DBG_START_LOCAL: - DecodeUnsignedLeb128(&stream); // reg. - DecodeUnsignedLeb128P1(&stream); // name. - DecodeUnsignedLeb128P1(&stream); // descriptor. - break; - case DBG_START_LOCAL_EXTENDED: - DecodeUnsignedLeb128(&stream); // reg. - DecodeUnsignedLeb128P1(&stream); // name. - DecodeUnsignedLeb128P1(&stream); // descriptor. - DecodeUnsignedLeb128P1(&stream); // signature. - break; - case DBG_END_LOCAL: - case DBG_RESTART_LOCAL: - DecodeUnsignedLeb128(&stream); // reg. - break; - case DBG_SET_PROLOGUE_END: - entry.prologue_end_ = true; - break; - case DBG_SET_EPILOGUE_BEGIN: - entry.epilogue_begin_ = true; - break; - case DBG_SET_FILE: { - uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); - entry.source_file_ = index_to_string_data(name_idx); - break; - } - default: { - int adjopcode = opcode - DBG_FIRST_SPECIAL; - entry.address_ += adjopcode / DBG_LINE_RANGE; - entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE); - if (position_functor(context, entry)) { - return true; // early exit. - } - entry.prologue_end_ = false; - entry.epilogue_begin_ = false; - break; - } - } - } -} - -template<typename DexDebugNewPosition> -bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset, - DexDebugNewPosition position_functor, - void* context) const { - return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), - [this](uint32_t idx) { - return StringDataByIdx(dex::StringIndex(idx)); - }, - position_functor, - context); -} - -inline const CompactDexFile* DexFile::AsCompactDexFile() const { - DCHECK(IsCompactDexFile()); - return down_cast<const CompactDexFile*>(this); -} - -inline const StandardDexFile* DexFile::AsStandardDexFile() const { - DCHECK(IsStandardDexFile()); - return down_cast<const StandardDexFile*>(this); -} - -// Get the base of the encoded data for the given DexCode. -inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& code_item_end, - uint32_t tries_size, - uint32_t offset) { - const uint8_t* handler_data = - reinterpret_cast<const uint8_t*>(GetTryItems(code_item_end, tries_size)); - return handler_data + offset; -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_INL_H_ diff --git a/runtime/dex/dex_file.cc b/runtime/dex/dex_file.cc deleted file mode 100644 index 18eb903551..0000000000 --- a/runtime/dex/dex_file.cc +++ /dev/null @@ -1,795 +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 "dex_file.h" - -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <zlib.h> - -#include <memory> -#include <sstream> -#include <type_traits> - -#include "android-base/stringprintf.h" - -#include "base/enums.h" -#include "base/stl_util.h" -#include "descriptors_names.h" -#include "dex_file-inl.h" -#include "leb128.h" -#include "standard_dex_file.h" -#include "utf-inl.h" - -namespace art { - -using android::base::StringPrintf; - -static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong"); -static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial"); -static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); -static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial"); - -uint32_t DexFile::CalculateChecksum() const { - return CalculateChecksum(Begin(), Size()); -} - -uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) { - const uint32_t non_sum_bytes = OFFSETOF_MEMBER(DexFile::Header, signature_); - return ChecksumMemoryRange(begin + non_sum_bytes, size - non_sum_bytes); -} - -uint32_t DexFile::ChecksumMemoryRange(const uint8_t* begin, size_t size) { - return adler32(adler32(0L, Z_NULL, 0), begin, size); -} - -int DexFile::GetPermissions() const { - CHECK(container_.get() != nullptr); - return container_->GetPermissions(); -} - -bool DexFile::IsReadOnly() const { - CHECK(container_.get() != nullptr); - return container_->IsReadOnly(); -} - -bool DexFile::EnableWrite() const { - CHECK(container_.get() != nullptr); - return container_->EnableWrite(); -} - -bool DexFile::DisableWrite() const { - CHECK(container_.get() != nullptr); - return container_->DisableWrite(); -} - -DexFile::DexFile(const uint8_t* base, - size_t size, - const uint8_t* data_begin, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - DexFileContainer* container, - bool is_compact_dex) - : begin_(base), - size_(size), - data_begin_(data_begin), - data_size_(data_size), - location_(location), - location_checksum_(location_checksum), - header_(reinterpret_cast<const Header*>(base)), - string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)), - type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)), - field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)), - method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)), - proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)), - class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)), - method_handles_(nullptr), - num_method_handles_(0), - call_site_ids_(nullptr), - num_call_site_ids_(0), - oat_dex_file_(oat_dex_file), - container_(container), - is_compact_dex_(is_compact_dex) { - CHECK(begin_ != nullptr) << GetLocation(); - CHECK_GT(size_, 0U) << GetLocation(); - // Check base (=header) alignment. - // Must be 4-byte aligned to avoid undefined behavior when accessing - // any of the sections via a pointer. - CHECK_ALIGNED(begin_, alignof(Header)); - - InitializeSectionsFromMapList(); -} - -DexFile::~DexFile() { - // We don't call DeleteGlobalRef on dex_object_ because we're only called by DestroyJavaVM, and - // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could - // re-attach, but cleaning up these global references is not obviously useful. It's not as if - // the global reference table is otherwise empty! -} - -bool DexFile::Init(std::string* error_msg) { - if (!CheckMagicAndVersion(error_msg)) { - return false; - } - return true; -} - -bool DexFile::CheckMagicAndVersion(std::string* error_msg) const { - if (!IsMagicValid()) { - std::ostringstream oss; - oss << "Unrecognized magic number in " << GetLocation() << ":" - << " " << header_->magic_[0] - << " " << header_->magic_[1] - << " " << header_->magic_[2] - << " " << header_->magic_[3]; - *error_msg = oss.str(); - return false; - } - if (!IsVersionValid()) { - std::ostringstream oss; - oss << "Unrecognized version number in " << GetLocation() << ":" - << " " << header_->magic_[4] - << " " << header_->magic_[5] - << " " << header_->magic_[6] - << " " << header_->magic_[7]; - *error_msg = oss.str(); - return false; - } - return true; -} - -void DexFile::InitializeSectionsFromMapList() { - const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_); - if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) { - // Bad offset. The dex file verifier runs after this method and will reject the file. - return; - } - const size_t count = map_list->size_; - - size_t map_limit = header_->map_off_ + count * sizeof(MapItem); - if (header_->map_off_ >= map_limit || map_limit > DataSize()) { - // Overflow or out out of bounds. The dex file verifier runs after - // this method and will reject the file as it is malformed. - return; - } - - for (size_t i = 0; i < count; ++i) { - const MapItem& map_item = map_list->list_[i]; - if (map_item.type_ == kDexTypeMethodHandleItem) { - method_handles_ = reinterpret_cast<const MethodHandleItem*>(Begin() + map_item.offset_); - num_method_handles_ = map_item.size_; - } else if (map_item.type_ == kDexTypeCallSiteIdItem) { - call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(Begin() + map_item.offset_); - num_call_site_ids_ = map_item.size_; - } - } -} - -uint32_t DexFile::Header::GetVersion() const { - const char* version = reinterpret_cast<const char*>(&magic_[kDexMagicSize]); - return atoi(version); -} - -const DexFile::ClassDef* DexFile::FindClassDef(dex::TypeIndex type_idx) const { - size_t num_class_defs = NumClassDefs(); - // Fast path for rare no class defs case. - if (num_class_defs == 0) { - return nullptr; - } - for (size_t i = 0; i < num_class_defs; ++i) { - const ClassDef& class_def = GetClassDef(i); - if (class_def.class_idx_ == type_idx) { - return &class_def; - } - } - return nullptr; -} - -uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def, - uint32_t method_idx) const { - const uint8_t* class_data = GetClassData(class_def); - CHECK(class_data != nullptr); - ClassDataItemIterator it(*this, class_data); - it.SkipAllFields(); - while (it.HasNextDirectMethod()) { - if (it.GetMemberIndex() == method_idx) { - return it.GetMethodCodeItemOffset(); - } - it.Next(); - } - while (it.HasNextVirtualMethod()) { - if (it.GetMemberIndex() == method_idx) { - return it.GetMethodCodeItemOffset(); - } - it.Next(); - } - LOG(FATAL) << "Unable to find method " << method_idx; - UNREACHABLE(); -} - -const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass, - const DexFile::StringId& name, - const DexFile::TypeId& type) const { - // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx - const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); - const dex::StringIndex name_idx = GetIndexForStringId(name); - const dex::TypeIndex type_idx = GetIndexForTypeId(type); - int32_t lo = 0; - int32_t hi = NumFieldIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::FieldId& field = GetFieldId(mid); - if (class_idx > field.class_idx_) { - lo = mid + 1; - } else if (class_idx < field.class_idx_) { - hi = mid - 1; - } else { - if (name_idx > field.name_idx_) { - lo = mid + 1; - } else if (name_idx < field.name_idx_) { - hi = mid - 1; - } else { - if (type_idx > field.type_idx_) { - lo = mid + 1; - } else if (type_idx < field.type_idx_) { - hi = mid - 1; - } else { - return &field; - } - } - } - } - return nullptr; -} - -const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_klass, - const DexFile::StringId& name, - const DexFile::ProtoId& signature) const { - // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx - const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); - const dex::StringIndex name_idx = GetIndexForStringId(name); - const uint16_t proto_idx = GetIndexForProtoId(signature); - int32_t lo = 0; - int32_t hi = NumMethodIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::MethodId& method = GetMethodId(mid); - if (class_idx > method.class_idx_) { - lo = mid + 1; - } else if (class_idx < method.class_idx_) { - hi = mid - 1; - } else { - if (name_idx > method.name_idx_) { - lo = mid + 1; - } else if (name_idx < method.name_idx_) { - hi = mid - 1; - } else { - if (proto_idx > method.proto_idx_) { - lo = mid + 1; - } else if (proto_idx < method.proto_idx_) { - hi = mid - 1; - } else { - return &method; - } - } - } - } - return nullptr; -} - -const DexFile::StringId* DexFile::FindStringId(const char* string) const { - int32_t lo = 0; - int32_t hi = NumStringIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); - const char* str = GetStringData(str_id); - int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); - if (compare > 0) { - lo = mid + 1; - } else if (compare < 0) { - hi = mid - 1; - } else { - return &str_id; - } - } - return nullptr; -} - -const DexFile::TypeId* DexFile::FindTypeId(const char* string) const { - int32_t lo = 0; - int32_t hi = NumTypeIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); - const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_); - const char* str = GetStringData(str_id); - int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); - if (compare > 0) { - lo = mid + 1; - } else if (compare < 0) { - hi = mid - 1; - } else { - return &type_id; - } - } - return nullptr; -} - -const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const { - int32_t lo = 0; - int32_t hi = NumStringIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); - const char* str = GetStringData(str_id); - int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length); - if (compare > 0) { - lo = mid + 1; - } else if (compare < 0) { - hi = mid - 1; - } else { - return &str_id; - } - } - return nullptr; -} - -const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const { - int32_t lo = 0; - int32_t hi = NumTypeIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); - if (string_idx > type_id.descriptor_idx_) { - lo = mid + 1; - } else if (string_idx < type_id.descriptor_idx_) { - hi = mid - 1; - } else { - return &type_id; - } - } - return nullptr; -} - -const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, - const dex::TypeIndex* signature_type_idxs, - uint32_t signature_length) const { - int32_t lo = 0; - int32_t hi = NumProtoIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::ProtoId& proto = GetProtoId(mid); - int compare = return_type_idx.index_ - proto.return_type_idx_.index_; - if (compare == 0) { - DexFileParameterIterator it(*this, proto); - size_t i = 0; - while (it.HasNext() && i < signature_length && compare == 0) { - compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_; - it.Next(); - i++; - } - if (compare == 0) { - if (it.HasNext()) { - compare = -1; - } else if (i < signature_length) { - compare = 1; - } - } - } - if (compare > 0) { - lo = mid + 1; - } else if (compare < 0) { - hi = mid - 1; - } else { - return &proto; - } - } - return nullptr; -} - -// Given a signature place the type ids into the given vector -bool DexFile::CreateTypeList(const StringPiece& signature, - dex::TypeIndex* return_type_idx, - std::vector<dex::TypeIndex>* param_type_idxs) const { - if (signature[0] != '(') { - return false; - } - size_t offset = 1; - size_t end = signature.size(); - bool process_return = false; - while (offset < end) { - size_t start_offset = offset; - char c = signature[offset]; - offset++; - if (c == ')') { - process_return = true; - continue; - } - while (c == '[') { // process array prefix - if (offset >= end) { // expect some descriptor following [ - return false; - } - c = signature[offset]; - offset++; - } - if (c == 'L') { // process type descriptors - do { - if (offset >= end) { // unexpected early termination of descriptor - return false; - } - c = signature[offset]; - offset++; - } while (c != ';'); - } - // TODO: avoid creating a std::string just to get a 0-terminated char array - std::string descriptor(signature.data() + start_offset, offset - start_offset); - const DexFile::TypeId* type_id = FindTypeId(descriptor.c_str()); - if (type_id == nullptr) { - return false; - } - dex::TypeIndex type_idx = GetIndexForTypeId(*type_id); - if (!process_return) { - param_type_idxs->push_back(type_idx); - } else { - *return_type_idx = type_idx; - return offset == end; // return true if the signature had reached a sensible end - } - } - return false; // failed to correctly parse return type -} - -const Signature DexFile::CreateSignature(const StringPiece& signature) const { - dex::TypeIndex return_type_idx; - std::vector<dex::TypeIndex> param_type_indices; - bool success = CreateTypeList(signature, &return_type_idx, ¶m_type_indices); - if (!success) { - return Signature::NoSignature(); - } - const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices); - if (proto_id == nullptr) { - return Signature::NoSignature(); - } - return Signature(this, *proto_id); -} - -int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) { - uint32_t min = 0; - uint32_t max = tries_size; - while (min < max) { - const uint32_t mid = (min + max) / 2; - - const art::DexFile::TryItem& ti = try_items[mid]; - const uint32_t start = ti.start_addr_; - const uint32_t end = start + ti.insn_count_; - - if (address < start) { - max = mid; - } else if (address >= end) { - min = mid + 1; - } else { // We have a winner! - return mid; - } - } - // No match. - return -1; -} - -bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) { - LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context); - - // We know that this callback will be called in - // ascending address order, so keep going until we find - // a match or we've just gone past it. - if (entry.address_ > context->address_) { - // The line number from the previous positions callback - // wil be the final result. - return true; - } else { - context->line_num_ = entry.line_; - return entry.address_ == context->address_; - } -} - -// Read a signed integer. "zwidth" is the zero-based byte count. -int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) { - int32_t val = 0; - for (int i = zwidth; i >= 0; --i) { - val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24); - } - val >>= (3 - zwidth) * 8; - return val; -} - -// Read an unsigned integer. "zwidth" is the zero-based byte count, -// "fill_on_right" indicates which side we want to zero-fill from. -uint32_t DexFile::ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) { - uint32_t val = 0; - for (int i = zwidth; i >= 0; --i) { - val = (val >> 8) | (((uint32_t)*ptr++) << 24); - } - if (!fill_on_right) { - val >>= (3 - zwidth) * 8; - } - return val; -} - -// Read a signed long. "zwidth" is the zero-based byte count. -int64_t DexFile::ReadSignedLong(const uint8_t* ptr, int zwidth) { - int64_t val = 0; - for (int i = zwidth; i >= 0; --i) { - val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56); - } - val >>= (7 - zwidth) * 8; - return val; -} - -// Read an unsigned long. "zwidth" is the zero-based byte count, -// "fill_on_right" indicates which side we want to zero-fill from. -uint64_t DexFile::ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) { - uint64_t val = 0; - for (int i = zwidth; i >= 0; --i) { - val = (val >> 8) | (((uint64_t)*ptr++) << 56); - } - if (!fill_on_right) { - val >>= (7 - zwidth) * 8; - } - return val; -} - -std::string DexFile::PrettyMethod(uint32_t method_idx, bool with_signature) const { - if (method_idx >= NumMethodIds()) { - return StringPrintf("<<invalid-method-idx-%d>>", method_idx); - } - const DexFile::MethodId& method_id = GetMethodId(method_idx); - std::string result; - const DexFile::ProtoId* proto_id = with_signature ? &GetProtoId(method_id.proto_idx_) : nullptr; - if (with_signature) { - AppendPrettyDescriptor(StringByTypeIdx(proto_id->return_type_idx_), &result); - result += ' '; - } - AppendPrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id), &result); - result += '.'; - result += GetMethodName(method_id); - if (with_signature) { - result += '('; - const DexFile::TypeList* params = GetProtoParameters(*proto_id); - if (params != nullptr) { - const char* separator = ""; - for (uint32_t i = 0u, size = params->Size(); i != size; ++i) { - result += separator; - separator = ", "; - AppendPrettyDescriptor(StringByTypeIdx(params->GetTypeItem(i).type_idx_), &result); - } - } - result += ')'; - } - return result; -} - -std::string DexFile::PrettyField(uint32_t field_idx, bool with_type) const { - if (field_idx >= NumFieldIds()) { - return StringPrintf("<<invalid-field-idx-%d>>", field_idx); - } - const DexFile::FieldId& field_id = GetFieldId(field_idx); - std::string result; - if (with_type) { - result += GetFieldTypeDescriptor(field_id); - result += ' '; - } - AppendPrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id), &result); - result += '.'; - result += GetFieldName(field_id); - return result; -} - -std::string DexFile::PrettyType(dex::TypeIndex type_idx) const { - if (type_idx.index_ >= NumTypeIds()) { - return StringPrintf("<<invalid-type-idx-%d>>", type_idx.index_); - } - const DexFile::TypeId& type_id = GetTypeId(type_idx); - return PrettyDescriptor(GetTypeDescriptor(type_id)); -} - -// Checks that visibility is as expected. Includes special behavior for M and -// before to allow runtime and build visibility when expecting runtime. -std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { - os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]", - dex_file.GetLocation().c_str(), - dex_file.GetHeader().checksum_, dex_file.GetLocationChecksum(), - dex_file.Begin(), dex_file.Begin() + dex_file.Size()); - return os; -} - -std::string Signature::ToString() const { - if (dex_file_ == nullptr) { - CHECK(proto_id_ == nullptr); - return "<no signature>"; - } - const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); - std::string result; - if (params == nullptr) { - result += "()"; - } else { - result += "("; - for (uint32_t i = 0; i < params->Size(); ++i) { - result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_); - } - result += ")"; - } - result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_); - return result; -} - -uint32_t Signature::GetNumberOfParameters() const { - const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); - return (params != nullptr) ? params->Size() : 0; -} - -bool Signature::IsVoid() const { - const char* return_type = dex_file_->GetReturnTypeDescriptor(*proto_id_); - return strcmp(return_type, "V") == 0; -} - -bool Signature::operator==(const StringPiece& rhs) const { - if (dex_file_ == nullptr) { - return false; - } - StringPiece tail(rhs); - if (!tail.starts_with("(")) { - return false; // Invalid signature - } - tail.remove_prefix(1); // "("; - const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); - if (params != nullptr) { - for (uint32_t i = 0; i < params->Size(); ++i) { - StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_)); - if (!tail.starts_with(param)) { - return false; - } - tail.remove_prefix(param.length()); - } - } - if (!tail.starts_with(")")) { - return false; - } - tail.remove_prefix(1); // ")"; - return tail == dex_file_->StringByTypeIdx(proto_id_->return_type_idx_); -} - -std::ostream& operator<<(std::ostream& os, const Signature& sig) { - return os << sig.ToString(); -} - -// Decodes the header section from the class data bytes. -void ClassDataItemIterator::ReadClassDataHeader() { - CHECK(ptr_pos_ != nullptr); - header_.static_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_); - header_.instance_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_); - header_.direct_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_); - header_.virtual_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_); -} - -void ClassDataItemIterator::ReadClassDataField() { - field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_); - field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); - // The user of the iterator is responsible for checking if there - // are unordered or duplicate indexes. -} - -void ClassDataItemIterator::ReadClassDataMethod() { - method_.method_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_); - method_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); - method_.code_off_ = DecodeUnsignedLeb128(&ptr_pos_); - if (last_idx_ != 0 && method_.method_idx_delta_ == 0) { - LOG(WARNING) << "Duplicate method in " << dex_file_.GetLocation(); - } -} - -EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file, - const uint8_t* array_data) - : dex_file_(dex_file), - array_size_(), - pos_(-1), - ptr_(array_data), - type_(kByte) { - array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0; - if (array_size_ > 0) { - Next(); - } -} - -void EncodedArrayValueIterator::Next() { - pos_++; - if (pos_ >= array_size_) { - return; - } - uint8_t value_type = *ptr_++; - uint8_t value_arg = value_type >> kEncodedValueArgShift; - size_t width = value_arg + 1; // assume and correct later - type_ = static_cast<ValueType>(value_type & kEncodedValueTypeMask); - switch (type_) { - case kBoolean: - jval_.i = (value_arg != 0) ? 1 : 0; - width = 0; - break; - case kByte: - jval_.i = DexFile::ReadSignedInt(ptr_, value_arg); - CHECK(IsInt<8>(jval_.i)); - break; - case kShort: - jval_.i = DexFile::ReadSignedInt(ptr_, value_arg); - CHECK(IsInt<16>(jval_.i)); - break; - case kChar: - jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false); - CHECK(IsUint<16>(jval_.i)); - break; - case kInt: - jval_.i = DexFile::ReadSignedInt(ptr_, value_arg); - break; - case kLong: - jval_.j = DexFile::ReadSignedLong(ptr_, value_arg); - break; - case kFloat: - jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, true); - break; - case kDouble: - jval_.j = DexFile::ReadUnsignedLong(ptr_, value_arg, true); - break; - case kString: - case kType: - case kMethodType: - case kMethodHandle: - jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false); - break; - case kField: - case kMethod: - case kEnum: - case kArray: - case kAnnotation: - UNIMPLEMENTED(FATAL) << ": type " << type_; - UNREACHABLE(); - case kNull: - jval_.l = nullptr; - width = 0; - break; - default: - LOG(FATAL) << "Unreached"; - UNREACHABLE(); - } - ptr_ += width; -} - -namespace dex { - -std::ostream& operator<<(std::ostream& os, const StringIndex& index) { - os << "StringIndex[" << index.index_ << "]"; - return os; -} - -std::ostream& operator<<(std::ostream& os, const TypeIndex& index) { - os << "TypeIndex[" << index.index_ << "]"; - return os; -} - -} // namespace dex - -} // namespace art diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h deleted file mode 100644 index cf8c840b59..0000000000 --- a/runtime/dex/dex_file.h +++ /dev/null @@ -1,1443 +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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_H_ -#define ART_RUNTIME_DEX_DEX_FILE_H_ - -#include <memory> -#include <string> -#include <vector> - -#include <android-base/logging.h> - -#include "base/iteration_range.h" -#include "base/macros.h" -#include "base/value_object.h" -#include "dex_file_types.h" -#include "dex_instruction_iterator.h" -#include "globals.h" -#include "hidden_api_access_flags.h" -#include "jni.h" -#include "modifiers.h" - -namespace art { - -class CompactDexFile; -enum InvokeType : uint32_t; -class MemMap; -class OatDexFile; -class Signature; -class StandardDexFile; -class StringPiece; -class ZipArchive; - -// Some instances of DexFile own the storage referred to by DexFile. Clients who create -// such management do so by subclassing Container. -class DexFileContainer { - public: - DexFileContainer() { } - virtual ~DexFileContainer() { } - virtual int GetPermissions() = 0; - virtual bool IsReadOnly() = 0; - virtual bool EnableWrite() = 0; - virtual bool DisableWrite() = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(DexFileContainer); -}; - -// Dex file is the API that exposes native dex files (ordinary dex files) and CompactDex. -// Originally, the dex file format used by ART was mostly the same as APKs. The only change was -// quickened opcodes and layout optimizations. -// Since ART needs to support both native dex files and CompactDex files, the DexFile interface -// provides an abstraction to facilitate this. -class DexFile { - public: - // Number of bytes in the dex file magic. - static constexpr size_t kDexMagicSize = 4; - static constexpr size_t kDexVersionLen = 4; - - // First Dex format version enforcing class definition ordering rules. - static const uint32_t kClassDefinitionOrderEnforcedVersion = 37; - - static constexpr size_t kSha1DigestSize = 20; - static constexpr uint32_t kDexEndianConstant = 0x12345678; - - // The value of an invalid index. - static const uint16_t kDexNoIndex16 = 0xFFFF; - - // Raw header_item. - struct Header { - uint8_t magic_[8] = {}; - uint32_t checksum_ = 0; // See also location_checksum_ - uint8_t signature_[kSha1DigestSize] = {}; - uint32_t file_size_ = 0; // size of entire file - uint32_t header_size_ = 0; // offset to start of next section - uint32_t endian_tag_ = 0; - uint32_t link_size_ = 0; // unused - uint32_t link_off_ = 0; // unused - uint32_t map_off_ = 0; // unused - uint32_t string_ids_size_ = 0; // number of StringIds - uint32_t string_ids_off_ = 0; // file offset of StringIds array - uint32_t type_ids_size_ = 0; // number of TypeIds, we don't support more than 65535 - uint32_t type_ids_off_ = 0; // file offset of TypeIds array - uint32_t proto_ids_size_ = 0; // number of ProtoIds, we don't support more than 65535 - uint32_t proto_ids_off_ = 0; // file offset of ProtoIds array - uint32_t field_ids_size_ = 0; // number of FieldIds - uint32_t field_ids_off_ = 0; // file offset of FieldIds array - uint32_t method_ids_size_ = 0; // number of MethodIds - uint32_t method_ids_off_ = 0; // file offset of MethodIds array - uint32_t class_defs_size_ = 0; // number of ClassDefs - uint32_t class_defs_off_ = 0; // file offset of ClassDef array - uint32_t data_size_ = 0; // size of data section - uint32_t data_off_ = 0; // file offset of data section - - // Decode the dex magic version - uint32_t GetVersion() const; - }; - - // Map item type codes. - enum MapItemType : uint16_t { // private - kDexTypeHeaderItem = 0x0000, - kDexTypeStringIdItem = 0x0001, - kDexTypeTypeIdItem = 0x0002, - kDexTypeProtoIdItem = 0x0003, - kDexTypeFieldIdItem = 0x0004, - kDexTypeMethodIdItem = 0x0005, - kDexTypeClassDefItem = 0x0006, - kDexTypeCallSiteIdItem = 0x0007, - kDexTypeMethodHandleItem = 0x0008, - kDexTypeMapList = 0x1000, - kDexTypeTypeList = 0x1001, - kDexTypeAnnotationSetRefList = 0x1002, - kDexTypeAnnotationSetItem = 0x1003, - kDexTypeClassDataItem = 0x2000, - kDexTypeCodeItem = 0x2001, - kDexTypeStringDataItem = 0x2002, - kDexTypeDebugInfoItem = 0x2003, - kDexTypeAnnotationItem = 0x2004, - kDexTypeEncodedArrayItem = 0x2005, - kDexTypeAnnotationsDirectoryItem = 0x2006, - }; - - struct MapItem { - uint16_t type_; - uint16_t unused_; - uint32_t size_; - uint32_t offset_; - }; - - struct MapList { - uint32_t size_; - MapItem list_[1]; - - private: - DISALLOW_COPY_AND_ASSIGN(MapList); - }; - - // Raw string_id_item. - struct StringId { - uint32_t string_data_off_; // offset in bytes from the base address - - private: - DISALLOW_COPY_AND_ASSIGN(StringId); - }; - - // Raw type_id_item. - struct TypeId { - dex::StringIndex descriptor_idx_; // index into string_ids - - private: - DISALLOW_COPY_AND_ASSIGN(TypeId); - }; - - // Raw field_id_item. - struct FieldId { - dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - dex::TypeIndex type_idx_; // index into type_ids_ array for field type - dex::StringIndex name_idx_; // index into string_ids_ array for field name - - private: - DISALLOW_COPY_AND_ASSIGN(FieldId); - }; - - // Raw proto_id_item. - struct ProtoId { - dex::StringIndex shorty_idx_; // index into string_ids array for shorty descriptor - dex::TypeIndex return_type_idx_; // index into type_ids array for return type - uint16_t pad_; // padding = 0 - uint32_t parameters_off_; // file offset to type_list for parameter types - - private: - DISALLOW_COPY_AND_ASSIGN(ProtoId); - }; - - // Raw method_id_item. - struct MethodId { - dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - uint16_t proto_idx_; // index into proto_ids_ array for method prototype - dex::StringIndex name_idx_; // index into string_ids_ array for method name - - private: - DISALLOW_COPY_AND_ASSIGN(MethodId); - }; - - // Raw class_def_item. - struct ClassDef { - dex::TypeIndex class_idx_; // index into type_ids_ array for this class - uint16_t pad1_; // padding = 0 - uint32_t access_flags_; - dex::TypeIndex superclass_idx_; // index into type_ids_ array for superclass - uint16_t pad2_; // padding = 0 - uint32_t interfaces_off_; // file offset to TypeList - dex::StringIndex source_file_idx_; // index into string_ids_ for source file name - uint32_t annotations_off_; // file offset to annotations_directory_item - uint32_t class_data_off_; // file offset to class_data_item - uint32_t static_values_off_; // file offset to EncodedArray - - // Returns the valid access flags, that is, Java modifier bits relevant to the ClassDef type - // (class or interface). These are all in the lower 16b and do not contain runtime flags. - uint32_t GetJavaAccessFlags() const { - // Make sure that none of our runtime-only flags are set. - static_assert((kAccValidClassFlags & kAccJavaFlagsMask) == kAccValidClassFlags, - "Valid class flags not a subset of Java flags"); - static_assert((kAccValidInterfaceFlags & kAccJavaFlagsMask) == kAccValidInterfaceFlags, - "Valid interface flags not a subset of Java flags"); - - if ((access_flags_ & kAccInterface) != 0) { - // Interface. - return access_flags_ & kAccValidInterfaceFlags; - } else { - // Class. - return access_flags_ & kAccValidClassFlags; - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDef); - }; - - // Raw type_item. - struct TypeItem { - dex::TypeIndex type_idx_; // index into type_ids section - - private: - DISALLOW_COPY_AND_ASSIGN(TypeItem); - }; - - // Raw type_list. - class TypeList { - public: - uint32_t Size() const { - return size_; - } - - const TypeItem& GetTypeItem(uint32_t idx) const { - DCHECK_LT(idx, this->size_); - return this->list_[idx]; - } - - // Size in bytes of the part of the list that is common. - static constexpr size_t GetHeaderSize() { - return 4U; - } - - // Size in bytes of the whole type list including all the stored elements. - static constexpr size_t GetListSize(size_t count) { - return GetHeaderSize() + sizeof(TypeItem) * count; - } - - private: - uint32_t size_; // size of the list, in entries - TypeItem list_[1]; // elements of the list - DISALLOW_COPY_AND_ASSIGN(TypeList); - }; - - // MethodHandle Types - enum class MethodHandleType : uint16_t { // private - kStaticPut = 0x0000, // a setter for a given static field. - kStaticGet = 0x0001, // a getter for a given static field. - kInstancePut = 0x0002, // a setter for a given instance field. - kInstanceGet = 0x0003, // a getter for a given instance field. - kInvokeStatic = 0x0004, // an invoker for a given static method. - kInvokeInstance = 0x0005, // invoke_instance : an invoker for a given instance method. This - // can be any non-static method on any class (or interface) except - // for “<init>”. - kInvokeConstructor = 0x0006, // an invoker for a given constructor. - kInvokeDirect = 0x0007, // an invoker for a direct (special) method. - kInvokeInterface = 0x0008, // an invoker for an interface method. - kLast = kInvokeInterface - }; - - // raw method_handle_item - struct MethodHandleItem { - uint16_t method_handle_type_; - uint16_t reserved1_; // Reserved for future use. - uint16_t field_or_method_idx_; // Field index for accessors, method index otherwise. - uint16_t reserved2_; // Reserved for future use. - private: - DISALLOW_COPY_AND_ASSIGN(MethodHandleItem); - }; - - // raw call_site_id_item - struct CallSiteIdItem { - uint32_t data_off_; // Offset into data section pointing to encoded array items. - private: - DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem); - }; - - // Base code_item, compact dex and standard dex have different code item layouts. - struct CodeItem { - protected: - CodeItem() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(CodeItem); - }; - - // Raw try_item. - struct TryItem { - static constexpr size_t kAlignment = sizeof(uint32_t); - - uint32_t start_addr_; - uint16_t insn_count_; - uint16_t handler_off_; - - private: - TryItem() = default; - friend class DexWriter; - DISALLOW_COPY_AND_ASSIGN(TryItem); - }; - - // Annotation constants. - enum { - kDexVisibilityBuild = 0x00, /* annotation visibility */ - kDexVisibilityRuntime = 0x01, - kDexVisibilitySystem = 0x02, - - kDexAnnotationByte = 0x00, - kDexAnnotationShort = 0x02, - kDexAnnotationChar = 0x03, - kDexAnnotationInt = 0x04, - kDexAnnotationLong = 0x06, - kDexAnnotationFloat = 0x10, - kDexAnnotationDouble = 0x11, - kDexAnnotationMethodType = 0x15, - kDexAnnotationMethodHandle = 0x16, - kDexAnnotationString = 0x17, - kDexAnnotationType = 0x18, - kDexAnnotationField = 0x19, - kDexAnnotationMethod = 0x1a, - kDexAnnotationEnum = 0x1b, - kDexAnnotationArray = 0x1c, - kDexAnnotationAnnotation = 0x1d, - kDexAnnotationNull = 0x1e, - kDexAnnotationBoolean = 0x1f, - - kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */ - kDexAnnotationValueArgShift = 5, - }; - - struct AnnotationsDirectoryItem { - uint32_t class_annotations_off_; - uint32_t fields_size_; - uint32_t methods_size_; - uint32_t parameters_size_; - - private: - DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem); - }; - - struct FieldAnnotationsItem { - uint32_t field_idx_; - uint32_t annotations_off_; - - private: - DISALLOW_COPY_AND_ASSIGN(FieldAnnotationsItem); - }; - - struct MethodAnnotationsItem { - uint32_t method_idx_; - uint32_t annotations_off_; - - private: - DISALLOW_COPY_AND_ASSIGN(MethodAnnotationsItem); - }; - - struct ParameterAnnotationsItem { - uint32_t method_idx_; - uint32_t annotations_off_; - - private: - DISALLOW_COPY_AND_ASSIGN(ParameterAnnotationsItem); - }; - - struct AnnotationSetRefItem { - uint32_t annotations_off_; - - private: - DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefItem); - }; - - struct AnnotationSetRefList { - uint32_t size_; - AnnotationSetRefItem list_[1]; - - private: - DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefList); - }; - - struct AnnotationSetItem { - uint32_t size_; - uint32_t entries_[1]; - - private: - DISALLOW_COPY_AND_ASSIGN(AnnotationSetItem); - }; - - struct AnnotationItem { - uint8_t visibility_; - uint8_t annotation_[1]; - - private: - DISALLOW_COPY_AND_ASSIGN(AnnotationItem); - }; - - enum AnnotationResultStyle { // private - kAllObjects, - kPrimitivesOrObjects, - kAllRaw - }; - - struct AnnotationValue; - - // Closes a .dex file. - virtual ~DexFile(); - - const std::string& GetLocation() const { - return location_; - } - - // For DexFiles directly from .dex files, this is the checksum from the DexFile::Header. - // For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex. - uint32_t GetLocationChecksum() const { - return location_checksum_; - } - - const Header& GetHeader() const { - DCHECK(header_ != nullptr) << GetLocation(); - return *header_; - } - - // Decode the dex magic version - uint32_t GetDexVersion() const { - return GetHeader().GetVersion(); - } - - // Returns true if the byte string points to the magic value. - virtual bool IsMagicValid() const = 0; - - // Returns true if the byte string after the magic is the correct value. - virtual bool IsVersionValid() const = 0; - - // Returns true if the dex file supports default methods. - virtual bool SupportsDefaultMethods() const = 0; - - // Returns the maximum size in bytes needed to store an equivalent dex file strictly conforming to - // the dex file specification. That is the size if we wanted to get rid of all the - // quickening/compact-dexing/etc. - // - // TODO This should really be an exact size! b/72402467 - virtual size_t GetDequickenedSize() const = 0; - - // Returns the number of string identifiers in the .dex file. - size_t NumStringIds() const { - DCHECK(header_ != nullptr) << GetLocation(); - return header_->string_ids_size_; - } - - // Returns the StringId at the specified index. - const StringId& GetStringId(dex::StringIndex idx) const { - DCHECK_LT(idx.index_, NumStringIds()) << GetLocation(); - return string_ids_[idx.index_]; - } - - dex::StringIndex GetIndexForStringId(const StringId& string_id) const { - CHECK_GE(&string_id, string_ids_) << GetLocation(); - CHECK_LT(&string_id, string_ids_ + header_->string_ids_size_) << GetLocation(); - return dex::StringIndex(&string_id - string_ids_); - } - - int32_t GetStringLength(const StringId& string_id) const; - - // Returns a pointer to the UTF-8 string data referred to by the given string_id as well as the - // length of the string when decoded as a UTF-16 string. Note the UTF-16 length is not the same - // as the string length of the string data. - const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const; - - const char* GetStringData(const StringId& string_id) const; - - // Index version of GetStringDataAndUtf16Length. - const char* StringDataAndUtf16LengthByIdx(dex::StringIndex idx, uint32_t* utf16_length) const; - - const char* StringDataByIdx(dex::StringIndex idx) const; - - // Looks up a string id for a given modified utf8 string. - const StringId* FindStringId(const char* string) const; - - const TypeId* FindTypeId(const char* string) const; - - // Looks up a string id for a given utf16 string. - const StringId* FindStringId(const uint16_t* string, size_t length) const; - - // Returns the number of type identifiers in the .dex file. - uint32_t NumTypeIds() const { - DCHECK(header_ != nullptr) << GetLocation(); - return header_->type_ids_size_; - } - - bool IsTypeIndexValid(dex::TypeIndex idx) const { - return idx.IsValid() && idx.index_ < NumTypeIds(); - } - - // Returns the TypeId at the specified index. - const TypeId& GetTypeId(dex::TypeIndex idx) const { - DCHECK_LT(idx.index_, NumTypeIds()) << GetLocation(); - return type_ids_[idx.index_]; - } - - dex::TypeIndex GetIndexForTypeId(const TypeId& type_id) const { - CHECK_GE(&type_id, type_ids_) << GetLocation(); - CHECK_LT(&type_id, type_ids_ + header_->type_ids_size_) << GetLocation(); - size_t result = &type_id - type_ids_; - DCHECK_LT(result, 65536U) << GetLocation(); - return dex::TypeIndex(static_cast<uint16_t>(result)); - } - - // Get the descriptor string associated with a given type index. - const char* StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const; - - const char* StringByTypeIdx(dex::TypeIndex idx) const; - - // Returns the type descriptor string of a type id. - const char* GetTypeDescriptor(const TypeId& type_id) const; - - // Looks up a type for the given string index - const TypeId* FindTypeId(dex::StringIndex string_idx) const; - - // Returns the number of field identifiers in the .dex file. - size_t NumFieldIds() const { - DCHECK(header_ != nullptr) << GetLocation(); - return header_->field_ids_size_; - } - - // Returns the FieldId at the specified index. - const FieldId& GetFieldId(uint32_t idx) const { - DCHECK_LT(idx, NumFieldIds()) << GetLocation(); - return field_ids_[idx]; - } - - uint32_t GetIndexForFieldId(const FieldId& field_id) const { - CHECK_GE(&field_id, field_ids_) << GetLocation(); - CHECK_LT(&field_id, field_ids_ + header_->field_ids_size_) << GetLocation(); - return &field_id - field_ids_; - } - - // Looks up a field by its declaring class, name and type - const FieldId* FindFieldId(const DexFile::TypeId& declaring_klass, - const DexFile::StringId& name, - const DexFile::TypeId& type) const; - - uint32_t FindCodeItemOffset(const DexFile::ClassDef& class_def, - uint32_t dex_method_idx) const; - - virtual uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item) const = 0; - - // Returns the declaring class descriptor string of a field id. - const char* GetFieldDeclaringClassDescriptor(const FieldId& field_id) const { - const DexFile::TypeId& type_id = GetTypeId(field_id.class_idx_); - return GetTypeDescriptor(type_id); - } - - // Returns the class descriptor string of a field id. - const char* GetFieldTypeDescriptor(const FieldId& field_id) const; - - // Returns the name of a field id. - const char* GetFieldName(const FieldId& field_id) const; - - // Returns the number of method identifiers in the .dex file. - size_t NumMethodIds() const { - DCHECK(header_ != nullptr) << GetLocation(); - return header_->method_ids_size_; - } - - // Returns the MethodId at the specified index. - const MethodId& GetMethodId(uint32_t idx) const { - DCHECK_LT(idx, NumMethodIds()) << GetLocation(); - return method_ids_[idx]; - } - - uint32_t GetIndexForMethodId(const MethodId& method_id) const { - CHECK_GE(&method_id, method_ids_) << GetLocation(); - CHECK_LT(&method_id, method_ids_ + header_->method_ids_size_) << GetLocation(); - return &method_id - method_ids_; - } - - // Looks up a method by its declaring class, name and proto_id - const MethodId* FindMethodId(const DexFile::TypeId& declaring_klass, - const DexFile::StringId& name, - const DexFile::ProtoId& signature) const; - - // Returns the declaring class descriptor string of a method id. - const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const; - - // Returns the prototype of a method id. - const ProtoId& GetMethodPrototype(const MethodId& method_id) const { - return GetProtoId(method_id.proto_idx_); - } - - // Returns a representation of the signature of a method id. - const Signature GetMethodSignature(const MethodId& method_id) const; - - // Returns a representation of the signature of a proto id. - const Signature GetProtoSignature(const ProtoId& proto_id) const; - - // Returns the name of a method id. - const char* GetMethodName(const MethodId& method_id) const; - - // Returns the shorty of a method by its index. - const char* GetMethodShorty(uint32_t idx) const; - - // Returns the shorty of a method id. - const char* GetMethodShorty(const MethodId& method_id) const; - const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const; - - // Returns the number of class definitions in the .dex file. - uint32_t NumClassDefs() const { - DCHECK(header_ != nullptr) << GetLocation(); - return header_->class_defs_size_; - } - - // Returns the ClassDef at the specified index. - const ClassDef& GetClassDef(uint16_t idx) const { - DCHECK_LT(idx, NumClassDefs()) << GetLocation(); - return class_defs_[idx]; - } - - uint16_t GetIndexForClassDef(const ClassDef& class_def) const { - CHECK_GE(&class_def, class_defs_) << GetLocation(); - CHECK_LT(&class_def, class_defs_ + header_->class_defs_size_) << GetLocation(); - return &class_def - class_defs_; - } - - // Returns the class descriptor string of a class definition. - const char* GetClassDescriptor(const ClassDef& class_def) const; - - // Looks up a class definition by its type index. - const ClassDef* FindClassDef(dex::TypeIndex type_idx) const; - - const TypeList* GetInterfacesList(const ClassDef& class_def) const { - return DataPointer<TypeList>(class_def.interfaces_off_); - } - - uint32_t NumMethodHandles() const { - return num_method_handles_; - } - - const MethodHandleItem& GetMethodHandle(uint32_t idx) const { - CHECK_LT(idx, NumMethodHandles()); - return method_handles_[idx]; - } - - uint32_t NumCallSiteIds() const { - return num_call_site_ids_; - } - - const CallSiteIdItem& GetCallSiteId(uint32_t idx) const { - CHECK_LT(idx, NumCallSiteIds()); - return call_site_ids_[idx]; - } - - // Returns a pointer to the raw memory mapped class_data_item - const uint8_t* GetClassData(const ClassDef& class_def) const { - return DataPointer<uint8_t>(class_def.class_data_off_); - } - - // Return the code item for a provided offset. - const CodeItem* GetCodeItem(const uint32_t code_off) const { - // May be null for native or abstract methods. - return DataPointer<CodeItem>(code_off); - } - - const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; - - // Returns the number of prototype identifiers in the .dex file. - size_t NumProtoIds() const { - DCHECK(header_ != nullptr) << GetLocation(); - return header_->proto_ids_size_; - } - - // Returns the ProtoId at the specified index. - const ProtoId& GetProtoId(uint16_t idx) const { - DCHECK_LT(idx, NumProtoIds()) << GetLocation(); - return proto_ids_[idx]; - } - - uint16_t GetIndexForProtoId(const ProtoId& proto_id) const { - CHECK_GE(&proto_id, proto_ids_) << GetLocation(); - CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation(); - return &proto_id - proto_ids_; - } - - // Looks up a proto id for a given return type and signature type list - const ProtoId* FindProtoId(dex::TypeIndex return_type_idx, - const dex::TypeIndex* signature_type_idxs, - uint32_t signature_length) const; - const ProtoId* FindProtoId(dex::TypeIndex return_type_idx, - const std::vector<dex::TypeIndex>& signature_type_idxs) const { - return FindProtoId(return_type_idx, &signature_type_idxs[0], signature_type_idxs.size()); - } - - // Given a signature place the type ids into the given vector, returns true on success - bool CreateTypeList(const StringPiece& signature, - dex::TypeIndex* return_type_idx, - std::vector<dex::TypeIndex>* param_type_idxs) const; - - // Create a Signature from the given string signature or return Signature::NoSignature if not - // possible. - const Signature CreateSignature(const StringPiece& signature) const; - - // Returns the short form method descriptor for the given prototype. - const char* GetShorty(uint32_t proto_idx) const; - - const TypeList* GetProtoParameters(const ProtoId& proto_id) const { - return DataPointer<TypeList>(proto_id.parameters_off_); - } - - const uint8_t* GetEncodedStaticFieldValuesArray(const ClassDef& class_def) const { - return DataPointer<uint8_t>(class_def.static_values_off_); - } - - const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const { - return DataBegin() + call_site_id.data_off_; - } - - static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset); - - // Get the base of the encoded data for the given DexCode. - static const uint8_t* GetCatchHandlerData(const DexInstructionIterator& code_item_end, - uint32_t tries_size, - uint32_t offset); - - // Find which try region is associated with the given address (ie dex pc). Returns -1 if none. - static int32_t FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address); - - // Get the pointer to the start of the debugging data - const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const { - // Check that the offset is in bounds. - // Note that although the specification says that 0 should be used if there - // is no debug information, some applications incorrectly use 0xFFFFFFFF. - return (debug_info_off == 0 || debug_info_off >= data_size_) - ? nullptr - : DataBegin() + debug_info_off; - } - - struct PositionInfo { - PositionInfo() = default; - - uint32_t address_ = 0; // In 16-bit code units. - uint32_t line_ = 0; // Source code line number starting at 1. - const char* source_file_ = nullptr; // nullptr if the file from ClassDef still applies. - bool prologue_end_ = false; - bool epilogue_begin_ = false; - }; - - struct LocalInfo { - LocalInfo() = default; - - const char* name_ = nullptr; // E.g., list. It can be nullptr if unknown. - const char* descriptor_ = nullptr; // E.g., Ljava/util/LinkedList; - const char* signature_ = nullptr; // E.g., java.util.LinkedList<java.lang.Integer> - uint32_t start_address_ = 0; // PC location where the local is first defined. - uint32_t end_address_ = 0; // PC location where the local is no longer defined. - uint16_t reg_ = 0; // Dex register which stores the values. - bool is_live_ = false; // Is the local defined and live. - }; - - // Callback for "new locals table entry". - typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry); - - static bool LineNumForPcCb(void* context, const PositionInfo& entry); - - const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const { - return DataPointer<AnnotationsDirectoryItem>(class_def.annotations_off_); - } - - const AnnotationSetItem* GetClassAnnotationSet(const AnnotationsDirectoryItem* anno_dir) const { - return DataPointer<AnnotationSetItem>(anno_dir->class_annotations_off_); - } - - const FieldAnnotationsItem* GetFieldAnnotations(const AnnotationsDirectoryItem* anno_dir) const { - return (anno_dir->fields_size_ == 0) - ? nullptr - : reinterpret_cast<const FieldAnnotationsItem*>(&anno_dir[1]); - } - - const MethodAnnotationsItem* GetMethodAnnotations(const AnnotationsDirectoryItem* anno_dir) - const { - if (anno_dir->methods_size_ == 0) { - return nullptr; - } - // Skip past the header and field annotations. - const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]); - addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem); - return reinterpret_cast<const MethodAnnotationsItem*>(addr); - } - - const ParameterAnnotationsItem* GetParameterAnnotations(const AnnotationsDirectoryItem* anno_dir) - const { - if (anno_dir->parameters_size_ == 0) { - return nullptr; - } - // Skip past the header, field annotations, and method annotations. - const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]); - addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem); - addr += anno_dir->methods_size_ * sizeof(MethodAnnotationsItem); - return reinterpret_cast<const ParameterAnnotationsItem*>(addr); - } - - const AnnotationSetItem* GetFieldAnnotationSetItem(const FieldAnnotationsItem& anno_item) const { - return DataPointer<AnnotationSetItem>(anno_item.annotations_off_); - } - - const AnnotationSetItem* GetMethodAnnotationSetItem(const MethodAnnotationsItem& anno_item) - const { - return DataPointer<AnnotationSetItem>(anno_item.annotations_off_); - } - - const AnnotationSetRefList* GetParameterAnnotationSetRefList( - const ParameterAnnotationsItem* anno_item) const { - return DataPointer<AnnotationSetRefList>(anno_item->annotations_off_); - } - - ALWAYS_INLINE const AnnotationItem* GetAnnotationItemAtOffset(uint32_t offset) const { - return DataPointer<AnnotationItem>(offset); - } - - const AnnotationItem* GetAnnotationItem(const AnnotationSetItem* set_item, uint32_t index) const { - DCHECK_LE(index, set_item->size_); - return GetAnnotationItemAtOffset(set_item->entries_[index]); - } - - const AnnotationSetItem* GetSetRefItemItem(const AnnotationSetRefItem* anno_item) const { - return DataPointer<AnnotationSetItem>(anno_item->annotations_off_); - } - - // Debug info opcodes and constants - enum { - DBG_END_SEQUENCE = 0x00, - DBG_ADVANCE_PC = 0x01, - DBG_ADVANCE_LINE = 0x02, - DBG_START_LOCAL = 0x03, - DBG_START_LOCAL_EXTENDED = 0x04, - DBG_END_LOCAL = 0x05, - DBG_RESTART_LOCAL = 0x06, - DBG_SET_PROLOGUE_END = 0x07, - DBG_SET_EPILOGUE_BEGIN = 0x08, - DBG_SET_FILE = 0x09, - DBG_FIRST_SPECIAL = 0x0a, - DBG_LINE_BASE = -4, - DBG_LINE_RANGE = 15, - }; - - struct LineNumFromPcContext { - LineNumFromPcContext(uint32_t address, uint32_t line_num) - : address_(address), line_num_(line_num) {} - uint32_t address_; - uint32_t line_num_; - private: - DISALLOW_COPY_AND_ASSIGN(LineNumFromPcContext); - }; - - // Returns false if there is no debugging information or if it cannot be decoded. - template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData> - static bool DecodeDebugLocalInfo(const uint8_t* stream, - const std::string& location, - const char* declaring_class_descriptor, - const std::vector<const char*>& arg_descriptors, - const std::string& method_name, - bool is_static, - uint16_t registers_size, - uint16_t ins_size, - uint16_t insns_size_in_code_units, - IndexToStringData index_to_string_data, - TypeIndexToStringData type_index_to_string_data, - NewLocalCallback new_local, - void* context); - template<typename NewLocalCallback> - bool DecodeDebugLocalInfo(uint32_t registers_size, - uint32_t ins_size, - uint32_t insns_size_in_code_units, - uint32_t debug_info_offset, - bool is_static, - uint32_t method_idx, - NewLocalCallback new_local, - void* context) const; - - // Returns false if there is no debugging information or if it cannot be decoded. - template<typename DexDebugNewPosition, typename IndexToStringData> - static bool DecodeDebugPositionInfo(const uint8_t* stream, - IndexToStringData index_to_string_data, - DexDebugNewPosition position_functor, - void* context); - template<typename DexDebugNewPosition> - bool DecodeDebugPositionInfo(uint32_t debug_info_offset, - DexDebugNewPosition position_functor, - void* context) const; - - const char* GetSourceFile(const ClassDef& class_def) const { - if (!class_def.source_file_idx_.IsValid()) { - return nullptr; - } else { - return StringDataByIdx(class_def.source_file_idx_); - } - } - - int GetPermissions() const; - - bool IsReadOnly() const; - - bool EnableWrite() const; - - bool DisableWrite() const; - - const uint8_t* Begin() const { - return begin_; - } - - size_t Size() const { - return size_; - } - - const uint8_t* DataBegin() const { - return data_begin_; - } - - size_t DataSize() const { - return data_size_; - } - - template <typename T> - const T* DataPointer(size_t offset) const { - DCHECK_LT(offset, DataSize()) << "Offset past end of data section"; - return (offset != 0u) ? reinterpret_cast<const T*>(DataBegin() + offset) : nullptr; - } - - const OatDexFile* GetOatDexFile() const { - return oat_dex_file_; - } - - // Used by oat writer. - void SetOatDexFile(OatDexFile* oat_dex_file) const { - oat_dex_file_ = oat_dex_file; - } - - // Read MapItems and validate/set remaining offsets. - const DexFile::MapList* GetMapList() const { - return reinterpret_cast<const DexFile::MapList*>(DataBegin() + header_->map_off_); - } - - // Utility methods for reading integral values from a buffer. - static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth); - static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right); - static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth); - static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right); - - // Recalculates the checksum of the dex file. Does not use the current value in the header. - virtual uint32_t CalculateChecksum() const; - static uint32_t CalculateChecksum(const uint8_t* begin, size_t size); - static uint32_t ChecksumMemoryRange(const uint8_t* begin, size_t size); - - // Returns a human-readable form of the method at an index. - std::string PrettyMethod(uint32_t method_idx, bool with_signature = true) const; - // Returns a human-readable form of the field at an index. - std::string PrettyField(uint32_t field_idx, bool with_type = true) const; - // Returns a human-readable form of the type at an index. - std::string PrettyType(dex::TypeIndex type_idx) const; - - // Not virtual for performance reasons. - ALWAYS_INLINE bool IsCompactDexFile() const { - return is_compact_dex_; - } - ALWAYS_INLINE bool IsStandardDexFile() const { - return !is_compact_dex_; - } - ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; - ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; - - bool IsInMainSection(const void* addr) const { - return Begin() <= addr && addr < Begin() + Size(); - } - - bool IsInDataSection(const void* addr) const { - return DataBegin() <= addr && addr < DataBegin() + DataSize(); - } - - DexFileContainer* GetContainer() const { - return container_.get(); - } - - protected: - // First Dex format version supporting default methods. - static const uint32_t kDefaultMethodsVersion = 37; - - DexFile(const uint8_t* base, - size_t size, - const uint8_t* data_begin, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - DexFileContainer* container, - bool is_compact_dex); - - // Top-level initializer that calls other Init methods. - bool Init(std::string* error_msg); - - // Returns true if the header magic and version numbers are of the expected values. - bool CheckMagicAndVersion(std::string* error_msg) const; - - // Initialize section info for sections only found in map. Returns true on success. - void InitializeSectionsFromMapList(); - - // The base address of the memory mapping. - const uint8_t* const begin_; - - // The size of the underlying memory allocation in bytes. - const size_t size_; - - // The base address of the data section (same as Begin() for standard dex). - const uint8_t* const data_begin_; - - // The size of the data section. - const size_t data_size_; - - // Typically the dex file name when available, alternatively some identifying string. - // - // The ClassLinker will use this to match DexFiles the boot class - // path to DexCache::GetLocation when loading from an image. - const std::string location_; - - const uint32_t location_checksum_; - - // Points to the header section. - const Header* const header_; - - // Points to the base of the string identifier list. - const StringId* const string_ids_; - - // Points to the base of the type identifier list. - const TypeId* const type_ids_; - - // Points to the base of the field identifier list. - const FieldId* const field_ids_; - - // Points to the base of the method identifier list. - const MethodId* const method_ids_; - - // Points to the base of the prototype identifier list. - const ProtoId* const proto_ids_; - - // Points to the base of the class definition list. - const ClassDef* const class_defs_; - - // Points to the base of the method handles list. - const MethodHandleItem* method_handles_; - - // Number of elements in the method handles list. - size_t num_method_handles_; - - // Points to the base of the call sites id list. - const CallSiteIdItem* call_site_ids_; - - // Number of elements in the call sites list. - size_t num_call_site_ids_; - - // If this dex file was loaded from an oat file, oat_dex_file_ contains a - // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is - // null. - mutable const OatDexFile* oat_dex_file_; - - // Manages the underlying memory allocation. - std::unique_ptr<DexFileContainer> container_; - - // If the dex file is a compact dex file. If false then the dex file is a standard dex file. - const bool is_compact_dex_; - - friend class DexFileLoader; - friend class DexFileVerifierTest; - friend class OatWriter; -}; - -std::ostream& operator<<(std::ostream& os, const DexFile& dex_file); - -// Iterate over a dex file's ProtoId's paramters -class DexFileParameterIterator { - public: - DexFileParameterIterator(const DexFile& dex_file, const DexFile::ProtoId& proto_id) - : dex_file_(dex_file) { - type_list_ = dex_file_.GetProtoParameters(proto_id); - if (type_list_ != nullptr) { - size_ = type_list_->Size(); - } - } - bool HasNext() const { return pos_ < size_; } - size_t Size() const { return size_; } - void Next() { ++pos_; } - dex::TypeIndex GetTypeIdx() { - return type_list_->GetTypeItem(pos_).type_idx_; - } - const char* GetDescriptor() { - return dex_file_.StringByTypeIdx(dex::TypeIndex(GetTypeIdx())); - } - private: - const DexFile& dex_file_; - const DexFile::TypeList* type_list_ = nullptr; - uint32_t size_ = 0; - uint32_t pos_ = 0; - DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator); -}; - -// Abstract the signature of a method. -class Signature : public ValueObject { - public: - std::string ToString() const; - - static Signature NoSignature() { - return Signature(); - } - - bool IsVoid() const; - uint32_t GetNumberOfParameters() const; - - bool operator==(const Signature& rhs) const; - bool operator!=(const Signature& rhs) const { - return !(*this == rhs); - } - - bool operator==(const StringPiece& rhs) const; - - private: - Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) { - } - - Signature() = default; - - friend class DexFile; - - const DexFile* const dex_file_ = nullptr; - const DexFile::ProtoId* const proto_id_ = nullptr; -}; -std::ostream& operator<<(std::ostream& os, const Signature& sig); - -// Iterate and decode class_data_item -class ClassDataItemIterator { - public: - ClassDataItemIterator(const DexFile& dex_file, const uint8_t* raw_class_data_item) - : dex_file_(dex_file), pos_(0), ptr_pos_(raw_class_data_item), last_idx_(0) { - ReadClassDataHeader(); - if (EndOfInstanceFieldsPos() > 0) { - ReadClassDataField(); - } else if (EndOfVirtualMethodsPos() > 0) { - ReadClassDataMethod(); - } - } - uint32_t NumStaticFields() const { - return header_.static_fields_size_; - } - uint32_t NumInstanceFields() const { - return header_.instance_fields_size_; - } - uint32_t NumDirectMethods() const { - return header_.direct_methods_size_; - } - uint32_t NumVirtualMethods() const { - return header_.virtual_methods_size_; - } - bool IsAtMethod() const { - return pos_ >= EndOfInstanceFieldsPos(); - } - bool HasNextStaticField() const { - return pos_ < EndOfStaticFieldsPos(); - } - bool HasNextInstanceField() const { - return pos_ >= EndOfStaticFieldsPos() && pos_ < EndOfInstanceFieldsPos(); - } - bool HasNextDirectMethod() const { - return pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfDirectMethodsPos(); - } - bool HasNextVirtualMethod() const { - return pos_ >= EndOfDirectMethodsPos() && pos_ < EndOfVirtualMethodsPos(); - } - bool HasNextMethod() const { - const bool result = pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfVirtualMethodsPos(); - DCHECK_EQ(result, HasNextDirectMethod() || HasNextVirtualMethod()); - return result; - } - void SkipStaticFields() { - while (HasNextStaticField()) { - Next(); - } - } - void SkipInstanceFields() { - while (HasNextInstanceField()) { - Next(); - } - } - void SkipAllFields() { - SkipStaticFields(); - SkipInstanceFields(); - } - void SkipDirectMethods() { - while (HasNextDirectMethod()) { - Next(); - } - } - void SkipVirtualMethods() { - while (HasNextVirtualMethod()) { - Next(); - } - } - bool HasNext() const { - return pos_ < EndOfVirtualMethodsPos(); - } - inline void Next() { - pos_++; - if (pos_ < EndOfStaticFieldsPos()) { - last_idx_ = GetMemberIndex(); - ReadClassDataField(); - } else if (pos_ == EndOfStaticFieldsPos() && NumInstanceFields() > 0) { - last_idx_ = 0; // transition to next array, reset last index - ReadClassDataField(); - } else if (pos_ < EndOfInstanceFieldsPos()) { - last_idx_ = GetMemberIndex(); - ReadClassDataField(); - } else if (pos_ == EndOfInstanceFieldsPos() && NumDirectMethods() > 0) { - last_idx_ = 0; // transition to next array, reset last index - ReadClassDataMethod(); - } else if (pos_ < EndOfDirectMethodsPos()) { - last_idx_ = GetMemberIndex(); - ReadClassDataMethod(); - } else if (pos_ == EndOfDirectMethodsPos() && NumVirtualMethods() > 0) { - last_idx_ = 0; // transition to next array, reset last index - ReadClassDataMethod(); - } else if (pos_ < EndOfVirtualMethodsPos()) { - last_idx_ = GetMemberIndex(); - ReadClassDataMethod(); - } else { - DCHECK(!HasNext()); - } - } - uint32_t GetMemberIndex() const { - if (pos_ < EndOfInstanceFieldsPos()) { - return last_idx_ + field_.field_idx_delta_; - } else { - DCHECK_LT(pos_, EndOfVirtualMethodsPos()); - return last_idx_ + method_.method_idx_delta_; - } - } - uint32_t GetRawMemberAccessFlags() const { - if (pos_ < EndOfInstanceFieldsPos()) { - return field_.access_flags_; - } else { - DCHECK_LT(pos_, EndOfVirtualMethodsPos()); - return method_.access_flags_; - } - } - uint32_t GetFieldAccessFlags() const { - return GetMemberAccessFlags() & kAccValidFieldFlags; - } - uint32_t GetMethodAccessFlags() const { - return GetMemberAccessFlags() & kAccValidMethodFlags; - } - uint32_t GetMemberAccessFlags() const { - return HiddenApiAccessFlags::RemoveFromDex(GetRawMemberAccessFlags()); - } - HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { - return HiddenApiAccessFlags::DecodeFromDex(GetRawMemberAccessFlags()); - } - bool MemberIsNative() const { - return GetRawMemberAccessFlags() & kAccNative; - } - bool MemberIsFinal() const { - return GetRawMemberAccessFlags() & kAccFinal; - } - ALWAYS_INLINE InvokeType GetMethodInvokeType(const DexFile::ClassDef& class_def) const; - const DexFile::CodeItem* GetMethodCodeItem() const { - return dex_file_.GetCodeItem(method_.code_off_); - } - uint32_t GetMethodCodeItemOffset() const { - return method_.code_off_; - } - const uint8_t* DataPointer() const { - return ptr_pos_; - } - const uint8_t* EndDataPointer() const { - CHECK(!HasNext()); - return ptr_pos_; - } - - private: - // A dex file's class_data_item is leb128 encoded, this structure holds a decoded form of the - // header for a class_data_item - struct ClassDataHeader { - uint32_t static_fields_size_; // the number of static fields - uint32_t instance_fields_size_; // the number of instance fields - uint32_t direct_methods_size_; // the number of direct methods - uint32_t virtual_methods_size_; // the number of virtual methods - } header_; - - // Read and decode header from a class_data_item stream into header - void ReadClassDataHeader(); - - uint32_t EndOfStaticFieldsPos() const { - return header_.static_fields_size_; - } - uint32_t EndOfInstanceFieldsPos() const { - return EndOfStaticFieldsPos() + header_.instance_fields_size_; - } - uint32_t EndOfDirectMethodsPos() const { - return EndOfInstanceFieldsPos() + header_.direct_methods_size_; - } - uint32_t EndOfVirtualMethodsPos() const { - return EndOfDirectMethodsPos() + header_.virtual_methods_size_; - } - - // A decoded version of the field of a class_data_item - struct ClassDataField { - uint32_t field_idx_delta_; // delta of index into the field_ids array for FieldId - uint32_t access_flags_; // access flags for the field - ClassDataField() : field_idx_delta_(0), access_flags_(0) {} - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDataField); - }; - ClassDataField field_; - - // Read and decode a field from a class_data_item stream into field - void ReadClassDataField(); - - // A decoded version of the method of a class_data_item - struct ClassDataMethod { - uint32_t method_idx_delta_; // delta of index into the method_ids array for MethodId - uint32_t access_flags_; - uint32_t code_off_; - ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {} - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDataMethod); - }; - ClassDataMethod method_; - - // Read and decode a method from a class_data_item stream into method - void ReadClassDataMethod(); - - const DexFile& dex_file_; - size_t pos_; // integral number of items passed - const uint8_t* ptr_pos_; // pointer into stream of class_data_item - uint32_t last_idx_; // last read field or method index to apply delta to - DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator); -}; - -class EncodedArrayValueIterator { - public: - EncodedArrayValueIterator(const DexFile& dex_file, const uint8_t* array_data); - - bool HasNext() const { return pos_ < array_size_; } - - void Next(); - - enum ValueType { - kByte = 0x00, - kShort = 0x02, - kChar = 0x03, - kInt = 0x04, - kLong = 0x06, - kFloat = 0x10, - kDouble = 0x11, - kMethodType = 0x15, - kMethodHandle = 0x16, - kString = 0x17, - kType = 0x18, - kField = 0x19, - kMethod = 0x1a, - kEnum = 0x1b, - kArray = 0x1c, - kAnnotation = 0x1d, - kNull = 0x1e, - kBoolean = 0x1f, - }; - - ValueType GetValueType() const { return type_; } - const jvalue& GetJavaValue() const { return jval_; } - - protected: - static constexpr uint8_t kEncodedValueTypeMask = 0x1f; // 0b11111 - static constexpr uint8_t kEncodedValueArgShift = 5; - - const DexFile& dex_file_; - size_t array_size_; // Size of array. - size_t pos_; // Current position. - const uint8_t* ptr_; // Pointer into encoded data array. - ValueType type_; // Type of current encoded value. - jvalue jval_; // Value of current encoded value. - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedArrayValueIterator); -}; -std::ostream& operator<<(std::ostream& os, const EncodedArrayValueIterator::ValueType& code); - -class EncodedStaticFieldValueIterator : public EncodedArrayValueIterator { - public: - EncodedStaticFieldValueIterator(const DexFile& dex_file, - const DexFile::ClassDef& class_def) - : EncodedArrayValueIterator(dex_file, - dex_file.GetEncodedStaticFieldValuesArray(class_def)) - {} - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedStaticFieldValueIterator); -}; -std::ostream& operator<<(std::ostream& os, const EncodedStaticFieldValueIterator::ValueType& code); - -class CallSiteArrayValueIterator : public EncodedArrayValueIterator { - public: - CallSiteArrayValueIterator(const DexFile& dex_file, - const DexFile::CallSiteIdItem& call_site_id) - : EncodedArrayValueIterator(dex_file, - dex_file.GetCallSiteEncodedValuesArray(call_site_id)) - {} - - uint32_t Size() const { return array_size_; } - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(CallSiteArrayValueIterator); -}; -std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code); - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_H_ diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index e01890f541..3431bb7efb 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -23,7 +23,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "jni_internal.h" #include "jvalue-inl.h" #include "mirror/field.h" diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index 26773729c2..d7ebf84b1c 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_ #define ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_ -#include "dex_file.h" +#include "dex/dex_file.h" #include "handle.h" #include "mirror/dex_cache.h" diff --git a/runtime/dex/dex_file_exception_helpers.cc b/runtime/dex/dex_file_exception_helpers.cc deleted file mode 100644 index 8e597fd3dd..0000000000 --- a/runtime/dex/dex_file_exception_helpers.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dex_file_exception_helpers.h" - -#include "code_item_accessors-inl.h" - -namespace art { - -CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address) { - handler_.address_ = -1; - int32_t offset = -1; - - // Short-circuit the overwhelmingly common cases. - switch (accessor.TriesSize()) { - case 0: - break; - case 1: { - const DexFile::TryItem* tries = accessor.TryItems().begin(); - uint32_t start = tries->start_addr_; - if (address >= start) { - uint32_t end = start + tries->insn_count_; - if (address < end) { - offset = tries->handler_off_; - } - } - break; - } - default: { - const DexFile::TryItem* try_item = accessor.FindTryItem(address); - offset = try_item != nullptr ? try_item->handler_off_ : -1; - break; - } - } - Init(accessor, offset); -} - -CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, - const DexFile::TryItem& try_item) { - handler_.address_ = -1; - Init(accessor, try_item.handler_off_); -} - -void CatchHandlerIterator::Init(const CodeItemDataAccessor& accessor, int32_t offset) { - if (offset >= 0) { - Init(accessor.GetCatchHandlerData(offset)); - } else { - // Not found, initialize as empty - current_data_ = nullptr; - remaining_count_ = -1; - catch_all_ = false; - DCHECK(!HasNext()); - } -} - -void CatchHandlerIterator::Init(const uint8_t* handler_data) { - current_data_ = handler_data; - remaining_count_ = DecodeSignedLeb128(¤t_data_); - - // If remaining_count_ is non-positive, then it is the negative of - // the number of catch types, and the catches are followed by a - // catch-all handler. - if (remaining_count_ <= 0) { - catch_all_ = true; - remaining_count_ = -remaining_count_; - } else { - catch_all_ = false; - } - Next(); -} - -void CatchHandlerIterator::Next() { - if (remaining_count_ > 0) { - handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(¤t_data_)); - handler_.address_ = DecodeUnsignedLeb128(¤t_data_); - remaining_count_--; - return; - } - - if (catch_all_) { - handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16); - handler_.address_ = DecodeUnsignedLeb128(¤t_data_); - catch_all_ = false; - return; - } - - // no more handler - remaining_count_ = -1; -} - -} // namespace art diff --git a/runtime/dex/dex_file_exception_helpers.h b/runtime/dex/dex_file_exception_helpers.h deleted file mode 100644 index bd6cb7e747..0000000000 --- a/runtime/dex/dex_file_exception_helpers.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ -#define ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ - -#include "dex_file.h" - -namespace art { - -class CodeItemDataAccessor; - -class CatchHandlerIterator { - public: - CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address); - - CatchHandlerIterator(const CodeItemDataAccessor& accessor, const DexFile::TryItem& try_item); - - explicit CatchHandlerIterator(const uint8_t* handler_data) { - Init(handler_data); - } - - dex::TypeIndex GetHandlerTypeIndex() const { - return handler_.type_idx_; - } - uint32_t GetHandlerAddress() const { - return handler_.address_; - } - void Next(); - bool HasNext() const { - return remaining_count_ != -1 || catch_all_; - } - // End of this set of catch blocks, convenience method to locate next set of catch blocks - const uint8_t* EndDataPointer() const { - CHECK(!HasNext()); - return current_data_; - } - - private: - void Init(const CodeItemDataAccessor& accessor, int32_t offset); - void Init(const uint8_t* handler_data); - - struct CatchHandlerItem { - dex::TypeIndex type_idx_; // type index of the caught exception type - uint32_t address_; // handler address - } handler_; - const uint8_t* current_data_; // the current handler in dex file. - int32_t remaining_count_; // number of handlers not read. - bool catch_all_; // is there a handler that will catch all exceptions in case - // that all typed handler does not match. -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ diff --git a/runtime/dex/dex_file_layout.cc b/runtime/dex/dex_file_layout.cc index 312898d82f..d85d61d56b 100644 --- a/runtime/dex/dex_file_layout.cc +++ b/runtime/dex/dex_file_layout.cc @@ -19,8 +19,7 @@ #include <sys/mman.h> #include "base/file_utils.h" -#include "descriptors_names.h" -#include "dex_file.h" +#include "dex/dex_file.h" namespace art { diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc deleted file mode 100644 index 2c75c5b5d9..0000000000 --- a/runtime/dex/dex_file_loader.cc +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dex_file_loader.h" - -#include "android-base/stringprintf.h" - -#include "base/stl_util.h" -#include "compact_dex_file.h" -#include "dex_file.h" -#include "dex_file_verifier.h" -#include "standard_dex_file.h" -#include "ziparchive/zip_archive.h" - -// system/core/zip_archive definitions. -struct ZipEntry; -typedef void* ZipArchiveHandle; - -namespace art { - -namespace { - -class VectorContainer : public DexFileContainer { - public: - explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { } - virtual ~VectorContainer() OVERRIDE { } - - int GetPermissions() OVERRIDE { - return 0; - } - - bool IsReadOnly() OVERRIDE { - return true; - } - - bool EnableWrite() OVERRIDE { - return false; - } - - bool DisableWrite() OVERRIDE { - return false; - } - - private: - std::vector<uint8_t> vector_; - DISALLOW_COPY_AND_ASSIGN(VectorContainer); -}; - -} // namespace - -using android::base::StringPrintf; - -class DexZipArchive; - -class DexZipEntry { - public: - // Extract this entry to memory. - // Returns null on failure and sets error_msg. - const std::vector<uint8_t> Extract(std::string* error_msg) { - std::vector<uint8_t> map(GetUncompressedLength()); - if (map.size() == 0) { - DCHECK(!error_msg->empty()); - return map; - } - const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size()); - if (error) { - *error_msg = std::string(ErrorCodeString(error)); - } - return map; - } - - virtual ~DexZipEntry() { - delete zip_entry_; - } - - uint32_t GetUncompressedLength() { - return zip_entry_->uncompressed_length; - } - - uint32_t GetCrc32() { - return zip_entry_->crc32; - } - - private: - DexZipEntry(ZipArchiveHandle handle, - ::ZipEntry* zip_entry, - const std::string& entry_name) - : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {} - - ZipArchiveHandle handle_; - ::ZipEntry* const zip_entry_; - std::string const entry_name_; - - friend class DexZipArchive; - DISALLOW_COPY_AND_ASSIGN(DexZipEntry); -}; - -class DexZipArchive { - public: - // return new DexZipArchive instance on success, null on error. - static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) { - ZipArchiveHandle handle; - uint8_t* nonconst_base = const_cast<uint8_t*>(base); - const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle); - if (error) { - *error_msg = std::string(ErrorCodeString(error)); - CloseArchive(handle); - return nullptr; - } - return new DexZipArchive(handle); - } - - DexZipEntry* Find(const char* name, std::string* error_msg) const { - DCHECK(name != nullptr); - // Resist the urge to delete the space. <: is a bigraph sequence. - std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry); - const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get()); - if (error) { - *error_msg = std::string(ErrorCodeString(error)); - return nullptr; - } - return new DexZipEntry(handle_, zip_entry.release(), name); - } - - ~DexZipArchive() { - CloseArchive(handle_); - } - - - private: - explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {} - ZipArchiveHandle handle_; - - friend class DexZipEntry; - DISALLOW_COPY_AND_ASSIGN(DexZipArchive); -}; - -static bool IsZipMagic(uint32_t magic) { - return (('P' == ((magic >> 0) & 0xff)) && - ('K' == ((magic >> 8) & 0xff))); -} - -bool DexFileLoader::IsMagicValid(uint32_t magic) { - return IsMagicValid(reinterpret_cast<uint8_t*>(&magic)); -} - -bool DexFileLoader::IsMagicValid(const uint8_t* magic) { - return StandardDexFile::IsMagicValid(magic) || - CompactDexFile::IsMagicValid(magic); -} - -bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) { - if (StandardDexFile::IsMagicValid(magic)) { - return StandardDexFile::IsVersionValid(magic); - } - if (CompactDexFile::IsMagicValid(magic)) { - return CompactDexFile::IsVersionValid(magic); - } - return false; -} - -bool DexFileLoader::IsMultiDexLocation(const char* location) { - return strrchr(location, kMultiDexSeparator) != nullptr; -} - -std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) { - return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1); -} - -std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) { - return (index == 0) - ? dex_location - : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1); -} - -std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) { - CHECK_NE(dex_location, static_cast<const char*>(nullptr)); - std::string base_location = GetBaseLocation(dex_location); - const char* suffix = dex_location + base_location.size(); - DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator); - UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr)); - if (path != nullptr && path.get() != base_location) { - return std::string(path.get()) + suffix; - } else if (suffix[0] == 0) { - return base_location; - } else { - return dex_location; - } -} - -// All of the implementations here should be independent of the runtime. -// TODO: implement all the virtual methods. - -bool DexFileLoader::GetMultiDexChecksums(const char* filename ATTRIBUTE_UNUSED, - std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED, - std::string* error_msg, - int zip_fd ATTRIBUTE_UNUSED) const { - *error_msg = "UNIMPLEMENTED"; - return false; -} - -std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const { - return OpenCommon(base, - size, - /*data_base*/ nullptr, - /*data_size*/ 0, - location, - location_checksum, - oat_dex_file, - verify, - verify_checksum, - error_msg, - /*container*/ nullptr, - /*verify_result*/ nullptr); -} - -std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection( - const uint8_t* base, - size_t size, - const uint8_t* data_base, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const { - return OpenCommon(base, - size, - data_base, - data_size, - location, - location_checksum, - oat_dex_file, - verify, - verify_checksum, - error_msg, - /*container*/ nullptr, - /*verify_result*/ nullptr); -} - -bool DexFileLoader::OpenAll( - const uint8_t* base, - size_t size, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files) const { - DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; - uint32_t magic = *reinterpret_cast<const uint32_t*>(base); - if (IsZipMagic(magic)) { - std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg)); - if (zip_archive.get() == nullptr) { - DCHECK(!error_msg->empty()); - return false; - } - return OpenAllDexFilesFromZip(*zip_archive.get(), - location, - verify, - verify_checksum, - error_msg, - dex_files); - } - if (IsMagicValid(magic)) { - const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base); - std::unique_ptr<const DexFile> dex_file(Open(base, - size, - location, - dex_header->checksum_, - /*oat_dex_file*/ nullptr, - verify, - verify_checksum, - error_msg)); - if (dex_file.get() != nullptr) { - dex_files->push_back(std::move(dex_file)); - return true; - } else { - return false; - } - } - *error_msg = StringPrintf("Expected valid zip or dex file"); - return false; -} - -std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, - size_t size, - const uint8_t* data_base, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg, - DexFileContainer* container, - VerifyResult* verify_result) { - if (verify_result != nullptr) { - *verify_result = VerifyResult::kVerifyNotAttempted; - } - std::unique_ptr<DexFile> dex_file; - if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) { - if (data_size != 0) { - CHECK_EQ(base, data_base) << "Unsupported for standard dex"; - } - dex_file.reset(new StandardDexFile(base, - size, - location, - location_checksum, - oat_dex_file, - container)); - } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) { - if (data_base == nullptr) { - // TODO: Is there a clean way to support both an explicit data section and reading the one - // from the header. - CHECK_EQ(data_size, 0u); - const CompactDexFile::Header* const header = CompactDexFile::Header::At(base); - data_base = base + header->data_off_; - data_size = header->data_size_; - } - dex_file.reset(new CompactDexFile(base, - size, - data_base, - data_size, - location, - location_checksum, - oat_dex_file, - container)); - } else { - *error_msg = "Invalid or truncated dex file"; - } - if (dex_file == nullptr) { - *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(), - error_msg->c_str()); - return nullptr; - } - if (!dex_file->Init(error_msg)) { - dex_file.reset(); - return nullptr; - } - if (verify && !DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - location.c_str(), - verify_checksum, - error_msg)) { - if (verify_result != nullptr) { - *verify_result = VerifyResult::kVerifyFailed; - } - return nullptr; - } - if (verify_result != nullptr) { - *verify_result = VerifyResult::kVerifySucceeded; - } - return dex_file; -} - -std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip( - const DexZipArchive& zip_archive, - const char* entry_name, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - ZipOpenErrorCode* error_code) const { - CHECK(!location.empty()); - std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg)); - if (zip_entry == nullptr) { - *error_code = ZipOpenErrorCode::kEntryNotFound; - return nullptr; - } - if (zip_entry->GetUncompressedLength() == 0) { - *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str()); - *error_code = ZipOpenErrorCode::kDexFileError; - return nullptr; - } - - std::vector<uint8_t> map(zip_entry->Extract(error_msg)); - if (map.size() == 0) { - *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), - error_msg->c_str()); - *error_code = ZipOpenErrorCode::kExtractToMemoryError; - return nullptr; - } - VerifyResult verify_result; - std::unique_ptr<const DexFile> dex_file = OpenCommon(map.data(), - map.size(), - /*data_base*/ nullptr, - /*data_size*/ 0u, - location, - zip_entry->GetCrc32(), - /*oat_dex_file*/ nullptr, - verify, - verify_checksum, - error_msg, - new VectorContainer(std::move(map)), - &verify_result); - if (dex_file == nullptr) { - if (verify_result == VerifyResult::kVerifyNotAttempted) { - *error_code = ZipOpenErrorCode::kDexFileError; - } else { - *error_code = ZipOpenErrorCode::kVerifyError; - } - return nullptr; - } - if (verify_result != VerifyResult::kVerifySucceeded) { - *error_code = ZipOpenErrorCode::kVerifyError; - return nullptr; - } - *error_code = ZipOpenErrorCode::kNoError; - return dex_file; -} - -// Technically we do not have a limitation with respect to the number of dex files that can be in a -// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols -// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what -// seems an excessive number. -static constexpr size_t kWarnOnManyDexFilesThreshold = 100; - -bool DexFileLoader::OpenAllDexFilesFromZip( - const DexZipArchive& zip_archive, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files) const { - DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr"; - ZipOpenErrorCode error_code; - std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive, - kClassesDex, - location, - verify, - verify_checksum, - error_msg, - &error_code)); - if (dex_file.get() == nullptr) { - return false; - } else { - // Had at least classes.dex. - dex_files->push_back(std::move(dex_file)); - - // Now try some more. - - // We could try to avoid std::string allocations by working on a char array directly. As we - // do not expect a lot of iterations, this seems too involved and brittle. - - for (size_t i = 1; ; ++i) { - std::string name = GetMultiDexClassesDexName(i); - std::string fake_location = GetMultiDexLocation(i, location.c_str()); - std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive, - name.c_str(), - fake_location, - verify, - verify_checksum, - error_msg, - &error_code)); - if (next_dex_file.get() == nullptr) { - if (error_code != ZipOpenErrorCode::kEntryNotFound) { - LOG(WARNING) << "Zip open failed: " << *error_msg; - } - break; - } else { - dex_files->push_back(std::move(next_dex_file)); - } - - if (i == kWarnOnManyDexFilesThreshold) { - LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold - << " dex files. Please consider coalescing and shrinking the number to " - " avoid runtime overhead."; - } - - if (i == std::numeric_limits<size_t>::max()) { - LOG(ERROR) << "Overflow in number of dex files!"; - break; - } - } - - return true; - } -} -} // namespace art diff --git a/runtime/dex/dex_file_loader.h b/runtime/dex/dex_file_loader.h deleted file mode 100644 index 508397cb00..0000000000 --- a/runtime/dex/dex_file_loader.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_LOADER_H_ -#define ART_RUNTIME_DEX_DEX_FILE_LOADER_H_ - -#include <cstdint> -#include <memory> -#include <string> -#include <vector> - -namespace art { - -class DexFile; -class DexFileContainer; -class MemMap; -class OatDexFile; - -class DexZipArchive; - -// Class that is used to open dex files and deal with corresponding multidex and location logic. -class DexFileLoader { - public: - // name of the DexFile entry within a zip archive - static constexpr const char* kClassesDex = "classes.dex"; - - // The separator character in MultiDex locations. - static constexpr char kMultiDexSeparator = '!'; - - // Return true if the magic is valid for dex or cdex. - static bool IsMagicValid(uint32_t magic); - static bool IsMagicValid(const uint8_t* magic); - - // Return true if the corresponding version and magic is valid. - static bool IsVersionAndMagicValid(const uint8_t* magic); - - // Check whether a location denotes a multidex dex file. This is a very simple check: returns - // whether the string contains the separator character. - static bool IsMultiDexLocation(const char* location); - - // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for - // index == 0, and classes{index + 1}.dex else. - static std::string GetMultiDexClassesDexName(size_t index); - - // Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for - // index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else. - static std::string GetMultiDexLocation(size_t index, const char* dex_location); - - // Returns the canonical form of the given dex location. - // - // There are different flavors of "dex locations" as follows: - // the file name of a dex file: - // The actual file path that the dex file has on disk. - // dex_location: - // This acts as a key for the class linker to know which dex file to load. - // It may correspond to either an old odex file or a particular dex file - // inside an oat file. In the first case it will also match the file name - // of the dex file. In the second case (oat) it will include the file name - // and possibly some multidex annotation to uniquely identify it. - // canonical_dex_location: - // the dex_location where it's file name part has been made canonical. - static std::string GetDexCanonicalLocation(const char* dex_location); - - // For normal dex files, location and base location coincide. If a dex file is part of a multidex - // archive, the base location is the name of the originating jar/apk, stripped of any internal - // classes*.dex path. - static std::string GetBaseLocation(const char* location) { - const char* pos = strrchr(location, kMultiDexSeparator); - return (pos == nullptr) ? location : std::string(location, pos - location); - } - - static std::string GetBaseLocation(const std::string& location) { - return GetBaseLocation(location.c_str()); - } - - // Returns the '!classes*.dex' part of the dex location. Returns an empty - // string if there is no multidex suffix for the given location. - // The kMultiDexSeparator is included in the returned suffix. - static std::string GetMultiDexSuffix(const std::string& location) { - size_t pos = location.rfind(kMultiDexSeparator); - return (pos == std::string::npos) ? std::string() : location.substr(pos); - } - - virtual ~DexFileLoader() { } - - // Returns the checksums of a file for comparison with GetLocationChecksum(). - // For .dex files, this is the single header checksum. - // For zip files, this is the zip entry CRC32 checksum for classes.dex and - // each additional multidex entry classes2.dex, classes3.dex, etc. - // If a valid zip_fd is provided the file content will be read directly from - // the descriptor and `filename` will be used as alias for error logging. If - // zip_fd is -1, the method will try to open the `filename` and read the - // content from it. - // Return true if the checksums could be found, false otherwise. - virtual bool GetMultiDexChecksums(const char* filename, - std::vector<uint32_t>* checksums, - std::string* error_msg, - int zip_fd = -1) const; - - // Opens .dex file, backed by existing memory - virtual std::unique_ptr<const DexFile> Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const; - - // Open a dex file with a separate data section. - virtual std::unique_ptr<const DexFile> OpenWithDataSection( - const uint8_t* base, - size_t size, - const uint8_t* data_base, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const; - - - // Opens all .dex files found in the memory map, guessing the container format based on file - // extension. - virtual bool OpenAll(const uint8_t* base, - size_t size, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files) const; - - protected: - enum class ZipOpenErrorCode { - kNoError, - kEntryNotFound, - kExtractToMemoryError, - kDexFileError, - kMakeReadOnlyError, - kVerifyError - }; - - enum class VerifyResult { // private - kVerifyNotAttempted, - kVerifySucceeded, - kVerifyFailed - }; - - static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base, - size_t size, - const uint8_t* data_base, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg, - DexFileContainer* container, - VerifyResult* verify_result); - - private: - // Open all classesXXX.dex files from a zip archive. - bool OpenAllDexFilesFromZip(const DexZipArchive& zip_archive, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files) const; - - // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null - // return. - std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const DexZipArchive& zip_archive, - const char* entry_name, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - ZipOpenErrorCode* error_code) const; -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_LOADER_H_ diff --git a/runtime/dex/dex_file_reference.h b/runtime/dex/dex_file_reference.h deleted file mode 100644 index 6f882900c6..0000000000 --- a/runtime/dex/dex_file_reference.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_ -#define ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_ - -#include <cstdint> - -namespace art { - -class DexFile; - -class DexFileReference { - public: - DexFileReference(const DexFile* file, uint32_t idx) : dex_file(file), index(idx) {} - const DexFile* dex_file; - uint32_t index; - - struct Comparator { - bool operator()(const DexFileReference& a, const DexFileReference& b) const { - if (a.dex_file != b.dex_file) { - return a.dex_file < b.dex_file; - } - return a.index < b.index; - } - }; -}; - -// Default comparators, compares the indicies, not the backing data. -inline bool operator<(const DexFileReference& a, const DexFileReference& b) { - return DexFileReference::Comparator()(a, b); -} -inline bool operator==(const DexFileReference& a, const DexFileReference& b) { - return a.dex_file == b.dex_file && a.index == b.index; -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_ diff --git a/runtime/dex/dex_file_test.cc b/runtime/dex/dex_file_test.cc deleted file mode 100644 index 2bb86672dc..0000000000 --- a/runtime/dex/dex_file_test.cc +++ /dev/null @@ -1,749 +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 "dex_file.h" - -#include <sys/mman.h> - -#include <memory> - -#include "art_dex_file_loader.h" -#include "base/stl_util.h" -#include "base/unix_file/fd_file.h" -#include "base64_test_util.h" -#include "code_item_accessors-inl.h" -#include "common_runtime_test.h" -#include "descriptors_names.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" -#include "mem_map.h" -#include "os.h" -#include "scoped_thread_state_change-inl.h" -#include "thread-current-inl.h" - -namespace art { - -class DexFileTest : public CommonRuntimeTest {}; - -TEST_F(DexFileTest, Open) { - ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested")); - ASSERT_TRUE(dex.get() != nullptr); -} - -static inline std::vector<uint8_t> DecodeBase64Vec(const char* src) { - std::vector<uint8_t> res; - size_t size; - std::unique_ptr<uint8_t[]> data(DecodeBase64(src, &size)); - res.resize(size); - memcpy(res.data(), data.get(), size); - return res; -} - -// Although this is the same content logically as the Nested test dex, -// the DexFileHeader test is sensitive to subtle changes in the -// contents due to the checksum etc, so we embed the exact input here. -// -// class Nested { -// class Inner { -// } -// } -static const char kRawDex[] = - "ZGV4CjAzNQAQedgAe7gM1B/WHsWJ6L7lGAISGC7yjD2IAwAAcAAAAHhWNBIAAAAAAAAAAMQCAAAP" - "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAABAAAA4AAAAAMAAADoAAAAAgAAAAABAABIAgAAQAEAAK4B" - "AAC2AQAAvQEAAM0BAADXAQAA+wEAABsCAAA+AgAAUgIAAF8CAABiAgAAZgIAAHMCAAB5AgAAgQIA" - "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABgAAAAAAAAAKAAAABgAAAKgBAAAAAAEA" - "DQAAAAAAAQAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAiAEAAKsCAAAA" - "AAAAAQAAAAAAAAAFAAAAAAAAAAgAAACYAQAAuAIAAAAAAAACAAAAlAIAAJoCAAABAAAAowIAAAIA" - "AgABAAAAiAIAAAYAAABbAQAAcBACAAAADgABAAEAAQAAAI4CAAAEAAAAcBACAAAADgBAAQAAAAAA" - "AAAAAAAAAAAATAEAAAAAAAAAAAAAAAAAAAEAAAABAAY8aW5pdD4ABUlubmVyAA5MTmVzdGVkJElu" - "bmVyOwAITE5lc3RlZDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2" - "aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNz" - "ZXM7ABJMamF2YS9sYW5nL09iamVjdDsAC05lc3RlZC5qYXZhAAFWAAJWTAALYWNjZXNzRmxhZ3MA" - "BG5hbWUABnRoaXMkMAAFdmFsdWUAAgEABw4AAQAHDjwAAgIBDhgBAgMCCwQADBcBAgQBDhwBGAAA" - "AQEAAJAgAICABNQCAAABAAGAgATwAgAAEAAAAAAAAAABAAAAAAAAAAEAAAAPAAAAcAAAAAIAAAAH" - "AAAArAAAAAMAAAACAAAAyAAAAAQAAAABAAAA4AAAAAUAAAADAAAA6AAAAAYAAAACAAAAAAEAAAMQ" - "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA" - "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA=="; - -// kRawDex{38,39,40,41} are dex'ed versions of the following Java source : -// -// public class Main { -// public static void main(String[] foo) { -// } -// } -// -// The dex file was manually edited to change its dex version code to 38 -// or 39, respectively. -static const char kRawDex38[] = - "ZGV4CjAzOAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB" - "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA" - "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA" - "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB" - "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW" - "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA" - "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA" - "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" - "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; - -static const char kRawDex39[] = - "ZGV4CjAzOQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB" - "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA" - "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA" - "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB" - "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW" - "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA" - "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA" - "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" - "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; - -static const char kRawDex40[] = - "ZGV4CjA0MAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB" - "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA" - "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA" - "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB" - "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW" - "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA" - "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA" - "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" - "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; - -static const char kRawDex41[] = - "ZGV4CjA0MQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB" - "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA" - "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA" - "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB" - "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW" - "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA" - "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA" - "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" - "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; - -static const char kRawDexZeroLength[] = - "UEsDBAoAAAAAAOhxAkkAAAAAAAAAAAAAAAALABwAY2xhc3Nlcy5kZXhVVAkAA2QNoVdnDaFXdXgL" - "AAEE5AMBAASIEwAAUEsBAh4DCgAAAAAA6HECSQAAAAAAAAAAAAAAAAsAGAAAAAAAAAAAAKCBAAAA" - "AGNsYXNzZXMuZGV4VVQFAANkDaFXdXgLAAEE5AMBAASIEwAAUEsFBgAAAAABAAEAUQAAAEUAAAAA" - "AA=="; - -static const char kRawZipClassesDexPresent[] = - "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VXdXgL" - "AAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMDQ0WY" - "iRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEIEEcA" - "cS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu4IOa" - "wczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhHIykL" - "LinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkVIGpA" - "Yc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEBAAAg" - "AgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEABIgTAABQ" - "SwUGAAAAAAEAAQBRAAAAdgEAAAAA"; - -static const char kRawZipClassesDexAbsent[] = - "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAAOABwAbm90Y2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VX" - "dXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMD" - "Q0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEI" - "EEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu" - "4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhH" - "IykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkV" - "IGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEB" - "AAAgAgAADgAYAAAAAAAAAAAAoIEAAAAAbm90Y2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEA" - "BIgTAABQSwUGAAAAAAEAAQBUAAAAeQEAAAAA"; - -static const char kRawZipThreeDexFiles[] = - "UEsDBBQAAAAIAP1WN0ms99lIMQEAACACAAAMABwAY2xhc3NlczIuZGV4VVQJAAOtbOVXrWzlV3V4" - "CwABBOQDAQAEiBMAAEtJreAyMLZg2GHVfXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NF" - "mIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBAAGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBH" - "AHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiDHWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCD" - "msHMwGaTmZdZYsfA5uObmJlnzSDkk5VYlqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMp" - "Cy4pysxLt2ZgyQUqAzmYj4EZTIL909jA8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBq" - "QGHOKAARB4UbkwLETFA8MEPVgMKCQQGiBhxOUPWgeAYAUEsDBBQAAAAIAABXN0ms99lIMQEAACAC" - "AAAMABwAY2xhc3NlczMuZGV4VVQJAAOvbOVXr2zlV3V4CwABBOQDAQAEiBMAAEtJreAyMLZg2GHV" - "fXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NFmIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBA" - "AGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBHAHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiD" - "HWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCDmsHMwGaTmZdZYsfA5uObmJlnzSDkk5VY" - "lqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMpCy4pysxLt2ZgyQUqAzmYj4EZTIL909jA" - "8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBqQGHOKAARB4UbkwLETFA8MEPVgMKCQQGi" - "BhxOUPWgeAYAUEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj" - "5VetbOVXdXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQC" - "EwNDAQMDQ0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGx" - "GxAHAnEIEEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8" - "UFGgP6Fu4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYx" - "MIX5MAhHIykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHC" - "mg0pvBkVIGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACAD9VjdJ" - "rPfZSDEBAAAgAgAADAAYAAAAAAAAAAAAoIEAAAAAY2xhc3NlczIuZGV4VVQFAAOtbOVXdXgLAAEE" - "5AMBAASIEwAAUEsBAh4DFAAAAAgAAFc3Saz32UgxAQAAIAIAAAwAGAAAAAAAAAAAAKCBdwEAAGNs" - "YXNzZXMzLmRleFVUBQADr2zlV3V4CwABBOQDAQAEiBMAAFBLAQIeAxQAAAAIANVRN0ms99lIMQEA" - "ACACAAALABgAAAAAAAAAAACgge4CAABjbGFzc2VzLmRleFVUBQADAWPlV3V4CwABBOQDAQAEiBMA" - "AFBLBQYAAAAAAwADAPUAAABkBAAAAAA="; - -static const char kRawDexBadMapOffset[] = - "ZGV4CjAzNQAZKGSz85r+tXJ1I24FYi+FpQtWbXtelAmoAQAAcAAAAHhWNBIAAAAAAAAAAEAwIBAF" - "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADcAAAAzAAAAOQA" - "AADsAAAA9AAAAPkAAAANAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAA" - "AAAAAAABAAAAAQAAAAAAAAABAAAAAAAAABUBAAAAAAAAAQABAAEAAAAQAQAABAAAAHAQAQAAAA4A" - "Bjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABAAcOAAAAAQAAgYAE" - "zAEACwAAAAAAAAABAAAAAAAAAAEAAAAFAAAAcAAAAAIAAAADAAAAhAAAAAMAAAABAAAAkAAAAAUA" - "AAACAAAAnAAAAAYAAAABAAAArAAAAAEgAAABAAAAzAAAAAIgAAAFAAAA5AAAAAMgAAABAAAAEAEA" - "AAAgAAABAAAAFQEAAAAQAAABAAAAIAEAAA=="; - -static const char kRawDexDebugInfoLocalNullType[] = - "ZGV4CjAzNQA+Kwj2g6OZMH88OvK9Ey6ycdIsFCt18ED8AQAAcAAAAHhWNBIAAAAAAAAAAHQBAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAMAQAA8AAAABwB" - "AAAkAQAALAEAAC8BAAA0AQAASAEAAEsBAABOAQAAAgAAAAMAAAAEAAAABQAAAAIAAAAAAAAAAAAA" - "AAUAAAADAAAAAAAAAAEAAQAAAAAAAQAAAAYAAAACAAEAAAAAAAEAAAABAAAAAgAAAAAAAAABAAAA" - "AAAAAGMBAAAAAAAAAQABAAEAAABUAQAABAAAAHAQAgAAAA4AAgABAAAAAABZAQAAAgAAABIQDwAG" - "PGluaXQ+AAZBLmphdmEAAUkAA0xBOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAFhAAR0aGlzAAEA" - "Bw4AAwAHDh4DAAcAAAAAAQEAgYAE8AEBAIgCAAAACwAAAAAAAAABAAAAAAAAAAEAAAAIAAAAcAAA" - "AAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAAuAAAAAYAAAABAAAA0AAAAAEgAAACAAAA" - "8AAAAAIgAAAIAAAAHAEAAAMgAAACAAAAVAEAAAAgAAABAAAAYwEAAAAQAAABAAAAdAEAAA=="; - -static void DecodeAndWriteDexFile(const char* base64, const char* location) { - // decode base64 - CHECK(base64 != nullptr); - std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64); - CHECK_NE(dex_bytes.size(), 0u); - - // write to provided file - std::unique_ptr<File> file(OS::CreateEmptyFile(location)); - CHECK(file.get() != nullptr); - if (!file->WriteFully(dex_bytes.data(), dex_bytes.size())) { - PLOG(FATAL) << "Failed to write base64 as dex file"; - } - if (file->FlushCloseOrErase() != 0) { - PLOG(FATAL) << "Could not flush and close test file."; - } -} - -static bool OpenDexFilesBase64(const char* base64, - const char* location, - std::vector<std::unique_ptr<const DexFile>>* dex_files, - std::string* error_msg) { - DecodeAndWriteDexFile(base64, location); - - // read dex file(s) - ScopedObjectAccess soa(Thread::Current()); - static constexpr bool kVerifyChecksum = true; - std::vector<std::unique_ptr<const DexFile>> tmp; - const ArtDexFileLoader dex_file_loader; - bool success = dex_file_loader.Open( - location, location, /* verify */ true, kVerifyChecksum, error_msg, &tmp); - if (success) { - for (std::unique_ptr<const DexFile>& dex_file : tmp) { - EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); - EXPECT_TRUE(dex_file->IsReadOnly()); - } - *dex_files = std::move(tmp); - } - return success; -} - -static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, - const char* location) { - // read dex files. - std::string error_msg; - std::vector<std::unique_ptr<const DexFile>> dex_files; - bool success = OpenDexFilesBase64(base64, location, &dex_files, &error_msg); - CHECK(success) << error_msg; - EXPECT_EQ(1U, dex_files.size()); - return std::move(dex_files[0]); -} - -static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64, - const char* location, - uint32_t location_checksum, - bool expect_success) { - CHECK(base64 != nullptr); - std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64); - CHECK_NE(dex_bytes.size(), 0u); - - std::string error_message; - std::unique_ptr<MemMap> region(MemMap::MapAnonymous("test-region", - nullptr, - dex_bytes.size(), - PROT_READ | PROT_WRITE, - /* low_4gb */ false, - /* reuse */ false, - &error_message)); - memcpy(region->Begin(), dex_bytes.data(), dex_bytes.size()); - const ArtDexFileLoader dex_file_loader; - std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location, - location_checksum, - std::move(region), - /* verify */ true, - /* verify_checksum */ true, - &error_message)); - if (expect_success) { - CHECK(dex_file != nullptr) << error_message; - } else { - CHECK(dex_file == nullptr) << "Expected dex file open to fail."; - } - return dex_file; -} - -static void ValidateDexFileHeader(std::unique_ptr<const DexFile> dex_file) { - static const uint8_t kExpectedDexFileMagic[8] = { - /* d */ 0x64, /* e */ 0x64, /* x */ 0x78, /* \n */ 0x0d, - /* 0 */ 0x30, /* 3 */ 0x33, /* 5 */ 0x35, /* \0 */ 0x00 - }; - static const uint8_t kExpectedSha1[DexFile::kSha1DigestSize] = { - 0x7b, 0xb8, 0x0c, 0xd4, 0x1f, 0xd6, 0x1e, 0xc5, - 0x89, 0xe8, 0xbe, 0xe5, 0x18, 0x02, 0x12, 0x18, - 0x2e, 0xf2, 0x8c, 0x3d, - }; - - const DexFile::Header& header = dex_file->GetHeader(); - EXPECT_EQ(*kExpectedDexFileMagic, *header.magic_); - EXPECT_EQ(0x00d87910U, header.checksum_); - EXPECT_EQ(*kExpectedSha1, *header.signature_); - EXPECT_EQ(904U, header.file_size_); - EXPECT_EQ(112U, header.header_size_); - EXPECT_EQ(0U, header.link_size_); - EXPECT_EQ(0U, header.link_off_); - EXPECT_EQ(15U, header.string_ids_size_); - EXPECT_EQ(112U, header.string_ids_off_); - EXPECT_EQ(7U, header.type_ids_size_); - EXPECT_EQ(172U, header.type_ids_off_); - EXPECT_EQ(2U, header.proto_ids_size_); - EXPECT_EQ(200U, header.proto_ids_off_); - EXPECT_EQ(1U, header.field_ids_size_); - EXPECT_EQ(224U, header.field_ids_off_); - EXPECT_EQ(3U, header.method_ids_size_); - EXPECT_EQ(232U, header.method_ids_off_); - EXPECT_EQ(2U, header.class_defs_size_); - EXPECT_EQ(256U, header.class_defs_off_); - EXPECT_EQ(584U, header.data_size_); - EXPECT_EQ(320U, header.data_off_); - - EXPECT_EQ(header.checksum_, dex_file->GetLocationChecksum()); -} - -TEST_F(DexFileTest, Header) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str())); - ValidateDexFileHeader(std::move(raw)); -} - -TEST_F(DexFileTest, HeaderInMemory) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw = - OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U, true); - ValidateDexFileHeader(std::move(raw)); -} - -TEST_F(DexFileTest, Version38Accepted) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex38, tmp.GetFilename().c_str())); - ASSERT_TRUE(raw.get() != nullptr); - - const DexFile::Header& header = raw->GetHeader(); - EXPECT_EQ(38u, header.GetVersion()); -} - -TEST_F(DexFileTest, Version39Accepted) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex39, tmp.GetFilename().c_str())); - ASSERT_TRUE(raw.get() != nullptr); - - const DexFile::Header& header = raw->GetHeader(); - EXPECT_EQ(39u, header.GetVersion()); -} - -TEST_F(DexFileTest, Version40Rejected) { - ScratchFile tmp; - const char* location = tmp.GetFilename().c_str(); - DecodeAndWriteDexFile(kRawDex40, location); - - ScopedObjectAccess soa(Thread::Current()); - static constexpr bool kVerifyChecksum = true; - std::string error_msg; - std::vector<std::unique_ptr<const DexFile>> dex_files; - const ArtDexFileLoader dex_file_loader; - ASSERT_FALSE(dex_file_loader.Open( - location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)); -} - -TEST_F(DexFileTest, Version41Rejected) { - ScratchFile tmp; - const char* location = tmp.GetFilename().c_str(); - DecodeAndWriteDexFile(kRawDex41, location); - - ScopedObjectAccess soa(Thread::Current()); - static constexpr bool kVerifyChecksum = true; - std::string error_msg; - std::vector<std::unique_ptr<const DexFile>> dex_files; - const ArtDexFileLoader dex_file_loader; - ASSERT_FALSE(dex_file_loader.Open( - location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)); -} - -TEST_F(DexFileTest, ZeroLengthDexRejected) { - ScratchFile tmp; - const char* location = tmp.GetFilename().c_str(); - DecodeAndWriteDexFile(kRawDexZeroLength, location); - - ScopedObjectAccess soa(Thread::Current()); - static constexpr bool kVerifyChecksum = true; - std::string error_msg; - std::vector<std::unique_ptr<const DexFile>> dex_files; - const ArtDexFileLoader dex_file_loader; - ASSERT_FALSE(dex_file_loader.Open( - location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)); -} - -TEST_F(DexFileTest, GetLocationChecksum) { - ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main")); - EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum()); -} - -TEST_F(DexFileTest, GetChecksum) { - std::vector<uint32_t> checksums; - ScopedObjectAccess soa(Thread::Current()); - std::string error_msg; - const ArtDexFileLoader dex_file_loader; - EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), - &checksums, - &error_msg)) - << error_msg; - ASSERT_EQ(1U, checksums.size()); - EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]); -} - -TEST_F(DexFileTest, GetMultiDexChecksums) { - std::string error_msg; - std::vector<uint32_t> checksums; - std::string multidex_file = GetTestDexFileName("MultiDex"); - const ArtDexFileLoader dex_file_loader; - EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(multidex_file.c_str(), - &checksums, - &error_msg)) << error_msg; - - std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex"); - ASSERT_EQ(2U, dexes.size()); - ASSERT_EQ(2U, checksums.size()); - - EXPECT_EQ(dexes[0]->GetLocation(), DexFileLoader::GetMultiDexLocation(0, multidex_file.c_str())); - EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]); - - EXPECT_EQ(dexes[1]->GetLocation(), DexFileLoader::GetMultiDexLocation(1, multidex_file.c_str())); - EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]); -} - -TEST_F(DexFileTest, ClassDefs) { - ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested")); - ASSERT_TRUE(raw.get() != nullptr); - EXPECT_EQ(3U, raw->NumClassDefs()); - - const DexFile::ClassDef& c0 = raw->GetClassDef(0); - EXPECT_STREQ("LNested$1;", raw->GetClassDescriptor(c0)); - - const DexFile::ClassDef& c1 = raw->GetClassDef(1); - EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c1)); - - const DexFile::ClassDef& c2 = raw->GetClassDef(2); - EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c2)); -} - -TEST_F(DexFileTest, GetMethodSignature) { - ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature")); - ASSERT_TRUE(raw.get() != nullptr); - EXPECT_EQ(1U, raw->NumClassDefs()); - - const DexFile::ClassDef& class_def = raw->GetClassDef(0); - ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def)); - - const uint8_t* class_data = raw->GetClassData(class_def); - ASSERT_TRUE(class_data != nullptr); - ClassDataItemIterator it(*raw, class_data); - - EXPECT_EQ(1u, it.NumDirectMethods()); - - // Check the signature for the static initializer. - { - ASSERT_EQ(1U, it.NumDirectMethods()); - const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); - const char* name = raw->StringDataByIdx(method_id.name_idx_); - ASSERT_STREQ("<init>", name); - std::string signature(raw->GetMethodSignature(method_id).ToString()); - ASSERT_EQ("()V", signature); - } - - // Check all virtual methods. - struct Result { - const char* name; - const char* signature; - const char* pretty_method; - }; - static const Result results[] = { - { - "m1", - "(IDJLjava/lang/Object;)Ljava/lang/Float;", - "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)" - }, - { - "m2", - "(ZSC)LGetMethodSignature;", - "GetMethodSignature GetMethodSignature.m2(boolean, short, char)" - }, - { - "m3", - "()V", - "void GetMethodSignature.m3()" - }, - { - "m4", - "(I)V", - "void GetMethodSignature.m4(int)" - }, - { - "m5", - "(II)V", - "void GetMethodSignature.m5(int, int)" - }, - { - "m6", - "(II[[I)V", - "void GetMethodSignature.m6(int, int, int[][])" - }, - { - "m7", - "(II[[ILjava/lang/Object;)V", - "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)" - }, - { - "m8", - "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V", - "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])" - }, - { - "m9", - "()I", - "int GetMethodSignature.m9()" - }, - { - "mA", - "()[[I", - "int[][] GetMethodSignature.mA()" - }, - { - "mB", - "()[[Ljava/lang/Object;", - "java.lang.Object[][] GetMethodSignature.mB()" - }, - }; - ASSERT_EQ(arraysize(results), it.NumVirtualMethods()); - for (const Result& r : results) { - it.Next(); - const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); - - const char* name = raw->StringDataByIdx(method_id.name_idx_); - ASSERT_STREQ(r.name, name); - - std::string signature(raw->GetMethodSignature(method_id).ToString()); - ASSERT_EQ(r.signature, signature); - - std::string plain_method = std::string("GetMethodSignature.") + r.name; - ASSERT_EQ(plain_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ false)); - ASSERT_EQ(r.pretty_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ true)); - } -} - -TEST_F(DexFileTest, FindStringId) { - ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature")); - ASSERT_TRUE(raw.get() != nullptr); - EXPECT_EQ(1U, raw->NumClassDefs()); - - const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;", - "D", "I", "J", nullptr }; - for (size_t i = 0; strings[i] != nullptr; i++) { - const char* str = strings[i]; - const DexFile::StringId* str_id = raw->FindStringId(str); - const char* dex_str = raw->GetStringData(*str_id); - EXPECT_STREQ(dex_str, str); - } -} - -TEST_F(DexFileTest, FindTypeId) { - for (size_t i = 0; i < java_lang_dex_file_->NumTypeIds(); i++) { - const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i)); - const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str); - ASSERT_TRUE(type_str_id != nullptr); - dex::StringIndex type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id); - const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx); - ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str)); - ASSERT_TRUE(type_id != nullptr); - EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id).index_, i); - } -} - -TEST_F(DexFileTest, FindProtoId) { - for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) { - const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i); - const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find); - std::vector<dex::TypeIndex> to_find_types; - if (to_find_tl != nullptr) { - for (size_t j = 0; j < to_find_tl->Size(); j++) { - to_find_types.push_back(to_find_tl->GetTypeItem(j).type_idx_); - } - } - const DexFile::ProtoId* found = - java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types); - ASSERT_TRUE(found != nullptr); - EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i); - } -} - -TEST_F(DexFileTest, FindMethodId) { - for (size_t i = 0; i < java_lang_dex_file_->NumMethodIds(); i++) { - const DexFile::MethodId& to_find = java_lang_dex_file_->GetMethodId(i); - const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_); - const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_); - const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_); - const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature); - ASSERT_TRUE(found != nullptr) << "Didn't find method " << i << ": " - << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "." - << java_lang_dex_file_->GetStringData(name) - << java_lang_dex_file_->GetMethodSignature(to_find); - EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i); - } -} - -TEST_F(DexFileTest, FindFieldId) { - for (size_t i = 0; i < java_lang_dex_file_->NumFieldIds(); i++) { - const DexFile::FieldId& to_find = java_lang_dex_file_->GetFieldId(i); - const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_); - const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_); - const DexFile::TypeId& type = java_lang_dex_file_->GetTypeId(to_find.type_idx_); - const DexFile::FieldId* found = java_lang_dex_file_->FindFieldId(klass, name, type); - ASSERT_TRUE(found != nullptr) << "Didn't find field " << i << ": " - << java_lang_dex_file_->StringByTypeIdx(to_find.type_idx_) << " " - << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "." - << java_lang_dex_file_->GetStringData(name); - EXPECT_EQ(java_lang_dex_file_->GetIndexForFieldId(*found), i); - } -} - -TEST_F(DexFileTest, GetMultiDexClassesDexName) { - ASSERT_EQ("classes.dex", DexFileLoader::GetMultiDexClassesDexName(0)); - ASSERT_EQ("classes2.dex", DexFileLoader::GetMultiDexClassesDexName(1)); - ASSERT_EQ("classes3.dex", DexFileLoader::GetMultiDexClassesDexName(2)); - ASSERT_EQ("classes100.dex", DexFileLoader::GetMultiDexClassesDexName(99)); -} - -TEST_F(DexFileTest, GetMultiDexLocation) { - std::string dex_location_str = "/system/app/framework.jar"; - const char* dex_location = dex_location_str.c_str(); - ASSERT_EQ("/system/app/framework.jar", DexFileLoader::GetMultiDexLocation(0, dex_location)); - ASSERT_EQ("/system/app/framework.jar!classes2.dex", - DexFileLoader::GetMultiDexLocation(1, dex_location)); - ASSERT_EQ("/system/app/framework.jar!classes101.dex", - DexFileLoader::GetMultiDexLocation(100, dex_location)); -} - -TEST_F(DexFileTest, GetDexCanonicalLocation) { - ScratchFile file; - UniqueCPtr<const char[]> dex_location_real(realpath(file.GetFilename().c_str(), nullptr)); - std::string dex_location(dex_location_real.get()); - - ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location.c_str())); - std::string multidex_location = DexFileLoader::GetMultiDexLocation(1, dex_location.c_str()); - ASSERT_EQ(multidex_location, DexFileLoader::GetDexCanonicalLocation(multidex_location.c_str())); - - std::string dex_location_sym = dex_location + "symlink"; - ASSERT_EQ(0, symlink(dex_location.c_str(), dex_location_sym.c_str())); - - ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location_sym.c_str())); - - std::string multidex_location_sym = DexFileLoader::GetMultiDexLocation( - 1, dex_location_sym.c_str()); - ASSERT_EQ(multidex_location, - DexFileLoader::GetDexCanonicalLocation(multidex_location_sym.c_str())); - - ASSERT_EQ(0, unlink(dex_location_sym.c_str())); -} - -TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) { - EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar")); - EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes2.dex")); - EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes8.dex")); - EXPECT_EQ("", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar")); - EXPECT_EQ("!classes2.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes2.dex")); - EXPECT_EQ("!classes8.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes8.dex")); -} - -TEST_F(DexFileTest, ZipOpenClassesPresent) { - ScratchFile tmp; - std::vector<std::unique_ptr<const DexFile>> dex_files; - std::string error_msg; - ASSERT_TRUE(OpenDexFilesBase64(kRawZipClassesDexPresent, tmp.GetFilename().c_str(), &dex_files, - &error_msg)); - EXPECT_EQ(dex_files.size(), 1u); -} - -TEST_F(DexFileTest, ZipOpenClassesAbsent) { - ScratchFile tmp; - std::vector<std::unique_ptr<const DexFile>> dex_files; - std::string error_msg; - ASSERT_FALSE(OpenDexFilesBase64(kRawZipClassesDexAbsent, tmp.GetFilename().c_str(), &dex_files, - &error_msg)); - EXPECT_EQ(dex_files.size(), 0u); -} - -TEST_F(DexFileTest, ZipOpenThreeDexFiles) { - ScratchFile tmp; - std::vector<std::unique_ptr<const DexFile>> dex_files; - std::string error_msg; - ASSERT_TRUE(OpenDexFilesBase64(kRawZipThreeDexFiles, tmp.GetFilename().c_str(), &dex_files, - &error_msg)); - EXPECT_EQ(dex_files.size(), 3u); -} - -TEST_F(DexFileTest, OpenDexBadMapOffset) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw = - OpenDexFileInMemoryBase64(kRawDexBadMapOffset, tmp.GetFilename().c_str(), 0xb3642819U, false); - EXPECT_EQ(raw, nullptr); -} - -TEST_F(DexFileTest, GetStringWithNoIndex) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str())); - dex::TypeIndex idx; - EXPECT_EQ(raw->StringByTypeIdx(idx), nullptr); -} - -static void Callback(void* context ATTRIBUTE_UNUSED, - const DexFile::LocalInfo& entry ATTRIBUTE_UNUSED) { -} - -TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { - ScratchFile tmp; - std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64( - kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true); - const DexFile::ClassDef& class_def = raw->GetClassDef(0); - constexpr uint32_t kMethodIdx = 1; - const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, - kMethodIdx)); - CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx); - ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr)); -} - -} // namespace art diff --git a/runtime/dex/dex_file_tracking_registrar.cc b/runtime/dex/dex_file_tracking_registrar.cc deleted file mode 100644 index 78ea9c16cb..0000000000 --- a/runtime/dex/dex_file_tracking_registrar.cc +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dex_file_tracking_registrar.h" - -#include <deque> -#include <tuple> - -#include <android-base/logging.h> - -// For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for -// the ifdefs and early include. -#ifdef ART_DEX_FILE_ACCESS_TRACKING -#ifndef ART_ENABLE_ADDRESS_SANITIZER -#define ART_ENABLE_ADDRESS_SANITIZER -#endif -#endif -#include "base/memory_tool.h" - -#include "code_item_accessors-inl.h" -#include "dex_file-inl.h" - -namespace art { -namespace dex { -namespace tracking { - -// If true, poison dex files to track accesses. -static constexpr bool kDexFileAccessTracking = -#ifdef ART_DEX_FILE_ACCESS_TRACKING - true; -#else - false; -#endif - -// The following are configurations of poisoning certain sections of a Dex File. -// More will be added -enum DexTrackingType { - // Poisons all of a Dex File when set. - kWholeDexTracking, - // Poisons all Code Items of a Dex File when set. - kCodeItemTracking, - // Poisons all subsections of a Code Item, except the Insns bytecode array - // section, when set for all Code Items in a Dex File. - kCodeItemNonInsnsTracking, - // Poisons all subsections of a Code Item, except the Insns bytecode array - // section, when set for all Code Items in a Dex File. - // Additionally unpoisons the entire Code Item when method is a class - // initializer. - kCodeItemNonInsnsNoClinitTracking, - // Poisons the size and offset information along with the first instruction. - // This is so that accessing multiple instructions while accessing a code item - // once will not trigger unnecessary accesses. - kCodeItemStartTracking, - // Poisons all String Data Items of a Dex Files when set. - kStringDataItemTracking, - // Poisons the first byte of the utf16_size value and the first byte of the - // data section for all String Data Items of a Dex File. - kStringDataItemStartTracking, - // Poisons based on a custom tracking system which can be specified in - // SetDexSections - kCustomTracking, -}; - -// Intended for local changes only. -// Represents the current configuration being run. -static constexpr DexTrackingType kCurrentTrackingSystem = kWholeDexTracking; - -// Intended for local changes only. -void DexFileTrackingRegistrar::SetDexSections() { - if (kDexFileAccessTracking && dex_file_ != nullptr) { - // Logs the Dex File's location and starting address if tracking is enabled - LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex - << reinterpret_cast<uintptr_t>(dex_file_->Begin()); - switch (kCurrentTrackingSystem) { - case kWholeDexTracking: - SetDexFileRegistration(true); - break; - case kCodeItemTracking: - SetAllCodeItemRegistration(true); - break; - case kCodeItemNonInsnsTracking: - SetAllCodeItemRegistration(true); - SetAllInsnsRegistration(false); - break; - case kCodeItemNonInsnsNoClinitTracking: - SetAllCodeItemRegistration(true); - SetAllInsnsRegistration(false); - SetCodeItemRegistration("<clinit>", false); - break; - case kCodeItemStartTracking: - SetAllCodeItemStartRegistration(true); - break; - case kStringDataItemTracking: - SetAllStringDataRegistration(true); - break; - case kStringDataItemStartTracking: - SetAllStringDataStartRegistration(true); - break; - case kCustomTracking: - // TODO: Add/remove additional calls here to (un)poison sections of - // dex_file_ - break; - default: - break; - } - } -} - -void RegisterDexFile(const DexFile* dex_file) { - DexFileTrackingRegistrar dex_tracking_registrar(dex_file); - dex_tracking_registrar.SetDexSections(); - dex_tracking_registrar.SetCurrentRanges(); -} - -inline void SetRegistrationRange(const void* begin, size_t size, bool should_poison) { - if (should_poison) { - MEMORY_TOOL_MAKE_NOACCESS(begin, size); - } else { - // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address - // Sanitizer. The difference has not been tested with Valgrind - MEMORY_TOOL_MAKE_DEFINED(begin, size); - } -} - -void DexFileTrackingRegistrar::SetCurrentRanges() { - // This also empties range_values_ to avoid redundant (un)poisoning upon - // subsequent calls. - while (!range_values_.empty()) { - const std::tuple<const void*, size_t, bool>& current_range = range_values_.front(); - SetRegistrationRange(std::get<0>(current_range), - std::get<1>(current_range), - std::get<2>(current_range)); - range_values_.pop_front(); - } -} - -void DexFileTrackingRegistrar::SetDexFileRegistration(bool should_poison) { - const void* dex_file_begin = reinterpret_cast<const void*>(dex_file_->Begin()); - size_t dex_file_size = dex_file_->Size(); - range_values_.push_back(std::make_tuple(dex_file_begin, dex_file_size, should_poison)); -} - -void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) { - for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { - const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); - const uint8_t* class_data = dex_file_->GetClassData(cd); - if (class_data != nullptr) { - ClassDataItemIterator cdit(*dex_file_, class_data); - cdit.SkipAllFields(); - while (cdit.HasNextMethod()) { - const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); - if (code_item != nullptr) { - const void* code_item_begin = reinterpret_cast<const void*>(code_item); - size_t code_item_size = dex_file_->GetCodeItemSize(*code_item); - range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison)); - } - cdit.Next(); - } - } - } -} - -void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) { - for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { - const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); - const uint8_t* class_data = dex_file_->GetClassData(cd); - if (class_data != nullptr) { - ClassDataItemIterator cdit(*dex_file_, class_data); - cdit.SkipAllFields(); - while (cdit.HasNextMethod()) { - const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); - if (code_item != nullptr) { - const void* code_item_begin = reinterpret_cast<const void*>(code_item); - size_t code_item_start = reinterpret_cast<size_t>(code_item); - CodeItemInstructionAccessor accessor(*dex_file_, code_item); - size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns()); - size_t code_item_start_size = code_item_start_end - code_item_start; - range_values_.push_back(std::make_tuple(code_item_begin, - code_item_start_size, - should_poison)); - } - cdit.Next(); - } - } - } -} - -void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) { - for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { - const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); - const uint8_t* class_data = dex_file_->GetClassData(cd); - if (class_data != nullptr) { - ClassDataItemIterator cdit(*dex_file_, class_data); - cdit.SkipAllFields(); - while (cdit.HasNextMethod()) { - const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); - if (code_item != nullptr) { - CodeItemInstructionAccessor accessor(*dex_file_, code_item); - const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns()); - // Member insns_size_in_code_units_ is in 2-byte units - size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2; - range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison)); - } - cdit.Next(); - } - } - } -} - -void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, bool should_poison) { - for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) { - const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr); - const uint8_t* class_data = dex_file_->GetClassData(cd); - if (class_data != nullptr) { - ClassDataItemIterator cdit(*dex_file_, class_data); - cdit.SkipAllFields(); - while (cdit.HasNextMethod()) { - const DexFile::MethodId& methodid_item = dex_file_->GetMethodId(cdit.GetMemberIndex()); - const char * methodid_name = dex_file_->GetMethodName(methodid_item); - const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); - if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) { - const void* code_item_begin = reinterpret_cast<const void*>(code_item); - size_t code_item_size = dex_file_->GetCodeItemSize(*code_item); - range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison)); - } - cdit.Next(); - } - } - } -} - -void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) { - for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) { - const DexFile::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr)); - const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_); - // Data Section of String Data Item - const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id)); - range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison)); - range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison)); - } -} - -void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) { - size_t map_offset = dex_file_->GetHeader().map_off_; - auto map_list = reinterpret_cast<const DexFile::MapList*>(dex_file_->Begin() + map_offset); - for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) { - const DexFile::MapItem& map_item = map_list->list_[map_ctr]; - if (map_item.type_ == DexFile::kDexTypeStringDataItem) { - const DexFile::MapItem& next_map_item = map_list->list_[map_ctr + 1]; - const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_); - size_t string_data_size = next_map_item.offset_ - map_item.offset_; - range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison)); - } - } -} - -} // namespace tracking -} // namespace dex -} // namespace art diff --git a/runtime/dex/dex_file_tracking_registrar.h b/runtime/dex/dex_file_tracking_registrar.h deleted file mode 100644 index 71b8ed7bde..0000000000 --- a/runtime/dex/dex_file_tracking_registrar.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ -#define ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ - -#include <deque> -#include <tuple> - -#include "dex_file.h" - -namespace art { -namespace dex { -namespace tracking { - -// Class for (un)poisoning various sections of Dex Files -// -// This class provides the means to log accesses only of sections whose -// accesses are needed. All accesses are displayed as stack traces in -// logcat. -class DexFileTrackingRegistrar { - public: - explicit DexFileTrackingRegistrar(const DexFile* const dex_file) - : dex_file_(dex_file) { - } - - // This function is where the functions below it are called to actually - // poison sections. - void SetDexSections(); - - // Uses data contained inside range_values_ to poison memory through the - // memory tool. - void SetCurrentRanges(); - - private: - void SetDexFileRegistration(bool should_poison); - - // Set of functions concerning Code Items of dex_file_ - void SetAllCodeItemRegistration(bool should_poison); - // Sets the insns_ section of all code items. - void SetAllInsnsRegistration(bool should_poison); - // This function finds the code item of a class based on class name. - void SetCodeItemRegistration(const char* class_name, bool should_poison); - // Sets the size and offset information along with first instruction in insns_ - // section of all code items. - void SetAllCodeItemStartRegistration(bool should_poison); - - // Set of functions concerning String Data Items of dex_file_ - void SetAllStringDataRegistration(bool should_poison); - // Sets the first byte of size value and data section of all string data - // items. - void SetAllStringDataStartRegistration(bool should_poison); - - // Contains tuples of all ranges of memory that need to be explicitly - // (un)poisoned by the memory tool. - std::deque<std::tuple<const void *, size_t, bool>> range_values_; - - const DexFile* const dex_file_; -}; - -// This function is meant to called externally to use DexfileTrackingRegistrar -void RegisterDexFile(const DexFile* dex_file); - -} // namespace tracking -} // namespace dex -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ diff --git a/runtime/dex/dex_file_types.h b/runtime/dex/dex_file_types.h deleted file mode 100644 index 2c508f9c99..0000000000 --- a/runtime/dex/dex_file_types.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_TYPES_H_ -#define ART_RUNTIME_DEX_DEX_FILE_TYPES_H_ - -#include <limits> -#include <ostream> - -namespace art { -namespace dex { - -constexpr uint32_t kDexNoIndex = 0xFFFFFFFF; - -class StringIndex { - public: - uint32_t index_; - - constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} - explicit constexpr StringIndex(uint32_t idx) : index_(idx) {} - - bool IsValid() const { - return index_ != std::numeric_limits<decltype(index_)>::max(); - } - static StringIndex Invalid() { - return StringIndex(std::numeric_limits<decltype(index_)>::max()); - } - - bool operator==(const StringIndex& other) const { - return index_ == other.index_; - } - bool operator!=(const StringIndex& other) const { - return index_ != other.index_; - } - bool operator<(const StringIndex& other) const { - return index_ < other.index_; - } - bool operator<=(const StringIndex& other) const { - return index_ <= other.index_; - } - bool operator>(const StringIndex& other) const { - return index_ > other.index_; - } - bool operator>=(const StringIndex& other) const { - return index_ >= other.index_; - } -}; -std::ostream& operator<<(std::ostream& os, const StringIndex& index); - -class TypeIndex { - public: - uint16_t index_; - - constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} - explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {} - - bool IsValid() const { - return index_ != std::numeric_limits<decltype(index_)>::max(); - } - static TypeIndex Invalid() { - return TypeIndex(std::numeric_limits<decltype(index_)>::max()); - } - - bool operator==(const TypeIndex& other) const { - return index_ == other.index_; - } - bool operator!=(const TypeIndex& other) const { - return index_ != other.index_; - } - bool operator<(const TypeIndex& other) const { - return index_ < other.index_; - } - bool operator<=(const TypeIndex& other) const { - return index_ <= other.index_; - } - bool operator>(const TypeIndex& other) const { - return index_ > other.index_; - } - bool operator>=(const TypeIndex& other) const { - return index_ >= other.index_; - } -}; -std::ostream& operator<<(std::ostream& os, const TypeIndex& index); - -} // namespace dex -} // namespace art - -namespace std { - -template<> struct hash<art::dex::StringIndex> { - size_t operator()(const art::dex::StringIndex& index) const { - return hash<uint32_t>()(index.index_); - } -}; - -template<> struct hash<art::dex::TypeIndex> { - size_t operator()(const art::dex::TypeIndex& index) const { - return hash<uint16_t>()(index.index_); - } -}; - -} // namespace std - -#endif // ART_RUNTIME_DEX_DEX_FILE_TYPES_H_ diff --git a/runtime/dex/dex_file_verifier.cc b/runtime/dex/dex_file_verifier.cc deleted file mode 100644 index 62667052ad..0000000000 --- a/runtime/dex/dex_file_verifier.cc +++ /dev/null @@ -1,3290 +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 "dex_file_verifier.h" - -#include <inttypes.h> - -#include <limits> -#include <memory> - -#include "android-base/stringprintf.h" - -#include "code_item_accessors-inl.h" -#include "descriptors_names.h" -#include "dex_file-inl.h" -#include "leb128.h" -#include "modifiers.h" -#include "utf-inl.h" - -namespace art { - -using android::base::StringAppendV; -using android::base::StringPrintf; - -static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max(); - -static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) { - return (high == 0) || ((high == 0xffffU) && (low == 0xffffU)); -} - -static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) { - return (high == 0); -} - -static uint32_t MapTypeToBitMask(DexFile::MapItemType map_item_type) { - switch (map_item_type) { - case DexFile::kDexTypeHeaderItem: return 1 << 0; - case DexFile::kDexTypeStringIdItem: return 1 << 1; - case DexFile::kDexTypeTypeIdItem: return 1 << 2; - case DexFile::kDexTypeProtoIdItem: return 1 << 3; - case DexFile::kDexTypeFieldIdItem: return 1 << 4; - case DexFile::kDexTypeMethodIdItem: return 1 << 5; - case DexFile::kDexTypeClassDefItem: return 1 << 6; - case DexFile::kDexTypeCallSiteIdItem: return 1 << 7; - case DexFile::kDexTypeMethodHandleItem: return 1 << 8; - case DexFile::kDexTypeMapList: return 1 << 9; - case DexFile::kDexTypeTypeList: return 1 << 10; - case DexFile::kDexTypeAnnotationSetRefList: return 1 << 11; - case DexFile::kDexTypeAnnotationSetItem: return 1 << 12; - case DexFile::kDexTypeClassDataItem: return 1 << 13; - case DexFile::kDexTypeCodeItem: return 1 << 14; - case DexFile::kDexTypeStringDataItem: return 1 << 15; - case DexFile::kDexTypeDebugInfoItem: return 1 << 16; - case DexFile::kDexTypeAnnotationItem: return 1 << 17; - case DexFile::kDexTypeEncodedArrayItem: return 1 << 18; - case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 19; - } - return 0; -} - -static bool IsDataSectionType(DexFile::MapItemType map_item_type) { - switch (map_item_type) { - case DexFile::kDexTypeHeaderItem: - case DexFile::kDexTypeStringIdItem: - case DexFile::kDexTypeTypeIdItem: - case DexFile::kDexTypeProtoIdItem: - case DexFile::kDexTypeFieldIdItem: - case DexFile::kDexTypeMethodIdItem: - case DexFile::kDexTypeClassDefItem: - return false; - case DexFile::kDexTypeCallSiteIdItem: - case DexFile::kDexTypeMethodHandleItem: - case DexFile::kDexTypeMapList: - case DexFile::kDexTypeTypeList: - case DexFile::kDexTypeAnnotationSetRefList: - case DexFile::kDexTypeAnnotationSetItem: - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeCodeItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - case DexFile::kDexTypeAnnotationsDirectoryItem: - return true; - } - return true; -} - -const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const char* error_string) { - if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumStringIds(), error_string))) { - return nullptr; - } - return dex_file_->StringDataByIdx(idx); -} - -// Try to find the name of the method with the given index. We do not want to rely on DexFile -// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and -// header_ of the DexFileVerifier. str will contain the pointer to the method name on success -// (flagged by the return value), otherwise error_msg will contain an error string. -static bool FindMethodName(uint32_t method_index, - const uint8_t* begin, - const DexFile::Header* header, - const char** str, - std::string* error_msg) { - if (method_index >= header->method_ids_size_) { - *error_msg = "Method index not available for method flags verification"; - return false; - } - uint32_t string_idx = - (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + - method_index)->name_idx_.index_; - if (string_idx >= header->string_ids_size_) { - *error_msg = "String index not available for method flags verification"; - return false; - } - uint32_t string_off = - (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)-> - string_data_off_; - if (string_off >= header->file_size_) { - *error_msg = "String offset out of bounds for method flags verification"; - return false; - } - const uint8_t* str_data_ptr = begin + string_off; - uint32_t dummy; - if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) { - *error_msg = "String size out of bounds for method flags verification"; - return false; - } - *str = reinterpret_cast<const char*>(str_data_ptr); - return true; -} - -// Gets constructor flags based on the |method_name|. Returns true if -// method_name is either <clinit> or <init> and sets -// |constructor_flags_by_name| appropriately. Otherwise set -// |constructor_flags_by_name| to zero and returns whether -// |method_name| is valid. -bool GetConstructorFlagsForMethodName(const char* method_name, - uint32_t* constructor_flags_by_name) { - if (method_name[0] != '<') { - *constructor_flags_by_name = 0; - return true; - } - if (strcmp(method_name + 1, "clinit>") == 0) { - *constructor_flags_by_name = kAccStatic | kAccConstructor; - return true; - } - if (strcmp(method_name + 1, "init>") == 0) { - *constructor_flags_by_name = kAccConstructor; - return true; - } - *constructor_flags_by_name = 0; - return false; -} - -const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, - const char* error_string) { - if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) { - return nullptr; - } - return CheckLoadStringByIdx(dex_file_->GetTypeId(type_idx).descriptor_idx_, error_string); -} - -const DexFile::FieldId* DexFileVerifier::CheckLoadFieldId(uint32_t idx, const char* error_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumFieldIds(), error_string))) { - return nullptr; - } - return &dex_file_->GetFieldId(idx); -} - -const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const char* err_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumMethodIds(), err_string))) { - return nullptr; - } - return &dex_file_->GetMethodId(idx); -} - -const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) { - return nullptr; - } - return &dex_file_->GetProtoId(idx); -} - -// Helper macro to load string and return false on error. -#define LOAD_STRING(var, idx, error) \ - const char* (var) = CheckLoadStringByIdx(idx, error); \ - if (UNLIKELY((var) == nullptr)) { \ - return false; \ - } - -// Helper macro to load string by type idx and return false on error. -#define LOAD_STRING_BY_TYPE(var, type_idx, error) \ - const char* (var) = CheckLoadStringByTypeIdx(type_idx, error); \ - if (UNLIKELY((var) == nullptr)) { \ - return false; \ - } - -// Helper macro to load method id. Return last parameter on error. -#define LOAD_METHOD(var, idx, error_string, error_stmt) \ - const DexFile::MethodId* (var) = CheckLoadMethodId(idx, error_string); \ - if (UNLIKELY((var) == nullptr)) { \ - error_stmt; \ - } - -// Helper macro to load method id. Return last parameter on error. -#define LOAD_FIELD(var, idx, fmt, error_stmt) \ - const DexFile::FieldId* (var) = CheckLoadFieldId(idx, fmt); \ - if (UNLIKELY((var) == nullptr)) { \ - error_stmt; \ - } - -bool DexFileVerifier::Verify(const DexFile* dex_file, - const uint8_t* begin, - size_t size, - const char* location, - bool verify_checksum, - std::string* error_msg) { - std::unique_ptr<DexFileVerifier> verifier( - new DexFileVerifier(dex_file, begin, size, location, verify_checksum)); - if (!verifier->Verify()) { - *error_msg = verifier->FailureReason(); - return false; - } - return true; -} - -bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, - bool is_return_type) { - switch (shorty_char) { - case 'V': - if (UNLIKELY(!is_return_type)) { - ErrorStringPrintf("Invalid use of void"); - return false; - } - FALLTHROUGH_INTENDED; - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'Z': - if (UNLIKELY((descriptor[0] != shorty_char) || (descriptor[1] != '\0'))) { - ErrorStringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'", - shorty_char, descriptor); - return false; - } - break; - case 'L': - if (UNLIKELY((descriptor[0] != 'L') && (descriptor[0] != '['))) { - ErrorStringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor); - return false; - } - break; - default: - ErrorStringPrintf("Bad shorty character: '%c'", shorty_char); - return false; - } - return true; -} - -bool DexFileVerifier::CheckListSize(const void* start, size_t count, size_t elem_size, - const char* label) { - // Check that size is not 0. - CHECK_NE(elem_size, 0U); - - const uint8_t* range_start = reinterpret_cast<const uint8_t*>(start); - const uint8_t* file_start = reinterpret_cast<const uint8_t*>(begin_); - - // Check for overflow. - uintptr_t max = 0 - 1; - size_t available_bytes_till_end_of_mem = max - reinterpret_cast<uintptr_t>(start); - size_t max_count = available_bytes_till_end_of_mem / elem_size; - if (max_count < count) { - ErrorStringPrintf("Overflow in range for %s: %zx for %zu@%zu", label, - static_cast<size_t>(range_start - file_start), - count, elem_size); - return false; - } - - const uint8_t* range_end = range_start + count * elem_size; - const uint8_t* file_end = file_start + size_; - if (UNLIKELY((range_start < file_start) || (range_end > file_end))) { - // Note: these two tests are enough as we make sure above that there's no overflow. - ErrorStringPrintf("Bad range for %s: %zx to %zx", label, - static_cast<size_t>(range_start - file_start), - static_cast<size_t>(range_end - file_start)); - return false; - } - return true; -} - -bool DexFileVerifier::CheckList(size_t element_size, const char* label, const uint8_t* *ptr) { - // Check that the list is available. The first 4B are the count. - if (!CheckListSize(*ptr, 1, 4U, label)) { - return false; - } - - uint32_t count = *reinterpret_cast<const uint32_t*>(*ptr); - if (count > 0) { - if (!CheckListSize(*ptr + 4, count, element_size, label)) { - return false; - } - } - - *ptr += 4 + count * element_size; - return true; -} - -bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) { - if (UNLIKELY(field >= limit)) { - ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit); - return false; - } - return true; -} - -bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset, - uint32_t size, - size_t alignment, - const char* label) { - if (size == 0) { - if (offset != 0) { - ErrorStringPrintf("Offset(%d) should be zero when size is zero for %s.", offset, label); - return false; - } - } - if (size_ <= offset) { - ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label); - return false; - } - if (alignment != 0 && !IsAlignedParam(offset, alignment)) { - ErrorStringPrintf("Offset(%d) should be aligned by %zu for %s.", offset, alignment, label); - return false; - } - return true; -} - -bool DexFileVerifier::CheckSizeLimit(uint32_t size, uint32_t limit, const char* label) { - if (size > limit) { - ErrorStringPrintf("Size(%u) should not exceed limit(%u) for %s.", size, limit, label); - return false; - } - return true; -} - -bool DexFileVerifier::CheckHeader() { - // Check file size from the header. - uint32_t expected_size = header_->file_size_; - if (size_ != expected_size) { - ErrorStringPrintf("Bad file size (%zd, expected %u)", size_, expected_size); - return false; - } - - uint32_t adler_checksum = dex_file_->CalculateChecksum(); - // Compute and verify the checksum in the header. - if (adler_checksum != header_->checksum_) { - if (verify_checksum_) { - ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); - return false; - } else { - LOG(WARNING) << StringPrintf( - "Ignoring bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); - } - } - - // Check the contents of the header. - if (header_->endian_tag_ != DexFile::kDexEndianConstant) { - ErrorStringPrintf("Unexpected endian_tag: %x", header_->endian_tag_); - return false; - } - - const uint32_t expected_header_size = dex_file_->IsCompactDexFile() - ? sizeof(CompactDexFile::Header) - : sizeof(StandardDexFile::Header); - - if (header_->header_size_ != expected_header_size) { - ErrorStringPrintf("Bad header size: %ud expected %ud", - header_->header_size_, - expected_header_size); - return false; - } - - // Check that all offsets are inside the file. - bool result = - CheckValidOffsetAndSize(header_->link_off_, - header_->link_size_, - 0 /* unaligned */, - "link") && - CheckValidOffsetAndSize(header_->map_off_, - header_->map_off_, - 4, - "map") && - CheckValidOffsetAndSize(header_->string_ids_off_, - header_->string_ids_size_, - 4, - "string-ids") && - CheckValidOffsetAndSize(header_->type_ids_off_, - header_->type_ids_size_, - 4, - "type-ids") && - CheckSizeLimit(header_->type_ids_size_, DexFile::kDexNoIndex16, "type-ids") && - CheckValidOffsetAndSize(header_->proto_ids_off_, - header_->proto_ids_size_, - 4, - "proto-ids") && - CheckSizeLimit(header_->proto_ids_size_, DexFile::kDexNoIndex16, "proto-ids") && - CheckValidOffsetAndSize(header_->field_ids_off_, - header_->field_ids_size_, - 4, - "field-ids") && - CheckValidOffsetAndSize(header_->method_ids_off_, - header_->method_ids_size_, - 4, - "method-ids") && - CheckValidOffsetAndSize(header_->class_defs_off_, - header_->class_defs_size_, - 4, - "class-defs") && - CheckValidOffsetAndSize(header_->data_off_, - header_->data_size_, - 0, // Unaligned, spec doesn't talk about it, even though size - // is supposed to be a multiple of 4. - "data"); - return result; -} - -bool DexFileVerifier::CheckMap() { - const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + - header_->map_off_); - // Check that map list content is available. - if (!CheckListSize(map, 1, sizeof(DexFile::MapList), "maplist content")) { - return false; - } - - const DexFile::MapItem* item = map->list_; - - uint32_t count = map->size_; - uint32_t last_offset = 0; - uint32_t last_type = 0; - uint32_t data_item_count = 0; - uint32_t data_items_left = header_->data_size_; - uint32_t used_bits = 0; - - // Sanity check the size of the map list. - if (!CheckListSize(item, count, sizeof(DexFile::MapItem), "map size")) { - return false; - } - - // Check the items listed in the map. - for (uint32_t i = 0; i < count; i++) { - if (UNLIKELY(last_offset >= item->offset_ && i != 0)) { - ErrorStringPrintf("Out of order map item: %x then %x for type %x last type was %x", - last_offset, - item->offset_, - static_cast<uint32_t>(item->type_), - last_type); - return false; - } - if (UNLIKELY(item->offset_ >= header_->file_size_)) { - ErrorStringPrintf("Map item after end of file: %x, size %x", - item->offset_, header_->file_size_); - return false; - } - - DexFile::MapItemType item_type = static_cast<DexFile::MapItemType>(item->type_); - if (IsDataSectionType(item_type)) { - uint32_t icount = item->size_; - if (UNLIKELY(icount > data_items_left)) { - ErrorStringPrintf("Too many items in data section: %ud item_type %zx", - data_item_count + icount, - static_cast<size_t>(item_type)); - return false; - } - data_items_left -= icount; - data_item_count += icount; - } - - uint32_t bit = MapTypeToBitMask(item_type); - - if (UNLIKELY(bit == 0)) { - ErrorStringPrintf("Unknown map section type %x", item->type_); - return false; - } - - if (UNLIKELY((used_bits & bit) != 0)) { - ErrorStringPrintf("Duplicate map section of type %x", item->type_); - return false; - } - - used_bits |= bit; - last_offset = item->offset_; - last_type = item->type_; - item++; - } - - // Check for missing sections in the map. - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0)) { - ErrorStringPrintf("Map is missing header entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0)) { - ErrorStringPrintf("Map is missing map_list entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 && - ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0)))) { - ErrorStringPrintf("Map is missing string_ids entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 && - ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0)))) { - ErrorStringPrintf("Map is missing type_ids entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 && - ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0)))) { - ErrorStringPrintf("Map is missing proto_ids entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 && - ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0)))) { - ErrorStringPrintf("Map is missing field_ids entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 && - ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0)))) { - ErrorStringPrintf("Map is missing method_ids entry"); - return false; - } - if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 && - ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0)))) { - ErrorStringPrintf("Map is missing class_defs entry"); - return false; - } - return true; -} - -uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) { - uint32_t result = 0; - if (LIKELY(CheckListSize(ptr_, size, sizeof(uint8_t), "encoded_value"))) { - for (uint32_t i = 0; i < size; i++) { - result |= ((uint32_t) *(ptr_++)) << (i * 8); - } - } - return result; -} - - -#define DECODE_UNSIGNED_CHECKED_FROM_WITH_ERROR_VALUE(ptr, var, error_value) \ - uint32_t var; \ - if (!DecodeUnsignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \ - return error_value; \ - } - -#define DECODE_UNSIGNED_CHECKED_FROM(ptr, var) \ - uint32_t var; \ - if (!DecodeUnsignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \ - ErrorStringPrintf("Read out of bounds"); \ - return false; \ - } - -#define DECODE_SIGNED_CHECKED_FROM(ptr, var) \ - int32_t var; \ - if (!DecodeSignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \ - ErrorStringPrintf("Read out of bounds"); \ - return false; \ - } - -bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item, - uint32_t* handler_offsets, uint32_t handlers_size) { - CodeItemDataAccessor accessor(*dex_file_, code_item); - const uint8_t* handlers_base = accessor.GetCatchHandlerData(); - - for (uint32_t i = 0; i < handlers_size; i++) { - bool catch_all; - size_t offset = ptr_ - handlers_base; - DECODE_SIGNED_CHECKED_FROM(ptr_, size); - - if (UNLIKELY((size < -65536) || (size > 65536))) { - ErrorStringPrintf("Invalid exception handler size: %d", size); - return false; - } - - if (size <= 0) { - catch_all = true; - size = -size; - } else { - catch_all = false; - } - - handler_offsets[i] = static_cast<uint32_t>(offset); - - while (size-- > 0) { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx); - if (!CheckIndex(type_idx, header_->type_ids_size_, "handler type_idx")) { - return false; - } - - DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr); - if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) { - ErrorStringPrintf("Invalid handler addr: %x", addr); - return false; - } - } - - if (catch_all) { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr); - if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) { - ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr); - return false; - } - } - } - - return true; -} - -bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, - uint32_t access_flags, - uint32_t class_access_flags, - dex::TypeIndex class_type_index, - bool expect_static) { - // Check for overflow. - if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) { - return false; - } - - // Check that it's the right class. - dex::TypeIndex my_class_index = - (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)-> - class_idx_; - if (class_type_index != my_class_index) { - ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16, - my_class_index.index_, - class_type_index.index_); - return false; - } - - // Check that it falls into the right class-data list. - bool is_static = (access_flags & kAccStatic) != 0; - if (UNLIKELY(is_static != expect_static)) { - ErrorStringPrintf("Static/instance field not in expected list"); - return false; - } - - // Check field access flags. - std::string error_msg; - if (!CheckFieldAccessFlags(idx, access_flags, class_access_flags, &error_msg)) { - ErrorStringPrintf("%s", error_msg.c_str()); - return false; - } - - return true; -} - -bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, - uint32_t access_flags, - uint32_t class_access_flags, - dex::TypeIndex class_type_index, - uint32_t code_offset, - std::unordered_set<uint32_t>* direct_method_indexes, - bool expect_direct) { - DCHECK(direct_method_indexes != nullptr); - // Check for overflow. - if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) { - return false; - } - - // Check that it's the right class. - dex::TypeIndex my_class_index = - (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)-> - class_idx_; - if (class_type_index != my_class_index) { - ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16, - my_class_index.index_, - class_type_index.index_); - return false; - } - - // Check that it's not defined as both direct and virtual. - if (expect_direct) { - direct_method_indexes->insert(idx); - } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) { - ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx); - return false; - } - - std::string error_msg; - const char* method_name; - if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) { - ErrorStringPrintf("%s", error_msg.c_str()); - return false; - } - - uint32_t constructor_flags_by_name = 0; - if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) { - ErrorStringPrintf("Bad method name: %s", method_name); - return false; - } - - bool has_code = (code_offset != 0); - if (!CheckMethodAccessFlags(idx, - access_flags, - class_access_flags, - constructor_flags_by_name, - has_code, - expect_direct, - &error_msg)) { - ErrorStringPrintf("%s", error_msg.c_str()); - return false; - } - - if (constructor_flags_by_name != 0) { - if (!CheckConstructorProperties(idx, constructor_flags_by_name)) { - DCHECK(FailureReasonIsSet()); - return false; - } - } - - return true; -} - -bool DexFileVerifier::CheckPadding(size_t offset, - uint32_t aligned_offset, - DexFile::MapItemType type) { - if (offset < aligned_offset) { - if (!CheckListSize(begin_ + offset, aligned_offset - offset, sizeof(uint8_t), "section")) { - return false; - } - while (offset < aligned_offset) { - if (UNLIKELY(*ptr_ != '\0')) { - ErrorStringPrintf("Non-zero padding %x before section of type %zu at offset 0x%zx", - *ptr_, - static_cast<size_t>(type), - offset); - return false; - } - ptr_++; - offset++; - } - } - return true; -} - -bool DexFileVerifier::CheckEncodedValue() { - if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "encoded_value header")) { - return false; - } - - uint8_t header_byte = *(ptr_++); - uint32_t value_type = header_byte & DexFile::kDexAnnotationValueTypeMask; - uint32_t value_arg = header_byte >> DexFile::kDexAnnotationValueArgShift; - - switch (value_type) { - case DexFile::kDexAnnotationByte: - if (UNLIKELY(value_arg != 0)) { - ErrorStringPrintf("Bad encoded_value byte size %x", value_arg); - return false; - } - ptr_++; - break; - case DexFile::kDexAnnotationShort: - case DexFile::kDexAnnotationChar: - if (UNLIKELY(value_arg > 1)) { - ErrorStringPrintf("Bad encoded_value char/short size %x", value_arg); - return false; - } - ptr_ += value_arg + 1; - break; - case DexFile::kDexAnnotationInt: - case DexFile::kDexAnnotationFloat: - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value int/float size %x", value_arg); - return false; - } - ptr_ += value_arg + 1; - break; - case DexFile::kDexAnnotationLong: - case DexFile::kDexAnnotationDouble: - ptr_ += value_arg + 1; - break; - case DexFile::kDexAnnotationString: { - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value string size %x", value_arg); - return false; - } - uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); - if (!CheckIndex(idx, header_->string_ids_size_, "encoded_value string")) { - return false; - } - break; - } - case DexFile::kDexAnnotationType: { - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value type size %x", value_arg); - return false; - } - uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); - if (!CheckIndex(idx, header_->type_ids_size_, "encoded_value type")) { - return false; - } - break; - } - case DexFile::kDexAnnotationField: - case DexFile::kDexAnnotationEnum: { - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value field/enum size %x", value_arg); - return false; - } - uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); - if (!CheckIndex(idx, header_->field_ids_size_, "encoded_value field")) { - return false; - } - break; - } - case DexFile::kDexAnnotationMethod: { - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value method size %x", value_arg); - return false; - } - uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); - if (!CheckIndex(idx, header_->method_ids_size_, "encoded_value method")) { - return false; - } - break; - } - case DexFile::kDexAnnotationArray: - if (UNLIKELY(value_arg != 0)) { - ErrorStringPrintf("Bad encoded_value array value_arg %x", value_arg); - return false; - } - if (!CheckEncodedArray()) { - return false; - } - break; - case DexFile::kDexAnnotationAnnotation: - if (UNLIKELY(value_arg != 0)) { - ErrorStringPrintf("Bad encoded_value annotation value_arg %x", value_arg); - return false; - } - if (!CheckEncodedAnnotation()) { - return false; - } - break; - case DexFile::kDexAnnotationNull: - if (UNLIKELY(value_arg != 0)) { - ErrorStringPrintf("Bad encoded_value null value_arg %x", value_arg); - return false; - } - break; - case DexFile::kDexAnnotationBoolean: - if (UNLIKELY(value_arg > 1)) { - ErrorStringPrintf("Bad encoded_value boolean size %x", value_arg); - return false; - } - break; - case DexFile::kDexAnnotationMethodType: { - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value method type size %x", value_arg); - return false; - } - uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); - if (!CheckIndex(idx, header_->proto_ids_size_, "method_type value")) { - return false; - } - break; - } - case DexFile::kDexAnnotationMethodHandle: { - if (UNLIKELY(value_arg > 3)) { - ErrorStringPrintf("Bad encoded_value method handle size %x", value_arg); - return false; - } - uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); - if (!CheckIndex(idx, dex_file_->NumMethodHandles(), "method_handle value")) { - return false; - } - break; - } - default: - ErrorStringPrintf("Bogus encoded_value value_type %x", value_type); - return false; - } - - return true; -} - -bool DexFileVerifier::CheckEncodedArray() { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, size); - - while (size--) { - if (!CheckEncodedValue()) { - failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str()); - return false; - } - } - return true; -} - -bool DexFileVerifier::CheckEncodedAnnotation() { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, anno_idx); - if (!CheckIndex(anno_idx, header_->type_ids_size_, "encoded_annotation type_idx")) { - return false; - } - - DECODE_UNSIGNED_CHECKED_FROM(ptr_, size); - uint32_t last_idx = 0; - - for (uint32_t i = 0; i < size; i++) { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, idx); - if (!CheckIndex(idx, header_->string_ids_size_, "annotation_element name_idx")) { - return false; - } - - if (UNLIKELY(last_idx >= idx && i != 0)) { - ErrorStringPrintf("Out-of-order annotation_element name_idx: %x then %x", - last_idx, idx); - return false; - } - - if (!CheckEncodedValue()) { - return false; - } - - last_idx = idx; - } - return true; -} - -bool DexFileVerifier::FindClassIndexAndDef(uint32_t index, - bool is_field, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** output_class_def) { - DCHECK(class_type_index != nullptr); - DCHECK(output_class_def != nullptr); - - // First check if the index is valid. - if (index >= (is_field ? header_->field_ids_size_ : header_->method_ids_size_)) { - return false; - } - - // Next get the type index. - if (is_field) { - *class_type_index = - (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + index)-> - class_idx_; - } else { - *class_type_index = - (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + index)-> - class_idx_; - } - - // Check if that is valid. - if (class_type_index->index_ >= header_->type_ids_size_) { - return false; - } - - // Now search for the class def. This is basically a specialized version of the DexFile code, as - // we should not trust that this is a valid DexFile just yet. - const DexFile::ClassDef* class_def_begin = - reinterpret_cast<const DexFile::ClassDef*>(begin_ + header_->class_defs_off_); - for (size_t i = 0; i < header_->class_defs_size_; ++i) { - const DexFile::ClassDef* class_def = class_def_begin + i; - if (class_def->class_idx_ == *class_type_index) { - *output_class_def = class_def; - return true; - } - } - - // Didn't find the class-def, not defined here... - return false; -} - -bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def) { - if (curr_index < prev_index) { - ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32, - type_descr, - prev_index, - curr_index); - return false; - } - - if (!*have_class) { - *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def); - if (!*have_class) { - // Should have really found one. - ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, - type_descr, - curr_index); - return false; - } - } - return true; -} - -bool DexFileVerifier::CheckStaticFieldTypes(const DexFile::ClassDef* class_def) { - if (class_def == nullptr) { - return true; - } - - ClassDataItemIterator field_it(*dex_file_, ptr_); - EncodedStaticFieldValueIterator array_it(*dex_file_, *class_def); - - for (; field_it.HasNextStaticField() && array_it.HasNext(); field_it.Next(), array_it.Next()) { - uint32_t index = field_it.GetMemberIndex(); - const DexFile::TypeId& type_id = dex_file_->GetTypeId(dex_file_->GetFieldId(index).type_idx_); - const char* field_type_name = - dex_file_->GetStringData(dex_file_->GetStringId(type_id.descriptor_idx_)); - Primitive::Type field_type = Primitive::GetType(field_type_name[0]); - EncodedArrayValueIterator::ValueType array_type = array_it.GetValueType(); - // Ensure this matches RuntimeEncodedStaticFieldValueIterator. - switch (array_type) { - case EncodedArrayValueIterator::ValueType::kBoolean: - if (field_type != Primitive::kPrimBoolean) { - ErrorStringPrintf("unexpected static field initial value type: 'Z' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kByte: - if (field_type != Primitive::kPrimByte) { - ErrorStringPrintf("unexpected static field initial value type: 'B' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kShort: - if (field_type != Primitive::kPrimShort) { - ErrorStringPrintf("unexpected static field initial value type: 'S' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kChar: - if (field_type != Primitive::kPrimChar) { - ErrorStringPrintf("unexpected static field initial value type: 'C' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kInt: - if (field_type != Primitive::kPrimInt) { - ErrorStringPrintf("unexpected static field initial value type: 'I' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kLong: - if (field_type != Primitive::kPrimLong) { - ErrorStringPrintf("unexpected static field initial value type: 'J' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kFloat: - if (field_type != Primitive::kPrimFloat) { - ErrorStringPrintf("unexpected static field initial value type: 'F' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kDouble: - if (field_type != Primitive::kPrimDouble) { - ErrorStringPrintf("unexpected static field initial value type: 'D' vs '%c'", - field_type_name[0]); - return false; - } - break; - case EncodedArrayValueIterator::ValueType::kNull: - case EncodedArrayValueIterator::ValueType::kString: - case EncodedArrayValueIterator::ValueType::kType: - if (field_type != Primitive::kPrimNot) { - ErrorStringPrintf("unexpected static field initial value type: 'L' vs '%c'", - field_type_name[0]); - return false; - } - break; - default: - ErrorStringPrintf("unexpected static field initial value type: %x", array_type); - return false; - } - } - - if (array_it.HasNext()) { - ErrorStringPrintf("too many static field initial values"); - return false; - } - return true; -} - -template <bool kStatic> -bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def) { - DCHECK(it != nullptr); - // These calls use the raw access flags to check whether the whole dex field is valid. - uint32_t prev_index = 0; - for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) { - uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassDef(true, - kStatic ? "static field" : "instance field", - curr_index, - prev_index, - have_class, - class_type_index, - class_def)) { - return false; - } - DCHECK(class_def != nullptr); - if (!CheckClassDataItemField(curr_index, - it->GetRawMemberAccessFlags(), - (*class_def)->access_flags_, - *class_type_index, - kStatic)) { - return false; - } - - prev_index = curr_index; - } - - return true; -} - -template <bool kDirect> -bool DexFileVerifier::CheckIntraClassDataItemMethods( - ClassDataItemIterator* it, - std::unordered_set<uint32_t>* direct_method_indexes, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def) { - uint32_t prev_index = 0; - for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) { - uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassDef(false, - kDirect ? "direct method" : "virtual method", - curr_index, - prev_index, - have_class, - class_type_index, - class_def)) { - return false; - } - DCHECK(class_def != nullptr); - if (!CheckClassDataItemMethod(curr_index, - it->GetRawMemberAccessFlags(), - (*class_def)->access_flags_, - *class_type_index, - it->GetMethodCodeItemOffset(), - direct_method_indexes, - kDirect)) { - return false; - } - - prev_index = curr_index; - } - - return true; -} - -bool DexFileVerifier::CheckIntraClassDataItem() { - ClassDataItemIterator it(*dex_file_, ptr_); - std::unordered_set<uint32_t> direct_method_indexes; - - // This code is complicated by the fact that we don't directly know which class this belongs to. - // So we need to explicitly search with the first item we find (either field or method), and then, - // as the lookup is expensive, cache the result. - bool have_class = false; - dex::TypeIndex class_type_index; - const DexFile::ClassDef* class_def = nullptr; - - // Check fields. - if (!CheckIntraClassDataItemFields<true>(&it, - &have_class, - &class_type_index, - &class_def)) { - return false; - } - if (!CheckIntraClassDataItemFields<false>(&it, - &have_class, - &class_type_index, - &class_def)) { - return false; - } - - // Check methods. - if (!CheckIntraClassDataItemMethods<true>(&it, - &direct_method_indexes, - &have_class, - &class_type_index, - &class_def)) { - return false; - } - if (!CheckIntraClassDataItemMethods<false>(&it, - &direct_method_indexes, - &have_class, - &class_type_index, - &class_def)) { - return false; - } - - const uint8_t* end_ptr = it.EndDataPointer(); - - // Check static field types against initial static values in encoded array. - if (!CheckStaticFieldTypes(class_def)) { - return false; - } - - ptr_ = end_ptr; - return true; -} - -bool DexFileVerifier::CheckIntraCodeItem() { - const DexFile::CodeItem* code_item = reinterpret_cast<const DexFile::CodeItem*>(ptr_); - if (!CheckListSize(code_item, 1, sizeof(DexFile::CodeItem), "code")) { - return false; - } - - CodeItemDataAccessor accessor(*dex_file_, code_item); - if (UNLIKELY(accessor.InsSize() > accessor.RegistersSize())) { - ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)", - accessor.InsSize(), accessor.RegistersSize()); - return false; - } - - if (UNLIKELY(accessor.OutsSize() > 5 && accessor.OutsSize() > accessor.RegistersSize())) { - /* - * outs_size can be up to 5, even if registers_size is smaller, since the - * short forms of method invocation allow repetitions of a register multiple - * times within a single parameter list. However, longer parameter lists - * need to be represented in-order in the register file. - */ - ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)", - accessor.OutsSize(), accessor.RegistersSize()); - return false; - } - - const uint16_t* insns = accessor.Insns(); - uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); - if (!CheckListSize(insns, insns_size, sizeof(uint16_t), "insns size")) { - return false; - } - - // Grab the end of the insns if there are no try_items. - uint32_t try_items_size = accessor.TriesSize(); - if (try_items_size == 0) { - ptr_ = reinterpret_cast<const uint8_t*>(&insns[insns_size]); - return true; - } - - // try_items are 4-byte aligned. Verify the spacer is 0. - if (((reinterpret_cast<uintptr_t>(&insns[insns_size]) & 3) != 0) && (insns[insns_size] != 0)) { - ErrorStringPrintf("Non-zero padding: %x", insns[insns_size]); - return false; - } - - const DexFile::TryItem* try_items = accessor.TryItems().begin(); - if (!CheckListSize(try_items, try_items_size, sizeof(DexFile::TryItem), "try_items size")) { - return false; - } - - ptr_ = accessor.GetCatchHandlerData(); - DECODE_UNSIGNED_CHECKED_FROM(ptr_, handlers_size); - - if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) { - ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size); - return false; - } - - std::unique_ptr<uint32_t[]> handler_offsets(new uint32_t[handlers_size]); - if (!CheckAndGetHandlerOffsets(code_item, &handler_offsets[0], handlers_size)) { - return false; - } - - uint32_t last_addr = 0; - while (try_items_size--) { - if (UNLIKELY(try_items->start_addr_ < last_addr)) { - ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_); - return false; - } - - if (UNLIKELY(try_items->start_addr_ >= insns_size)) { - ErrorStringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_); - return false; - } - - uint32_t i; - for (i = 0; i < handlers_size; i++) { - if (try_items->handler_off_ == handler_offsets[i]) { - break; - } - } - - if (UNLIKELY(i == handlers_size)) { - ErrorStringPrintf("Bogus handler offset: %x", try_items->handler_off_); - return false; - } - - last_addr = try_items->start_addr_ + try_items->insn_count_; - if (UNLIKELY(last_addr > insns_size)) { - ErrorStringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_); - return false; - } - - try_items++; - } - - return true; -} - -bool DexFileVerifier::CheckIntraStringDataItem() { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, size); - const uint8_t* file_end = begin_ + size_; - - for (uint32_t i = 0; i < size; i++) { - CHECK_LT(i, size); // b/15014252 Prevents hitting the impossible case below - if (UNLIKELY(ptr_ >= file_end)) { - ErrorStringPrintf("String data would go beyond end-of-file"); - return false; - } - - uint8_t byte = *(ptr_++); - - // Switch on the high 4 bits. - switch (byte >> 4) { - case 0x00: - // Special case of bit pattern 0xxx. - if (UNLIKELY(byte == 0)) { - CHECK_LT(i, size); // b/15014252 Actually hit this impossible case with clang - ErrorStringPrintf("String data shorter than indicated utf16_size %x", size); - return false; - } - break; - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - // No extra checks necessary for bit pattern 0xxx. - break; - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0f: - // Illegal bit patterns 10xx or 1111. - // Note: 1111 is valid for normal UTF-8, but not here. - ErrorStringPrintf("Illegal start byte %x in string data", byte); - return false; - case 0x0c: - case 0x0d: { - // Bit pattern 110x has an additional byte. - uint8_t byte2 = *(ptr_++); - if (UNLIKELY((byte2 & 0xc0) != 0x80)) { - ErrorStringPrintf("Illegal continuation byte %x in string data", byte2); - return false; - } - uint16_t value = ((byte & 0x1f) << 6) | (byte2 & 0x3f); - if (UNLIKELY((value != 0) && (value < 0x80))) { - ErrorStringPrintf("Illegal representation for value %x in string data", value); - return false; - } - break; - } - case 0x0e: { - // Bit pattern 1110 has 2 additional bytes. - uint8_t byte2 = *(ptr_++); - if (UNLIKELY((byte2 & 0xc0) != 0x80)) { - ErrorStringPrintf("Illegal continuation byte %x in string data", byte2); - return false; - } - uint8_t byte3 = *(ptr_++); - if (UNLIKELY((byte3 & 0xc0) != 0x80)) { - ErrorStringPrintf("Illegal continuation byte %x in string data", byte3); - return false; - } - uint16_t value = ((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f); - if (UNLIKELY(value < 0x800)) { - ErrorStringPrintf("Illegal representation for value %x in string data", value); - return false; - } - break; - } - } - } - - if (UNLIKELY(*(ptr_++) != '\0')) { - ErrorStringPrintf("String longer than indicated size %x", size); - return false; - } - - return true; -} - -bool DexFileVerifier::CheckIntraDebugInfoItem() { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, dummy); - DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameters_size); - if (UNLIKELY(parameters_size > 65536)) { - ErrorStringPrintf("Invalid parameters_size: %x", parameters_size); - return false; - } - - for (uint32_t j = 0; j < parameters_size; j++) { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameter_name); - if (parameter_name != 0) { - parameter_name--; - if (!CheckIndex(parameter_name, header_->string_ids_size_, "debug_info_item parameter_name")) { - return false; - } - } - } - - while (true) { - uint8_t opcode = *(ptr_++); - switch (opcode) { - case DexFile::DBG_END_SEQUENCE: { - return true; - } - case DexFile::DBG_ADVANCE_PC: { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, advance_pc_dummy); - break; - } - case DexFile::DBG_ADVANCE_LINE: { - DECODE_SIGNED_CHECKED_FROM(ptr_, advance_line_dummy); - break; - } - case DexFile::DBG_START_LOCAL: { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num); - if (UNLIKELY(reg_num >= 65536)) { - ErrorStringPrintf("Bad reg_num for opcode %x", opcode); - return false; - } - DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx); - if (name_idx != 0) { - name_idx--; - if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL name_idx")) { - return false; - } - } - DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx); - if (type_idx != 0) { - type_idx--; - if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL type_idx")) { - return false; - } - } - break; - } - case DexFile::DBG_END_LOCAL: - case DexFile::DBG_RESTART_LOCAL: { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num); - if (UNLIKELY(reg_num >= 65536)) { - ErrorStringPrintf("Bad reg_num for opcode %x", opcode); - return false; - } - break; - } - case DexFile::DBG_START_LOCAL_EXTENDED: { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num); - if (UNLIKELY(reg_num >= 65536)) { - ErrorStringPrintf("Bad reg_num for opcode %x", opcode); - return false; - } - DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx); - if (name_idx != 0) { - name_idx--; - if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED name_idx")) { - return false; - } - } - DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx); - if (type_idx != 0) { - type_idx--; - if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL_EXTENDED type_idx")) { - return false; - } - } - DECODE_UNSIGNED_CHECKED_FROM(ptr_, sig_idx); - if (sig_idx != 0) { - sig_idx--; - if (!CheckIndex(sig_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED sig_idx")) { - return false; - } - } - break; - } - case DexFile::DBG_SET_FILE: { - DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx); - if (name_idx != 0) { - name_idx--; - if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_SET_FILE name_idx")) { - return false; - } - } - break; - } - } - } -} - -bool DexFileVerifier::CheckIntraAnnotationItem() { - if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "annotation visibility")) { - return false; - } - - // Check visibility - switch (*(ptr_++)) { - case DexFile::kDexVisibilityBuild: - case DexFile::kDexVisibilityRuntime: - case DexFile::kDexVisibilitySystem: - break; - default: - ErrorStringPrintf("Bad annotation visibility: %x", *ptr_); - return false; - } - - if (!CheckEncodedAnnotation()) { - return false; - } - - return true; -} - -bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { - const DexFile::AnnotationsDirectoryItem* item = - reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_); - if (!CheckListSize(item, 1, sizeof(DexFile::AnnotationsDirectoryItem), "annotations_directory")) { - return false; - } - - // Field annotations follow immediately after the annotations directory. - const DexFile::FieldAnnotationsItem* field_item = - reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1); - uint32_t field_count = item->fields_size_; - if (!CheckListSize(field_item, field_count, sizeof(DexFile::FieldAnnotationsItem), "field_annotations list")) { - return false; - } - - uint32_t last_idx = 0; - for (uint32_t i = 0; i < field_count; i++) { - if (UNLIKELY(last_idx >= field_item->field_idx_ && i != 0)) { - ErrorStringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_); - return false; - } - last_idx = field_item->field_idx_; - field_item++; - } - - // Method annotations follow immediately after field annotations. - const DexFile::MethodAnnotationsItem* method_item = - reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item); - uint32_t method_count = item->methods_size_; - if (!CheckListSize(method_item, method_count, sizeof(DexFile::MethodAnnotationsItem), "method_annotations list")) { - return false; - } - - last_idx = 0; - for (uint32_t i = 0; i < method_count; i++) { - if (UNLIKELY(last_idx >= method_item->method_idx_ && i != 0)) { - ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x", - last_idx, method_item->method_idx_); - return false; - } - last_idx = method_item->method_idx_; - method_item++; - } - - // Parameter annotations follow immediately after method annotations. - const DexFile::ParameterAnnotationsItem* parameter_item = - reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item); - uint32_t parameter_count = item->parameters_size_; - if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem), - "parameter_annotations list")) { - return false; - } - - last_idx = 0; - for (uint32_t i = 0; i < parameter_count; i++) { - if (UNLIKELY(last_idx >= parameter_item->method_idx_ && i != 0)) { - ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x", - last_idx, parameter_item->method_idx_); - return false; - } - last_idx = parameter_item->method_idx_; - parameter_item++; - } - - // Return a pointer to the end of the annotations. - ptr_ = reinterpret_cast<const uint8_t*>(parameter_item); - return true; -} - -bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count, - DexFile::MapItemType type) { - // Get the right alignment mask for the type of section. - size_t alignment_mask; - switch (type) { - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - alignment_mask = sizeof(uint8_t) - 1; - break; - default: - alignment_mask = sizeof(uint32_t) - 1; - break; - } - - // Iterate through the items in the section. - for (uint32_t i = 0; i < section_count; i++) { - size_t aligned_offset = (offset + alignment_mask) & ~alignment_mask; - - // Check the padding between items. - if (!CheckPadding(offset, aligned_offset, type)) { - return false; - } - - // Check depending on the section type. - const uint8_t* start_ptr = ptr_; - switch (type) { - case DexFile::kDexTypeStringIdItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) { - return false; - } - ptr_ += sizeof(DexFile::StringId); - break; - } - case DexFile::kDexTypeTypeIdItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::TypeId), "type_ids")) { - return false; - } - ptr_ += sizeof(DexFile::TypeId); - break; - } - case DexFile::kDexTypeProtoIdItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::ProtoId), "proto_ids")) { - return false; - } - ptr_ += sizeof(DexFile::ProtoId); - break; - } - case DexFile::kDexTypeFieldIdItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::FieldId), "field_ids")) { - return false; - } - ptr_ += sizeof(DexFile::FieldId); - break; - } - case DexFile::kDexTypeMethodIdItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodId), "method_ids")) { - return false; - } - ptr_ += sizeof(DexFile::MethodId); - break; - } - case DexFile::kDexTypeClassDefItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::ClassDef), "class_defs")) { - return false; - } - ptr_ += sizeof(DexFile::ClassDef); - break; - } - case DexFile::kDexTypeCallSiteIdItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::CallSiteIdItem), "call_site_ids")) { - return false; - } - ptr_ += sizeof(DexFile::CallSiteIdItem); - break; - } - case DexFile::kDexTypeMethodHandleItem: { - if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodHandleItem), "method_handles")) { - return false; - } - ptr_ += sizeof(DexFile::MethodHandleItem); - break; - } - case DexFile::kDexTypeTypeList: { - if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationSetRefList: { - if (!CheckList(sizeof(DexFile::AnnotationSetRefItem), "annotation_set_ref_list", &ptr_)) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationSetItem: { - if (!CheckList(sizeof(uint32_t), "annotation_set_item", &ptr_)) { - return false; - } - break; - } - case DexFile::kDexTypeClassDataItem: { - if (!CheckIntraClassDataItem()) { - return false; - } - break; - } - case DexFile::kDexTypeCodeItem: { - if (!CheckIntraCodeItem()) { - return false; - } - break; - } - case DexFile::kDexTypeStringDataItem: { - if (!CheckIntraStringDataItem()) { - return false; - } - break; - } - case DexFile::kDexTypeDebugInfoItem: { - if (!CheckIntraDebugInfoItem()) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationItem: { - if (!CheckIntraAnnotationItem()) { - return false; - } - break; - } - case DexFile::kDexTypeEncodedArrayItem: { - if (!CheckEncodedArray()) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationsDirectoryItem: { - if (!CheckIntraAnnotationsDirectoryItem()) { - return false; - } - break; - } - case DexFile::kDexTypeHeaderItem: - case DexFile::kDexTypeMapList: - break; - } - - if (start_ptr == ptr_) { - ErrorStringPrintf("Unknown map item type %x", type); - return false; - } - - if (IsDataSectionType(type)) { - if (aligned_offset == 0u) { - ErrorStringPrintf("Item %d offset is 0", i); - return false; - } - DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end()); - offset_to_type_map_.Insert(std::pair<uint32_t, uint16_t>(aligned_offset, type)); - } - - aligned_offset = ptr_ - begin_; - if (UNLIKELY(aligned_offset > size_)) { - ErrorStringPrintf("Item %d at ends out of bounds", i); - return false; - } - - offset = aligned_offset; - } - - return true; -} - -bool DexFileVerifier::CheckIntraIdSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { - uint32_t expected_offset; - uint32_t expected_size; - - // Get the expected offset and size from the header. - switch (type) { - case DexFile::kDexTypeStringIdItem: - expected_offset = header_->string_ids_off_; - expected_size = header_->string_ids_size_; - break; - case DexFile::kDexTypeTypeIdItem: - expected_offset = header_->type_ids_off_; - expected_size = header_->type_ids_size_; - break; - case DexFile::kDexTypeProtoIdItem: - expected_offset = header_->proto_ids_off_; - expected_size = header_->proto_ids_size_; - break; - case DexFile::kDexTypeFieldIdItem: - expected_offset = header_->field_ids_off_; - expected_size = header_->field_ids_size_; - break; - case DexFile::kDexTypeMethodIdItem: - expected_offset = header_->method_ids_off_; - expected_size = header_->method_ids_size_; - break; - case DexFile::kDexTypeClassDefItem: - expected_offset = header_->class_defs_off_; - expected_size = header_->class_defs_size_; - break; - default: - ErrorStringPrintf("Bad type for id section: %x", type); - return false; - } - - // Check that the offset and size are what were expected from the header. - if (UNLIKELY(offset != expected_offset)) { - ErrorStringPrintf("Bad offset for section: got %zx, expected %x", offset, expected_offset); - return false; - } - if (UNLIKELY(count != expected_size)) { - ErrorStringPrintf("Bad size for section: got %x, expected %x", count, expected_size); - return false; - } - - return CheckIntraSectionIterate(offset, count, type); -} - -bool DexFileVerifier::CheckIntraDataSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { - size_t data_start = header_->data_off_; - size_t data_end = data_start + header_->data_size_; - - // Sanity check the offset of the section. - if (UNLIKELY((offset < data_start) || (offset > data_end))) { - ErrorStringPrintf("Bad offset for data subsection: %zx", offset); - return false; - } - - if (!CheckIntraSectionIterate(offset, count, type)) { - return false; - } - - size_t next_offset = ptr_ - begin_; - if (next_offset > data_end) { - ErrorStringPrintf("Out-of-bounds end of data subsection: %zu data_off=%u data_size=%u", - next_offset, - header_->data_off_, - header_->data_size_); - return false; - } - - return true; -} - -bool DexFileVerifier::CheckIntraSection() { - const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_); - const DexFile::MapItem* item = map->list_; - size_t offset = 0; - uint32_t count = map->size_; - ptr_ = begin_; - - // Check the items listed in the map. - while (count--) { - const size_t current_offset = offset; - uint32_t section_offset = item->offset_; - uint32_t section_count = item->size_; - DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_); - - // Check for padding and overlap between items. - if (!CheckPadding(offset, section_offset, type)) { - return false; - } else if (UNLIKELY(offset > section_offset)) { - ErrorStringPrintf("Section overlap or out-of-order map: %zx, %x", offset, section_offset); - return false; - } - - // Check each item based on its type. - switch (type) { - case DexFile::kDexTypeHeaderItem: - if (UNLIKELY(section_count != 1)) { - ErrorStringPrintf("Multiple header items"); - return false; - } - if (UNLIKELY(section_offset != 0)) { - ErrorStringPrintf("Header at %x, not at start of file", section_offset); - return false; - } - ptr_ = begin_ + header_->header_size_; - offset = header_->header_size_; - break; - case DexFile::kDexTypeStringIdItem: - case DexFile::kDexTypeTypeIdItem: - case DexFile::kDexTypeProtoIdItem: - case DexFile::kDexTypeFieldIdItem: - case DexFile::kDexTypeMethodIdItem: - case DexFile::kDexTypeClassDefItem: - if (!CheckIntraIdSection(section_offset, section_count, type)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeMapList: - if (UNLIKELY(section_count != 1)) { - ErrorStringPrintf("Multiple map list items"); - return false; - } - if (UNLIKELY(section_offset != header_->map_off_)) { - ErrorStringPrintf("Map not at header-defined offset: %x, expected %x", - section_offset, header_->map_off_); - return false; - } - ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); - offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); - break; - case DexFile::kDexTypeMethodHandleItem: - case DexFile::kDexTypeCallSiteIdItem: - CheckIntraSectionIterate(section_offset, section_count, type); - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeTypeList: - case DexFile::kDexTypeAnnotationSetRefList: - case DexFile::kDexTypeAnnotationSetItem: - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeCodeItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - case DexFile::kDexTypeAnnotationsDirectoryItem: - if (!CheckIntraDataSection(section_offset, section_count, type)) { - return false; - } - offset = ptr_ - begin_; - break; - } - - if (offset == current_offset) { - ErrorStringPrintf("Unknown map item type %x", type); - return false; - } - - item++; - } - - return true; -} - -bool DexFileVerifier::CheckOffsetToTypeMap(size_t offset, uint16_t type) { - DCHECK_NE(offset, 0u); - auto it = offset_to_type_map_.Find(offset); - if (UNLIKELY(it == offset_to_type_map_.end())) { - ErrorStringPrintf("No data map entry found @ %zx; expected %x", offset, type); - return false; - } - if (UNLIKELY(it->second != type)) { - ErrorStringPrintf("Unexpected data map entry @ %zx; expected %x, found %x", - offset, type, it->second); - return false; - } - return true; -} - -dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) { - ClassDataItemIterator it(*dex_file_, ptr); - *success = true; - - if (it.HasNextStaticField() || it.HasNextInstanceField()) { - LOAD_FIELD(field, it.GetMemberIndex(), "first_class_data_definer field_id", - *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) - return field->class_idx_; - } - - if (it.HasNextMethod()) { - LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id", - *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) - return method->class_idx_; - } - - return dex::TypeIndex(DexFile::kDexNoIndex16); -} - -dex::TypeIndex DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, - bool* success) { - const DexFile::AnnotationsDirectoryItem* item = - reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr); - *success = true; - - if (item->fields_size_ != 0) { - DexFile::FieldAnnotationsItem* field_items = (DexFile::FieldAnnotationsItem*) (item + 1); - LOAD_FIELD(field, field_items[0].field_idx_, "first_annotations_dir_definer field_id", - *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) - return field->class_idx_; - } - - if (item->methods_size_ != 0) { - DexFile::MethodAnnotationsItem* method_items = (DexFile::MethodAnnotationsItem*) (item + 1); - LOAD_METHOD(method, method_items[0].method_idx_, "first_annotations_dir_definer method id", - *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) - return method->class_idx_; - } - - if (item->parameters_size_ != 0) { - DexFile::ParameterAnnotationsItem* parameter_items = (DexFile::ParameterAnnotationsItem*) (item + 1); - LOAD_METHOD(method, parameter_items[0].method_idx_, "first_annotations_dir_definer method id", - *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) - return method->class_idx_; - } - - return dex::TypeIndex(DexFile::kDexNoIndex16); -} - -bool DexFileVerifier::CheckInterStringIdItem() { - const DexFile::StringId* item = reinterpret_cast<const DexFile::StringId*>(ptr_); - - // Check the map to make sure it has the right offset->type. - if (!CheckOffsetToTypeMap(item->string_data_off_, DexFile::kDexTypeStringDataItem)) { - return false; - } - - // Check ordering between items. - if (previous_item_ != nullptr) { - const DexFile::StringId* prev_item = reinterpret_cast<const DexFile::StringId*>(previous_item_); - const char* prev_str = dex_file_->GetStringData(*prev_item); - const char* str = dex_file_->GetStringData(*item); - if (UNLIKELY(CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0)) { - ErrorStringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str); - return false; - } - } - - ptr_ += sizeof(DexFile::StringId); - return true; -} - -bool DexFileVerifier::CheckInterTypeIdItem() { - const DexFile::TypeId* item = reinterpret_cast<const DexFile::TypeId*>(ptr_); - - LOAD_STRING(descriptor, item->descriptor_idx_, "inter_type_id_item descriptor_idx") - - // Check that the descriptor is a valid type. - if (UNLIKELY(!IsValidDescriptor(descriptor))) { - ErrorStringPrintf("Invalid type descriptor: '%s'", descriptor); - return false; - } - - // Check ordering between items. - if (previous_item_ != nullptr) { - const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_); - if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) { - ErrorStringPrintf("Out-of-order type_ids: %x then %x", - prev_item->descriptor_idx_.index_, - item->descriptor_idx_.index_); - return false; - } - } - - ptr_ += sizeof(DexFile::TypeId); - return true; -} - -bool DexFileVerifier::CheckInterProtoIdItem() { - const DexFile::ProtoId* item = reinterpret_cast<const DexFile::ProtoId*>(ptr_); - - LOAD_STRING(shorty, item->shorty_idx_, "inter_proto_id_item shorty_idx") - - if (item->parameters_off_ != 0 && - !CheckOffsetToTypeMap(item->parameters_off_, DexFile::kDexTypeTypeList)) { - return false; - } - - // Check that return type is representable as a uint16_t; - if (UNLIKELY(!IsValidOrNoTypeId(item->return_type_idx_.index_, item->pad_))) { - ErrorStringPrintf("proto with return type idx outside uint16_t range '%x:%x'", - item->pad_, item->return_type_idx_.index_); - return false; - } - // Check the return type and advance the shorty. - LOAD_STRING_BY_TYPE(return_type, item->return_type_idx_, "inter_proto_id_item return_type_idx") - if (!CheckShortyDescriptorMatch(*shorty, return_type, true)) { - return false; - } - shorty++; - - DexFileParameterIterator it(*dex_file_, *item); - while (it.HasNext() && *shorty != '\0') { - if (!CheckIndex(it.GetTypeIdx().index_, - dex_file_->NumTypeIds(), - "inter_proto_id_item shorty type_idx")) { - return false; - } - const char* descriptor = it.GetDescriptor(); - if (!CheckShortyDescriptorMatch(*shorty, descriptor, false)) { - return false; - } - it.Next(); - shorty++; - } - if (UNLIKELY(it.HasNext() || *shorty != '\0')) { - ErrorStringPrintf("Mismatched length for parameters and shorty"); - return false; - } - - // Check ordering between items. This relies on type_ids being in order. - if (previous_item_ != nullptr) { - const DexFile::ProtoId* prev = reinterpret_cast<const DexFile::ProtoId*>(previous_item_); - if (UNLIKELY(prev->return_type_idx_ > item->return_type_idx_)) { - ErrorStringPrintf("Out-of-order proto_id return types"); - return false; - } else if (prev->return_type_idx_ == item->return_type_idx_) { - DexFileParameterIterator curr_it(*dex_file_, *item); - DexFileParameterIterator prev_it(*dex_file_, *prev); - - while (curr_it.HasNext() && prev_it.HasNext()) { - dex::TypeIndex prev_idx = prev_it.GetTypeIdx(); - dex::TypeIndex curr_idx = curr_it.GetTypeIdx(); - DCHECK_NE(prev_idx, dex::TypeIndex(DexFile::kDexNoIndex16)); - DCHECK_NE(curr_idx, dex::TypeIndex(DexFile::kDexNoIndex16)); - - if (prev_idx < curr_idx) { - break; - } else if (UNLIKELY(prev_idx > curr_idx)) { - ErrorStringPrintf("Out-of-order proto_id arguments"); - return false; - } - - prev_it.Next(); - curr_it.Next(); - } - if (!curr_it.HasNext()) { - // Either a duplicate ProtoId or a ProtoId with a shorter argument list follows - // a ProtoId with a longer one. Both cases are forbidden by the specification. - ErrorStringPrintf("Out-of-order proto_id arguments"); - return false; - } - } - } - - ptr_ += sizeof(DexFile::ProtoId); - return true; -} - -bool DexFileVerifier::CheckInterFieldIdItem() { - const DexFile::FieldId* item = reinterpret_cast<const DexFile::FieldId*>(ptr_); - - // Check that the class descriptor is valid. - LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_field_id_item class_idx") - if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) { - ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor); - return false; - } - - // Check that the type descriptor is a valid field name. - LOAD_STRING_BY_TYPE(type_descriptor, item->type_idx_, "inter_field_id_item type_idx") - if (UNLIKELY(!IsValidDescriptor(type_descriptor) || type_descriptor[0] == 'V')) { - ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", type_descriptor); - return false; - } - - // Check that the name is valid. - LOAD_STRING(descriptor, item->name_idx_, "inter_field_id_item name_idx") - if (UNLIKELY(!IsValidMemberName(descriptor))) { - ErrorStringPrintf("Invalid field name: '%s'", descriptor); - return false; - } - - // Check ordering between items. This relies on the other sections being in order. - if (previous_item_ != nullptr) { - const DexFile::FieldId* prev_item = reinterpret_cast<const DexFile::FieldId*>(previous_item_); - if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) { - ErrorStringPrintf("Out-of-order field_ids"); - return false; - } else if (prev_item->class_idx_ == item->class_idx_) { - if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) { - ErrorStringPrintf("Out-of-order field_ids"); - return false; - } else if (prev_item->name_idx_ == item->name_idx_) { - if (UNLIKELY(prev_item->type_idx_ >= item->type_idx_)) { - ErrorStringPrintf("Out-of-order field_ids"); - return false; - } - } - } - } - - ptr_ += sizeof(DexFile::FieldId); - return true; -} - -bool DexFileVerifier::CheckInterMethodIdItem() { - const DexFile::MethodId* item = reinterpret_cast<const DexFile::MethodId*>(ptr_); - - // Check that the class descriptor is a valid reference name. - LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_method_id_item class_idx") - if (UNLIKELY(!IsValidDescriptor(class_descriptor) || (class_descriptor[0] != 'L' && - class_descriptor[0] != '['))) { - ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor); - return false; - } - - // Check that the name is valid. - LOAD_STRING(descriptor, item->name_idx_, "inter_method_id_item name_idx") - if (UNLIKELY(!IsValidMemberName(descriptor))) { - ErrorStringPrintf("Invalid method name: '%s'", descriptor); - return false; - } - - // Check that the proto id is valid. - if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(), - "inter_method_id_item proto_idx"))) { - return false; - } - - // Check ordering between items. This relies on the other sections being in order. - if (previous_item_ != nullptr) { - const DexFile::MethodId* prev_item = reinterpret_cast<const DexFile::MethodId*>(previous_item_); - if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) { - ErrorStringPrintf("Out-of-order method_ids"); - return false; - } else if (prev_item->class_idx_ == item->class_idx_) { - if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) { - ErrorStringPrintf("Out-of-order method_ids"); - return false; - } else if (prev_item->name_idx_ == item->name_idx_) { - if (UNLIKELY(prev_item->proto_idx_ >= item->proto_idx_)) { - ErrorStringPrintf("Out-of-order method_ids"); - return false; - } - } - } - } - - ptr_ += sizeof(DexFile::MethodId); - return true; -} - -bool DexFileVerifier::CheckInterClassDefItem() { - const DexFile::ClassDef* item = reinterpret_cast<const DexFile::ClassDef*>(ptr_); - - // Check that class_idx_ is representable as a uint16_t; - if (UNLIKELY(!IsValidTypeId(item->class_idx_.index_, item->pad1_))) { - ErrorStringPrintf("class with type idx outside uint16_t range '%x:%x'", item->pad1_, - item->class_idx_.index_); - return false; - } - // Check that superclass_idx_ is representable as a uint16_t; - if (UNLIKELY(!IsValidOrNoTypeId(item->superclass_idx_.index_, item->pad2_))) { - ErrorStringPrintf("class with superclass type idx outside uint16_t range '%x:%x'", item->pad2_, - item->superclass_idx_.index_); - return false; - } - // Check for duplicate class def. - if (defined_classes_.find(item->class_idx_) != defined_classes_.end()) { - ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_.index_); - return false; - } - defined_classes_.insert(item->class_idx_); - - LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_class_def_item class_idx") - if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) { - ErrorStringPrintf("Invalid class descriptor: '%s'", class_descriptor); - return false; - } - - // Only allow non-runtime modifiers. - if ((item->access_flags_ & ~kAccJavaFlagsMask) != 0) { - ErrorStringPrintf("Invalid class flags: '%d'", item->access_flags_); - return false; - } - - if (item->interfaces_off_ != 0 && - !CheckOffsetToTypeMap(item->interfaces_off_, DexFile::kDexTypeTypeList)) { - return false; - } - if (item->annotations_off_ != 0 && - !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationsDirectoryItem)) { - return false; - } - if (item->class_data_off_ != 0 && - !CheckOffsetToTypeMap(item->class_data_off_, DexFile::kDexTypeClassDataItem)) { - return false; - } - if (item->static_values_off_ != 0 && - !CheckOffsetToTypeMap(item->static_values_off_, DexFile::kDexTypeEncodedArrayItem)) { - return false; - } - - if (item->superclass_idx_.IsValid()) { - if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) { - // Check that a class does not inherit from itself directly (by having - // the same type idx as its super class). - if (UNLIKELY(item->superclass_idx_ == item->class_idx_)) { - ErrorStringPrintf("Class with same type idx as its superclass: '%d'", - item->class_idx_.index_); - return false; - } - - // Check that a class is defined after its super class (if the - // latter is defined in the same Dex file). - const DexFile::ClassDef* superclass_def = dex_file_->FindClassDef(item->superclass_idx_); - if (superclass_def != nullptr) { - // The superclass is defined in this Dex file. - if (superclass_def > item) { - // ClassDef item for super class appearing after the class' ClassDef item. - ErrorStringPrintf("Invalid class definition ordering:" - " class with type idx: '%d' defined before" - " superclass with type idx: '%d'", - item->class_idx_.index_, - item->superclass_idx_.index_); - return false; - } - } - } - - LOAD_STRING_BY_TYPE(superclass_descriptor, item->superclass_idx_, - "inter_class_def_item superclass_idx") - if (UNLIKELY(!IsValidDescriptor(superclass_descriptor) || superclass_descriptor[0] != 'L')) { - ErrorStringPrintf("Invalid superclass: '%s'", superclass_descriptor); - return false; - } - } - - // Check interfaces. - const DexFile::TypeList* interfaces = dex_file_->GetInterfacesList(*item); - if (interfaces != nullptr) { - uint32_t size = interfaces->Size(); - for (uint32_t i = 0; i < size; i++) { - if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) { - // Check that a class does not implement itself directly (by having the - // same type idx as one of its immediate implemented interfaces). - if (UNLIKELY(interfaces->GetTypeItem(i).type_idx_ == item->class_idx_)) { - ErrorStringPrintf("Class with same type idx as implemented interface: '%d'", - item->class_idx_.index_); - return false; - } - - // Check that a class is defined after the interfaces it implements - // (if they are defined in the same Dex file). - const DexFile::ClassDef* interface_def = - dex_file_->FindClassDef(interfaces->GetTypeItem(i).type_idx_); - if (interface_def != nullptr) { - // The interface is defined in this Dex file. - if (interface_def > item) { - // ClassDef item for interface appearing after the class' ClassDef item. - ErrorStringPrintf("Invalid class definition ordering:" - " class with type idx: '%d' defined before" - " implemented interface with type idx: '%d'", - item->class_idx_.index_, - interfaces->GetTypeItem(i).type_idx_.index_); - return false; - } - } - } - - // Ensure that the interface refers to a class (not an array nor a primitive type). - LOAD_STRING_BY_TYPE(inf_descriptor, interfaces->GetTypeItem(i).type_idx_, - "inter_class_def_item interface type_idx") - if (UNLIKELY(!IsValidDescriptor(inf_descriptor) || inf_descriptor[0] != 'L')) { - ErrorStringPrintf("Invalid interface: '%s'", inf_descriptor); - return false; - } - } - - /* - * Ensure that there are no duplicates. This is an O(N^2) test, but in - * practice the number of interfaces implemented by any given class is low. - */ - for (uint32_t i = 1; i < size; i++) { - dex::TypeIndex idx1 = interfaces->GetTypeItem(i).type_idx_; - for (uint32_t j =0; j < i; j++) { - dex::TypeIndex idx2 = interfaces->GetTypeItem(j).type_idx_; - if (UNLIKELY(idx1 == idx2)) { - ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1)); - return false; - } - } - } - } - - // Check that references in class_data_item are to the right class. - if (item->class_data_off_ != 0) { - const uint8_t* data = begin_ + item->class_data_off_; - bool success; - dex::TypeIndex data_definer = FindFirstClassDataDefiner(data, &success); - if (!success) { - return false; - } - if (UNLIKELY((data_definer != item->class_idx_) && - (data_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) { - ErrorStringPrintf("Invalid class_data_item"); - return false; - } - } - - // Check that references in annotations_directory_item are to right class. - if (item->annotations_off_ != 0) { - // annotations_off_ is supposed to be aligned by 4. - if (!IsAlignedParam(item->annotations_off_, 4)) { - ErrorStringPrintf("Invalid annotations_off_, not aligned by 4"); - return false; - } - const uint8_t* data = begin_ + item->annotations_off_; - bool success; - dex::TypeIndex annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success); - if (!success) { - return false; - } - if (UNLIKELY((annotations_definer != item->class_idx_) && - (annotations_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) { - ErrorStringPrintf("Invalid annotations_directory_item"); - return false; - } - } - - ptr_ += sizeof(DexFile::ClassDef); - return true; -} - -bool DexFileVerifier::CheckInterCallSiteIdItem() { - const DexFile::CallSiteIdItem* item = reinterpret_cast<const DexFile::CallSiteIdItem*>(ptr_); - - // Check call site referenced by item is in encoded array section. - if (!CheckOffsetToTypeMap(item->data_off_, DexFile::kDexTypeEncodedArrayItem)) { - ErrorStringPrintf("Invalid offset in CallSideIdItem"); - return false; - } - - CallSiteArrayValueIterator it(*dex_file_, *item); - - // Check Method Handle - if (!it.HasNext() || it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) { - ErrorStringPrintf("CallSiteArray missing method handle"); - return false; - } - - uint32_t handle_index = static_cast<uint32_t>(it.GetJavaValue().i); - if (handle_index >= dex_file_->NumMethodHandles()) { - ErrorStringPrintf("CallSite has bad method handle id: %x", handle_index); - return false; - } - - // Check target method name. - it.Next(); - if (!it.HasNext() || - it.GetValueType() != EncodedArrayValueIterator::ValueType::kString) { - ErrorStringPrintf("CallSiteArray missing target method name"); - return false; - } - - uint32_t name_index = static_cast<uint32_t>(it.GetJavaValue().i); - if (name_index >= dex_file_->NumStringIds()) { - ErrorStringPrintf("CallSite has bad method name id: %x", name_index); - return false; - } - - // Check method type. - it.Next(); - if (!it.HasNext() || - it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodType) { - ErrorStringPrintf("CallSiteArray missing method type"); - return false; - } - - uint32_t proto_index = static_cast<uint32_t>(it.GetJavaValue().i); - if (proto_index >= dex_file_->NumProtoIds()) { - ErrorStringPrintf("CallSite has bad method type: %x", proto_index); - return false; - } - - ptr_ += sizeof(DexFile::CallSiteIdItem); - return true; -} - -bool DexFileVerifier::CheckInterMethodHandleItem() { - const DexFile::MethodHandleItem* item = reinterpret_cast<const DexFile::MethodHandleItem*>(ptr_); - - DexFile::MethodHandleType method_handle_type = - static_cast<DexFile::MethodHandleType>(item->method_handle_type_); - if (method_handle_type > DexFile::MethodHandleType::kLast) { - ErrorStringPrintf("Bad method handle type %x", item->method_handle_type_); - return false; - } - - uint32_t index = item->field_or_method_idx_; - switch (method_handle_type) { - case DexFile::MethodHandleType::kStaticPut: - case DexFile::MethodHandleType::kStaticGet: - case DexFile::MethodHandleType::kInstancePut: - case DexFile::MethodHandleType::kInstanceGet: { - LOAD_FIELD(field, index, "method_handle_item field_idx", return false); - break; - } - case DexFile::MethodHandleType::kInvokeStatic: - case DexFile::MethodHandleType::kInvokeInstance: - case DexFile::MethodHandleType::kInvokeConstructor: - case DexFile::MethodHandleType::kInvokeDirect: - case DexFile::MethodHandleType::kInvokeInterface: { - LOAD_METHOD(method, index, "method_handle_item method_idx", return false); - break; - } - } - - ptr_ += sizeof(DexFile::MethodHandleItem); - return true; -} - -bool DexFileVerifier::CheckInterAnnotationSetRefList() { - const DexFile::AnnotationSetRefList* list = - reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_); - const DexFile::AnnotationSetRefItem* item = list->list_; - uint32_t count = list->size_; - - while (count--) { - if (item->annotations_off_ != 0 && - !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) { - return false; - } - item++; - } - - ptr_ = reinterpret_cast<const uint8_t*>(item); - return true; -} - -bool DexFileVerifier::CheckInterAnnotationSetItem() { - const DexFile::AnnotationSetItem* set = reinterpret_cast<const DexFile::AnnotationSetItem*>(ptr_); - const uint32_t* offsets = set->entries_; - uint32_t count = set->size_; - uint32_t last_idx = 0; - - for (uint32_t i = 0; i < count; i++) { - if (*offsets != 0 && !CheckOffsetToTypeMap(*offsets, DexFile::kDexTypeAnnotationItem)) { - return false; - } - - // Get the annotation from the offset and the type index for the annotation. - const DexFile::AnnotationItem* annotation = - reinterpret_cast<const DexFile::AnnotationItem*>(begin_ + *offsets); - const uint8_t* data = annotation->annotation_; - DECODE_UNSIGNED_CHECKED_FROM(data, idx); - - if (UNLIKELY(last_idx >= idx && i != 0)) { - ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx); - return false; - } - - last_idx = idx; - offsets++; - } - - ptr_ = reinterpret_cast<const uint8_t*>(offsets); - return true; -} - -bool DexFileVerifier::CheckInterClassDataItem() { - ClassDataItemIterator it(*dex_file_, ptr_); - bool success; - dex::TypeIndex defining_class = FindFirstClassDataDefiner(ptr_, &success); - if (!success) { - return false; - } - - for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) { - LOAD_FIELD(field, it.GetMemberIndex(), "inter_class_data_item field_id", return false) - if (UNLIKELY(field->class_idx_ != defining_class)) { - ErrorStringPrintf("Mismatched defining class for class_data_item field"); - return false; - } - } - for (; it.HasNextMethod(); it.Next()) { - uint32_t code_off = it.GetMethodCodeItemOffset(); - if (code_off != 0 && !CheckOffsetToTypeMap(code_off, DexFile::kDexTypeCodeItem)) { - return false; - } - LOAD_METHOD(method, it.GetMemberIndex(), "inter_class_data_item method_id", return false) - if (UNLIKELY(method->class_idx_ != defining_class)) { - ErrorStringPrintf("Mismatched defining class for class_data_item method"); - return false; - } - } - - ptr_ = it.EndDataPointer(); - return true; -} - -bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { - const DexFile::AnnotationsDirectoryItem* item = - reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_); - bool success; - dex::TypeIndex defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success); - if (!success) { - return false; - } - - if (item->class_annotations_off_ != 0 && - !CheckOffsetToTypeMap(item->class_annotations_off_, DexFile::kDexTypeAnnotationSetItem)) { - return false; - } - - // Field annotations follow immediately after the annotations directory. - const DexFile::FieldAnnotationsItem* field_item = - reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1); - uint32_t field_count = item->fields_size_; - for (uint32_t i = 0; i < field_count; i++) { - LOAD_FIELD(field, field_item->field_idx_, "inter_annotations_directory_item field_id", - return false) - if (UNLIKELY(field->class_idx_ != defining_class)) { - ErrorStringPrintf("Mismatched defining class for field_annotation"); - return false; - } - if (!CheckOffsetToTypeMap(field_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) { - return false; - } - field_item++; - } - - // Method annotations follow immediately after field annotations. - const DexFile::MethodAnnotationsItem* method_item = - reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item); - uint32_t method_count = item->methods_size_; - for (uint32_t i = 0; i < method_count; i++) { - LOAD_METHOD(method, method_item->method_idx_, "inter_annotations_directory_item method_id", - return false) - if (UNLIKELY(method->class_idx_ != defining_class)) { - ErrorStringPrintf("Mismatched defining class for method_annotation"); - return false; - } - if (!CheckOffsetToTypeMap(method_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) { - return false; - } - method_item++; - } - - // Parameter annotations follow immediately after method annotations. - const DexFile::ParameterAnnotationsItem* parameter_item = - reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item); - uint32_t parameter_count = item->parameters_size_; - for (uint32_t i = 0; i < parameter_count; i++) { - LOAD_METHOD(parameter_method, parameter_item->method_idx_, - "inter_annotations_directory_item parameter method_id", return false) - if (UNLIKELY(parameter_method->class_idx_ != defining_class)) { - ErrorStringPrintf("Mismatched defining class for parameter_annotation"); - return false; - } - if (!CheckOffsetToTypeMap(parameter_item->annotations_off_, - DexFile::kDexTypeAnnotationSetRefList)) { - return false; - } - parameter_item++; - } - - ptr_ = reinterpret_cast<const uint8_t*>(parameter_item); - return true; -} - -bool DexFileVerifier::CheckInterSectionIterate(size_t offset, - uint32_t count, - DexFile::MapItemType type) { - // Get the right alignment mask for the type of section. - size_t alignment_mask; - switch (type) { - case DexFile::kDexTypeClassDataItem: - alignment_mask = sizeof(uint8_t) - 1; - break; - default: - alignment_mask = sizeof(uint32_t) - 1; - break; - } - - // Iterate through the items in the section. - previous_item_ = nullptr; - for (uint32_t i = 0; i < count; i++) { - uint32_t new_offset = (offset + alignment_mask) & ~alignment_mask; - ptr_ = begin_ + new_offset; - const uint8_t* prev_ptr = ptr_; - - if (MapTypeToBitMask(type) == 0) { - ErrorStringPrintf("Unknown map item type %x", type); - return false; - } - - // Check depending on the section type. - switch (type) { - case DexFile::kDexTypeHeaderItem: - case DexFile::kDexTypeMapList: - case DexFile::kDexTypeTypeList: - case DexFile::kDexTypeCodeItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - break; - case DexFile::kDexTypeStringIdItem: { - if (!CheckInterStringIdItem()) { - return false; - } - break; - } - case DexFile::kDexTypeTypeIdItem: { - if (!CheckInterTypeIdItem()) { - return false; - } - break; - } - case DexFile::kDexTypeProtoIdItem: { - if (!CheckInterProtoIdItem()) { - return false; - } - break; - } - case DexFile::kDexTypeFieldIdItem: { - if (!CheckInterFieldIdItem()) { - return false; - } - break; - } - case DexFile::kDexTypeMethodIdItem: { - if (!CheckInterMethodIdItem()) { - return false; - } - break; - } - case DexFile::kDexTypeClassDefItem: { - // There shouldn't be more class definitions than type ids allow. - // This check should be redundant, since there are checks that the - // class_idx_ is within range and that there is only one definition - // for a given type id. - if (i > kTypeIdLimit) { - ErrorStringPrintf("Too many class definition items"); - return false; - } - if (!CheckInterClassDefItem()) { - return false; - } - break; - } - case DexFile::kDexTypeCallSiteIdItem: { - if (!CheckInterCallSiteIdItem()) { - return false; - } - break; - } - case DexFile::kDexTypeMethodHandleItem: { - if (!CheckInterMethodHandleItem()) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationSetRefList: { - if (!CheckInterAnnotationSetRefList()) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationSetItem: { - if (!CheckInterAnnotationSetItem()) { - return false; - } - break; - } - case DexFile::kDexTypeClassDataItem: { - // There shouldn't be more class data than type ids allow. - // This check should be redundant, since there are checks that the - // class_idx_ is within range and that there is only one definition - // for a given type id. - if (i > kTypeIdLimit) { - ErrorStringPrintf("Too many class data items"); - return false; - } - if (!CheckInterClassDataItem()) { - return false; - } - break; - } - case DexFile::kDexTypeAnnotationsDirectoryItem: { - if (!CheckInterAnnotationsDirectoryItem()) { - return false; - } - break; - } - } - - previous_item_ = prev_ptr; - offset = ptr_ - begin_; - } - - return true; -} - -bool DexFileVerifier::CheckInterSection() { - const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_); - const DexFile::MapItem* item = map->list_; - uint32_t count = map->size_; - - // Cross check the items listed in the map. - while (count--) { - uint32_t section_offset = item->offset_; - uint32_t section_count = item->size_; - DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_); - bool found = false; - - switch (type) { - case DexFile::kDexTypeHeaderItem: - case DexFile::kDexTypeMapList: - case DexFile::kDexTypeTypeList: - case DexFile::kDexTypeCodeItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - found = true; - break; - case DexFile::kDexTypeStringIdItem: - case DexFile::kDexTypeTypeIdItem: - case DexFile::kDexTypeProtoIdItem: - case DexFile::kDexTypeFieldIdItem: - case DexFile::kDexTypeMethodIdItem: - case DexFile::kDexTypeClassDefItem: - case DexFile::kDexTypeCallSiteIdItem: - case DexFile::kDexTypeMethodHandleItem: - case DexFile::kDexTypeAnnotationSetRefList: - case DexFile::kDexTypeAnnotationSetItem: - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeAnnotationsDirectoryItem: { - if (!CheckInterSectionIterate(section_offset, section_count, type)) { - return false; - } - found = true; - break; - } - } - - if (!found) { - ErrorStringPrintf("Unknown map item type %x", item->type_); - return false; - } - - item++; - } - - return true; -} - -bool DexFileVerifier::Verify() { - // Check the header. - if (!CheckHeader()) { - return false; - } - - // Check the map section. - if (!CheckMap()) { - return false; - } - - // Check structure within remaining sections. - if (!CheckIntraSection()) { - return false; - } - - // Check references from one section to another. - if (!CheckInterSection()) { - return false; - } - - return true; -} - -void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - DCHECK(failure_reason_.empty()) << failure_reason_; - failure_reason_ = StringPrintf("Failure to verify dex file '%s': ", location_); - StringAppendV(&failure_reason_, fmt, ap); - va_end(ap); -} - -// Fields and methods may have only one of public/protected/private. -static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) { - size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) + - (((flags & kAccProtected) == 0) ? 0 : 1) + - (((flags & kAccPrivate) == 0) ? 0 : 1); - return count <= 1; -} - -// Helper functions to retrieve names from the dex file. We do not want to rely on DexFile -// functionality, as we're still verifying the dex file. begin and header correspond to the -// underscored variants in the DexFileVerifier. - -static std::string GetStringOrError(const uint8_t* const begin, - const DexFile::Header* const header, - dex::StringIndex string_idx) { - // The `string_idx` is not guaranteed to be valid yet. - if (header->string_ids_size_ <= string_idx.index_) { - return "(error)"; - } - - const DexFile::StringId* string_id = - reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) - + string_idx.index_; - - // Assume that the data is OK at this point. String data has been checked at this point. - - const uint8_t* ptr = begin + string_id->string_data_off_; - uint32_t dummy; - if (!DecodeUnsignedLeb128Checked(&ptr, begin + header->file_size_, &dummy)) { - return "(error)"; - } - return reinterpret_cast<const char*>(ptr); -} - -static std::string GetClassOrError(const uint8_t* const begin, - const DexFile::Header* const header, - dex::TypeIndex class_idx) { - // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and - // it has already been checked in `DexFileVerifier::CheckClassDataItemField()` - // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match - // a valid defining class. - CHECK_LT(class_idx.index_, header->type_ids_size_); - - const DexFile::TypeId* type_id = - reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx.index_; - - // Assume that the data is OK at this point. Type id offsets have been checked at this point. - - return GetStringOrError(begin, header, type_id->descriptor_idx_); -} - -static std::string GetFieldDescriptionOrError(const uint8_t* const begin, - const DexFile::Header* const header, - uint32_t idx) { - // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemField()`. - CHECK_LT(idx, header->field_ids_size_); - - const DexFile::FieldId* field_id = - reinterpret_cast<const DexFile::FieldId*>(begin + header->field_ids_off_) + idx; - - // Assume that the data is OK at this point. Field id offsets have been checked at this point. - - std::string class_name = GetClassOrError(begin, header, field_id->class_idx_); - std::string field_name = GetStringOrError(begin, header, field_id->name_idx_); - - return class_name + "." + field_name; -} - -static std::string GetMethodDescriptionOrError(const uint8_t* const begin, - const DexFile::Header* const header, - uint32_t idx) { - // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemMethod()`. - CHECK_LT(idx, header->method_ids_size_); - - const DexFile::MethodId* method_id = - reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + idx; - - // Assume that the data is OK at this point. Method id offsets have been checked at this point. - - std::string class_name = GetClassOrError(begin, header, method_id->class_idx_); - std::string method_name = GetStringOrError(begin, header, method_id->name_idx_); - - return class_name + "." + method_name; -} - -bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, - uint32_t field_access_flags, - uint32_t class_access_flags, - std::string* error_msg) { - // Generally sort out >16-bit flags. - if ((field_access_flags & ~kAccJavaFlagsMask) != 0) { - *error_msg = StringPrintf("Bad field access_flags for %s: %x(%s)", - GetFieldDescriptionOrError(begin_, header_, idx).c_str(), - field_access_flags, - PrettyJavaAccessFlags(field_access_flags).c_str()); - return false; - } - - // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored. - constexpr uint32_t kFieldAccessFlags = kAccPublic | - kAccPrivate | - kAccProtected | - kAccStatic | - kAccFinal | - kAccVolatile | - kAccTransient | - kAccSynthetic | - kAccEnum; - - // Fields may have only one of public/protected/final. - if (!CheckAtMostOneOfPublicProtectedPrivate(field_access_flags)) { - *error_msg = StringPrintf("Field may have only one of public/protected/private, %s: %x(%s)", - GetFieldDescriptionOrError(begin_, header_, idx).c_str(), - field_access_flags, - PrettyJavaAccessFlags(field_access_flags).c_str()); - return false; - } - - // Interfaces have a pretty restricted list. - if ((class_access_flags & kAccInterface) != 0) { - // Interface fields must be public final static. - constexpr uint32_t kPublicFinalStatic = kAccPublic | kAccFinal | kAccStatic; - if ((field_access_flags & kPublicFinalStatic) != kPublicFinalStatic) { - *error_msg = StringPrintf("Interface field is not public final static, %s: %x(%s)", - GetFieldDescriptionOrError(begin_, header_, idx).c_str(), - field_access_flags, - PrettyJavaAccessFlags(field_access_flags).c_str()); - if (dex_file_->SupportsDefaultMethods()) { - return false; - } else { - // Allow in older versions, but warn. - LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: " - << *error_msg; - } - } - // Interface fields may be synthetic, but may not have other flags. - constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic); - if ((field_access_flags & kFieldAccessFlags & kDisallowed) != 0) { - *error_msg = StringPrintf("Interface field has disallowed flag, %s: %x(%s)", - GetFieldDescriptionOrError(begin_, header_, idx).c_str(), - field_access_flags, - PrettyJavaAccessFlags(field_access_flags).c_str()); - if (dex_file_->SupportsDefaultMethods()) { - return false; - } else { - // Allow in older versions, but warn. - LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: " - << *error_msg; - } - } - return true; - } - - // Volatile fields may not be final. - constexpr uint32_t kVolatileFinal = kAccVolatile | kAccFinal; - if ((field_access_flags & kVolatileFinal) == kVolatileFinal) { - *error_msg = StringPrintf("Fields may not be volatile and final: %s", - GetFieldDescriptionOrError(begin_, header_, idx).c_str()); - return false; - } - - return true; -} - -bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, - uint32_t method_access_flags, - uint32_t class_access_flags, - uint32_t constructor_flags_by_name, - bool has_code, - bool expect_direct, - std::string* error_msg) { - // Generally sort out >16-bit flags, except dex knows Constructor and DeclaredSynchronized. - constexpr uint32_t kAllMethodFlags = - kAccJavaFlagsMask | kAccConstructor | kAccDeclaredSynchronized; - if ((method_access_flags & ~kAllMethodFlags) != 0) { - *error_msg = StringPrintf("Bad method access_flags for %s: %x", - GetMethodDescriptionOrError(begin_, header_, method_index).c_str(), - method_access_flags); - return false; - } - - // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored. - constexpr uint32_t kMethodAccessFlags = kAccPublic | - kAccPrivate | - kAccProtected | - kAccStatic | - kAccFinal | - kAccSynthetic | - kAccSynchronized | - kAccBridge | - kAccVarargs | - kAccNative | - kAccAbstract | - kAccStrict; - - // Methods may have only one of public/protected/final. - if (!CheckAtMostOneOfPublicProtectedPrivate(method_access_flags)) { - *error_msg = StringPrintf("Method may have only one of public/protected/private, %s: %x", - GetMethodDescriptionOrError(begin_, header_, method_index).c_str(), - method_access_flags); - return false; - } - - constexpr uint32_t kConstructorFlags = kAccStatic | kAccConstructor; - const bool is_constructor_by_name = (constructor_flags_by_name & kConstructorFlags) != 0; - const bool is_clinit_by_name = constructor_flags_by_name == kConstructorFlags; - - // Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce - // the reverse for backwards compatibility reasons. - if (((method_access_flags & kAccConstructor) != 0) && !is_constructor_by_name) { - *error_msg = - StringPrintf("Method %" PRIu32 "(%s) is marked constructor, but doesn't match name", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - return false; - } - - if (is_constructor_by_name) { - // Check that the static constructor (= static initializer) is named "<clinit>" and that the - // instance constructor is called "<init>". - bool is_static = (method_access_flags & kAccStatic) != 0; - if (is_static ^ is_clinit_by_name) { - *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (dex_file_->SupportsDefaultMethods()) { - return false; - } else { - // Allow in older versions, but warn. - LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: " - << *error_msg; - } - } - } - - // Check that static and private methods, as well as constructors, are in the direct methods list, - // and other methods in the virtual methods list. - bool is_direct = ((method_access_flags & (kAccStatic | kAccPrivate)) != 0) || - is_constructor_by_name; - if (is_direct != expect_direct) { - *error_msg = StringPrintf("Direct/virtual method %" PRIu32 "(%s) not in expected list %d", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str(), - expect_direct); - return false; - } - - // From here on out it is easier to mask out the bits we're supposed to ignore. - method_access_flags &= kMethodAccessFlags; - - // Interfaces are special. - if ((class_access_flags & kAccInterface) != 0) { - // Non-static interface methods must be public or private. - uint32_t desired_flags = (kAccPublic | kAccStatic); - if (dex_file_->SupportsDefaultMethods()) { - desired_flags |= kAccPrivate; - } - if ((method_access_flags & desired_flags) == 0) { - *error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (dex_file_->SupportsDefaultMethods()) { - return false; - } else { - // Allow in older versions, but warn. - LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: " - << *error_msg; - } - } - } - - // If there aren't any instructions, make sure that's expected. - if (!has_code) { - // Only native or abstract methods may not have code. - if ((method_access_flags & (kAccNative | kAccAbstract)) == 0) { - *error_msg = StringPrintf("Method %" PRIu32 "(%s) has no code, but is not marked native or " - "abstract", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - return false; - } - // Constructors must always have code. - if (is_constructor_by_name) { - *error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (dex_file_->SupportsDefaultMethods()) { - return false; - } else { - // Allow in older versions, but warn. - LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: " - << *error_msg; - } - } - if ((method_access_flags & kAccAbstract) != 0) { - // Abstract methods are not allowed to have the following flags. - constexpr uint32_t kForbidden = - kAccPrivate | kAccStatic | kAccFinal | kAccNative | kAccStrict | kAccSynchronized; - if ((method_access_flags & kForbidden) != 0) { - *error_msg = StringPrintf("Abstract method %" PRIu32 "(%s) has disallowed access flags %x", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str(), - method_access_flags); - return false; - } - // Abstract methods should be in an abstract class or interface. - if ((class_access_flags & (kAccInterface | kAccAbstract)) == 0) { - LOG(WARNING) << "Method " << GetMethodDescriptionOrError(begin_, header_, method_index) - << " is abstract, but the declaring class is neither abstract nor an " - << "interface in dex file " - << dex_file_->GetLocation(); - } - } - // Interfaces are special. - if ((class_access_flags & kAccInterface) != 0) { - // Interface methods without code must be abstract. - if ((method_access_flags & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) { - *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (dex_file_->SupportsDefaultMethods()) { - return false; - } else { - // Allow in older versions, but warn. - LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: " - << *error_msg; - } - } - // At this point, we know the method is public and abstract. This means that all the checks - // for invalid combinations above applies. In addition, interface methods must not be - // protected. This is caught by the check for only-one-of-public-protected-private. - } - return true; - } - - // When there's code, the method must not be native or abstract. - if ((method_access_flags & (kAccNative | kAccAbstract)) != 0) { - *error_msg = StringPrintf("Method %" PRIu32 "(%s) has code, but is marked native or abstract", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - return false; - } - - // Instance constructors must not be synchronized and a few other flags. - if (constructor_flags_by_name == kAccConstructor) { - static constexpr uint32_t kInitAllowed = - kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic; - if ((method_access_flags & ~kInitAllowed) != 0) { - *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) flagged inappropriately %x", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str(), - method_access_flags); - return false; - } - } - - return true; -} - -bool DexFileVerifier::CheckConstructorProperties( - uint32_t method_index, - uint32_t constructor_flags) { - DCHECK(constructor_flags == kAccConstructor || - constructor_flags == (kAccConstructor | kAccStatic)); - - // Check signature matches expectations. - const DexFile::MethodId* const method_id = CheckLoadMethodId(method_index, - "Bad <init>/<clinit> method id"); - if (method_id == nullptr) { - return false; - } - - // Check the ProtoId for the corresponding method. - // - // TODO(oth): the error message here is to satisfy the MethodId test - // in the DexFileVerifierTest. The test is checking that the error - // contains this string if the index is out of range. - const DexFile::ProtoId* const proto_id = CheckLoadProtoId(method_id->proto_idx_, - "inter_method_id_item proto_idx"); - if (proto_id == nullptr) { - return false; - } - - Signature signature = dex_file_->GetMethodSignature(*method_id); - if (constructor_flags == (kAccStatic | kAccConstructor)) { - if (!signature.IsVoid() || signature.GetNumberOfParameters() != 0) { - ErrorStringPrintf("<clinit> must have descriptor ()V"); - return false; - } - } else if (!signature.IsVoid()) { - ErrorStringPrintf("Constructor %u(%s) must be void", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - return false; - } - - return true; -} - -} // namespace art diff --git a/runtime/dex/dex_file_verifier.h b/runtime/dex/dex_file_verifier.h deleted file mode 100644 index 6cb5d4c629..0000000000 --- a/runtime/dex/dex_file_verifier.h +++ /dev/null @@ -1,257 +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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_ -#define ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_ - -#include <unordered_set> - -#include "base/allocator.h" -#include "base/hash_map.h" -#include "dex_file.h" -#include "dex_file_types.h" -#include "safe_map.h" - -namespace art { - -class DexFileVerifier { - public: - static bool Verify(const DexFile* dex_file, - const uint8_t* begin, - size_t size, - const char* location, - bool verify_checksum, - std::string* error_msg); - - const std::string& FailureReason() const { - return failure_reason_; - } - - private: - DexFileVerifier(const DexFile* dex_file, - const uint8_t* begin, - size_t size, - const char* location, - bool verify_checksum) - : dex_file_(dex_file), - begin_(begin), - size_(size), - location_(location), - verify_checksum_(verify_checksum), - header_(&dex_file->GetHeader()), - ptr_(nullptr), - previous_item_(nullptr) { - } - - bool Verify(); - - bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type); - bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label); - // Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If - // successful, the ptr will be moved forward the amount covered by the list. - bool CheckList(size_t element_size, const char* label, const uint8_t* *ptr); - // Checks whether the offset is zero (when size is zero) or that the offset falls within the area - // claimed by the file. - bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, size_t alignment, const char* label); - // Checks whether the size is less than the limit. - bool CheckSizeLimit(uint32_t size, uint32_t limit, const char* label); - bool CheckIndex(uint32_t field, uint32_t limit, const char* label); - - bool CheckHeader(); - bool CheckMap(); - - uint32_t ReadUnsignedLittleEndian(uint32_t size); - bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item, - uint32_t* handler_offsets, uint32_t handlers_size); - bool CheckClassDataItemField(uint32_t idx, - uint32_t access_flags, - uint32_t class_access_flags, - dex::TypeIndex class_type_index, - bool expect_static); - bool CheckClassDataItemMethod(uint32_t idx, - uint32_t access_flags, - uint32_t class_access_flags, - dex::TypeIndex class_type_index, - uint32_t code_offset, - std::unordered_set<uint32_t>* direct_method_indexes, - bool expect_direct); - bool CheckOrderAndGetClassDef(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def); - bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def); - - bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type); - bool CheckEncodedValue(); - bool CheckEncodedArray(); - bool CheckEncodedAnnotation(); - - bool CheckIntraClassDataItem(); - // Check all fields of the given type from the given iterator. Load the class data from the first - // field, if necessary (and return it), or use the given values. - template <bool kStatic> - bool CheckIntraClassDataItemFields(ClassDataItemIterator* it, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def); - // Check all methods of the given type from the given iterator. Load the class data from the first - // method, if necessary (and return it), or use the given values. - template <bool kDirect> - bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it, - std::unordered_set<uint32_t>* direct_method_indexes, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def); - - bool CheckIntraCodeItem(); - bool CheckIntraStringDataItem(); - bool CheckIntraDebugInfoItem(); - bool CheckIntraAnnotationItem(); - bool CheckIntraAnnotationsDirectoryItem(); - - bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraSection(); - - bool CheckOffsetToTypeMap(size_t offset, uint16_t type); - - // Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an - // additional out parameter to signal any errors loading an index. - dex::TypeIndex FindFirstClassDataDefiner(const uint8_t* ptr, bool* success); - dex::TypeIndex FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success); - - bool CheckInterStringIdItem(); - bool CheckInterTypeIdItem(); - bool CheckInterProtoIdItem(); - bool CheckInterFieldIdItem(); - bool CheckInterMethodIdItem(); - bool CheckInterClassDefItem(); - bool CheckInterCallSiteIdItem(); - bool CheckInterMethodHandleItem(); - bool CheckInterAnnotationSetRefList(); - bool CheckInterAnnotationSetItem(); - bool CheckInterClassDataItem(); - bool CheckInterAnnotationsDirectoryItem(); - - bool CheckInterSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckInterSection(); - - // Load a string by (type) index. Checks whether the index is in bounds, printing the error if - // not. If there is an error, null is returned. - const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt); - const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt); - - // Load a field/method/proto Id by index. Checks whether the index is in bounds, printing the - // error if not. If there is an error, null is returned. - const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt); - const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt); - const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt); - - void ErrorStringPrintf(const char* fmt, ...) - __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; - bool FailureReasonIsSet() const { return failure_reason_.size() != 0; } - - // Retrieve class index and class def from the given member. index is the member index, which is - // taken as either a field or a method index (as designated by is_field). The result, if the - // member and declaring class could be found, is stored in class_type_index and class_def. - // This is an expensive lookup, as we have to find the class def by type index, which is a - // linear search. The output values should thus be cached by the caller. - bool FindClassIndexAndDef(uint32_t index, - bool is_field, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** output_class_def); - - // Check validity of the given access flags, interpreted for a field in the context of a class - // with the given second access flags. - bool CheckFieldAccessFlags(uint32_t idx, - uint32_t field_access_flags, - uint32_t class_access_flags, - std::string* error_message); - - // Check validity of the given method and access flags, in the context of a class with the given - // second access flags. - bool CheckMethodAccessFlags(uint32_t method_index, - uint32_t method_access_flags, - uint32_t class_access_flags, - uint32_t constructor_flags_by_name, - bool has_code, - bool expect_direct, - std::string* error_message); - - // Check validity of given method if it's a constructor or class initializer. - bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags); - - const DexFile* const dex_file_; - const uint8_t* const begin_; - const size_t size_; - const char* const location_; - const bool verify_checksum_; - const DexFile::Header* const header_; - - struct OffsetTypeMapEmptyFn { - // Make a hash map slot empty by making the offset 0. Offset 0 is a valid dex file offset that - // is in the offset of the dex file header. However, we only store data section items in the - // map, and these are after the header. - void MakeEmpty(std::pair<uint32_t, uint16_t>& pair) const { - pair.first = 0u; - } - // Check if a hash map slot is empty. - bool IsEmpty(const std::pair<uint32_t, uint16_t>& pair) const { - return pair.first == 0; - } - }; - struct OffsetTypeMapHashCompareFn { - // Hash function for offset. - size_t operator()(const uint32_t key) const { - return key; - } - // std::equal function for offset. - bool operator()(const uint32_t a, const uint32_t b) const { - return a == b; - } - }; - // Map from offset to dex file type, HashMap for performance reasons. - template<class Key, - class T, - class EmptyFn, - AllocatorTag kTag, - class Hash = std::hash<Key>, - class Pred = std::equal_to<Key>> - using AllocationTrackingHashMap = HashMap< - Key, T, EmptyFn, Hash, Pred, TrackingAllocator<std::pair<Key, T>, kTag>>; - - AllocationTrackingHashMap<uint32_t, - uint16_t, - OffsetTypeMapEmptyFn, - kAllocatorTagDexFileVerifier, - OffsetTypeMapHashCompareFn, - OffsetTypeMapHashCompareFn> offset_to_type_map_; - const uint8_t* ptr_; - const void* previous_item_; - - std::string failure_reason_; - - // Set of type ids for which there are ClassDef elements in the dex file. - std::unordered_set<decltype(DexFile::ClassDef::class_idx_)> defined_classes_; -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_ diff --git a/runtime/dex/dex_file_verifier_test.cc b/runtime/dex/dex_file_verifier_test.cc deleted file mode 100644 index 1cd4b2c07b..0000000000 --- a/runtime/dex/dex_file_verifier_test.cc +++ /dev/null @@ -1,2186 +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 "dex_file_verifier.h" - -#include <zlib.h> - -#include <functional> -#include <memory> - -#include "base/bit_utils.h" -#include "base/macros.h" -#include "base64_test_util.h" -#include "descriptors_names.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" -#include "dex_file_types.h" -#include "gtest/gtest.h" -#include "leb128.h" -#include "standard_dex_file.h" - -namespace art { - -static constexpr char kLocationString[] = "dex_file_location"; - -// Make the Dex file version 37. -static void MakeDexVersion37(DexFile* dex_file) { - size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6; - CHECK_EQ(*(dex_file->Begin() + offset), '5'); - *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7'; -} - -static void FixUpChecksum(uint8_t* dex_file) { - DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file); - uint32_t expected_size = header->file_size_; - uint32_t adler_checksum = adler32(0L, Z_NULL, 0); - const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_); - const uint8_t* non_sum_ptr = dex_file + non_sum; - adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum); - header->checksum_ = adler_checksum; -} - -class DexFileVerifierTest : public testing::Test { - protected: - DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) { - return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr); - } - - void VerifyModification(const char* dex_file_base64_content, - const char* location, - const std::function<void(DexFile*)>& f, - const char* expected_error) { - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_base64_content, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - f(dex_file.get()); - FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin())); - - static constexpr bool kVerifyChecksum = true; - std::string error_msg; - bool success = DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - location, - kVerifyChecksum, - &error_msg); - if (expected_error == nullptr) { - EXPECT_TRUE(success) << error_msg; - } else { - EXPECT_FALSE(success) << "Expected " << expected_error; - if (!success) { - EXPECT_NE(error_msg.find(expected_error), std::string::npos) << error_msg; - } - } - } -}; - -static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, - const char* location, - std::string* error_msg) { - // decode base64 - CHECK(base64 != nullptr); - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length)); - CHECK(dex_bytes.get() != nullptr); - - // read dex - std::vector<std::unique_ptr<const DexFile>> tmp; - const DexFileLoader dex_file_loader; - bool success = dex_file_loader.OpenAll(dex_bytes.get(), - length, - location, - /* verify */ true, - /* verify_checksum */ true, - error_msg, - &tmp); - CHECK(success) << *error_msg; - EXPECT_EQ(1U, tmp.size()); - std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]); - return dex_file; -} - -// To generate a base64 encoded Dex file (such as kGoodTestDex, below) -// from Smali files, use: -// -// smali assemble -o classes.dex class1.smali [class2.smali ...] -// base64 classes.dex >classes.dex.base64 - -// For reference. -static const char kGoodTestDex[] = - "ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN" - "AAAAcAAAAAYAAACkAAAAAgAAALwAAAABAAAA1AAAAAQAAADcAAAAAQAAAPwAAACIAQAAHAEAAFoB" - "AABiAQAAagEAAIEBAACVAQAAqQEAAL0BAADDAQAAzgEAANEBAADVAQAA2gEAAN8BAAABAAAAAgAA" - "AAMAAAAEAAAABQAAAAgAAAAIAAAABQAAAAAAAAAJAAAABQAAAFQBAAAEAAEACwAAAAAAAAAAAAAA" - "AAAAAAoAAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAcAAAAAAAAA8wEAAAAAAAAB" - "AAEAAQAAAOgBAAAEAAAAcBADAAAADgACAAAAAgAAAO0BAAAIAAAAYgAAABoBBgBuIAIAEAAOAAEA" - "AAADAAY8aW5pdD4ABkxUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09i" - "amVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AARUZXN0AAlUZXN0" - "LmphdmEAAVYAAlZMAANmb28AA291dAAHcHJpbnRsbgABAAcOAAMABw54AAAAAgAAgYAEnAIBCbQC" - "AAAADQAAAAAAAAABAAAAAAAAAAEAAAANAAAAcAAAAAIAAAAGAAAApAAAAAMAAAACAAAAvAAAAAQA" - "AAABAAAA1AAAAAUAAAAEAAAA3AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" - "AAIgAAANAAAAWgEAAAMgAAACAAAA6AEAAAAgAAABAAAA8wEAAAAQAAABAAAABAIAAA=="; - -TEST_F(DexFileVerifierTest, GoodDex) { - std::string error_msg; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex, - kLocationString, - &error_msg)); - ASSERT_TRUE(raw.get() != nullptr) << error_msg; -} - -TEST_F(DexFileVerifierTest, MethodId) { - // Class idx error. - VerifyModification( - kGoodTestDex, - "method_id_class_idx", - [](DexFile* dex_file) { - DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0)); - method_id->class_idx_ = dex::TypeIndex(0xFF); - }, - "could not find declaring class for direct method index 0"); - - // Proto idx error. - VerifyModification( - kGoodTestDex, - "method_id_proto_idx", - [](DexFile* dex_file) { - DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0)); - method_id->proto_idx_ = 0xFF; - }, - "inter_method_id_item proto_idx"); - - // Name idx error. - VerifyModification( - kGoodTestDex, - "method_id_name_idx", - [](DexFile* dex_file) { - DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0)); - method_id->name_idx_ = dex::StringIndex(0xFF); - }, - "String index not available for method flags verification"); -} - -// Method flags test class generated from the following smali code. The declared-synchronized -// flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout -// the code, but we need to remove them before doing tests. -// -// .class public LMethodFlags; -// .super Ljava/lang/Object; -// -// .method public static constructor <clinit>()V -// .registers 1 -// return-void -// .end method -// -// .method public constructor <init>()V -// .registers 1 -// return-void -// .end method -// -// .method private declared-synchronized foo()V -// .registers 1 -// return-void -// .end method -// -// .method public declared-synchronized bar()V -// .registers 1 -// return-void -// .end method - -static const char kMethodFlagsTestDex[] = - "ZGV4CjAzNQCyOQrJaDBwiIWv5MIuYKXhxlLLsQcx5SwgAgAAcAAAAHhWNBIAAAAAAAAAAJgBAAAH" - "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAA8AQAA5AAAAOQA" - "AADuAAAA9gAAAAUBAAAZAQAAHAEAACEBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA" - "AAAAAAABAAAAAAAAAAUAAAAAAAAABgAAAAAAAAABAAAAAQAAAAAAAAD/////AAAAAHoBAAAAAAAA" - "CDxjbGluaXQ+AAY8aW5pdD4ADUxNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAD" - "YmFyAANmb28AAAAAAAAAAQAAAAAAAAAAAAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAEA" - "AQAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAADAQCJgASsAgGBgATAAgKCgAjU" - "AgKBgAjoAgAACwAAAAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAADAAAAjAAAAAMAAAABAAAA" - "mAAAAAUAAAAEAAAApAAAAAYAAAABAAAAxAAAAAIgAAAHAAAA5AAAAAMQAAABAAAAKAEAAAEgAAAE" - "AAAALAEAAAAgAAABAAAAegEAAAAQAAABAAAAmAEAAA=="; - -// Find the method data for the first method with the given name (from class 0). Note: the pointer -// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index -// delta. -static const uint8_t* FindMethodData(const DexFile* dex_file, - const char* name, - /*out*/ uint32_t* method_idx = nullptr) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(0); - const uint8_t* class_data = dex_file->GetClassData(class_def); - - ClassDataItemIterator it(*dex_file, class_data); - - const uint8_t* trailing = class_data; - // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first - // element has already been loaded into the iterator. - DecodeUnsignedLeb128(&trailing); - DecodeUnsignedLeb128(&trailing); - DecodeUnsignedLeb128(&trailing); - DecodeUnsignedLeb128(&trailing); - - // Skip all fields. - while (it.HasNextStaticField() || it.HasNextInstanceField()) { - trailing = it.DataPointer(); - it.Next(); - } - - while (it.HasNextMethod()) { - uint32_t method_index = it.GetMemberIndex(); - dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_; - const DexFile::StringId& string_id = dex_file->GetStringId(name_index); - const char* str = dex_file->GetStringData(string_id); - if (strcmp(name, str) == 0) { - if (method_idx != nullptr) { - *method_idx = method_index; - } - DecodeUnsignedLeb128(&trailing); - return trailing; - } - - trailing = it.DataPointer(); - it.Next(); - } - - return nullptr; -} - -// Set the method flags to the given value. -static void SetMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) { - uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method)); - CHECK(method_flags_ptr != nullptr) << method; - - // Unroll this, as we only have three bytes, anyways. - uint8_t base1 = static_cast<uint8_t>(mask & 0x7F); - *(method_flags_ptr++) = (base1 | 0x80); - mask >>= 7; - - uint8_t base2 = static_cast<uint8_t>(mask & 0x7F); - *(method_flags_ptr++) = (base2 | 0x80); - mask >>= 7; - - uint8_t base3 = static_cast<uint8_t>(mask & 0x7F); - *method_flags_ptr = base3; -} - -static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) { - const uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method)); - CHECK(method_flags_ptr != nullptr) << method; - return DecodeUnsignedLeb128(&method_flags_ptr); -} - -// Apply the given mask to method flags. -static void ApplyMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) { - uint32_t value = GetMethodFlags(dex_file, method); - value &= mask; - SetMethodFlags(dex_file, method, value); -} - -// Apply the given mask to method flags. -static void OrMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) { - uint32_t value = GetMethodFlags(dex_file, method); - value |= mask; - SetMethodFlags(dex_file, method, value); -} - -// Set code_off to 0 for the method. -static void RemoveCode(DexFile* dex_file, const char* method) { - const uint8_t* ptr = FindMethodData(dex_file, method); - // Next is flags, pass. - DecodeUnsignedLeb128(&ptr); - - // Figure out how many bytes the code_off is. - const uint8_t* tmp = ptr; - DecodeUnsignedLeb128(&tmp); - size_t bytes = tmp - ptr; - - uint8_t* mod = const_cast<uint8_t*>(ptr); - for (size_t i = 1; i < bytes; ++i) { - *(mod++) = 0x80; - } - *mod = 0x00; -} - -TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) { - // Check that it's OK when the wrong declared-synchronized flag is removed from "foo." - VerifyModification( - kMethodFlagsTestDex, - "method_flags_ok", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - }, - nullptr); -} - -TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) { - // Make sure we still accept constructors without their flags. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_missing_constructor_tag_ok", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccConstructor); - ApplyMaskToMethodFlags(dex_file, "<clinit>", ~kAccConstructor); - }, - nullptr); - - constexpr const char* kConstructors[] = { "<clinit>", "<init>"}; - for (size_t i = 0; i < 2; ++i) { - // Constructor with code marked native. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_constructor_native", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative); - }, - "has code, but is marked native or abstract"); - // Constructor with code marked abstract. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_constructor_abstract", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract); - }, - "has code, but is marked native or abstract"); - // Constructor as-is without code. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_constructor_nocode", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - RemoveCode(dex_file, kConstructors[i]); - }, - "has no code, but is not marked native or abstract"); - // Constructor without code marked native. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_constructor_native_nocode", - [&](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative); - RemoveCode(dex_file, kConstructors[i]); - }, - "must not be abstract or native"); - // Constructor without code marked abstract. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_constructor_abstract_nocode", - [&](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract); - RemoveCode(dex_file, kConstructors[i]); - }, - "must not be abstract or native"); - } - // <init> may only have (modulo ignored): - // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic - static constexpr uint32_t kInitAllowed[] = { - 0, - kAccPrivate, - kAccProtected, - kAccPublic, - kAccStrict, - kAccVarargs, - kAccSynthetic - }; - for (size_t i = 0; i < arraysize(kInitAllowed); ++i) { - VerifyModification( - kMethodFlagsTestDex, - "init_allowed_flags", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]); - }, - nullptr); - } - // Only one of public-private-protected. - for (size_t i = 1; i < 8; ++i) { - if (POPCOUNT(i) < 2) { - continue; - } - // Technically the flags match, but just be defensive here. - uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) | - ((i & 2) != 0 ? kAccProtected : 0) | - ((i & 4) != 0 ? kAccPublic : 0); - VerifyModification( - kMethodFlagsTestDex, - "init_one_of_ppp", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "<init>", mask); - }, - "Method may have only one of public/protected/private"); - } - // <init> doesn't allow - // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge - // Need to handle static separately as it has its own error message. - VerifyModification( - kMethodFlagsTestDex, - "init_not_allowed_flags", - [&](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "<init>", kAccStatic); - }, - "Constructor 1(LMethodFlags;.<init>) is not flagged correctly wrt/ static"); - static constexpr uint32_t kInitNotAllowed[] = { - kAccFinal, - kAccSynchronized, - kAccBridge - }; - for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) { - VerifyModification( - kMethodFlagsTestDex, - "init_not_allowed_flags", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]); - }, - "Constructor 1(LMethodFlags;.<init>) flagged inappropriately"); - } -} - -TEST_F(DexFileVerifierTest, MethodAccessFlagsMethods) { - constexpr const char* kMethods[] = { "foo", "bar"}; - for (size_t i = 0; i < arraysize(kMethods); ++i) { - // Make sure we reject non-constructors marked as constructors. - VerifyModification( - kMethodFlagsTestDex, - "method_flags_non_constructor", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kMethods[i], kAccConstructor); - }, - "is marked constructor, but doesn't match name"); - - VerifyModification( - kMethodFlagsTestDex, - "method_flags_native_with_code", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kMethods[i], kAccNative); - }, - "has code, but is marked native or abstract"); - - VerifyModification( - kMethodFlagsTestDex, - "method_flags_abstract_with_code", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract); - }, - "has code, but is marked native or abstract"); - - VerifyModification( - kMethodFlagsTestDex, - "method_flags_non_abstract_native_no_code", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - RemoveCode(dex_file, kMethods[i]); - }, - "has no code, but is not marked native or abstract"); - - // Abstract methods may not have the following flags. - constexpr uint32_t kAbstractDisallowed[] = { - kAccPrivate, - kAccStatic, - kAccFinal, - kAccNative, - kAccStrict, - kAccSynchronized, - }; - for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) { - VerifyModification( - kMethodFlagsTestDex, - "method_flags_abstract_and_disallowed_no_code", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - RemoveCode(dex_file, kMethods[i]); - - // Can't check private and static with foo, as it's in the virtual list and gives a - // different error. - if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) && - ((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) { - // Use another breaking flag. - OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal); - } else { - OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]); - } - }, - "has disallowed access flags"); - } - - // Only one of public-private-protected. - for (size_t j = 1; j < 8; ++j) { - if (POPCOUNT(j) < 2) { - continue; - } - // Technically the flags match, but just be defensive here. - uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) | - ((j & 2) != 0 ? kAccProtected : 0) | - ((j & 4) != 0 ? kAccPublic : 0); - VerifyModification( - kMethodFlagsTestDex, - "method_flags_one_of_ppp", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic); - OrMaskToMethodFlags(dex_file, kMethods[i], mask); - }, - "Method may have only one of public/protected/private"); - } - } -} - -TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) { - constexpr const char* kMethods[] = { "<clinit>", "<init>", "foo", "bar"}; - for (size_t i = 0; i < arraysize(kMethods); ++i) { - // All interesting method flags, other flags are to be ignored. - constexpr uint32_t kAllMethodFlags = - kAccPublic | - kAccPrivate | - kAccProtected | - kAccStatic | - kAccFinal | - kAccSynchronized | - kAccBridge | - kAccVarargs | - kAccNative | - kAccAbstract | - kAccStrict | - kAccSynthetic; - constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF; - VerifyModification( - kMethodFlagsTestDex, - "method_flags_ignored", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, kMethods[i], kIgnoredMask); - }, - nullptr); - } -} - -TEST_F(DexFileVerifierTest, B28552165) { - // Regression test for bad error string retrieval in different situations. - // Using invalid access flags to trigger the error. - VerifyModification( - kMethodFlagsTestDex, - "b28552165", - [](DexFile* dex_file) { - OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected); - }, - "Method may have only one of public/protected/private, LMethodFlags;.foo"); -} - -// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's -// just easier to break up bad cases. - -// Standard interface. Use declared-synchronized again for 3B encoding. -// -// .class public interface LInterfaceMethodFlags; -// .super Ljava/lang/Object; -// -// .method public static constructor <clinit>()V -// .registers 1 -// return-void -// .end method -// -// .method public abstract declared-synchronized foo()V -// .end method -static const char kMethodFlagsInterface[] = - "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF" - "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA" - "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA" - "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0" - "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB" - "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA" - "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA" - "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA"; - -// To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply -// to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on. -static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) { - uint32_t result = 0; - uint32_t mask_index = 0; - while (src_value != 0) { - uint32_t index = CTZ(src_value); - if (((src_value & (1 << index)) != 0) && - ((mask & (1 << mask_index)) != 0)) { - result |= (1 << index); - } - src_value &= ~(1 << index); - mask_index++; - } - return result; -} - -TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) { - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_ok", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - }, - nullptr); - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_ok37", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - }, - nullptr); - - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_non_public", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_non_public", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - }, - "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public"); - - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_non_abstract", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract); - }, - "Method 1(LInterfaceMethodFlags;.foo) has no code, but is not marked native or abstract"); - - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_static", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - OrMaskToMethodFlags(dex_file, "foo", kAccStatic); - }, - "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0"); - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_private", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "foo", kAccPrivate); - }, - "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0"); - - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_non_public", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_non_public", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - }, - "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public"); - - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_protected", - [](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "foo", kAccProtected); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_protected", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - OrMaskToMethodFlags(dex_file, "foo", kAccProtected); - }, - "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public"); - - constexpr uint32_t kAllMethodFlags = - kAccPublic | - kAccPrivate | - kAccProtected | - kAccStatic | - kAccFinal | - kAccSynchronized | - kAccBridge | - kAccVarargs | - kAccNative | - kAccAbstract | - kAccStrict | - kAccSynthetic; - constexpr uint32_t kInterfaceMethodFlags = - kAccPublic | kAccAbstract | kAccVarargs | kAccBridge | kAccSynthetic; - constexpr uint32_t kInterfaceDisallowed = kAllMethodFlags & - ~kInterfaceMethodFlags & - // Already tested, needed to be separate. - ~kAccStatic & - ~kAccPrivate & - ~kAccProtected; - static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags."); - - uint32_t bits = POPCOUNT(kInterfaceDisallowed); - for (uint32_t i = 1; i < (1u << bits); ++i) { - VerifyModification( - kMethodFlagsInterface, - "method_flags_interface_non_abstract", - [&](DexFile* dex_file) { - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); - if ((mask & kAccProtected) != 0) { - mask &= ~kAccProtected; - ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); - } - OrMaskToMethodFlags(dex_file, "foo", mask); - }, - "Abstract method 1(LInterfaceMethodFlags;.foo) has disallowed access flags"); - } -} - -/////////////////////////////////////////////////////////////////// - -// Field flags. - -// Find the method data for the first method with the given name (from class 0). Note: the pointer -// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index -// delta. -static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(0); - const uint8_t* class_data = dex_file->GetClassData(class_def); - - ClassDataItemIterator it(*dex_file, class_data); - - const uint8_t* trailing = class_data; - // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first - // element has already been loaded into the iterator. - DecodeUnsignedLeb128(&trailing); - DecodeUnsignedLeb128(&trailing); - DecodeUnsignedLeb128(&trailing); - DecodeUnsignedLeb128(&trailing); - - while (it.HasNextStaticField() || it.HasNextInstanceField()) { - uint32_t field_index = it.GetMemberIndex(); - dex::StringIndex name_index = dex_file->GetFieldId(field_index).name_idx_; - const DexFile::StringId& string_id = dex_file->GetStringId(name_index); - const char* str = dex_file->GetStringData(string_id); - if (strcmp(name, str) == 0) { - DecodeUnsignedLeb128(&trailing); - return trailing; - } - - trailing = it.DataPointer(); - it.Next(); - } - - return nullptr; -} - -// Set the method flags to the given value. -static void SetFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) { - uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field)); - CHECK(field_flags_ptr != nullptr) << field; - - // Unroll this, as we only have three bytes, anyways. - uint8_t base1 = static_cast<uint8_t>(mask & 0x7F); - *(field_flags_ptr++) = (base1 | 0x80); - mask >>= 7; - - uint8_t base2 = static_cast<uint8_t>(mask & 0x7F); - *(field_flags_ptr++) = (base2 | 0x80); - mask >>= 7; - - uint8_t base3 = static_cast<uint8_t>(mask & 0x7F); - *field_flags_ptr = base3; -} - -static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) { - const uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field)); - CHECK(field_flags_ptr != nullptr) << field; - return DecodeUnsignedLeb128(&field_flags_ptr); -} - -// Apply the given mask to method flags. -static void ApplyMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) { - uint32_t value = GetFieldFlags(dex_file, field); - value &= mask; - SetFieldFlags(dex_file, field, value); -} - -// Apply the given mask to method flags. -static void OrMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) { - uint32_t value = GetFieldFlags(dex_file, field); - value |= mask; - SetFieldFlags(dex_file, field, value); -} - -// Standard class. Use declared-synchronized again for 3B encoding. -// -// .class public LFieldFlags; -// .super Ljava/lang/Object; -// -// .field declared-synchronized public foo:I -// -// .field declared-synchronized public static bar:I - -static const char kFieldFlagsTestDex[] = - "ZGV4CjAzNQBtLw7hydbfv4TdXidZyzAB70W7w3vnYJRwAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAF" - "AAAAcAAAAAMAAACEAAAAAAAAAAAAAAACAAAAkAAAAAAAAAAAAAAAAQAAAKAAAACwAAAAwAAAAMAA" - "AADDAAAA0QAAAOUAAADqAAAAAAAAAAEAAAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAABAAAAAgAA" - "AAAAAAD/////AAAAAPQAAAAAAAAAAUkADExGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7" - "AANiYXIAA2ZvbwAAAAAAAAEBAAAAiYAIAYGACAkAAAAAAAAAAQAAAAAAAAABAAAABQAAAHAAAAAC" - "AAAAAwAAAIQAAAAEAAAAAgAAAJAAAAAGAAAAAQAAAKAAAAACIAAABQAAAMAAAAADEAAAAQAAAPAA" - "AAAAIAAAAQAAAPQAAAAAEAAAAQAAAAABAAA="; - -TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) { - // Check that it's OK when the wrong declared-synchronized flag is removed from "foo." - VerifyModification( - kFieldFlagsTestDex, - "field_flags_ok", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - }, - nullptr); -} - -TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) { - // Mark the field so that it should appear in the opposite list (instance vs static). - VerifyModification( - kFieldFlagsTestDex, - "field_flags_wrong_list", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToFieldFlags(dex_file, "foo", kAccStatic); - }, - "Static/instance field not in expected list"); - VerifyModification( - kFieldFlagsTestDex, - "field_flags_wrong_list", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic); - }, - "Static/instance field not in expected list"); -} - -TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) { - static const char* kFields[] = { "foo", "bar" }; - for (size_t i = 0; i < arraysize(kFields); ++i) { - // Should be OK to remove public. - VerifyModification( - kFieldFlagsTestDex, - "field_flags_non_public", - [&](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic); - }, - nullptr); - constexpr uint32_t kAccFlags = kAccPublic | kAccPrivate | kAccProtected; - uint32_t bits = POPCOUNT(kAccFlags); - for (uint32_t j = 1; j < (1u << bits); ++j) { - if (POPCOUNT(j) < 2) { - continue; - } - VerifyModification( - kFieldFlagsTestDex, - "field_flags_ppp", - [&](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic); - uint32_t mask = ApplyMaskShifted(kAccFlags, j); - OrMaskToFieldFlags(dex_file, kFields[i], mask); - }, - "Field may have only one of public/protected/private"); - } - } -} - -TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) { - constexpr const char* kFields[] = { "foo", "bar"}; - for (size_t i = 0; i < arraysize(kFields); ++i) { - // All interesting method flags, other flags are to be ignored. - constexpr uint32_t kAllFieldFlags = - kAccPublic | - kAccPrivate | - kAccProtected | - kAccStatic | - kAccFinal | - kAccVolatile | - kAccTransient | - kAccSynthetic | - kAccEnum; - constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF; - VerifyModification( - kFieldFlagsTestDex, - "field_flags_ignored", - [&](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToFieldFlags(dex_file, kFields[i], kIgnoredMask); - }, - nullptr); - } -} - -TEST_F(DexFileVerifierTest, FieldAccessFlagsVolatileFinal) { - constexpr const char* kFields[] = { "foo", "bar"}; - for (size_t i = 0; i < arraysize(kFields); ++i) { - VerifyModification( - kFieldFlagsTestDex, - "field_flags_final_and_volatile", - [&](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); - - OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal); - }, - "Fields may not be volatile and final"); - } -} - -// Standard interface. Needs to be separate from class as interfaces do not allow instance fields. -// Use declared-synchronized again for 3B encoding. -// -// .class public interface LInterfaceFieldFlags; -// .super Ljava/lang/Object; -// -// .field declared-synchronized public static final foo:I - -static const char kFieldFlagsInterfaceTestDex[] = - "ZGV4CjAzNQCVMHfEimR1zZPk6hl6O9GPAYqkl3u0umFkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE" - "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA" - "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA" - "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm" - "b28AAAAAAAABAAAAAJmACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE" - "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA" - "AAAAEAAAAQAAAPQAAAA="; - -TEST_F(DexFileVerifierTest, FieldAccessFlagsInterface) { - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - }, - nullptr); - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - }, - nullptr); - - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_non_public", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_non_public", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - }, - "Interface field is not public final static"); - - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_non_final", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_non_final", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal); - }, - "Interface field is not public final static"); - - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_protected", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - OrMaskToFieldFlags(dex_file, "foo", kAccProtected); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_protected", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - OrMaskToFieldFlags(dex_file, "foo", kAccProtected); - }, - "Interface field is not public final static"); - - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_private", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - OrMaskToFieldFlags(dex_file, "foo", kAccPrivate); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_private", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - OrMaskToFieldFlags(dex_file, "foo", kAccPrivate); - }, - "Interface field is not public final static"); - - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_synthetic", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - OrMaskToFieldFlags(dex_file, "foo", kAccSynthetic); - }, - nullptr); - - constexpr uint32_t kAllFieldFlags = - kAccPublic | - kAccPrivate | - kAccProtected | - kAccStatic | - kAccFinal | - kAccVolatile | - kAccTransient | - kAccSynthetic | - kAccEnum; - constexpr uint32_t kInterfaceFieldFlags = kAccPublic | kAccStatic | kAccFinal | kAccSynthetic; - constexpr uint32_t kInterfaceDisallowed = kAllFieldFlags & - ~kInterfaceFieldFlags & - ~kAccProtected & - ~kAccPrivate; - static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags."); - - uint32_t bits = POPCOUNT(kInterfaceDisallowed); - for (uint32_t i = 1; i < (1u << bits); ++i) { - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_disallowed", - [&](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); - if ((mask & kAccProtected) != 0) { - mask &= ~kAccProtected; - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - } - OrMaskToFieldFlags(dex_file, "foo", mask); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kFieldFlagsInterfaceTestDex, - "field_flags_interface_disallowed", - [&](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - - uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); - if ((mask & kAccProtected) != 0) { - mask &= ~kAccProtected; - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); - } - OrMaskToFieldFlags(dex_file, "foo", mask); - }, - "Interface field has disallowed flag"); - } -} - -// Standard bad interface. Needs to be separate from class as interfaces do not allow instance -// fields. Use declared-synchronized again for 3B encoding. -// -// .class public interface LInterfaceFieldFlags; -// .super Ljava/lang/Object; -// -// .field declared-synchronized public final foo:I - -static const char kFieldFlagsInterfaceBadTestDex[] = - "ZGV4CjAzNQByMUnqYKHBkUpvvNp+9CnZ2VyDkKnRN6VkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE" - "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA" - "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA" - "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm" - "b28AAAAAAAAAAQAAAJGACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE" - "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA" - "AAAAEAAAAQAAAPQAAAA="; - -TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) { - VerifyModification( - kFieldFlagsInterfaceBadTestDex, - "field_flags_interface_non_static", - [](DexFile* dex_file) { - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - }, - nullptr); // Should be allowed in older dex versions for backwards compatibility. - VerifyModification( - kFieldFlagsInterfaceBadTestDex, - "field_flags_interface_non_static", - [](DexFile* dex_file) { - MakeDexVersion37(dex_file); - ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); - }, - "Interface field is not public final static"); -} - -// Generated from: -// -// .class public LTest; -// .super Ljava/lang/Object; -// .source "Test.java" -// -// .method public constructor <init>()V -// .registers 1 -// -// .prologue -// .line 1 -// invoke-direct {p0}, Ljava/lang/Object;-><init>()V -// -// return-void -// .end method -// -// .method public static main()V -// .registers 2 -// -// const-string v0, "a" -// const-string v0, "b" -// const-string v0, "c" -// const-string v0, "d" -// const-string v0, "e" -// const-string v0, "f" -// const-string v0, "g" -// const-string v0, "h" -// const-string v0, "i" -// const-string v0, "j" -// const-string v0, "k" -// -// .local v1, "local_var":Ljava/lang/String; -// const-string v1, "test" -// .end method - -static const char kDebugInfoTestDex[] = - "ZGV4CjAzNQCHRkHix2eIMQgvLD/0VGrlllZLo0Rb6VyUAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAU" - "AAAAcAAAAAQAAADAAAAAAQAAANAAAAAAAAAAAAAAAAMAAADcAAAAAQAAAPQAAACAAQAAFAEAABQB" - "AAAcAQAAJAEAADgBAABMAQAAVwEAAFoBAABdAQAAYAEAAGMBAABmAQAAaQEAAGwBAABvAQAAcgEA" - "AHUBAAB4AQAAewEAAIYBAACMAQAAAQAAAAIAAAADAAAABQAAAAUAAAADAAAAAAAAAAAAAAAAAAAA" - "AAAAABIAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAEAAAAAAAAAPwBAAAAAAAABjxpbml0PgAG" - "TFRlc3Q7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAJVGVzdC5qYXZh" - "AAFWAAFhAAFiAAFjAAFkAAFlAAFmAAFnAAFoAAFpAAFqAAFrAAlsb2NhbF92YXIABG1haW4ABHRl" - "c3QAAAABAAcOAAAAARYDARIDAAAAAQABAAEAAACUAQAABAAAAHAQAgAAAA4AAgAAAAAAAACZAQAA" - "GAAAABoABgAaAAcAGgAIABoACQAaAAoAGgALABoADAAaAA0AGgAOABoADwAaABAAGgETAAAAAgAA" - "gYAEpAMBCbwDAAALAAAAAAAAAAEAAAAAAAAAAQAAABQAAABwAAAAAgAAAAQAAADAAAAAAwAAAAEA" - "AADQAAAABQAAAAMAAADcAAAABgAAAAEAAAD0AAAAAiAAABQAAAAUAQAAAyAAAAIAAACUAQAAASAA" - "AAIAAACkAQAAACAAAAEAAAD8AQAAABAAAAEAAAAMAgAA"; - -TEST_F(DexFileVerifierTest, DebugInfoTypeIdxTest) { - { - // The input dex file should be good before modification. - std::string error_msg; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kDebugInfoTestDex, - kLocationString, - &error_msg)); - ASSERT_TRUE(raw.get() != nullptr) << error_msg; - } - - // Modify the debug information entry. - VerifyModification( - kDebugInfoTestDex, - "debug_start_type_idx", - [](DexFile* dex_file) { - *(const_cast<uint8_t*>(dex_file->Begin()) + 416) = 0x14U; - }, - "DBG_START_LOCAL type_idx"); -} - -TEST_F(DexFileVerifierTest, SectionAlignment) { - { - // The input dex file should be good before modification. Any file is fine, as long as it - // uses all sections. - std::string error_msg; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex, - kLocationString, - &error_msg)); - ASSERT_TRUE(raw.get() != nullptr) << error_msg; - } - - // Modify all section offsets to be unaligned. - constexpr size_t kSections = 7; - for (size_t i = 0; i < kSections; ++i) { - VerifyModification( - kGoodTestDex, - "section_align", - [&](DexFile* dex_file) { - DexFile::Header* header = const_cast<DexFile::Header*>( - reinterpret_cast<const DexFile::Header*>(dex_file->Begin())); - uint32_t* off_ptr; - switch (i) { - case 0: - off_ptr = &header->map_off_; - break; - case 1: - off_ptr = &header->string_ids_off_; - break; - case 2: - off_ptr = &header->type_ids_off_; - break; - case 3: - off_ptr = &header->proto_ids_off_; - break; - case 4: - off_ptr = &header->field_ids_off_; - break; - case 5: - off_ptr = &header->method_ids_off_; - break; - case 6: - off_ptr = &header->class_defs_off_; - break; - - static_assert(kSections == 7, "kSections is wrong"); - default: - LOG(FATAL) << "Unexpected section"; - UNREACHABLE(); - } - ASSERT_TRUE(off_ptr != nullptr); - ASSERT_NE(*off_ptr, 0U) << i; // Should already contain a value (in use). - (*off_ptr)++; // Add one, which should misalign it (all the sections - // above are aligned by 4). - }, - "should be aligned by 4 for"); - } -} - -// Generated from -// -// .class LOverloading; -// -// .super Ljava/lang/Object; -// -// .method public static foo()V -// .registers 1 -// return-void -// .end method -// -// .method public static foo(I)V -// .registers 1 -// return-void -// .end method -static const char kProtoOrderingTestDex[] = - "ZGV4CjAzNQA1L+ABE6voQ9Lr4Ci//efB53oGnDr5PinsAQAAcAAAAHhWNBIAAAAAAAAAAFgBAAAG" - "AAAAcAAAAAQAAACIAAAAAgAAAJgAAAAAAAAAAAAAAAIAAACwAAAAAQAAAMAAAAAMAQAA4AAAAOAA" - "AADjAAAA8gAAAAYBAAAJAQAADQEAAAAAAAABAAAAAgAAAAMAAAADAAAAAwAAAAAAAAAEAAAAAwAA" - "ABQBAAABAAAABQAAAAEAAQAFAAAAAQAAAAAAAAACAAAAAAAAAP////8AAAAASgEAAAAAAAABSQAN" - "TE92ZXJsb2FkaW5nOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAJWSQADZm9vAAAAAQAAAAAAAAAA" - "AAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAACAAAJpAIBCbgC" - "AAAMAAAAAAAAAAEAAAAAAAAAAQAAAAYAAABwAAAAAgAAAAQAAACIAAAAAwAAAAIAAACYAAAABQAA" - "AAIAAACwAAAABgAAAAEAAADAAAAAAiAAAAYAAADgAAAAARAAAAEAAAAUAQAAAxAAAAIAAAAcAQAA" - "ASAAAAIAAAAkAQAAACAAAAEAAABKAQAAABAAAAEAAABYAQAA"; - -TEST_F(DexFileVerifierTest, ProtoOrdering) { - { - // The input dex file should be good before modification. - std::string error_msg; - std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kProtoOrderingTestDex, - kLocationString, - &error_msg)); - ASSERT_TRUE(raw.get() != nullptr) << error_msg; - } - - // Modify the order of the ProtoIds for two overloads of "foo" with the - // same return type and one having longer parameter list than the other. - for (size_t i = 0; i != 2; ++i) { - VerifyModification( - kProtoOrderingTestDex, - "proto_ordering", - [i](DexFile* dex_file) { - uint32_t method_idx; - const uint8_t* data = FindMethodData(dex_file, "foo", &method_idx); - CHECK(data != nullptr); - // There should be 2 methods called "foo". - CHECK_LT(method_idx + 1u, dex_file->NumMethodIds()); - CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_, - dex_file->GetMethodId(method_idx + 1).name_idx_); - CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u, - dex_file->GetMethodId(method_idx + 1).proto_idx_); - // Their return types should be the same. - uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; - const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx); - const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u); - CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_); - // And the first should not have any parameters while the second should have some. - CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext()); - CHECK(DexFileParameterIterator(*dex_file, proto2).HasNext()); - if (i == 0) { - // Swap the proto parameters and shorties to break the ordering. - std::swap(const_cast<uint32_t&>(proto1.parameters_off_), - const_cast<uint32_t&>(proto2.parameters_off_)); - std::swap(const_cast<dex::StringIndex&>(proto1.shorty_idx_), - const_cast<dex::StringIndex&>(proto2.shorty_idx_)); - } else { - // Copy the proto parameters and shorty to create duplicate proto id. - const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_; - const_cast<dex::StringIndex&>(proto1.shorty_idx_) = proto2.shorty_idx_; - } - }, - "Out-of-order proto_id arguments"); - } -} - -// To generate a base64 encoded Dex file version 037 from Smali files, use: -// -// smali assemble --api 24 -o classes.dex class1.smali [class2.smali ...] -// base64 classes.dex >classes.dex.base64 - -// Dex file version 037 generated from: -// -// .class public LB28685551; -// .super LB28685551; - -static const char kClassExtendsItselfTestDex[] = - "ZGV4CjAzNwDeGbgRg1kb6swszpcTWrrOAALB++F4OPT0AAAAcAAAAHhWNBIAAAAAAAAAAKgAAAAB" - "AAAAcAAAAAEAAAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAHgAAABcAAAAmAAAAJgA" - "AAAAAAAAAAAAAAEAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAALTEIyODY4NTU1MTsAAAAABgAA" - "AAAAAAABAAAAAAAAAAEAAAABAAAAcAAAAAIAAAABAAAAdAAAAAYAAAABAAAAeAAAAAIgAAABAAAA" - "mAAAAAAQAAABAAAAqAAAAA=="; - -TEST_F(DexFileVerifierTest, ClassExtendsItself) { - VerifyModification( - kClassExtendsItselfTestDex, - "class_extends_itself", - [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, - "Class with same type idx as its superclass: '0'"); -} - -// Dex file version 037 generated from: -// -// .class public LFoo; -// .super LBar; -// -// and: -// -// .class public LBar; -// .super LFoo; - -static const char kClassesExtendOneAnotherTestDex[] = - "ZGV4CjAzNwBXHSrwpDMwRBkg+L+JeQCuFNRLhQ86duEcAQAAcAAAAHhWNBIAAAAAAAAAANAAAAAC" - "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAABcAAAAwAAAAMAA" - "AADHAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAABAAAAAQAA" - "AAAAAAD/////AAAAAAAAAAAAAAAABUxCYXI7AAVMRm9vOwAAAAYAAAAAAAAAAQAAAAAAAAABAAAA" - "AgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAgAAAIAAAAACIAAAAgAAAMAAAAAAEAAAAQAAANAAAAA="; - -TEST_F(DexFileVerifierTest, ClassesExtendOneAnother) { - VerifyModification( - kClassesExtendOneAnotherTestDex, - "classes_extend_one_another", - [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, - "Invalid class definition ordering: class with type idx: '1' defined before" - " superclass with type idx: '0'"); -} - -// Dex file version 037 generated from: -// -// .class public LAll; -// .super LYour; -// -// and: -// -// .class public LYour; -// .super LBase; -// -// and: -// -// .class public LBase; -// .super LAll; - -static const char kCircularClassInheritanceTestDex[] = - "ZGV4CjAzNwBMJxgP0SJz6oLXnKfl+J7lSEORLRwF5LNMAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAD" - "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAIgAAABkAAAA6AAAAOgA" - "AADvAAAA9wAAAAAAAAABAAAAAgAAAAEAAAABAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAgAA" - "AAEAAAABAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAAAAAA/////wAAAAAAAAAA" - "AAAAAAVMQWxsOwAGTEJhc2U7AAZMWW91cjsAAAYAAAAAAAAAAQAAAAAAAAABAAAAAwAAAHAAAAAC" - "AAAAAwAAAHwAAAAGAAAAAwAAAIgAAAACIAAAAwAAAOgAAAAAEAAAAQAAAAABAAA="; - -TEST_F(DexFileVerifierTest, CircularClassInheritance) { - VerifyModification( - kCircularClassInheritanceTestDex, - "circular_class_inheritance", - [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, - "Invalid class definition ordering: class with type idx: '1' defined before" - " superclass with type idx: '0'"); -} - -// Dex file version 037 generated from: -// -// .class public abstract interface LInterfaceImplementsItself; -// .super Ljava/lang/Object; -// .implements LInterfaceImplementsItself; - -static const char kInterfaceImplementsItselfTestDex[] = - "ZGV4CjAzNwCKKrjatp8XbXl5S/bEVJnqaBhjZkQY4440AQAAcAAAAHhWNBIAAAAAAAAAANwAAAAC" - "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAIAAAACUAAAAoAAAAKAA" - "AAC9AAAAAAAAAAEAAAAAAAAAAQYAAAEAAADUAAAA/////wAAAAAAAAAAAAAAABtMSW50ZXJmYWNl" - "SW1wbGVtZW50c0l0c2VsZjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAAABAAAAAAAAAAcAAAAAAAAA" - "AQAAAAAAAAABAAAAAgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAQAAAIAAAAACIAAAAgAAAKAAAAAB" - "EAAAAQAAANQAAAAAEAAAAQAAANwAAAA="; - -TEST_F(DexFileVerifierTest, InterfaceImplementsItself) { - VerifyModification( - kInterfaceImplementsItselfTestDex, - "interface_implements_itself", - [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, - "Class with same type idx as implemented interface: '0'"); -} - -// Dex file version 037 generated from: -// -// .class public abstract interface LPing; -// .super Ljava/lang/Object; -// .implements LPong; -// -// and: -// -// .class public abstract interface LPong; -// .super Ljava/lang/Object; -// .implements LPing; - -static const char kInterfacesImplementOneAnotherTestDex[] = - "ZGV4CjAzNwD0Kk9sxlYdg3Dy1Cff0gQCuJAQfEP6ohZUAQAAcAAAAHhWNBIAAAAAAAAAAPwAAAAD" - "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIgAAACMAAAAyAAAAMgA" - "AADQAAAA2AAAAAAAAAABAAAAAgAAAAEAAAABBgAAAgAAAOwAAAD/////AAAAAAAAAAAAAAAAAAAA" - "AAEGAAACAAAA9AAAAP////8AAAAAAAAAAAAAAAAGTFBpbmc7AAZMUG9uZzsAEkxqYXZhL2xhbmcv" - "T2JqZWN0OwABAAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAADAAAAcAAAAAIAAAAD" - "AAAAfAAAAAYAAAACAAAAiAAAAAIgAAADAAAAyAAAAAEQAAACAAAA7AAAAAAQAAABAAAA/AAAAA=="; - -TEST_F(DexFileVerifierTest, InterfacesImplementOneAnother) { - VerifyModification( - kInterfacesImplementOneAnotherTestDex, - "interfaces_implement_one_another", - [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, - "Invalid class definition ordering: class with type idx: '1' defined before" - " implemented interface with type idx: '0'"); -} - -// Dex file version 037 generated from: -// -// .class public abstract interface LA; -// .super Ljava/lang/Object; -// .implements LB; -// -// and: -// -// .class public abstract interface LB; -// .super Ljava/lang/Object; -// .implements LC; -// -// and: -// -// .class public abstract interface LC; -// .super Ljava/lang/Object; -// .implements LA; - -static const char kCircularInterfaceImplementationTestDex[] = - "ZGV4CjAzNwCzKmD5Fol6XAU6ichYHcUTIP7Z7MdTcEmEAQAAcAAAAHhWNBIAAAAAAAAAACwBAAAE" - "AAAAcAAAAAQAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAJAAAACUAAAA8AAAAPAA" - "AAD1AAAA+gAAAP8AAAAAAAAAAQAAAAIAAAADAAAAAgAAAAEGAAADAAAAHAEAAP////8AAAAAAAAA" - "AAAAAAABAAAAAQYAAAMAAAAUAQAA/////wAAAAAAAAAAAAAAAAAAAAABBgAAAwAAACQBAAD/////" - "AAAAAAAAAAAAAAAAA0xBOwADTEI7AANMQzsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAQAAAAIAAAAB" - "AAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAAEAAAAcAAAAAIAAAAEAAAAgAAAAAYA" - "AAADAAAAkAAAAAIgAAAEAAAA8AAAAAEQAAADAAAAFAEAAAAQAAABAAAALAEAAA=="; - -TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) { - VerifyModification( - kCircularInterfaceImplementationTestDex, - "circular_interface_implementation", - [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, - "Invalid class definition ordering: class with type idx: '2' defined before" - " implemented interface with type idx: '0'"); -} - -TEST_F(DexFileVerifierTest, Checksum) { - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - - // Good checksum: all pass. - EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "good checksum, no verify", - /*verify_checksum*/ false, - &error_msg)); - EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "good checksum, verify", - /*verify_checksum*/ true, - &error_msg)); - - // Bad checksum: !verify_checksum passes verify_checksum fails. - DexFile::Header* header = reinterpret_cast<DexFile::Header*>( - const_cast<uint8_t*>(dex_file->Begin())); - header->checksum_ = 0; - EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad checksum, no verify", - /*verify_checksum*/ false, - &error_msg)); - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad checksum, verify", - /*verify_checksum*/ true, - &error_msg)); - EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg; -} - -TEST_F(DexFileVerifierTest, BadStaticMethodName) { - // Generated DEX file version (037) from: - // - // .class public LBadName; - // .super Ljava/lang/Object; - // - // .method public static <bad_name> (II)V - // .registers 2 - // .prologue - // return-void - // .end method - // - // .method public constructor <init>()V - // .registers 1 - // .prologue - // .line 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // return-void - // .end method - // - static const char kDexBase64[] = - "ZGV4CjAzNwC2NYlwyxEc/h6hv+hMeUVQPtiX6MQBcfgwAgAAcAAAAHhWNBIAAAAAAAAAAJABAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABAAQAA8AAAAPAA" - "AAD8AAAABAEAABIBAAAVAQAAIAEAADQBAAA3AQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA" - "AAcAAAADAAAAPAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA" - "AAAAAIABAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgAMQmFkTmFtZS5qYXZhAAFJAAlMQmFkTmFt" - "ZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgADVklJAAIAAAAAAAAAAAAAAAACAAAHAAEABw4AAAIA" - "AgAAAAAASAEAAAEAAAAOAAAAAQABAAEAAABOAQAABAAAAHAQAgAAAA4AAAACAAAJ1AIBgYAE6AIA" - "AA0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAAAgAAAKAAAAAFAAAA" - "AwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAADwBAAADEAAAAQAAAEQBAAAD" - "IAAAAgAAAEgBAAABIAAAAgAAAFQBAAAAIAAAAQAAAIABAAAAEAAAAQAAAJABAAA="; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad static method name", - /*verify_checksum*/ true, - &error_msg)); -} - -TEST_F(DexFileVerifierTest, BadVirtualMethodName) { - // Generated DEX file version (037) from: - // - // .class public LBadVirtualName; - // .super Ljava/lang/Object; - // - // .method public <bad_name> (II)V - // .registers 2 - // return-void - // .end method - // - // .method public constructor <init>()V - // .registers 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // return-void - // .end method - // - static const char kDexBase64[] = - "ZGV4CjAzNwDcPC8B2E7kYTZmeHX2u2IqrpWV9EXBHpE8AgAAcAAAAHhWNBIAAAAAAAAAAJwBAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABMAQAA8AAAAPAA" - "AAD8AAAABAEAABkBAAAcAQAALgEAAEIBAABFAQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA" - "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA" - "AAAAAI4BAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgATQmFkVmlydHVhbE5hbWUuamF2YQABSQAQ" - "TEJhZFZpcnR1YWxOYW1lOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAANWSUkAAAACAAAAAAAAAAAA" - "AAABAAcOAAACAAAHAAABAAEAAQAAAFgBAAAEAAAAcBACAAAADgADAAMAAAAAAF0BAAABAAAADgAA" - "AAEBAYGABOQCAAH8Ag0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAA" - "AgAAAKAAAAAFAAAAAwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAAEwBAAAD" - "EAAAAQAAAFQBAAADIAAAAgAAAFgBAAABIAAAAgAAAGQBAAAAIAAAAQAAAI4BAAAAEAAAAQAAAJwB" - "AAA="; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad virtual method name", - /*verify_checksum*/ true, - &error_msg)); -} - -TEST_F(DexFileVerifierTest, BadClinitSignature) { - // Generated DEX file version (037) from: - // - // .class public LOneClinitBadSig; - // .super Ljava/lang/Object; - // - // .method public static constructor <clinit>(II)V - // .registers 2 - // return-void - // .end method - // - // .method public constructor <init>()V - // .registers 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // return-void - // .end method - // - static const char kDexBase64[] = - "ZGV4CjAzNwBNOwTbfJmWq5eMOlxUY4EICGiEGJMVg8RAAgAAcAAAAHhWNBIAAAAAAAAAAKABAAAI" - "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABQAQAA8AAAAPAA" - "AAD6AAAAAgEAAAUBAAAYAQAALAEAAEIBAABFAQAAAgAAAAMAAAAEAAAABgAAAAYAAAADAAAAAAAA" - "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAAFAAAA" - "AAAAAJABAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AAUkAEUxPbmVDbGluaXRCYWRTaWc7ABJMamF2" - "YS9sYW5nL09iamVjdDsAFE9uZUNsaW5pdEJhZFNpZy5qYXZhAAFWAANWSUkAAAACAAAAAAAAAAAA" - "AAAAAgAABwABAAcOAAACAAIAAAAAAFgBAAABAAAADgAAAAEAAQABAAAAXgEAAAQAAABwEAIAAAAO" - "AAAAAgAAiYAE5AIBgYAE+AINAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAA" - "AwAAAAIAAACgAAAABQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABM" - "AQAAAxAAAAEAAABUAQAAAyAAAAIAAABYAQAAASAAAAIAAABkAQAAACAAAAEAAACQAQAAABAAAAEA" - "AACgAQAA"; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad clinit signature", - /*verify_checksum*/ true, - &error_msg)); -} - -TEST_F(DexFileVerifierTest, BadClinitSignatureAgain) { - // Generated DEX file version (037) from: - // - // .class public LOneClinitBadSigAgain; - // .super Ljava/lang/Object; - // - // .method public static constructor <clinit>()I - // .registers 1 - // const/4 v0, 1 - // return v0 - // .end method - // - // .method public constructor <init>()V - // .registers 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // return-void - // .end method - // - static const char kDexBase64[] = - "ZGV4CjAzNwBfPcPu5NVwKUqZIu/YR8xqVlVD5UzTk0gEAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH" - "AAAAcAAAAAQAAACMAAAAAgAAAJwAAAAAAAAAAAAAAAMAAAC0AAAAAQAAAMwAAAAYAQAA7AAAAOwA" - "AAD2AAAA/gAAAAEBAAAZAQAALQEAAEgBAAACAAAAAwAAAAQAAAAGAAAAAgAAAAAAAAAAAAAABgAA" - "AAMAAAAAAAAAAQAAAAAAAAABAAEAAQAAAAIAAQABAAAAAQAAAAEAAAACAAAAAAAAAAUAAAAAAAAA" - "eAEAAAAAAAAIPGNsaW5pdD4ABjxpbml0PgABSQAWTE9uZUNsaW5pdEJhZFNpZ0FnYWluOwASTGph" - "dmEvbGFuZy9PYmplY3Q7ABlPbmVDbGluaXRCYWRTaWdBZ2Fpbi5qYXZhAAFWAAABAAAAAAAAAAAA" - "AAACAAAAEhAPAAEAAQABAAAAAAAAAAQAAABwEAIAAAAOAAAAAgAAiYAEzAIBgYAE4AIKAAAAAAAA" - "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAIAAACcAAAABQAAAAMAAAC0AAAA" - "BgAAAAEAAADMAAAAAiAAAAcAAADsAAAAASAAAAIAAABMAQAAACAAAAEAAAB4AQAAABAAAAEAAACI" - "AQAA"; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad clinit signature", - /*verify_checksum*/ true, - &error_msg)); -} - -TEST_F(DexFileVerifierTest, BadInitSignature) { - // Generated DEX file version (037) from: - // - // .class public LBadInitSig; - // .super Ljava/lang/Object; - // - // .method public constructor <init>()I - // .registers 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // const v0, 1 - // return v0 - // .end method - // - static const char kDexBase64[] = - "ZGV4CjAzNwCdMdeh1KoHWamF2Prq32LF39YZ78fV7q+wAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF" - "AAAAcAAAAAQAAACEAAAAAgAAAJQAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAADUAAAA3AAAANwA" - "AADkAAAA5wAAAPUAAAAJAQAAAQAAAAIAAAADAAAABAAAAAEAAAAAAAAAAAAAAAQAAAADAAAAAAAA" - "AAEAAAAAAAAAAgABAAAAAAABAAAAAQAAAAIAAAAAAAAA/////wAAAAAqAQAAAAAAAAY8aW5pdD4A" - "AUkADExCYWRJbml0U2lnOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEAAQABAAAAAAAAAAcAAABw" - "EAEAAAAUAAEAAAAPAAAAAQAAgYAEjAIKAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAQA" - "AACEAAAAAwAAAAIAAACUAAAABQAAAAIAAACsAAAABgAAAAEAAAC8AAAAAiAAAAUAAADcAAAAASAA" - "AAEAAAAMAQAAACAAAAEAAAAqAQAAABAAAAEAAAA0AQAA"; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad init signature", - /*verify_checksum*/ true, - &error_msg)); -} - -static const char* kInvokeCustomDexFiles[] = { - // TODO(oth): Revisit this test when we have smali / dx support. - // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test001/Tests.java - "ZGV4CjAzOAAEj12s/acmmdGuDL92SWSBh6iLBjxgomWkCAAAcAAAAHhWNBIAAAAAAAAAALwHAAAx" - "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAAAMBgAAmAIAAMID" - "AADKAwAAzQMAANIDAADhAwAA5AMAAOoDAAAfBAAAUgQAAIMEAAC4BAAA1AQAAOsEAAD+BAAAEgUA" - "ACYFAAA6BQAAUQUAAG4FAACTBQAAtAUAAN0FAAD/BQAAHgYAADgGAABKBgAAVgYAAFkGAABdBgAA" - "YgYAAGYGAAB7BgAAgAYAAI8GAACdBgAAtAYAAMMGAADSBgAA3gYAAPIGAAD4BgAABgcAAA4HAAAU" - "BwAAGgcAAB8HAAAoBwAANAcAADoHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A" - "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA" - "AAAAAACMAwAABQAAAAwAAACUAwAABQAAAA4AAACgAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA" - "GwAAABQAAACsAwAAHAAAABQAAACMAwAAHQAAABQAAAC0AwAAHQAAABQAAAC8AwAAAwADAAMAAAAE" - "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA" - "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAJYHAACWBwAABAAA" - "AAEAAAAIAAAAAAAAABkAAABkAwAAnQcAAAAAAAAEAAAAAgAAAAEAAABjBwAAAQAAAIsHAAACAAAA" - "iwcAAJMHAAABAAEAAQAAAEEHAAAEAAAAcBAGAAAADgADAAIAAAAAAEYHAAADAAAAkAABAg8AAAAF" - "AAMABAAAAE0HAAAQAAAAcQAJAAAADAAcAQQAbkAIABBDDAAiAQ0AcCAHAAEAEQEEAAEAAgAAAFYH" - "AAAMAAAAYgACABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAABdBwAACwAAABIgEjH8IAEAEAAK" - "ABJRcSAKAAEADgAAAAAAAAAAAAAAAwAAAAAAAAABAAAAmAIAAAIAAACgAgAABAAAAKgCAAACAAAA" - "AAAAAAMAAAAPAAkAEQAAAAMAAAAHAAkAEQAAAAEAAAAAAAAAAQAAAA4AAAABAAAAFQAGPGluaXQ+" - "AAFJAANJSUkADUlOVk9LRV9TVEFUSUMAAUwABExMTEwAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90" - "YXRpb25zL0NhbGxlZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlv" - "bnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0" - "aG9kSGFuZGxlS2luZDsAM0xjb20vYW5kcm9pZC9qYWNrL2phdmE3L2ludm9rZWN1c3RvbS90ZXN0" - "MDAxL1Rlc3RzOwAaTGRhbHZpay9hbm5vdGF0aW9uL1Rocm93czsAFUxqYXZhL2lvL1ByaW50U3Ry" - "ZWFtOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9T" - "dHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEv" - "bGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRl" - "OwAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0" - "aG9kSGFuZGxlcyRMb29rdXA7ACBMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGph" - "dmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTsAGExqdW5pdC9mcmFtZXdvcmsvQXNzZXJ0OwAQTG9y" - "Zy9qdW5pdC9UZXN0OwAKVGVzdHMuamF2YQABVgACVkkAA1ZJSQACVkwAE1tMamF2YS9sYW5nL1N0" - "cmluZzsAA2FkZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQu" - "MC1lbmcADWVuY2xvc2luZ1R5cGUADWZpZWxkQ2FsbFNpdGUACmZpbmRTdGF0aWMAEmludm9rZU1l" - "dGhvZEhhbmRsZQAEa2luZAAMbGlua2VyTWV0aG9kAAZsb29rdXAABG1haW4ABG5hbWUAA291dAAH" - "cHJpbnRsbgAKcmV0dXJuVHlwZQAEdGVzdAAFdmFsdWUAIgAHDgAvAgAABw4ANQMAAAAHDqUAPwEA" - "Bw60ADsABw6lAAABBCAcAhgAGAAmHAEdAgQgHAMYDxgJGBEjGAQnGwArFygrFx8uGAACBQEwHAEY" - "CwETAAMWABcfFQABAAQBAQkAgYAEtAUBCswFAQrkBQEJlAYEAbwGAAAAEwAAAAAAAAABAAAAAAAA" - "AAEAAAAxAAAAcAAAAAIAAAAWAAAANAEAAAMAAAAJAAAAjAEAAAQAAAADAAAA+AEAAAUAAAALAAAA" - "EAIAAAcAAAACAAAAaAIAAAYAAAABAAAAcAIAAAgAAAABAAAAkAIAAAMQAAADAAAAmAIAAAEgAAAF" - "AAAAtAIAAAYgAAABAAAAZAMAAAEQAAAGAAAAjAMAAAIgAAAxAAAAwgMAAAMgAAAFAAAAQQcAAAQg" - "AAADAAAAYwcAAAUgAAABAAAAlgcAAAAgAAABAAAAnQcAAAAQAAABAAAAvAcAAA==", - // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test002/Tests.java - "ZGV4CjAzOAAzq3aGAwKhT4QQj4lqNfZJAO8Tm24uTyNICQAAcAAAAHhWNBIAAAAAAAAAAGAIAAA2" - "AAAAcAAAABgAAABIAQAACQAAAKgBAAAEAAAAFAIAAA0AAAA0AgAAAQAAAKQCAAB8BgAAzAIAACYE" - "AAAwBAAAOAQAAEQEAABHBAAATAQAAE8EAABVBAAAigQAALwEAADtBAAAIgUAAD4FAABVBQAAaAUA" - "AH0FAACRBQAApQUAALkFAADQBQAA7QUAABIGAAAzBgAAXAYAAH4GAACdBgAAtwYAAMkGAADPBgAA" - "2wYAAN4GAADiBgAA5wYAAOsGAAD/BgAAFAcAABkHAAAoBwAANgcAAE0HAABcBwAAawcAAH4HAACK" - "BwAAkAcAAJgHAACeBwAAqgcAALAHAAC1BwAAxgcAAM8HAADbBwAA4QcAAAMAAAAHAAAACAAAAAkA" - "AAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAA" - "ABgAAAAZAAAAGgAAAB0AAAAhAAAAIgAAAAQAAAAAAAAA8AMAAAYAAAAPAAAA+AMAAAUAAAAQAAAA" - "AAAAAAYAAAASAAAABAQAAB0AAAAVAAAAAAAAAB4AAAAVAAAAEAQAAB8AAAAVAAAA8AMAACAAAAAV" - "AAAAGAQAACAAAAAVAAAAIAQAAAMAAwACAAAABAANACgAAAAIAAcAGwAAAAsABgAwAAAABAAEAAAA" - "AAAEAAQAAQAAAAQAAAAjAAAABAAIAC0AAAAEAAQANAAAAAYABQAyAAAACQAEAAEAAAAMAAQAMQAA" - "AA4ABwABAAAAEAABACoAAAARAAIALAAAABIAAwAuAAAAEwAGACUAAAA4CAAAOAgAAAQAAAABAAAA" - "CQAAAAAAAAAcAAAA0AMAAD8IAAAAAAAAAQAAAAEAAAABAAAADggAAAIAAAAtCAAANQgAAAgAAAAE" - "AAEA6AcAACoAAABxAAoAAAAMABwBBAAbAiMAAABiAwIAYgQCABIVI1UWAGIGAgASB00GBQdxMAsA" - "QwUMA25ACQAQMgwAIgEOAHAgCAABAGkBAQAOAA0AbhAHAAAAKPsAAAAAJAABAAEBDCUBAAEAAQAA" - "APUHAAAEAAAAcBAGAAAADgADAAIAAAAAAPoHAAADAAAAkAABAg8AAAAEAAEAAgAAAAEIAAAMAAAA" - "YgADABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAAAICAAACwAAABIgEjH8IAEAEAAKABJRcSAM" - "AAEADgAAAAAAAAAAAAAAAgAAAAAAAAACAAAAzAIAAAQAAADUAgAAAgAAAAAAAAADAAAABwAKABIA" - "AAADAAAABwAHABYAAAABAAAAAAAAAAEAAAAPAAAAAQAAABcACDxjbGluaXQ+AAY8aW5pdD4ACkdF" - "VF9TVEFUSUMAAUkAA0lJSQABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMv" - "Q2FsbGVkQnlJbnZva2VDdXN0b207ADBMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5r" - "ZXJGaWVsZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRs" - "ZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwMi9UZXN0" - "czsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEUxq" - "YXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVjdDsA" - "EkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93" - "YWJsZTsAG0xqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwAjTGphdmEvbGFuZy9pbnZva2UvQ29u" - "c3RhbnRDYWxsU2l0ZTsAH0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsAJ0xqYXZhL2xh" - "bmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwOwAgTGphdmEvbGFuZy9pbnZva2UvTWV0aG9k" - "SGFuZGxlczsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ABhManVuaXQvZnJhbWV3b3Jr" - "L0Fzc2VydDsAEExvcmcvanVuaXQvVGVzdDsABFRZUEUAClRlc3RzLmphdmEAAVYAAlZJAANWSUkA" - "AlZMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1l" - "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQuMC1lbmcADWVuY2xvc2luZ1R5" - "cGUADWZpZWxkQ2FsbFNpdGUAEWZpZWxkTWV0aG9kSGFuZGxlAApmaW5kU3RhdGljAARraW5kAAZs" - "b29rdXAABG1haW4ACm1ldGhvZFR5cGUABG5hbWUAA291dAAPcHJpbnRTdGFja1RyYWNlAAdwcmlu" - "dGxuAApyZXR1cm5UeXBlAAR0ZXN0AAV2YWx1ZQAoAAcOAR0PAnh3Jh4AIQAHDgA2AgAABw4APwEA" - "Bw60ADsABw6lAAABBCQcAhgAGAApHAEdAgMnGAQrGwAvFygvFyMzGAACBQE1HAEYDAEUAAMWABcj" - "FQABAAQBAQkAiIAE4AUBgYAE0AYBCugGAQmABwQBqAcAAAATAAAAAAAAAAEAAAAAAAAAAQAAADYA" - "AABwAAAAAgAAABgAAABIAQAAAwAAAAkAAACoAQAABAAAAAQAAAAUAgAABQAAAA0AAAA0AgAABwAA" - "AAIAAACcAgAABgAAAAEAAACkAgAACAAAAAEAAADEAgAAAxAAAAIAAADMAgAAASAAAAUAAADgAgAA" - "BiAAAAEAAADQAwAAARAAAAYAAADwAwAAAiAAADYAAAAmBAAAAyAAAAUAAADoBwAABCAAAAMAAAAO" - "CAAABSAAAAEAAAA4CAAAACAAAAEAAAA/CAAAABAAAAEAAABgCAAA", - // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test003/Tests.java - "ZGV4CjAzOABjnhkFatj30/7cHTCJsfr7vAjz9/p+Y+TcCAAAcAAAAHhWNBIAAAAAAAAAAPQHAAAx" - "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAABEBgAAmAIAAOoD" - "AADyAwAA9QMAAP4DAAANBAAAEAQAABYEAABLBAAAfgQAAK8EAADkBAAAAAUAABcFAAAqBQAAPgUA" - "AFIFAABmBQAAfQUAAJoFAAC/BQAA4AUAAAkGAAArBgAASgYAAGQGAAB2BgAAggYAAIUGAACJBgAA" - "jgYAAJIGAACnBgAArAYAALsGAADJBgAA4AYAAO8GAAD+BgAACgcAAB4HAAAkBwAAMgcAADoHAABA" - "BwAARgcAAEsHAABUBwAAYAcAAGYHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A" - "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA" - "AAAAAACkAwAABQAAAAwAAAC0AwAABQAAAA4AAADAAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA" - "GwAAABQAAADMAwAAHAAAABQAAADUAwAAHQAAABQAAADcAwAAHQAAABQAAADkAwAAAwADAAMAAAAE" - "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA" - "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAM4HAADOBwAABAAA" - "AAEAAAAIAAAAAAAAABkAAAB8AwAA1QcAAAAAAAAEAAAAAgAAAAEAAACTBwAAAQAAAMMHAAACAAAA" - "wwcAAMsHAAABAAEAAQAAAG0HAAAEAAAAcBAGAAAADgAHAAYAAAAAAHIHAAAHAAAAkAABArAwsECw" - "ULBgDwAAAAUAAwAEAAAAfQcAABAAAABxAAkAAAAMABwBBABuQAgAEEMMACIBDQBwIAcAAQARAQgA" - "AQACAAAAhgcAABAAAABiBgIAEhASIRIyEkMSVBJl/QYAAAAACgBuIAUABgAOAAcAAQACAAAAjQcA" - "ABAAAAASEBIhEjISQxJUEmX9BgEAAAAKABMBFQBxIAoAAQAOAAAAAAAAAAAAAwAAAAAAAAABAAAA" - "mAIAAAIAAACgAgAABAAAAKgCAAAGAAAAAAAAAAAAAAAAAAAAAwAAAA8ACQARAAAAAwAAAAcACQAR" - "AAAAAQAAAAAAAAACAAAAAAAAAAEAAAAOAAAAAQAAABUABjxpbml0PgABSQAHSUlJSUlJSQANSU5W" - "T0tFX1NUQVRJQwABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvQ2FsbGVk" - "QnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5rZXJNZXRo" - "b2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9NZXRob2RIYW5kbGVLaW5k" - "OwAzTGNvbS9hbmRyb2lkL2phY2svamF2YTcvaW52b2tlY3VzdG9tL3Rlc3QwMDMvVGVzdHM7ABpM" - "ZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9s" - "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" - "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D" - "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n" - "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv" - "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r" - "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7" - "AApUZXN0cy5qYXZhAAFWAAJWSQADVklJAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwADYWRkAA1h" - "cmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAFWVtaXR0ZXI6IGphY2stNC4wLWVuZwANZW5jbG9z" - "aW5nVHlwZQANZmllbGRDYWxsU2l0ZQAKZmluZFN0YXRpYwASaW52b2tlTWV0aG9kSGFuZGxlAARr" - "aW5kAAxsaW5rZXJNZXRob2QABmxvb2t1cAAEbWFpbgAEbmFtZQADb3V0AAdwcmludGxuAApyZXR1" - "cm5UeXBlAAR0ZXN0AAV2YWx1ZQAiAAcOAC8GAAAAAAAABw4ANQMAAAAHDqUAPwEABw7wADsABw7w" - "AAABBCAcBhgAGAAYABgAGAAYACYcAR0CBCAcAxgPGAkYESMYBCcbACsXKCsXHy4YAAIFATAcARgL" - "ARMAAxYAFx8VAAEABAEBCQCBgAS0BQEKzAUBCuwFAQmcBgQBzAYAAAATAAAAAAAAAAEAAAAAAAAA" - "AQAAADEAAABwAAAAAgAAABYAAAA0AQAAAwAAAAkAAACMAQAABAAAAAMAAAD4AQAABQAAAAsAAAAQ" - "AgAABwAAAAIAAABoAgAABgAAAAEAAABwAgAACAAAAAEAAACQAgAAAxAAAAMAAACYAgAAASAAAAUA" - "AAC0AgAABiAAAAEAAAB8AwAAARAAAAcAAACkAwAAAiAAADEAAADqAwAAAyAAAAUAAABtBwAABCAA" - "AAMAAACTBwAABSAAAAEAAADOBwAAACAAAAEAAADVBwAAABAAAAEAAAD0BwAA", - // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test004/Tests.java - "ZGV4CjAzOABvUVfbV74qWbSOEsgKP+EzahlNQLW2/8TMDAAAcAAAAHhWNBIAAAAAAAAAAOQLAABS" - "AAAAcAAAAB8AAAC4AQAAEAAAADQCAAADAAAA9AIAABIAAAAMAwAAAQAAAKQDAAAACQAAzAMAANYF" - "AADZBQAA4QUAAOkFAADsBQAA7wUAAPIFAAD1BQAA/AUAAP8FAAAEBgAAEwYAABYGAAAZBgAAHwYA" - "AC8GAABkBgAAjQYAAMAGAADxBgAAJgcAAEUHAABhBwAAeAcAAIoHAACdBwAAsQcAAMUHAADZBwAA" - "8AcAAA0IAAAyCAAAUwgAAHwIAACeCAAAvQgAANcIAADpCAAA7AgAAPgIAAD7CAAAAAkAAAYJAAAM" - "CQAAEAkAABUJAAAaCQAAHgkAACMJAAAnCQAAKgkAADMJAABICQAATQkAAFwJAABqCQAAdgkAAIQJ" - "AACPCQAAmgkAAKYJAACzCQAAygkAANkJAADoCQAA9AkAAAAKAAAKCgAAHgoAACQKAAAyCgAAPQoA" - "AEUKAABLCgAAYgoAAGgKAABtCgAAdgoAAIIKAACOCgAAmwoAAKEKAAADAAAABAAAAAUAAAAGAAAA" - "CAAAAAsAAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABgAAAAZAAAAGgAAABsAAAAc" - "AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJwAAADEAAAAzAAAACQAAAAQA" - "AABMBQAADgAAABMAAABUBQAADQAAABUAAAB0BQAADAAAABYAAAAAAAAAJwAAABwAAAAAAAAAKAAA" - "ABwAAACABQAAKQAAABwAAACIBQAAKgAAABwAAACUBQAAKwAAABwAAACgBQAALAAAABwAAABMBQAA" - "LQAAABwAAACoBQAALwAAABwAAACwBQAALwAAABwAAAC4BQAALgAAABwAAADABQAAMAAAABwAAADI" - "BQAALgAAABwAAADQBQAACQAJAAoAAAAKABMAPwAAABEADQBLAAAACgAEAAIAAAAKAAAANAAAAAoA" - "AQBFAAAACgAPAEgAAAAKAAQAUAAAAA0ACABMAAAADwAEAAIAAAAUAA0AAgAAABYAAgBAAAAAFwAD" - "AEcAAAAZAAUANgAAABkABgA2AAAAGQAHADYAAAAZAAkANgAAABkACgA2AAAAGQALADYAAAAZAAwA" - "NgAAABkADgA3AAAAnQsAAJ0LAAAKAAAAAQAAAA8AAAAAAAAAJgAAACQFAADGCwAAAAAAAAQAAAAC" - "AAAAAQAAAN4KAAACAAAAegsAAJILAAACAAAAkgsAAJoLAAABAAEAAQAAAKgKAAAEAAAAcBAGAAAA" - "DgADAAIAAAAAAK0KAAADAAAAkAABAg8AAAAYAA8ABgAAALQKAABTAAAAcRARAAwAEhJxIA0A0gAT" - "AmEAcSAKAOIAEwIABHEgDQDyABISAgAQAHEgDQACABICFAOamTFBAgARAHEwDAADAhYGAAAYApqZ" - "mZmZmQFABQQSAHcGCwACABsCBwAAAAgAFABxIBAAAgAcAgoACAAVAHEgDwACABcCFc1bBwUAFgBx" - "QA4AMhBxAAkAAAAMAhwDCgBuQAgAMroMAiIDFABwIAcAIwARAwAABAABAAIAAADRCgAADAAAAGIA" - "AgASIRIy/CAAACEACgFuIAUAEAAOAAMAAQACAAAA2AoAAAsAAAASIBIx/CABABAACgASUXEgDQAB" - "AA4AAAAAAAAAAAAAAAMAAAAAAAAAAQAAAMwDAAACAAAA1AMAAAQAAADgAwAAAgAAAAQABAANAAAA" - "FgAQABgAHQAAAAEAGwAEAAMAAgAQAA4ABQAAAAMAAAAOABAAGAAAAAIAAAABAAEAAwAAAAIAAgAC" - "AAAAAwAAAAMAAwADAAAAAQAAAAQAAAACAAAABQAFAAIAAAAPAA8AAgAAABAAEAABAAAAFQAAAAEA" - "AAAdAAAAAQAAAB4AASgABjwqPjtKKQAGPGluaXQ+AAFCAAFDAAFEAAFGAAVIZWxsbwABSQADSUlJ" - "AA1JTlZPS0VfU1RBVElDAAFKAAFMAARMTExMAA5MTExMWkJDU0lGRExMSgAzTGNvbS9hbmRyb2lk" - "L2phY2svYW5ub3RhdGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ACdMY29tL2FuZHJvaWQvamFj" - "ay9hbm5vdGF0aW9ucy9Db25zdGFudDsAMUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xp" - "bmtlck1ldGhvZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhh" - "bmRsZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwNC9U" - "ZXN0czsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlrL2Fubm90YXRpb24v" - "VGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" - "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" - "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D" - "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n" - "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv" - "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r" - "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7" - "AAFTAApUZXN0cy5qYXZhAAFWAANWQ0MABFZEREQABFZGRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZM" - "TAACVloAAVoAB1pCQ1NJRkQAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1lbnRUeXBl" - "cwAMYXNzZXJ0RXF1YWxzAAphc3NlcnRUcnVlAAxib29sZWFuVmFsdWUACWJ5dGVWYWx1ZQAJY2hh" - "clZhbHVlAApjbGFzc1ZhbHVlAAtkb3VibGVWYWx1ZQAVZW1pdHRlcjogamFjay00LjAtZW5nAA1l" - "bmNsb3NpbmdUeXBlAA1maWVsZENhbGxTaXRlAApmaW5kU3RhdGljAApmbG9hdFZhbHVlAAhpbnRW" - "YWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAxsaW5rZXJNZXRob2QACWxvbmdWYWx1ZQAG" - "bG9va3VwAARtYWluABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MABG5hbWUAA291dAAHcHJpbnRsbgAK" - "cmV0dXJuVHlwZQAKc2hvcnRWYWx1ZQALc3RyaW5nVmFsdWUABHRlc3QABXZhbHVlACMABw4ANwIA" - "AAcOAD4NAAAAAAAAAAAAAAAAAAcOPEtaWmmWw4d4h6UAUgEABw60AE4ABw6lAAAGBTUcAhgEGARD" - "HAEdCAQ1HA0YFhgQGBgYHRgAGAEYGxgEGAMYAhgQGA4YBT4YCkQbAEoXRUkcCh0HATgcAT8dBwE5" - "HAEAAR0HATocAQNhHQcBThwBIgAEHQcBQhwBBAEdBwFBHAFwmpkxQR0HATwcAfGamZmZmZkBQB0H" - "AU8cARcHHQcBOxwBGAodBwFGHAFmFc1bB0oXNE0YBAILAVEcCRcAFyAXGhciFzIXGhcXFwEXHQIM" - "AVEcARgSARoADRYAFzQVAAQBBAEEYSQABAQBcJqZMUHxmpmZmZmZAUAXBxgKZhXNWwcBAAQBAQkA" - "gYAE7AcBCoQIAQqcCAEJ1AkEAfwJAAATAAAAAAAAAAEAAAAAAAAAAQAAAFIAAABwAAAAAgAAAB8A" - "AAC4AQAAAwAAABAAAAA0AgAABAAAAAMAAAD0AgAABQAAABIAAAAMAwAABwAAAAIAAACcAwAABgAA" - "AAEAAACkAwAACAAAAAEAAADEAwAAAxAAAAMAAADMAwAAASAAAAUAAADsAwAABiAAAAEAAAAkBQAA" - "ARAAAA0AAABMBQAAAiAAAFIAAADWBQAAAyAAAAUAAACoCgAABCAAAAQAAADeCgAABSAAAAEAAACd" - "CwAAACAAAAEAAADGCwAAABAAAAEAAADkCwAA" -}; - -TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) { - for (size_t i = 0; i < arraysize(kInvokeCustomDexFiles); ++i) { - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kInvokeCustomDexFiles[i], &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "good checksum, verify", - /*verify_checksum*/ true, - &error_msg)); - // TODO(oth): Test corruptions (b/35308502) - } -} - -TEST_F(DexFileVerifierTest, BadStaticFieldInitialValuesArray) { - // Generated DEX file version (037) from: - // - // .class public LBadStaticFieldInitialValuesArray; - // .super Ljava/lang/Object; - // - // # static fields - // .field static final c:C = 'c' - // .field static final i:I = 0x1 - // .field static final s:Ljava/lang/String; = "s" - // - // # direct methods - // .method public constructor <init>()V - // .registers 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // return-void - // .end method - // - // Output file was hex edited so that static field "i" has string typing in initial values array. - static const char kDexBase64[] = - "ZGV4CjAzNQBrMi4cCPcMvvXNRw0uI6RRubwMPwgEYXIsAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAL" - "AAAAcAAAAAYAAACcAAAAAQAAALQAAAADAAAAwAAAAAIAAADYAAAAAQAAAOgAAAAkAQAACAEAACAB" - "AAAoAQAAMAEAADMBAAA2AQAAOwEAAE8BAABjAQAAZgEAAGkBAABsAQAAAgAAAAMAAAAEAAAABQAA" - "AAYAAAAHAAAABwAAAAUAAAAAAAAAAgAAAAgAAAACAAEACQAAAAIABAAKAAAAAgAAAAAAAAADAAAA" - "AAAAAAIAAAABAAAAAwAAAAAAAAABAAAAAAAAAHsBAAB0AQAAAQABAAEAAABvAQAABAAAAHAQAQAA" - "AA4ABjxpbml0PgAGQS5qYXZhAAFDAAFJAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" - "bGFuZy9TdHJpbmc7AAFWAAFjAAFpAAFzAAEABw4AAwNjFwoXCgMAAQAAGAEYARgAgYAEiAIADQAA" - "AAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAGAAAAnAAAAAMAAAABAAAAtAAAAAQAAAADAAAA" - "wAAAAAUAAAACAAAA2AAAAAYAAAABAAAA6AAAAAEgAAABAAAACAEAAAIgAAALAAAAIAEAAAMgAAAB" - "AAAAbwEAAAUgAAABAAAAdAEAAAAgAAABAAAAewEAAAAQAAABAAAAjAEAAA=="; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "bad static field initial values array", - /*verify_checksum*/ true, - &error_msg)); -} - -TEST_F(DexFileVerifierTest, GoodStaticFieldInitialValuesArray) { - // Generated DEX file version (037) from: - // - // .class public LGoodStaticFieldInitialValuesArray; - // .super Ljava/lang/Object; - // - // # static fields - // .field static final b:B = 0x1t - // .field static final c:C = 'c' - // .field static final d:D = 0.6 - // .field static final f:F = 0.5f - // .field static final i:I = 0x3 - // .field static final j:J = 0x4L - // .field static final l1:Ljava/lang/String; - // .field static final l2:Ljava/lang/String; = "s" - // .field static final l3:Ljava/lang/Class; = Ljava/lang/String; - // .field static final s:S = 0x2s - // .field static final z:Z = true - // - // # direct methods - // .method public constructor <init>()V - // .registers 1 - // invoke-direct {p0}, Ljava/lang/Object;-><init>()V - // return-void - // .end method - static const char kDexBase64[] = - "ZGV4CjAzNQAwWxLbdhFa1NGiFWjsy5fhUCHxe5QHtPY8AwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAZ" - "AAAAcAAAAA0AAADUAAAAAQAAAAgBAAALAAAAFAEAAAIAAABsAQAAAQAAAHwBAACgAQAAnAEAAJwB" - "AACkAQAApwEAAKoBAACtAQAAsAEAALMBAAC2AQAA2wEAAO4BAAACAgAAFgIAABkCAAAcAgAAHwIA" - "ACICAAAlAgAAKAIAACsCAAAuAgAAMQIAADUCAAA5AgAAPQIAAEACAAABAAAAAgAAAAMAAAAEAAAA" - "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADAAAAAsAAAAAAAAABgAAAA4AAAAG" - "AAEADwAAAAYAAgAQAAAABgADABEAAAAGAAQAEgAAAAYABQATAAAABgAJABQAAAAGAAkAFQAAAAYA" - "BwAWAAAABgAKABcAAAAGAAwAGAAAAAYAAAAAAAAACAAAAAAAAAAGAAAAAQAAAAgAAAAAAAAA////" - "/wAAAAB8AgAARAIAAAY8aW5pdD4AAUIAAUMAAUQAAUYAAUkAAUoAI0xHb29kU3RhdGljRmllbGRJ" - "bml0aWFsVmFsdWVzQXJyYXk7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7" - "ABJMamF2YS9sYW5nL1N0cmluZzsAAVMAAVYAAVoAAWIAAWMAAWQAAWYAAWkAAWoAAmwxAAJsMgAC" - "bDMAAXMAAXoAAAsAAQNj8TMzMzMzM+M/ED8EAwYEHhcXGAkCAj8AAAAAAQABAAEAAAAAAAAABAAA" - "AHAQAQAAAA4ACwABAAAYARgBGAEYARgBGAEYARgBGAEYARgAgYAE5AQNAAAAAAAAAAEAAAAAAAAA" - "AQAAABkAAABwAAAAAgAAAA0AAADUAAAAAwAAAAEAAAAIAQAABAAAAAsAAAAUAQAABQAAAAIAAABs" - "AQAABgAAAAEAAAB8AQAAAiAAABkAAACcAQAABSAAAAEAAABEAgAAAxAAAAEAAABgAgAAASAAAAEA" - "AABkAgAAACAAAAEAAAB8AgAAABAAAAEAAACcAgAA"; - - size_t length; - std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); - CHECK(dex_bytes != nullptr); - // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); - std::string error_msg; - EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), - dex_file->Begin(), - dex_file->Size(), - "good static field initial values array", - /*verify_checksum*/ true, - &error_msg)); -} - -} // namespace art diff --git a/runtime/dex/dex_instruction-inl.h b/runtime/dex/dex_instruction-inl.h deleted file mode 100644 index a6b8414e62..0000000000 --- a/runtime/dex/dex_instruction-inl.h +++ /dev/null @@ -1,558 +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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_ -#define ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_ - -#include "dex_instruction.h" - -namespace art { - -//------------------------------------------------------------------------------ -// VRegA -//------------------------------------------------------------------------------ -inline bool Instruction::HasVRegA() const { - switch (FormatOf(Opcode())) { - case k10t: return true; - case k10x: return true; - case k11n: return true; - case k11x: return true; - case k12x: return true; - case k20t: return true; - case k21c: return true; - case k21h: return true; - case k21s: return true; - case k21t: return true; - case k22b: return true; - case k22c: return true; - case k22s: return true; - case k22t: return true; - case k22x: return true; - case k23x: return true; - case k30t: return true; - case k31c: return true; - case k31i: return true; - case k31t: return true; - case k32x: return true; - case k35c: return true; - case k3rc: return true; - case k45cc: return true; - case k4rcc: return true; - case k51l: return true; - default: return false; - } -} - -inline int32_t Instruction::VRegA() const { - switch (FormatOf(Opcode())) { - case k10t: return VRegA_10t(); - case k10x: return VRegA_10x(); - case k11n: return VRegA_11n(); - case k11x: return VRegA_11x(); - case k12x: return VRegA_12x(); - case k20t: return VRegA_20t(); - case k21c: return VRegA_21c(); - case k21h: return VRegA_21h(); - case k21s: return VRegA_21s(); - case k21t: return VRegA_21t(); - case k22b: return VRegA_22b(); - case k22c: return VRegA_22c(); - case k22s: return VRegA_22s(); - case k22t: return VRegA_22t(); - case k22x: return VRegA_22x(); - case k23x: return VRegA_23x(); - case k30t: return VRegA_30t(); - case k31c: return VRegA_31c(); - case k31i: return VRegA_31i(); - case k31t: return VRegA_31t(); - case k32x: return VRegA_32x(); - case k35c: return VRegA_35c(); - case k3rc: return VRegA_3rc(); - case k45cc: return VRegA_45cc(); - case k4rcc: return VRegA_4rcc(); - case k51l: return VRegA_51l(); - default: - LOG(FATAL) << "Tried to access vA of instruction " << Name() << " which has no A operand."; - exit(EXIT_FAILURE); - } -} - -inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k10t); - return static_cast<int8_t>(InstAA(inst_data)); -} - -inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k10x); - return InstAA(inst_data); -} - -inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k11n); - return InstA(inst_data); -} - -inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k11x); - return InstAA(inst_data); -} - -inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k12x); - return InstA(inst_data); -} - -inline int16_t Instruction::VRegA_20t() const { - DCHECK_EQ(FormatOf(Opcode()), k20t); - return static_cast<int16_t>(Fetch16(1)); -} - -inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k21c); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k21h); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k21s); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k21t); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22b); - return InstAA(inst_data); -} - -inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22c); - return InstA(inst_data); -} - -inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22s); - return InstA(inst_data); -} - -inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22t); - return InstA(inst_data); -} - -inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22x); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k23x); - return InstAA(inst_data); -} - -inline int32_t Instruction::VRegA_30t() const { - DCHECK_EQ(FormatOf(Opcode()), k30t); - return static_cast<int32_t>(Fetch32(1)); -} - -inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k31c); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k31i); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k31t); - return InstAA(inst_data); -} - -inline uint16_t Instruction::VRegA_32x() const { - DCHECK_EQ(FormatOf(Opcode()), k32x); - return Fetch16(1); -} - -inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k35c); - return InstB(inst_data); // This is labeled A in the spec. -} - -inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k3rc); - return InstAA(inst_data); -} - -inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k51l); - return InstAA(inst_data); -} - -inline uint4_t Instruction::VRegA_45cc(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k45cc); - return InstB(inst_data); // This is labeled A in the spec. -} - -inline uint8_t Instruction::VRegA_4rcc(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k4rcc); - return InstAA(inst_data); -} - -//------------------------------------------------------------------------------ -// VRegB -//------------------------------------------------------------------------------ -inline bool Instruction::HasVRegB() const { - switch (FormatOf(Opcode())) { - case k11n: return true; - case k12x: return true; - case k21c: return true; - case k21h: return true; - case k21s: return true; - case k21t: return true; - case k22b: return true; - case k22c: return true; - case k22s: return true; - case k22t: return true; - case k22x: return true; - case k23x: return true; - case k31c: return true; - case k31i: return true; - case k31t: return true; - case k32x: return true; - case k35c: return true; - case k3rc: return true; - case k45cc: return true; - case k4rcc: return true; - case k51l: return true; - default: return false; - } -} - -inline bool Instruction::HasWideVRegB() const { - return FormatOf(Opcode()) == k51l; -} - -inline int32_t Instruction::VRegB() const { - switch (FormatOf(Opcode())) { - case k11n: return VRegB_11n(); - case k12x: return VRegB_12x(); - case k21c: return VRegB_21c(); - case k21h: return VRegB_21h(); - case k21s: return VRegB_21s(); - case k21t: return VRegB_21t(); - case k22b: return VRegB_22b(); - case k22c: return VRegB_22c(); - case k22s: return VRegB_22s(); - case k22t: return VRegB_22t(); - case k22x: return VRegB_22x(); - case k23x: return VRegB_23x(); - case k31c: return VRegB_31c(); - case k31i: return VRegB_31i(); - case k31t: return VRegB_31t(); - case k32x: return VRegB_32x(); - case k35c: return VRegB_35c(); - case k3rc: return VRegB_3rc(); - case k45cc: return VRegB_45cc(); - case k4rcc: return VRegB_4rcc(); - case k51l: return VRegB_51l(); - default: - LOG(FATAL) << "Tried to access vB of instruction " << Name() << " which has no B operand."; - exit(EXIT_FAILURE); - } -} - -inline uint64_t Instruction::WideVRegB() const { - return VRegB_51l(); -} - -inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k11n); - return static_cast<int4_t>((InstB(inst_data) << 28) >> 28); -} - -inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k12x); - return InstB(inst_data); -} - -inline uint16_t Instruction::VRegB_21c() const { - DCHECK_EQ(FormatOf(Opcode()), k21c); - return Fetch16(1); -} - -inline uint16_t Instruction::VRegB_21h() const { - DCHECK_EQ(FormatOf(Opcode()), k21h); - return Fetch16(1); -} - -inline int16_t Instruction::VRegB_21s() const { - DCHECK_EQ(FormatOf(Opcode()), k21s); - return static_cast<int16_t>(Fetch16(1)); -} - -inline int16_t Instruction::VRegB_21t() const { - DCHECK_EQ(FormatOf(Opcode()), k21t); - return static_cast<int16_t>(Fetch16(1)); -} - -inline uint8_t Instruction::VRegB_22b() const { - DCHECK_EQ(FormatOf(Opcode()), k22b); - return static_cast<uint8_t>(Fetch16(1) & 0xff); -} - -inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22c); - return InstB(inst_data); -} - -inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22s); - return InstB(inst_data); -} - -inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const { - DCHECK_EQ(FormatOf(Opcode()), k22t); - return InstB(inst_data); -} - -inline uint16_t Instruction::VRegB_22x() const { - DCHECK_EQ(FormatOf(Opcode()), k22x); - return Fetch16(1); -} - -inline uint8_t Instruction::VRegB_23x() const { - DCHECK_EQ(FormatOf(Opcode()), k23x); - return static_cast<uint8_t>(Fetch16(1) & 0xff); -} - -inline uint32_t Instruction::VRegB_31c() const { - DCHECK_EQ(FormatOf(Opcode()), k31c); - return Fetch32(1); -} - -inline int32_t Instruction::VRegB_31i() const { - DCHECK_EQ(FormatOf(Opcode()), k31i); - return static_cast<int32_t>(Fetch32(1)); -} - -inline int32_t Instruction::VRegB_31t() const { - DCHECK_EQ(FormatOf(Opcode()), k31t); - return static_cast<int32_t>(Fetch32(1)); -} - -inline uint16_t Instruction::VRegB_32x() const { - DCHECK_EQ(FormatOf(Opcode()), k32x); - return Fetch16(2); -} - -inline uint16_t Instruction::VRegB_35c() const { - DCHECK_EQ(FormatOf(Opcode()), k35c); - return Fetch16(1); -} - -inline uint16_t Instruction::VRegB_3rc() const { - DCHECK_EQ(FormatOf(Opcode()), k3rc); - return Fetch16(1); -} - -inline uint16_t Instruction::VRegB_45cc() const { - DCHECK_EQ(FormatOf(Opcode()), k45cc); - return Fetch16(1); -} - -inline uint16_t Instruction::VRegB_4rcc() const { - DCHECK_EQ(FormatOf(Opcode()), k4rcc); - return Fetch16(1); -} - -inline uint64_t Instruction::VRegB_51l() const { - DCHECK_EQ(FormatOf(Opcode()), k51l); - uint64_t vB_wide = Fetch32(1) | ((uint64_t) Fetch32(3) << 32); - return vB_wide; -} - -//------------------------------------------------------------------------------ -// VRegC -//------------------------------------------------------------------------------ -inline bool Instruction::HasVRegC() const { - switch (FormatOf(Opcode())) { - case k22b: return true; - case k22c: return true; - case k22s: return true; - case k22t: return true; - case k23x: return true; - case k35c: return true; - case k3rc: return true; - case k45cc: return true; - case k4rcc: return true; - default: return false; - } -} - -inline int32_t Instruction::VRegC() const { - switch (FormatOf(Opcode())) { - case k22b: return VRegC_22b(); - case k22c: return VRegC_22c(); - case k22s: return VRegC_22s(); - case k22t: return VRegC_22t(); - case k23x: return VRegC_23x(); - case k35c: return VRegC_35c(); - case k3rc: return VRegC_3rc(); - case k45cc: return VRegC_45cc(); - case k4rcc: return VRegC_4rcc(); - default: - LOG(FATAL) << "Tried to access vC of instruction " << Name() << " which has no C operand."; - exit(EXIT_FAILURE); - } -} - -inline int8_t Instruction::VRegC_22b() const { - DCHECK_EQ(FormatOf(Opcode()), k22b); - return static_cast<int8_t>(Fetch16(1) >> 8); -} - -inline uint16_t Instruction::VRegC_22c() const { - DCHECK_EQ(FormatOf(Opcode()), k22c); - return Fetch16(1); -} - -inline int16_t Instruction::VRegC_22s() const { - DCHECK_EQ(FormatOf(Opcode()), k22s); - return static_cast<int16_t>(Fetch16(1)); -} - -inline int16_t Instruction::VRegC_22t() const { - DCHECK_EQ(FormatOf(Opcode()), k22t); - return static_cast<int16_t>(Fetch16(1)); -} - -inline uint8_t Instruction::VRegC_23x() const { - DCHECK_EQ(FormatOf(Opcode()), k23x); - return static_cast<uint8_t>(Fetch16(1) >> 8); -} - -inline uint4_t Instruction::VRegC_35c() const { - DCHECK_EQ(FormatOf(Opcode()), k35c); - return static_cast<uint4_t>(Fetch16(2) & 0x0f); -} - -inline uint16_t Instruction::VRegC_3rc() const { - DCHECK_EQ(FormatOf(Opcode()), k3rc); - return Fetch16(2); -} - -inline uint4_t Instruction::VRegC_45cc() const { - DCHECK_EQ(FormatOf(Opcode()), k45cc); - return static_cast<uint4_t>(Fetch16(2) & 0x0f); -} - -inline uint16_t Instruction::VRegC_4rcc() const { - DCHECK_EQ(FormatOf(Opcode()), k4rcc); - return Fetch16(2); -} - -//------------------------------------------------------------------------------ -// VRegH -//------------------------------------------------------------------------------ -inline bool Instruction::HasVRegH() const { - switch (FormatOf(Opcode())) { - case k45cc: return true; - case k4rcc: return true; - default : return false; - } -} - -inline int32_t Instruction::VRegH() const { - switch (FormatOf(Opcode())) { - case k45cc: return VRegH_45cc(); - case k4rcc: return VRegH_4rcc(); - default : - LOG(FATAL) << "Tried to access vH of instruction " << Name() << " which has no H operand."; - exit(EXIT_FAILURE); - } -} - -inline uint16_t Instruction::VRegH_45cc() const { - DCHECK_EQ(FormatOf(Opcode()), k45cc); - return Fetch16(3); -} - -inline uint16_t Instruction::VRegH_4rcc() const { - DCHECK_EQ(FormatOf(Opcode()), k4rcc); - return Fetch16(3); -} - -inline bool Instruction::HasVarArgs() const { - return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc); -} - -inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const { - DCHECK(HasVarArgs()); - - /* - * Note that the fields mentioned in the spec don't appear in - * their "usual" positions here compared to most formats. This - * was done so that the field names for the argument count and - * reference index match between this format and the corresponding - * range formats (3rc and friends). - * - * Bottom line: The argument count is always in vA, and the - * method constant (or equivalent) is always in vB. - */ - uint16_t regList = Fetch16(2); - uint4_t count = InstB(inst_data); // This is labeled A in the spec. - DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")"; - - /* - * Copy the argument registers into the arg[] array, and - * also copy the first argument (if any) into vC. (The - * DecodedInstruction structure doesn't have separate - * fields for {vD, vE, vF, vG}, so there's no need to make - * copies of those.) Note that cases 5..2 fall through. - */ - switch (count) { - case 5: - arg[4] = InstA(inst_data); - FALLTHROUGH_INTENDED; - case 4: - arg[3] = (regList >> 12) & 0x0f; - FALLTHROUGH_INTENDED; - case 3: - arg[2] = (regList >> 8) & 0x0f; - FALLTHROUGH_INTENDED; - case 2: - arg[1] = (regList >> 4) & 0x0f; - FALLTHROUGH_INTENDED; - case 1: - arg[0] = regList & 0x0f; - break; - default: // case 0 - break; // Valid, but no need to do anything. - } -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_ diff --git a/runtime/dex/dex_instruction.cc b/runtime/dex/dex_instruction.cc deleted file mode 100644 index b84791ffae..0000000000 --- a/runtime/dex/dex_instruction.cc +++ /dev/null @@ -1,561 +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 "dex_instruction-inl.h" - -#include <inttypes.h> - -#include <iomanip> -#include <sstream> - -#include "android-base/stringprintf.h" - -#include "dex_file-inl.h" -#include "utf.h" - -namespace art { - -using android::base::StringPrintf; - -const char* const Instruction::kInstructionNames[] = { -#define INSTRUCTION_NAME(o, c, pname, f, i, a, e, v) pname, -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_NAME) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_NAME -}; - -static_assert(sizeof(Instruction::InstructionDescriptor) == 8u, "Unexpected descriptor size"); - -static constexpr int8_t InstructionSizeInCodeUnitsByOpcode(Instruction::Code opcode, - Instruction::Format format) { - if (opcode == Instruction::Code::NOP) { - return -1; - } else if ((format >= Instruction::Format::k10x) && (format <= Instruction::Format::k10t)) { - return 1; - } else if ((format >= Instruction::Format::k20t) && (format <= Instruction::Format::k22c)) { - return 2; - } else if ((format >= Instruction::Format::k32x) && (format <= Instruction::Format::k3rc)) { - return 3; - } else if ((format >= Instruction::Format::k45cc) && (format <= Instruction::Format::k4rcc)) { - return 4; - } else if (format == Instruction::Format::k51l) { - return 5; - } else { - return -1; - } -} - -Instruction::InstructionDescriptor const Instruction::kInstructionDescriptors[] = { -#define INSTRUCTION_DESCR(opcode, c, p, format, index, flags, eflags, vflags) \ - { vflags, \ - format, \ - index, \ - flags, \ - InstructionSizeInCodeUnitsByOpcode((c), (format)), \ - }, -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_DESCR) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_DESCR -}; - -int32_t Instruction::GetTargetOffset() const { - switch (FormatOf(Opcode())) { - // Cases for conditional branches follow. - case k22t: return VRegC_22t(); - case k21t: return VRegB_21t(); - // Cases for unconditional branches follow. - case k10t: return VRegA_10t(); - case k20t: return VRegA_20t(); - case k30t: return VRegA_30t(); - default: LOG(FATAL) << "Tried to access the branch offset of an instruction " << Name() << - " which does not have a target operand."; - } - return 0; -} - -bool Instruction::CanFlowThrough() const { - const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); - uint16_t insn = *insns; - Code opcode = static_cast<Code>(insn & 0xFF); - return FlagsOf(opcode) & Instruction::kContinue; -} - -size_t Instruction::SizeInCodeUnitsComplexOpcode() const { - const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); - // Handle special NOP encoded variable length sequences. - switch (*insns) { - case kPackedSwitchSignature: - return (4 + insns[1] * 2); - case kSparseSwitchSignature: - return (2 + insns[1] * 4); - case kArrayDataSignature: { - uint16_t element_size = insns[1]; - uint32_t length = insns[2] | (((uint32_t)insns[3]) << 16); - // The plus 1 is to round up for odd size and width. - return (4 + (element_size * length + 1) / 2); - } - default: - if ((*insns & 0xFF) == 0) { - return 1; // NOP. - } else { - LOG(FATAL) << "Unreachable: " << DumpString(nullptr); - UNREACHABLE(); - } - } -} - -size_t Instruction::CodeUnitsRequiredForSizeOfComplexOpcode() const { - const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); - // Handle special NOP encoded variable length sequences. - switch (*insns) { - case kPackedSwitchSignature: - FALLTHROUGH_INTENDED; - case kSparseSwitchSignature: - return 2; - case kArrayDataSignature: - return 4; - default: - if ((*insns & 0xFF) == 0) { - return 1; // NOP. - } else { - LOG(FATAL) << "Unreachable: " << DumpString(nullptr); - UNREACHABLE(); - } - } -} - -std::string Instruction::DumpHex(size_t code_units) const { - size_t inst_length = SizeInCodeUnits(); - if (inst_length > code_units) { - inst_length = code_units; - } - std::ostringstream os; - const uint16_t* insn = reinterpret_cast<const uint16_t*>(this); - for (size_t i = 0; i < inst_length; i++) { - os << StringPrintf("0x%04x", insn[i]) << " "; - } - for (size_t i = inst_length; i < code_units; i++) { - os << " "; - } - return os.str(); -} - -std::string Instruction::DumpHexLE(size_t instr_code_units) const { - size_t inst_length = SizeInCodeUnits(); - if (inst_length > instr_code_units) { - inst_length = instr_code_units; - } - std::ostringstream os; - const uint16_t* insn = reinterpret_cast<const uint16_t*>(this); - for (size_t i = 0; i < inst_length; i++) { - os << StringPrintf("%02x%02x", static_cast<uint8_t>(insn[i] & 0x00FF), - static_cast<uint8_t>((insn[i] & 0xFF00) >> 8)) << " "; - } - for (size_t i = inst_length; i < instr_code_units; i++) { - os << " "; - } - return os.str(); -} - -std::string Instruction::DumpString(const DexFile* file) const { - std::ostringstream os; - const char* opcode = kInstructionNames[Opcode()]; - switch (FormatOf(Opcode())) { - case k10x: os << opcode; break; - case k12x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_12x(), VRegB_12x()); break; - case k11n: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_11n(), VRegB_11n()); break; - case k11x: os << StringPrintf("%s v%d", opcode, VRegA_11x()); break; - case k10t: os << StringPrintf("%s %+d", opcode, VRegA_10t()); break; - case k20t: os << StringPrintf("%s %+d", opcode, VRegA_20t()); break; - case k22x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_22x(), VRegB_22x()); break; - case k21t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_21t(), VRegB_21t()); break; - case k21s: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_21s(), VRegB_21s()); break; - case k21h: { - // op vAA, #+BBBB0000[00000000] - if (Opcode() == CONST_HIGH16) { - uint32_t value = VRegB_21h() << 16; - os << StringPrintf("%s v%d, #int %+d // 0x%x", opcode, VRegA_21h(), value, value); - } else { - uint64_t value = static_cast<uint64_t>(VRegB_21h()) << 48; - os << StringPrintf("%s v%d, #long %+" PRId64 " // 0x%" PRIx64, opcode, VRegA_21h(), - value, value); - } - } - break; - case k21c: { - switch (Opcode()) { - case CONST_STRING: - if (file != nullptr) { - uint32_t string_idx = VRegB_21c(); - if (string_idx < file->NumStringIds()) { - os << StringPrintf( - "const-string v%d, %s // string@%d", - VRegA_21c(), - PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(), - string_idx); - } else { - os << StringPrintf("const-string v%d, <<invalid-string-idx-%d>> // string@%d", - VRegA_21c(), - string_idx, - string_idx); - } - break; - } - FALLTHROUGH_INTENDED; - case CHECK_CAST: - case CONST_CLASS: - case NEW_INSTANCE: - if (file != nullptr) { - dex::TypeIndex type_idx(VRegB_21c()); - os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " - << file->PrettyType(type_idx) << " // type@" << type_idx; - break; - } - FALLTHROUGH_INTENDED; - case SGET: - case SGET_WIDE: - case SGET_OBJECT: - case SGET_BOOLEAN: - case SGET_BYTE: - case SGET_CHAR: - case SGET_SHORT: - if (file != nullptr) { - uint32_t field_idx = VRegB_21c(); - os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true) - << " // field@" << field_idx; - break; - } - FALLTHROUGH_INTENDED; - case SPUT: - case SPUT_WIDE: - case SPUT_OBJECT: - case SPUT_BOOLEAN: - case SPUT_BYTE: - case SPUT_CHAR: - case SPUT_SHORT: - if (file != nullptr) { - uint32_t field_idx = VRegB_21c(); - os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true) - << " // field@" << field_idx; - break; - } - FALLTHROUGH_INTENDED; - default: - os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_21c(), VRegB_21c()); - break; - } - break; - } - case k23x: os << StringPrintf("%s v%d, v%d, v%d", opcode, VRegA_23x(), VRegB_23x(), VRegC_23x()); break; - case k22b: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22b(), VRegB_22b(), VRegC_22b()); break; - case k22t: os << StringPrintf("%s v%d, v%d, %+d", opcode, VRegA_22t(), VRegB_22t(), VRegC_22t()); break; - case k22s: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22s(), VRegB_22s(), VRegC_22s()); break; - case k22c: { - switch (Opcode()) { - case IGET: - case IGET_WIDE: - case IGET_OBJECT: - case IGET_BOOLEAN: - case IGET_BYTE: - case IGET_CHAR: - case IGET_SHORT: - if (file != nullptr) { - uint32_t field_idx = VRegC_22c(); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " - << file->PrettyField(field_idx, true) << " // field@" << field_idx; - break; - } - FALLTHROUGH_INTENDED; - case IGET_QUICK: - case IGET_OBJECT_QUICK: - if (file != nullptr) { - uint32_t field_idx = VRegC_22c(); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " - << "// offset@" << field_idx; - break; - } - FALLTHROUGH_INTENDED; - case IPUT: - case IPUT_WIDE: - case IPUT_OBJECT: - case IPUT_BOOLEAN: - case IPUT_BYTE: - case IPUT_CHAR: - case IPUT_SHORT: - if (file != nullptr) { - uint32_t field_idx = VRegC_22c(); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " - << file->PrettyField(field_idx, true) << " // field@" << field_idx; - break; - } - FALLTHROUGH_INTENDED; - case IPUT_QUICK: - case IPUT_OBJECT_QUICK: - if (file != nullptr) { - uint32_t field_idx = VRegC_22c(); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " - << "// offset@" << field_idx; - break; - } - FALLTHROUGH_INTENDED; - case INSTANCE_OF: - if (file != nullptr) { - dex::TypeIndex type_idx(VRegC_22c()); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" - << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx) - << " // type@" << type_idx.index_; - break; - } - FALLTHROUGH_INTENDED; - case NEW_ARRAY: - if (file != nullptr) { - dex::TypeIndex type_idx(VRegC_22c()); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" - << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx) - << " // type@" << type_idx.index_; - break; - } - FALLTHROUGH_INTENDED; - default: - os << StringPrintf("%s v%d, v%d, thing@%d", opcode, VRegA_22c(), VRegB_22c(), VRegC_22c()); - break; - } - break; - } - case k32x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_32x(), VRegB_32x()); break; - case k30t: os << StringPrintf("%s %+d", opcode, VRegA_30t()); break; - case k31t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_31t(), VRegB_31t()); break; - case k31i: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_31i(), VRegB_31i()); break; - case k31c: - if (Opcode() == CONST_STRING_JUMBO) { - uint32_t string_idx = VRegB_31c(); - if (file != nullptr) { - if (string_idx < file->NumStringIds()) { - os << StringPrintf( - "%s v%d, %s // string@%d", - opcode, - VRegA_31c(), - PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(), - string_idx); - } else { - os << StringPrintf("%s v%d, <<invalid-string-idx-%d>> // string@%d", - opcode, - VRegA_31c(), - string_idx, - string_idx); - } - } else { - os << StringPrintf("%s v%d, string@%d", opcode, VRegA_31c(), string_idx); - } - } else { - os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break; - } - break; - case k35c: { - uint32_t arg[kMaxVarArgRegs]; - GetVarArgs(arg); - auto DumpArgs = [&](size_t count) { - for (size_t i = 0; i < count; ++i) { - if (i != 0) { - os << ", "; - } - os << "v" << arg[i]; - } - }; - switch (Opcode()) { - case FILLED_NEW_ARRAY: - { - os << opcode << " {"; - DumpArgs(VRegA_35c()); - os << "}, type@" << VRegB_35c(); - } - break; - - case INVOKE_VIRTUAL: - case INVOKE_SUPER: - case INVOKE_DIRECT: - case INVOKE_STATIC: - case INVOKE_INTERFACE: - if (file != nullptr) { - os << opcode << " {"; - uint32_t method_idx = VRegB_35c(); - DumpArgs(VRegA_35c()); - os << "}, " << file->PrettyMethod(method_idx) << " // method@" << method_idx; - break; - } - FALLTHROUGH_INTENDED; - case INVOKE_VIRTUAL_QUICK: - if (file != nullptr) { - os << opcode << " {"; - uint32_t method_idx = VRegB_35c(); - DumpArgs(VRegA_35c()); - os << "}, // vtable@" << method_idx; - break; - } - FALLTHROUGH_INTENDED; - case INVOKE_CUSTOM: - if (file != nullptr) { - os << opcode << " {"; - uint32_t call_site_idx = VRegB_35c(); - DumpArgs(VRegA_35c()); - os << "}, // call_site@" << call_site_idx; - break; - } - FALLTHROUGH_INTENDED; - default: - os << opcode << " {"; - DumpArgs(VRegA_35c()); - os << "}, thing@" << VRegB_35c(); - break; - } - break; - } - case k3rc: { - uint16_t first_reg = VRegC_3rc(); - uint16_t last_reg = VRegC_3rc() + VRegA_3rc() - 1; - switch (Opcode()) { - case INVOKE_VIRTUAL_RANGE: - case INVOKE_SUPER_RANGE: - case INVOKE_DIRECT_RANGE: - case INVOKE_STATIC_RANGE: - case INVOKE_INTERFACE_RANGE: - if (file != nullptr) { - uint32_t method_idx = VRegB_3rc(); - os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) - << file->PrettyMethod(method_idx) << " // method@" << method_idx; - break; - } - FALLTHROUGH_INTENDED; - case INVOKE_VIRTUAL_RANGE_QUICK: - if (file != nullptr) { - uint32_t method_idx = VRegB_3rc(); - os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) - << "// vtable@" << method_idx; - break; - } - FALLTHROUGH_INTENDED; - case INVOKE_CUSTOM_RANGE: - if (file != nullptr) { - uint32_t call_site_idx = VRegB_3rc(); - os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) - << "// call_site@" << call_site_idx; - break; - } - FALLTHROUGH_INTENDED; - default: - os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) - << "thing@" << VRegB_3rc(); - break; - } - break; - } - case k45cc: { - uint32_t arg[kMaxVarArgRegs]; - GetVarArgs(arg); - uint32_t method_idx = VRegB_45cc(); - uint32_t proto_idx = VRegH_45cc(); - os << opcode << " {"; - for (int i = 0; i < VRegA_45cc(); ++i) { - if (i != 0) { - os << ", "; - } - os << "v" << arg[i]; - } - os << "}"; - if (file != nullptr) { - os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) - << " // "; - } else { - os << ", "; - } - os << "method@" << method_idx << ", proto@" << proto_idx; - break; - } - case k4rcc: - switch (Opcode()) { - case INVOKE_POLYMORPHIC_RANGE: { - if (file != nullptr) { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); - os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) - << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) - << " // method@" << method_idx << ", proto@" << proto_idx; - break; - } - } - FALLTHROUGH_INTENDED; - default: { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); - os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) - << "}, method@" << method_idx << ", proto@" << proto_idx; - } - } - break; - case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break; - } - return os.str(); -} - -// Add some checks that ensure the flags make sense. We need a subclass to be in the context of -// Instruction. Otherwise the flags from the instruction list don't work. -struct InstructionStaticAsserts : private Instruction { - #define IMPLIES(a, b) (!(a) || (b)) - - #define VAR_ARGS_CHECK(o, c, pname, f, i, a, e, v) \ - static_assert(IMPLIES((f) == k35c || (f) == k45cc, \ - ((v) & (kVerifyVarArg | kVerifyVarArgNonZero)) != 0), \ - "Missing var-arg verification"); - #include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(VAR_ARGS_CHECK) - #undef DEX_INSTRUCTION_LIST - #undef VAR_ARGS_CHECK - - #define VAR_ARGS_RANGE_CHECK(o, c, pname, f, i, a, e, v) \ - static_assert(IMPLIES((f) == k3rc || (f) == k4rcc, \ - ((v) & (kVerifyVarArgRange | kVerifyVarArgRangeNonZero)) != 0), \ - "Missing var-arg verification"); - #include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(VAR_ARGS_RANGE_CHECK) - #undef DEX_INSTRUCTION_LIST - #undef VAR_ARGS_RANGE_CHECK - - #define EXPERIMENTAL_CHECK(o, c, pname, f, i, a, e, v) \ - static_assert(kHaveExperimentalInstructions || (((a) & kExperimental) == 0), \ - "Unexpected experimental instruction."); - #include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(EXPERIMENTAL_CHECK) - #undef DEX_INSTRUCTION_LIST - #undef EXPERIMENTAL_CHECK -}; - -std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) { - return os << Instruction::Name(code); -} - -uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const { - DCHECK_LT(operand_index, GetNumberOfOperands()); - return first_operand_ + operand_index; -} - -uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const { - DCHECK_LT(operand_index, GetNumberOfOperands()); - return operands_[operand_index]; -} - -} // namespace art diff --git a/runtime/dex/dex_instruction.h b/runtime/dex/dex_instruction.h deleted file mode 100644 index 8b1a5ce670..0000000000 --- a/runtime/dex/dex_instruction.h +++ /dev/null @@ -1,742 +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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ -#define ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ - -#include <android-base/logging.h> - -#include "base/macros.h" -#include "globals.h" - -typedef uint8_t uint4_t; -typedef int8_t int4_t; - -namespace art { - -class DexFile; - -enum { - kNumPackedOpcodes = 0x100 -}; - -class Instruction { - public: - // NOP-encoded switch-statement signatures. - enum Signatures { - kPackedSwitchSignature = 0x0100, - kSparseSwitchSignature = 0x0200, - kArrayDataSignature = 0x0300, - }; - - struct PACKED(4) PackedSwitchPayload { - const uint16_t ident; - const uint16_t case_count; - const int32_t first_key; - const int32_t targets[]; - - private: - DISALLOW_COPY_AND_ASSIGN(PackedSwitchPayload); - }; - - struct PACKED(4) SparseSwitchPayload { - const uint16_t ident; - const uint16_t case_count; - const int32_t keys_and_targets[]; - - public: - const int32_t* GetKeys() const { - return keys_and_targets; - } - - const int32_t* GetTargets() const { - return keys_and_targets + case_count; - } - - private: - DISALLOW_COPY_AND_ASSIGN(SparseSwitchPayload); - }; - - struct PACKED(4) ArrayDataPayload { - const uint16_t ident; - const uint16_t element_width; - const uint32_t element_count; - const uint8_t data[]; - - private: - DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload); - }; - - enum Code { // private marker to avoid generate-operator-out.py from processing. -#define INSTRUCTION_ENUM(opcode, cname, p, f, i, a, e, v) cname = (opcode), -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_ENUM - RSUB_INT_LIT16 = RSUB_INT, - }; - - enum Format : uint8_t { - k10x, // op - k12x, // op vA, vB - k11n, // op vA, #+B - k11x, // op vAA - k10t, // op +AA - k20t, // op +AAAA - k22x, // op vAA, vBBBB - k21t, // op vAA, +BBBB - k21s, // op vAA, #+BBBB - k21h, // op vAA, #+BBBB00000[00000000] - k21c, // op vAA, thing@BBBB - k23x, // op vAA, vBB, vCC - k22b, // op vAA, vBB, #+CC - k22t, // op vA, vB, +CCCC - k22s, // op vA, vB, #+CCCC - k22c, // op vA, vB, thing@CCCC - k32x, // op vAAAA, vBBBB - k30t, // op +AAAAAAAA - k31t, // op vAA, +BBBBBBBB - k31i, // op vAA, #+BBBBBBBB - k31c, // op vAA, thing@BBBBBBBB - k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG) - k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB - - // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count) - // format: AG op BBBB FEDC HHHH - k45cc, - - // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count) - // format: AA op BBBB CCCC HHHH - k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count) - - k51l, // op vAA, #+BBBBBBBBBBBBBBBB - }; - - enum IndexType : uint8_t { - kIndexUnknown = 0, - kIndexNone, // has no index - kIndexTypeRef, // type reference index - kIndexStringRef, // string reference index - kIndexMethodRef, // method reference index - kIndexFieldRef, // field reference index - kIndexFieldOffset, // field offset (for static linked fields) - kIndexVtableOffset, // vtable offset (for static linked methods) - kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic) - kIndexCallSiteRef, // call site reference index - kIndexMethodHandleRef, // constant method handle reference index - kIndexProtoRef, // prototype reference index - }; - - enum Flags : uint8_t { - kBranch = 0x01, // conditional or unconditional branch - kContinue = 0x02, // flow can continue to next statement - kSwitch = 0x04, // switch statement - kThrow = 0x08, // could cause an exception to be thrown - kReturn = 0x10, // returns, no additional statements - kInvoke = 0x20, // a flavor of invoke - kUnconditional = 0x40, // unconditional branch - kExperimental = 0x80, // is an experimental opcode - }; - - // Old flags. Keeping them around in case we might need them again some day. - enum ExtendedFlags : uint32_t { - kAdd = 0x0000080, // addition - kSubtract = 0x0000100, // subtract - kMultiply = 0x0000200, // multiply - kDivide = 0x0000400, // division - kRemainder = 0x0000800, // remainder - kAnd = 0x0001000, // and - kOr = 0x0002000, // or - kXor = 0x0004000, // xor - kShl = 0x0008000, // shl - kShr = 0x0010000, // shr - kUshr = 0x0020000, // ushr - kCast = 0x0040000, // cast - kStore = 0x0080000, // store opcode - kLoad = 0x0100000, // load opcode - kClobber = 0x0200000, // clobbers memory in a big way (not just a write) - kRegCFieldOrConstant = 0x0400000, // is the third virtual register a field or literal constant (vC) - kRegBFieldOrConstant = 0x0800000, // is the second virtual register a field or literal constant (vB) - }; - - enum VerifyFlag : uint32_t { - kVerifyNone = 0x0000000, - kVerifyRegA = 0x0000001, - kVerifyRegAWide = 0x0000002, - kVerifyRegB = 0x0000004, - kVerifyRegBField = 0x0000008, - kVerifyRegBMethod = 0x0000010, - kVerifyRegBNewInstance = 0x0000020, - kVerifyRegBString = 0x0000040, - kVerifyRegBType = 0x0000080, - kVerifyRegBWide = 0x0000100, - kVerifyRegC = 0x0000200, - kVerifyRegCField = 0x0000400, - kVerifyRegCNewArray = 0x0000800, - kVerifyRegCType = 0x0001000, - kVerifyRegCWide = 0x0002000, - kVerifyArrayData = 0x0004000, - kVerifyBranchTarget = 0x0008000, - kVerifySwitchTargets = 0x0010000, - kVerifyVarArg = 0x0020000, - kVerifyVarArgNonZero = 0x0040000, - kVerifyVarArgRange = 0x0080000, - kVerifyVarArgRangeNonZero = 0x0100000, - kVerifyRuntimeOnly = 0x0200000, - kVerifyError = 0x0400000, - kVerifyRegHPrototype = 0x0800000, - kVerifyRegBCallSite = 0x1000000, - kVerifyRegBMethodHandle = 0x2000000, - kVerifyRegBPrototype = 0x4000000, - }; - - // Collect the enums in a struct for better locality. - struct InstructionDescriptor { - uint32_t verify_flags; // Set of VerifyFlag. - Format format; - IndexType index_type; - uint8_t flags; // Set of Flags. - int8_t size_in_code_units; - }; - - static constexpr uint32_t kMaxVarArgRegs = 5; - - static constexpr bool kHaveExperimentalInstructions = false; - - // Returns the size (in 2 byte code units) of this instruction. - size_t SizeInCodeUnits() const { - int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units; - if (UNLIKELY(result < 0)) { - return SizeInCodeUnitsComplexOpcode(); - } else { - return static_cast<size_t>(result); - } - } - - // Code units required to calculate the size of the instruction. - size_t CodeUnitsRequiredForSizeComputation() const { - const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units; - return UNLIKELY(result < 0) ? CodeUnitsRequiredForSizeOfComplexOpcode() : 1; - } - - // Reads an instruction out of the stream at the specified address. - static const Instruction* At(const uint16_t* code) { - DCHECK(code != nullptr); - return reinterpret_cast<const Instruction*>(code); - } - - // Reads an instruction out of the stream from the current address plus an offset. - const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED { - return At(reinterpret_cast<const uint16_t*>(this) + offset); - } - - // Returns a pointer to the next instruction in the stream. - const Instruction* Next() const { - return RelativeAt(SizeInCodeUnits()); - } - - // Returns a pointer to the instruction after this 1xx instruction in the stream. - const Instruction* Next_1xx() const { - DCHECK(FormatOf(Opcode()) >= k10x && FormatOf(Opcode()) <= k10t); - return RelativeAt(1); - } - - // Returns a pointer to the instruction after this 2xx instruction in the stream. - const Instruction* Next_2xx() const { - DCHECK(FormatOf(Opcode()) >= k20t && FormatOf(Opcode()) <= k22c); - return RelativeAt(2); - } - - // Returns a pointer to the instruction after this 3xx instruction in the stream. - const Instruction* Next_3xx() const { - DCHECK(FormatOf(Opcode()) >= k32x && FormatOf(Opcode()) <= k3rc); - return RelativeAt(3); - } - - // Returns a pointer to the instruction after this 4xx instruction in the stream. - const Instruction* Next_4xx() const { - DCHECK(FormatOf(Opcode()) >= k45cc && FormatOf(Opcode()) <= k4rcc); - return RelativeAt(4); - } - - // Returns a pointer to the instruction after this 51l instruction in the stream. - const Instruction* Next_51l() const { - DCHECK(FormatOf(Opcode()) == k51l); - return RelativeAt(5); - } - - // Returns the name of this instruction's opcode. - const char* Name() const { - return Instruction::Name(Opcode()); - } - - // Returns the name of the given opcode. - static const char* Name(Code opcode) { - return kInstructionNames[opcode]; - } - - // VRegA - bool HasVRegA() const; - ALWAYS_INLINE int32_t VRegA() const; - - int8_t VRegA_10t() const { - return VRegA_10t(Fetch16(0)); - } - uint8_t VRegA_10x() const { - return VRegA_10x(Fetch16(0)); - } - uint4_t VRegA_11n() const { - return VRegA_11n(Fetch16(0)); - } - uint8_t VRegA_11x() const { - return VRegA_11x(Fetch16(0)); - } - uint4_t VRegA_12x() const { - return VRegA_12x(Fetch16(0)); - } - int16_t VRegA_20t() const; - uint8_t VRegA_21c() const { - return VRegA_21c(Fetch16(0)); - } - uint8_t VRegA_21h() const { - return VRegA_21h(Fetch16(0)); - } - uint8_t VRegA_21s() const { - return VRegA_21s(Fetch16(0)); - } - uint8_t VRegA_21t() const { - return VRegA_21t(Fetch16(0)); - } - uint8_t VRegA_22b() const { - return VRegA_22b(Fetch16(0)); - } - uint4_t VRegA_22c() const { - return VRegA_22c(Fetch16(0)); - } - uint4_t VRegA_22s() const { - return VRegA_22s(Fetch16(0)); - } - uint4_t VRegA_22t() const { - return VRegA_22t(Fetch16(0)); - } - uint8_t VRegA_22x() const { - return VRegA_22x(Fetch16(0)); - } - uint8_t VRegA_23x() const { - return VRegA_23x(Fetch16(0)); - } - int32_t VRegA_30t() const; - uint8_t VRegA_31c() const { - return VRegA_31c(Fetch16(0)); - } - uint8_t VRegA_31i() const { - return VRegA_31i(Fetch16(0)); - } - uint8_t VRegA_31t() const { - return VRegA_31t(Fetch16(0)); - } - uint16_t VRegA_32x() const; - uint4_t VRegA_35c() const { - return VRegA_35c(Fetch16(0)); - } - uint8_t VRegA_3rc() const { - return VRegA_3rc(Fetch16(0)); - } - uint8_t VRegA_51l() const { - return VRegA_51l(Fetch16(0)); - } - uint4_t VRegA_45cc() const { - return VRegA_45cc(Fetch16(0)); - } - uint8_t VRegA_4rcc() const { - return VRegA_4rcc(Fetch16(0)); - } - - // The following methods return the vA operand for various instruction formats. The "inst_data" - // parameter holds the first 16 bits of instruction which the returned value is decoded from. - int8_t VRegA_10t(uint16_t inst_data) const; - uint8_t VRegA_10x(uint16_t inst_data) const; - uint4_t VRegA_11n(uint16_t inst_data) const; - uint8_t VRegA_11x(uint16_t inst_data) const; - uint4_t VRegA_12x(uint16_t inst_data) const; - uint8_t VRegA_21c(uint16_t inst_data) const; - uint8_t VRegA_21h(uint16_t inst_data) const; - uint8_t VRegA_21s(uint16_t inst_data) const; - uint8_t VRegA_21t(uint16_t inst_data) const; - uint8_t VRegA_22b(uint16_t inst_data) const; - uint4_t VRegA_22c(uint16_t inst_data) const; - uint4_t VRegA_22s(uint16_t inst_data) const; - uint4_t VRegA_22t(uint16_t inst_data) const; - uint8_t VRegA_22x(uint16_t inst_data) const; - uint8_t VRegA_23x(uint16_t inst_data) const; - uint8_t VRegA_31c(uint16_t inst_data) const; - uint8_t VRegA_31i(uint16_t inst_data) const; - uint8_t VRegA_31t(uint16_t inst_data) const; - uint4_t VRegA_35c(uint16_t inst_data) const; - uint8_t VRegA_3rc(uint16_t inst_data) const; - uint8_t VRegA_51l(uint16_t inst_data) const; - uint4_t VRegA_45cc(uint16_t inst_data) const; - uint8_t VRegA_4rcc(uint16_t inst_data) const; - - // VRegB - bool HasVRegB() const; - int32_t VRegB() const; - - bool HasWideVRegB() const; - uint64_t WideVRegB() const; - - int4_t VRegB_11n() const { - return VRegB_11n(Fetch16(0)); - } - uint4_t VRegB_12x() const { - return VRegB_12x(Fetch16(0)); - } - uint16_t VRegB_21c() const; - uint16_t VRegB_21h() const; - int16_t VRegB_21s() const; - int16_t VRegB_21t() const; - uint8_t VRegB_22b() const; - uint4_t VRegB_22c() const { - return VRegB_22c(Fetch16(0)); - } - uint4_t VRegB_22s() const { - return VRegB_22s(Fetch16(0)); - } - uint4_t VRegB_22t() const { - return VRegB_22t(Fetch16(0)); - } - uint16_t VRegB_22x() const; - uint8_t VRegB_23x() const; - uint32_t VRegB_31c() const; - int32_t VRegB_31i() const; - int32_t VRegB_31t() const; - uint16_t VRegB_32x() const; - uint16_t VRegB_35c() const; - uint16_t VRegB_3rc() const; - uint64_t VRegB_51l() const; // vB_wide - uint16_t VRegB_45cc() const; - uint16_t VRegB_4rcc() const; - - // The following methods return the vB operand for all instruction formats where it is encoded in - // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned - // value is decoded from it. - int4_t VRegB_11n(uint16_t inst_data) const; - uint4_t VRegB_12x(uint16_t inst_data) const; - uint4_t VRegB_22c(uint16_t inst_data) const; - uint4_t VRegB_22s(uint16_t inst_data) const; - uint4_t VRegB_22t(uint16_t inst_data) const; - - // VRegC - bool HasVRegC() const; - int32_t VRegC() const; - - int8_t VRegC_22b() const; - uint16_t VRegC_22c() const; - int16_t VRegC_22s() const; - int16_t VRegC_22t() const; - uint8_t VRegC_23x() const; - uint4_t VRegC_35c() const; - uint16_t VRegC_3rc() const; - uint4_t VRegC_45cc() const; - uint16_t VRegC_4rcc() const; - - - // VRegH - bool HasVRegH() const; - int32_t VRegH() const; - uint16_t VRegH_45cc() const; - uint16_t VRegH_4rcc() const; - - // Fills the given array with the 'arg' array of the instruction. - bool HasVarArgs() const; - void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; - void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { - return GetVarArgs(args, Fetch16(0)); - } - - // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first - // 16 bits of instruction. - Code Opcode(uint16_t inst_data) const { - DCHECK_EQ(inst_data, Fetch16(0)); - return static_cast<Code>(inst_data & 0xFF); - } - - // Returns the opcode field of the instruction from the first 16 bits of instruction. - Code Opcode() const { - return Opcode(Fetch16(0)); - } - - void SetOpcode(Code opcode) { - DCHECK_LT(static_cast<uint16_t>(opcode), 256u); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode); - } - - void SetVRegA_10x(uint8_t val) { - DCHECK(FormatOf(Opcode()) == k10x); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[0] = (val << 8) | (insns[0] & 0x00ff); - } - - void SetVRegB_3rc(uint16_t val) { - DCHECK(FormatOf(Opcode()) == k3rc); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[1] = val; - } - - void SetVRegB_35c(uint16_t val) { - DCHECK(FormatOf(Opcode()) == k35c); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[1] = val; - } - - void SetVRegC_22c(uint16_t val) { - DCHECK(FormatOf(Opcode()) == k22c); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[1] = val; - } - - void SetVRegA_21c(uint8_t val) { - DCHECK(FormatOf(Opcode()) == k21c); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[0] = (val << 8) | (insns[0] & 0x00ff); - } - - void SetVRegB_21c(uint16_t val) { - DCHECK(FormatOf(Opcode()) == k21c); - uint16_t* insns = reinterpret_cast<uint16_t*>(this); - insns[1] = val; - } - - // Returns the format of the given opcode. - static Format FormatOf(Code opcode) { - return kInstructionDescriptors[opcode].format; - } - - // Returns the index type of the given opcode. - static IndexType IndexTypeOf(Code opcode) { - return kInstructionDescriptors[opcode].index_type; - } - - // Returns the flags for the given opcode. - static uint8_t FlagsOf(Code opcode) { - return kInstructionDescriptors[opcode].flags; - } - - // Return the verify flags for the given opcode. - static uint32_t VerifyFlagsOf(Code opcode) { - return kInstructionDescriptors[opcode].verify_flags; - } - - // Returns true if this instruction is a branch. - bool IsBranch() const { - return (kInstructionDescriptors[Opcode()].flags & kBranch) != 0; - } - - // Returns true if this instruction is a unconditional branch. - bool IsUnconditional() const { - return (kInstructionDescriptors[Opcode()].flags & kUnconditional) != 0; - } - - // Returns the branch offset if this instruction is a branch. - int32_t GetTargetOffset() const; - - // Returns true if the instruction allows control flow to go to the following instruction. - bool CanFlowThrough() const; - - // Returns true if the instruction is a quickened instruction. - bool IsQuickened() const { - return (kInstructionDescriptors[Opcode()].index_type == kIndexFieldOffset) || - (kInstructionDescriptors[Opcode()].index_type == kIndexVtableOffset); - } - - // Returns true if this instruction is a switch. - bool IsSwitch() const { - return (kInstructionDescriptors[Opcode()].flags & kSwitch) != 0; - } - - // Returns true if this instruction can throw. - bool IsThrow() const { - return (kInstructionDescriptors[Opcode()].flags & kThrow) != 0; - } - - // Determine if the instruction is any of 'return' instructions. - bool IsReturn() const { - return (kInstructionDescriptors[Opcode()].flags & kReturn) != 0; - } - - // Determine if this instruction ends execution of its basic block. - bool IsBasicBlockEnd() const { - return IsBranch() || IsReturn() || Opcode() == THROW; - } - - // Determine if this instruction is an invoke. - bool IsInvoke() const { - return (kInstructionDescriptors[Opcode()].flags & kInvoke) != 0; - } - - // Determine if this instruction is experimental. - bool IsExperimental() const { - return (kInstructionDescriptors[Opcode()].flags & kExperimental) != 0; - } - - int GetVerifyTypeArgumentA() const { - return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegA | kVerifyRegAWide)); - } - - int GetVerifyTypeArgumentB() const { - return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegB | kVerifyRegBField | - kVerifyRegBMethod | kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType | - kVerifyRegBWide)); - } - - int GetVerifyTypeArgumentC() const { - return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegC | kVerifyRegCField | - kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide)); - } - - int GetVerifyTypeArgumentH() const { - return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRegHPrototype); - } - - int GetVerifyExtraFlags() const { - return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyArrayData | - kVerifyBranchTarget | kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgNonZero | - kVerifyVarArgRange | kVerifyVarArgRangeNonZero | kVerifyError)); - } - - bool GetVerifyIsRuntimeOnly() const { - return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRuntimeOnly) != 0; - } - - // Get the dex PC of this instruction as a offset in code units from the beginning of insns. - uint32_t GetDexPc(const uint16_t* insns) const { - return (reinterpret_cast<const uint16_t*>(this) - insns); - } - - // Dump decoded version of instruction - std::string DumpString(const DexFile*) const; - - // Dump code_units worth of this instruction, padding to code_units for shorter instructions - std::string DumpHex(size_t code_units) const; - - // Little-endian dump code_units worth of this instruction, padding to code_units for - // shorter instructions - std::string DumpHexLE(size_t instr_code_units) const; - - uint16_t Fetch16(size_t offset) const { - const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); - return insns[offset]; - } - - private: - size_t SizeInCodeUnitsComplexOpcode() const; - - // Return how many code unit words are required to compute the size of the opcode. - size_t CodeUnitsRequiredForSizeOfComplexOpcode() const; - - uint32_t Fetch32(size_t offset) const { - return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16)); - } - - uint4_t InstA() const { - return InstA(Fetch16(0)); - } - - uint4_t InstB() const { - return InstB(Fetch16(0)); - } - - uint8_t InstAA() const { - return InstAA(Fetch16(0)); - } - - uint4_t InstA(uint16_t inst_data) const { - DCHECK_EQ(inst_data, Fetch16(0)); - return static_cast<uint4_t>((inst_data >> 8) & 0x0f); - } - - uint4_t InstB(uint16_t inst_data) const { - DCHECK_EQ(inst_data, Fetch16(0)); - return static_cast<uint4_t>(inst_data >> 12); - } - - uint8_t InstAA(uint16_t inst_data) const { - DCHECK_EQ(inst_data, Fetch16(0)); - return static_cast<uint8_t>(inst_data >> 8); - } - - static const char* const kInstructionNames[]; - - static const InstructionDescriptor kInstructionDescriptors[]; - - DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction); -}; -std::ostream& operator<<(std::ostream& os, const Instruction::Code& code); -std::ostream& operator<<(std::ostream& os, const Instruction::Format& format); -std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags); -std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags); - -// Base class for accessing instruction operands. Unifies operand -// access for instructions that have range and varargs forms -// (e.g. invoke-polymoprhic/range and invoke-polymorphic). -class InstructionOperands { - public: - explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {} - virtual ~InstructionOperands() {} - virtual uint32_t GetOperand(size_t index) const = 0; - size_t GetNumberOfOperands() const { return num_operands_; } - - private: - size_t num_operands_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands); -}; - -// Class for accessing operands for instructions with a range format -// (e.g. 3rc and 4rcc). -class RangeInstructionOperands FINAL : public InstructionOperands { - public: - RangeInstructionOperands(uint32_t first_operand, size_t num_operands) - : InstructionOperands(num_operands), first_operand_(first_operand) {} - ~RangeInstructionOperands() {} - uint32_t GetOperand(size_t operand_index) const OVERRIDE; - - private: - const uint32_t first_operand_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands); -}; - -// Class for accessing operands for instructions with a variable -// number of arguments format (e.g. 35c and 45cc). -class VarArgsInstructionOperands FINAL : public InstructionOperands { - public: - VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs], - size_t num_operands) - : InstructionOperands(num_operands), operands_(operands) {} - ~VarArgsInstructionOperands() {} - uint32_t GetOperand(size_t operand_index) const OVERRIDE; - - private: - const uint32_t (&operands_)[Instruction::kMaxVarArgRegs]; - - DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands); -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ diff --git a/runtime/dex/dex_instruction_iterator.h b/runtime/dex/dex_instruction_iterator.h deleted file mode 100644 index c1b3118f85..0000000000 --- a/runtime/dex/dex_instruction_iterator.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_ -#define ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_ - -#include <iterator> - -#include <android-base/logging.h> - -#include "base/macros.h" -#include "dex_instruction.h" - -namespace art { - -class DexInstructionPcPair { - public: - ALWAYS_INLINE const Instruction& Inst() const { - return *Instruction::At(instructions_ + DexPc()); - } - - ALWAYS_INLINE const Instruction* operator->() const { - return &Inst(); - } - - ALWAYS_INLINE uint32_t DexPc() const { - return dex_pc_; - } - - ALWAYS_INLINE const uint16_t* Instructions() const { - return instructions_; - } - - protected: - explicit DexInstructionPcPair(const uint16_t* instructions, uint32_t dex_pc) - : instructions_(instructions), dex_pc_(dex_pc) {} - - const uint16_t* instructions_ = nullptr; - uint32_t dex_pc_ = 0; - - friend class DexInstructionIteratorBase; - friend class DexInstructionIterator; - friend class SafeDexInstructionIterator; -}; - -// Base helper class to prevent duplicated comparators. -class DexInstructionIteratorBase : public - std::iterator<std::forward_iterator_tag, DexInstructionPcPair> { - public: - using value_type = std::iterator<std::forward_iterator_tag, DexInstructionPcPair>::value_type; - using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type; - - DexInstructionIteratorBase() = default; - explicit DexInstructionIteratorBase(const Instruction* inst, uint32_t dex_pc) - : data_(reinterpret_cast<const uint16_t*>(inst), dex_pc) {} - - const Instruction& Inst() const { - return data_.Inst(); - } - - // Return the dex pc for an iterator compared to the code item begin. - ALWAYS_INLINE uint32_t DexPc() const { - return data_.DexPc(); - } - - // Instructions from the start of the code item. - ALWAYS_INLINE const uint16_t* Instructions() const { - return data_.Instructions(); - } - - protected: - DexInstructionPcPair data_; -}; - -static ALWAYS_INLINE inline bool operator==(const DexInstructionIteratorBase& lhs, - const DexInstructionIteratorBase& rhs) { - DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items."; - return lhs.DexPc() == rhs.DexPc(); -} - -static inline bool operator!=(const DexInstructionIteratorBase& lhs, - const DexInstructionIteratorBase& rhs) { - return !(lhs == rhs); -} - -static inline bool operator<(const DexInstructionIteratorBase& lhs, - const DexInstructionIteratorBase& rhs) { - DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items."; - return lhs.DexPc() < rhs.DexPc(); -} - -static inline bool operator>(const DexInstructionIteratorBase& lhs, - const DexInstructionIteratorBase& rhs) { - return rhs < lhs; -} - -static inline bool operator<=(const DexInstructionIteratorBase& lhs, - const DexInstructionIteratorBase& rhs) { - return !(rhs < lhs); -} - -static inline bool operator>=(const DexInstructionIteratorBase& lhs, - const DexInstructionIteratorBase& rhs) { - return !(lhs < rhs); -} - -// A helper class for a code_item's instructions using range based for loop syntax. -class DexInstructionIterator : public DexInstructionIteratorBase { - public: - using DexInstructionIteratorBase::DexInstructionIteratorBase; - - explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc) - : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {} - - explicit DexInstructionIterator(const DexInstructionPcPair& pair) - : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {} - - // Value after modification. - DexInstructionIterator& operator++() { - data_.dex_pc_ += Inst().SizeInCodeUnits(); - return *this; - } - - // Value before modification. - DexInstructionIterator operator++(int) { - DexInstructionIterator temp = *this; - ++*this; - return temp; - } - - const value_type& operator*() const { - return data_; - } - - const Instruction* operator->() const { - return &data_.Inst(); - } - - // Return the dex pc for the iterator. - ALWAYS_INLINE uint32_t DexPc() const { - return data_.DexPc(); - } -}; - -// A safe version of DexInstructionIterator that is guaranteed to not go past the end of the code -// item. -class SafeDexInstructionIterator : public DexInstructionIteratorBase { - public: - explicit SafeDexInstructionIterator(const DexInstructionIteratorBase& start, - const DexInstructionIteratorBase& end) - : DexInstructionIteratorBase(&start.Inst(), start.DexPc()) - , num_code_units_(end.DexPc()) { - DCHECK_EQ(start.Instructions(), end.Instructions()) - << "start and end must be in the same code item."; - } - - // Value after modification, does not read past the end of the allowed region. May increment past - // the end of the code item though. - SafeDexInstructionIterator& operator++() { - AssertValid(); - const size_t size_code_units = Inst().CodeUnitsRequiredForSizeComputation(); - const size_t available = NumCodeUnits() - DexPc(); - if (UNLIKELY(size_code_units > available)) { - error_state_ = true; - return *this; - } - const size_t instruction_code_units = Inst().SizeInCodeUnits(); - if (UNLIKELY(instruction_code_units > available)) { - error_state_ = true; - return *this; - } - data_.dex_pc_ += instruction_code_units; - return *this; - } - - // Value before modification. - SafeDexInstructionIterator operator++(int) { - SafeDexInstructionIterator temp = *this; - ++*this; - return temp; - } - - const value_type& operator*() const { - AssertValid(); - return data_; - } - - const Instruction* operator->() const { - AssertValid(); - return &data_.Inst(); - } - - // Return the current instruction of the iterator. - ALWAYS_INLINE const Instruction& Inst() const { - return data_.Inst(); - } - - const uint16_t* Instructions() const { - return data_.Instructions(); - } - - // Returns true if the iterator is in an error state. This occurs when an instruction couldn't - // have its size computed without reading past the end iterator. - bool IsErrorState() const { - return error_state_; - } - - private: - ALWAYS_INLINE void AssertValid() const { - DCHECK(!IsErrorState()); - DCHECK_LT(DexPc(), NumCodeUnits()); - } - - ALWAYS_INLINE uint32_t NumCodeUnits() const { - return num_code_units_; - } - - const uint32_t num_code_units_ = 0; - bool error_state_ = false; -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_ diff --git a/runtime/dex/dex_instruction_list.h b/runtime/dex/dex_instruction_list.h deleted file mode 100644 index aa63fadb66..0000000000 --- a/runtime/dex/dex_instruction_list.h +++ /dev/null @@ -1,308 +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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ -#define ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ - -// V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags); -#define DEX_INSTRUCTION_LIST(V) \ - V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \ - V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x04, MOVE_WIDE, "move-wide", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x05, MOVE_WIDE_FROM16, "move-wide/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x06, MOVE_WIDE_16, "move-wide/16", k32x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x07, MOVE_OBJECT, "move-object", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x08, MOVE_OBJECT_FROM16, "move-object/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x09, MOVE_OBJECT_16, "move-object/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x0A, MOVE_RESULT, "move-result", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \ - V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \ - V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \ - V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \ - V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \ - V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \ - V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \ - V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \ - V(0x12, CONST_4, "const/4", k11n, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \ - V(0x13, CONST_16, "const/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \ - V(0x14, CONST, "const", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \ - V(0x15, CONST_HIGH16, "const/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \ - V(0x16, CONST_WIDE_16, "const-wide/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \ - V(0x17, CONST_WIDE_32, "const-wide/32", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \ - V(0x18, CONST_WIDE, "const-wide", k51l, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \ - V(0x19, CONST_WIDE_HIGH16, "const-wide/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \ - V(0x1A, CONST_STRING, "const-string", k21c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \ - V(0x1B, CONST_STRING_JUMBO, "const-string/jumbo", k31c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \ - V(0x1C, CONST_CLASS, "const-class", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \ - V(0x1D, MONITOR_ENTER, "monitor-enter", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \ - V(0x1E, MONITOR_EXIT, "monitor-exit", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \ - V(0x1F, CHECK_CAST, "check-cast", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \ - V(0x20, INSTANCE_OF, "instance-of", k22c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB | kVerifyRegCType) \ - V(0x21, ARRAY_LENGTH, "array-length", k12x, kIndexNone, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB) \ - V(0x22, NEW_INSTANCE, "new-instance", k21c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegBNewInstance) \ - V(0x23, NEW_ARRAY, "new-array", k22c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegB | kVerifyRegCNewArray) \ - V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArg) \ - V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArgRange) \ - V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyArrayData) \ - V(0x27, THROW, "throw", k11x, kIndexNone, kThrow, 0, kVerifyRegA) \ - V(0x28, GOTO, "goto", k10t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \ - V(0x29, GOTO_16, "goto/16", k20t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \ - V(0x2A, GOTO_32, "goto/32", k30t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \ - V(0x2B, PACKED_SWITCH, "packed-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \ - V(0x2C, SPARSE_SWITCH, "sparse-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \ - V(0x2D, CMPL_FLOAT, "cmpl-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x2E, CMPG_FLOAT, "cmpg-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x2F, CMPL_DOUBLE, "cmpl-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x30, CMPG_DOUBLE, "cmpg-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x31, CMP_LONG, "cmp-long", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x32, IF_EQ, "if-eq", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \ - V(0x33, IF_NE, "if-ne", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \ - V(0x34, IF_LT, "if-lt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \ - V(0x35, IF_GE, "if-ge", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \ - V(0x36, IF_GT, "if-gt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \ - V(0x37, IF_LE, "if-le", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \ - V(0x38, IF_EQZ, "if-eqz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \ - V(0x39, IF_NEZ, "if-nez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \ - V(0x3A, IF_LTZ, "if-ltz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \ - V(0x3B, IF_GEZ, "if-gez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \ - V(0x3C, IF_GTZ, "if-gtz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \ - V(0x3D, IF_LEZ, "if-lez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \ - V(0x3E, UNUSED_3E, "unused-3e", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x3F, UNUSED_3F, "unused-3f", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x40, UNUSED_40, "unused-40", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x41, UNUSED_41, "unused-41", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x42, UNUSED_42, "unused-42", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x43, UNUSED_43, "unused-43", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x44, AGET, "aget", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x45, AGET_WIDE, "aget-wide", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \ - V(0x46, AGET_OBJECT, "aget-object", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x47, AGET_BOOLEAN, "aget-boolean", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x48, AGET_BYTE, "aget-byte", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x49, AGET_CHAR, "aget-char", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x4A, AGET_SHORT, "aget-short", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x4B, APUT, "aput", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x4C, APUT_WIDE, "aput-wide", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \ - V(0x4D, APUT_OBJECT, "aput-object", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x4E, APUT_BOOLEAN, "aput-boolean", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x4F, APUT_BYTE, "aput-byte", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x50, APUT_CHAR, "aput-char", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x51, APUT_SHORT, "aput-short", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x52, IGET, "iget", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x53, IGET_WIDE, "iget-wide", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \ - V(0x54, IGET_OBJECT, "iget-object", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x55, IGET_BOOLEAN, "iget-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x56, IGET_BYTE, "iget-byte", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x57, IGET_CHAR, "iget-char", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x58, IGET_SHORT, "iget-short", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x59, IPUT, "iput", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x5A, IPUT_WIDE, "iput-wide", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \ - V(0x5B, IPUT_OBJECT, "iput-object", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x5C, IPUT_BOOLEAN, "iput-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x5D, IPUT_BYTE, "iput-byte", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x5E, IPUT_CHAR, "iput-char", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x5F, IPUT_SHORT, "iput-short", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \ - V(0x60, SGET, "sget", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x61, SGET_WIDE, "sget-wide", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \ - V(0x62, SGET_OBJECT, "sget-object", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x63, SGET_BOOLEAN, "sget-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x64, SGET_BYTE, "sget-byte", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x65, SGET_CHAR, "sget-char", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x66, SGET_SHORT, "sget-short", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x67, SPUT, "sput", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x68, SPUT_WIDE, "sput-wide", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \ - V(0x69, SPUT_OBJECT, "sput-object", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x6A, SPUT_BOOLEAN, "sput-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x6B, SPUT_BYTE, "sput-byte", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x6C, SPUT_CHAR, "sput-char", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x6D, SPUT_SHORT, "sput-short", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \ - V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \ - V(0x6F, INVOKE_SUPER, "invoke-super", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \ - V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \ - V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \ - V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \ - V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \ - V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ - V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ - V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ - V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRange) \ - V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ - V(0x79, UNUSED_79, "unused-79", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x7A, UNUSED_7A, "unused-7a", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0x7B, NEG_INT, "neg-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x7C, NOT_INT, "not-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x7D, NEG_LONG, "neg-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x7E, NOT_LONG, "not-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x7F, NEG_FLOAT, "neg-float", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ - V(0x80, NEG_DOUBLE, "neg-double", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x81, INT_TO_LONG, "int-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \ - V(0x82, INT_TO_FLOAT, "int-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \ - V(0x83, INT_TO_DOUBLE, "int-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \ - V(0x84, LONG_TO_INT, "long-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \ - V(0x85, LONG_TO_FLOAT, "long-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \ - V(0x86, LONG_TO_DOUBLE, "long-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x87, FLOAT_TO_INT, "float-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \ - V(0x88, FLOAT_TO_LONG, "float-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \ - V(0x89, FLOAT_TO_DOUBLE, "float-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \ - V(0x8A, DOUBLE_TO_INT, "double-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \ - V(0x8B, DOUBLE_TO_LONG, "double-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \ - V(0x8C, DOUBLE_TO_FLOAT, "double-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \ - V(0x8D, INT_TO_BYTE, "int-to-byte", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \ - V(0x8E, INT_TO_CHAR, "int-to-char", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \ - V(0x8F, INT_TO_SHORT, "int-to-short", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \ - V(0x90, ADD_INT, "add-int", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x91, SUB_INT, "sub-int", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x92, MUL_INT, "mul-int", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x93, DIV_INT, "div-int", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x94, REM_INT, "rem-int", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x95, AND_INT, "and-int", k23x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x96, OR_INT, "or-int", k23x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x97, XOR_INT, "xor-int", k23x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x98, SHL_INT, "shl-int", k23x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x99, SHR_INT, "shr-int", k23x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x9A, USHR_INT, "ushr-int", k23x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0x9B, ADD_LONG, "add-long", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x9C, SUB_LONG, "sub-long", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x9D, MUL_LONG, "mul-long", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x9E, DIV_LONG, "div-long", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0x9F, REM_LONG, "rem-long", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xA0, AND_LONG, "and-long", k23x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xA1, OR_LONG, "or-long", k23x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xA2, XOR_LONG, "xor-long", k23x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xA3, SHL_LONG, "shl-long", k23x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \ - V(0xA4, SHR_LONG, "shr-long", k23x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \ - V(0xA5, USHR_LONG, "ushr-long", k23x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \ - V(0xA6, ADD_FLOAT, "add-float", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0xA7, SUB_FLOAT, "sub-float", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0xA8, MUL_FLOAT, "mul-float", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0xA9, DIV_FLOAT, "div-float", k23x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0xAA, REM_FLOAT, "rem-float", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \ - V(0xAB, ADD_DOUBLE, "add-double", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xAC, SUB_DOUBLE, "sub-double", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xAD, MUL_DOUBLE, "mul-double", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xAE, DIV_DOUBLE, "div-double", k23x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xAF, REM_DOUBLE, "rem-double", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \ - V(0xB0, ADD_INT_2ADDR, "add-int/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \ - V(0xB1, SUB_INT_2ADDR, "sub-int/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \ - V(0xB2, MUL_INT_2ADDR, "mul-int/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \ - V(0xB3, DIV_INT_2ADDR, "div-int/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB) \ - V(0xB4, REM_INT_2ADDR, "rem-int/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB) \ - V(0xB5, AND_INT_2ADDR, "and-int/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB) \ - V(0xB6, OR_INT_2ADDR, "or-int/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB) \ - V(0xB7, XOR_INT_2ADDR, "xor-int/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB) \ - V(0xB8, SHL_INT_2ADDR, "shl-int/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB) \ - V(0xB9, SHR_INT_2ADDR, "shr-int/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB) \ - V(0xBA, USHR_INT_2ADDR, "ushr-int/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB) \ - V(0xBB, ADD_LONG_2ADDR, "add-long/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xBC, SUB_LONG_2ADDR, "sub-long/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xBD, MUL_LONG_2ADDR, "mul-long/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xBE, DIV_LONG_2ADDR, "div-long/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xBF, REM_LONG_2ADDR, "rem-long/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xC0, AND_LONG_2ADDR, "and-long/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xC1, OR_LONG_2ADDR, "or-long/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xC2, XOR_LONG_2ADDR, "xor-long/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xC3, SHL_LONG_2ADDR, "shl-long/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegB) \ - V(0xC4, SHR_LONG_2ADDR, "shr-long/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegB) \ - V(0xC5, USHR_LONG_2ADDR, "ushr-long/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegB) \ - V(0xC6, ADD_FLOAT_2ADDR, "add-float/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \ - V(0xC7, SUB_FLOAT_2ADDR, "sub-float/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \ - V(0xC8, MUL_FLOAT_2ADDR, "mul-float/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \ - V(0xC9, DIV_FLOAT_2ADDR, "div-float/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB) \ - V(0xCA, REM_FLOAT_2ADDR, "rem-float/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB) \ - V(0xCB, ADD_DOUBLE_2ADDR, "add-double/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xCC, SUB_DOUBLE_2ADDR, "sub-double/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xCD, MUL_DOUBLE_2ADDR, "mul-double/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xCE, DIV_DOUBLE_2ADDR, "div-double/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xCF, REM_DOUBLE_2ADDR, "rem-double/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \ - V(0xD0, ADD_INT_LIT16, "add-int/lit16", k22s, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD1, RSUB_INT, "rsub-int", k22s, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD2, MUL_INT_LIT16, "mul-int/lit16", k22s, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD3, DIV_INT_LIT16, "div-int/lit16", k22s, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD4, REM_INT_LIT16, "rem-int/lit16", k22s, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD5, AND_INT_LIT16, "and-int/lit16", k22s, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD6, OR_INT_LIT16, "or-int/lit16", k22s, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD7, XOR_INT_LIT16, "xor-int/lit16", k22s, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD8, ADD_INT_LIT8, "add-int/lit8", k22b, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xD9, RSUB_INT_LIT8, "rsub-int/lit8", k22b, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xDA, MUL_INT_LIT8, "mul-int/lit8", k22b, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xDB, DIV_INT_LIT8, "div-int/lit8", k22b, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xDC, REM_INT_LIT8, "rem-int/lit8", k22b, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xDD, AND_INT_LIT8, "and-int/lit8", k22b, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xDE, OR_INT_LIT8, "or-int/lit8", k22b, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xDF, XOR_INT_LIT8, "xor-int/lit8", k22b, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, kIndexNone, kContinue, kShl | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, kIndexNone, kContinue, kShr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, kIndexNone, kContinue, kUshr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \ - V(0xE3, IGET_QUICK, "iget-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE6, IPUT_QUICK, "iput-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \ - V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \ - V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xEF, IGET_BOOLEAN_QUICK, "iget-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xF0, IGET_BYTE_QUICK, "iget-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xF1, IGET_CHAR_QUICK, "iget-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xF3, UNUSED_F3, "unused-f3", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xF4, UNUSED_F4, "unused-f4", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xF5, UNUSED_F5, "unused-f5", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xF6, UNUSED_F6, "unused-f6", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xF7, UNUSED_F7, "unused-f7", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xF8, UNUSED_F8, "unused-f8", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \ - V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \ - V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArg) \ - V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArgRange) \ - V(0xFE, CONST_METHOD_HANDLE, "const-method-handle", k21c, kIndexMethodHandleRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBMethodHandle) \ - V(0xFF, CONST_METHOD_TYPE, "const-method-type", k21c, kIndexProtoRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBPrototype) - -#define DEX_INSTRUCTION_FORMAT_LIST(V) \ - V(k10x) \ - V(k12x) \ - V(k11n) \ - V(k11x) \ - V(k10t) \ - V(k20t) \ - V(k22x) \ - V(k21t) \ - V(k21s) \ - V(k21h) \ - V(k21c) \ - V(k23x) \ - V(k22b) \ - V(k22t) \ - V(k22s) \ - V(k22c) \ - V(k32x) \ - V(k30t) \ - V(k31t) \ - V(k31i) \ - V(k31c) \ - V(k35c) \ - V(k3rc) \ - V(k45cc) \ - V(k4rcc) \ - V(k51l) - -#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ -#undef ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ // the guard in this file is just for cpplint diff --git a/runtime/dex/dex_instruction_test.cc b/runtime/dex/dex_instruction_test.cc deleted file mode 100644 index c944085b9e..0000000000 --- a/runtime/dex/dex_instruction_test.cc +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2015 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 "dex_instruction-inl.h" -#include "dex_instruction_iterator.h" -#include "gtest/gtest.h" - -namespace art { - -TEST(StaticGetters, PropertiesOfNopTest) { - Instruction::Code nop = Instruction::NOP; - EXPECT_STREQ("nop", Instruction::Name(nop)); - EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop)); - EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop)); - EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop)); - EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop)); -} - -static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx, - uint16_t arg_regs, uint16_t* out) { - // A = num argument registers - // B = method_idx - // C - G = argument registers - // H = proto_idx - // - // op = 0xFA - // - // format: - // AG op BBBB FEDC HHHH - out[0] = 0; - out[0] |= (num_args << 12); - out[0] |= 0x00FA; - - out[1] = method_idx; - out[2] = arg_regs; - out[3] = proto_idx; -} - -static void Build4rcc(uint16_t num_args, uint16_t method_idx, uint16_t proto_idx, - uint16_t arg_regs_start, uint16_t* out) { - // A = num argument registers - // B = method_idx - // C = first argument register - // H = proto_idx - // - // op = 0xFB - // - // format: - // AA op BBBB CCCC HHHH - out[0] = 0; - out[0] |= (num_args << 8); - out[0] |= 0x00FB; - - out[1] = method_idx; - out[2] = arg_regs_start; - out[3] = proto_idx; -} - -TEST(Instruction, PropertiesOf45cc) { - uint16_t instruction[4]; - Build45cc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */, - 0xcafe /* arg_regs */, instruction); - - DexInstructionIterator ins(instruction, /*dex_pc*/ 0u); - ASSERT_EQ(4u, ins->SizeInCodeUnits()); - - ASSERT_TRUE(ins->HasVRegA()); - ASSERT_EQ(4, ins->VRegA()); - ASSERT_EQ(4u, ins->VRegA_45cc()); - ASSERT_EQ(4u, ins->VRegA_45cc(instruction[0])); - - ASSERT_TRUE(ins->HasVRegB()); - ASSERT_EQ(16, ins->VRegB()); - ASSERT_EQ(16u, ins->VRegB_45cc()); - - ASSERT_TRUE(ins->HasVRegC()); - ASSERT_EQ(0xe, ins->VRegC()); - ASSERT_EQ(0xe, ins->VRegC_45cc()); - - ASSERT_TRUE(ins->HasVRegH()); - ASSERT_EQ(32, ins->VRegH()); - ASSERT_EQ(32, ins->VRegH_45cc()); - - ASSERT_TRUE(ins->HasVarArgs()); - - uint32_t arg_regs[Instruction::kMaxVarArgRegs]; - ins->GetVarArgs(arg_regs); - ASSERT_EQ(0xeu, arg_regs[0]); - ASSERT_EQ(0xfu, arg_regs[1]); - ASSERT_EQ(0xau, arg_regs[2]); - ASSERT_EQ(0xcu, arg_regs[3]); -} - -TEST(Instruction, PropertiesOf4rcc) { - uint16_t instruction[4]; - Build4rcc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */, - 0xcafe /* arg_regs */, instruction); - - DexInstructionIterator ins(instruction, /*dex_pc*/ 0u); - ASSERT_EQ(4u, ins->SizeInCodeUnits()); - - ASSERT_TRUE(ins->HasVRegA()); - ASSERT_EQ(4, ins->VRegA()); - ASSERT_EQ(4u, ins->VRegA_4rcc()); - ASSERT_EQ(4u, ins->VRegA_4rcc(instruction[0])); - - ASSERT_TRUE(ins->HasVRegB()); - ASSERT_EQ(16, ins->VRegB()); - ASSERT_EQ(16u, ins->VRegB_4rcc()); - - ASSERT_TRUE(ins->HasVRegC()); - ASSERT_EQ(0xcafe, ins->VRegC()); - ASSERT_EQ(0xcafe, ins->VRegC_4rcc()); - - ASSERT_TRUE(ins->HasVRegH()); - ASSERT_EQ(32, ins->VRegH()); - ASSERT_EQ(32, ins->VRegH_4rcc()); - - ASSERT_FALSE(ins->HasVarArgs()); -} - -static void Build35c(uint16_t* out, - Instruction::Code code, - uint16_t method_idx, - std::vector<uint16_t> args) { - out[0] = 0; - out[0] |= (args.size() << 12); - out[0] |= static_cast<uint16_t>(code); - out[1] = method_idx; - size_t i = 0; - out[2] = 0; - for (; i < 4 && i < args.size(); ++i) { - out[2] |= args[i] << (i * 4); - } - if (args.size() == 5) { - out[0] |= args[4] << 8; - } -} - -static std::string DumpInst35c(Instruction::Code code, - uint16_t method_idx, - std::vector<uint16_t> args) { - uint16_t inst[6] = {}; - Build35c(inst, code, method_idx, args); - return Instruction::At(inst)->DumpString(nullptr); -} - -TEST(Instruction, DumpString) { - EXPECT_EQ(DumpInst35c(Instruction::FILLED_NEW_ARRAY, 1234, {3, 2}), - "filled-new-array {v3, v2}, type@1234"); - EXPECT_EQ(DumpInst35c(Instruction::INVOKE_VIRTUAL, 1234, {3, 2, 1, 5, 6}), - "invoke-virtual {v3, v2, v1, v5, v6}, thing@1234"); - EXPECT_EQ(DumpInst35c(Instruction::INVOKE_VIRTUAL_QUICK, 1234, {3, 2, 1, 5}), - "invoke-virtual-quick {v3, v2, v1, v5}, thing@1234"); - EXPECT_EQ(DumpInst35c(Instruction::INVOKE_CUSTOM, 1234, {3, 2, 1}), - "invoke-custom {v3, v2, v1}, thing@1234"); -} - -} // namespace art diff --git a/runtime/dex/dex_instruction_utils.h b/runtime/dex/dex_instruction_utils.h deleted file mode 100644 index 27501927e7..0000000000 --- a/runtime/dex/dex_instruction_utils.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ - -#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_ -#define ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_ - -#include "dex_instruction.h" - -namespace art { - -// Dex invoke type corresponds to the ordering of INVOKE instructions; -// this order is the same for range and non-range invokes. -enum DexInvokeType : uint8_t { - kDexInvokeVirtual = 0, // invoke-virtual, invoke-virtual-range - kDexInvokeSuper, // invoke-super, invoke-super-range - kDexInvokeDirect, // invoke-direct, invoke-direct-range - kDexInvokeStatic, // invoke-static, invoke-static-range - kDexInvokeInterface, // invoke-interface, invoke-interface-range - kDexInvokeTypeCount -}; - -// Dex instruction memory access types correspond to the ordering of GET/PUT instructions; -// this order is the same for IGET, IPUT, SGET, SPUT, AGET and APUT. -enum DexMemAccessType : uint8_t { - kDexMemAccessWord = 0, // op 0; int or float, the actual type is not encoded. - kDexMemAccessWide, // op_WIDE 1; long or double, the actual type is not encoded. - kDexMemAccessObject, // op_OBJECT 2; the actual reference type is not encoded. - kDexMemAccessBoolean, // op_BOOLEAN 3 - kDexMemAccessByte, // op_BYTE 4 - kDexMemAccessChar, // op_CHAR 5 - kDexMemAccessShort, // op_SHORT 6 - kDexMemAccessTypeCount -}; - -std::ostream& operator<<(std::ostream& os, const DexMemAccessType& type); - -// NOTE: The following functions disregard quickened instructions. - -// By "direct" const we mean to exclude const-string and const-class -// which load data from somewhere else, i.e. indirectly. -constexpr bool IsInstructionDirectConst(Instruction::Code opcode) { - return Instruction::CONST_4 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16; -} - -constexpr bool IsInstructionConstWide(Instruction::Code opcode) { - return Instruction::CONST_WIDE_16 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16; -} - -constexpr bool IsInstructionReturn(Instruction::Code opcode) { - return Instruction::RETURN_VOID <= opcode && opcode <= Instruction::RETURN_OBJECT; -} - -constexpr bool IsInstructionInvoke(Instruction::Code opcode) { - return Instruction::INVOKE_VIRTUAL <= opcode && opcode <= Instruction::INVOKE_INTERFACE_RANGE && - opcode != Instruction::RETURN_VOID_NO_BARRIER; -} - -constexpr bool IsInstructionQuickInvoke(Instruction::Code opcode) { - return opcode == Instruction::INVOKE_VIRTUAL_QUICK || - opcode == Instruction::INVOKE_VIRTUAL_RANGE_QUICK; -} - -constexpr bool IsInstructionInvokeStatic(Instruction::Code opcode) { - return opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE; -} - -constexpr bool IsInstructionGoto(Instruction::Code opcode) { - return Instruction::GOTO <= opcode && opcode <= Instruction::GOTO_32; -} - -constexpr bool IsInstructionIfCc(Instruction::Code opcode) { - return Instruction::IF_EQ <= opcode && opcode <= Instruction::IF_LE; -} - -constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) { - return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ; -} - -constexpr bool IsInstructionIGet(Instruction::Code code) { - return Instruction::IGET <= code && code <= Instruction::IGET_SHORT; -} - -constexpr bool IsInstructionIPut(Instruction::Code code) { - return Instruction::IPUT <= code && code <= Instruction::IPUT_SHORT; -} - -constexpr bool IsInstructionSGet(Instruction::Code code) { - return Instruction::SGET <= code && code <= Instruction::SGET_SHORT; -} - -constexpr bool IsInstructionSPut(Instruction::Code code) { - return Instruction::SPUT <= code && code <= Instruction::SPUT_SHORT; -} - -constexpr bool IsInstructionAGet(Instruction::Code code) { - return Instruction::AGET <= code && code <= Instruction::AGET_SHORT; -} - -constexpr bool IsInstructionAPut(Instruction::Code code) { - return Instruction::APUT <= code && code <= Instruction::APUT_SHORT; -} - -constexpr bool IsInstructionIGetOrIPut(Instruction::Code code) { - return Instruction::IGET <= code && code <= Instruction::IPUT_SHORT; -} - -constexpr bool IsInstructionIGetQuickOrIPutQuick(Instruction::Code code) { - return (code >= Instruction::IGET_QUICK && code <= Instruction::IPUT_OBJECT_QUICK) || - (code >= Instruction::IPUT_BOOLEAN_QUICK && code <= Instruction::IGET_SHORT_QUICK); -} - -constexpr bool IsInstructionSGetOrSPut(Instruction::Code code) { - return Instruction::SGET <= code && code <= Instruction::SPUT_SHORT; -} - -constexpr bool IsInstructionAGetOrAPut(Instruction::Code code) { - return Instruction::AGET <= code && code <= Instruction::APUT_SHORT; -} - -constexpr bool IsInstructionBinOp2Addr(Instruction::Code code) { - return Instruction::ADD_INT_2ADDR <= code && code <= Instruction::REM_DOUBLE_2ADDR; -} - -constexpr bool IsInvokeInstructionRange(Instruction::Code opcode) { - DCHECK(IsInstructionInvoke(opcode)); - return opcode >= Instruction::INVOKE_VIRTUAL_RANGE; -} - -constexpr DexInvokeType InvokeInstructionType(Instruction::Code opcode) { - DCHECK(IsInstructionInvoke(opcode)); - return static_cast<DexInvokeType>(IsInvokeInstructionRange(opcode) - ? (opcode - Instruction::INVOKE_VIRTUAL_RANGE) - : (opcode - Instruction::INVOKE_VIRTUAL)); -} - -constexpr DexMemAccessType IGetMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionIGet(code)); - return static_cast<DexMemAccessType>(code - Instruction::IGET); -} - -constexpr DexMemAccessType IPutMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionIPut(code)); - return static_cast<DexMemAccessType>(code - Instruction::IPUT); -} - -constexpr DexMemAccessType SGetMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionSGet(code)); - return static_cast<DexMemAccessType>(code - Instruction::SGET); -} - -constexpr DexMemAccessType SPutMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionSPut(code)); - return static_cast<DexMemAccessType>(code - Instruction::SPUT); -} - -constexpr DexMemAccessType AGetMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionAGet(code)); - return static_cast<DexMemAccessType>(code - Instruction::AGET); -} - -constexpr DexMemAccessType APutMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionAPut(code)); - return static_cast<DexMemAccessType>(code - Instruction::APUT); -} - -constexpr DexMemAccessType IGetOrIPutMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionIGetOrIPut(code)); - return (code >= Instruction::IPUT) ? IPutMemAccessType(code) : IGetMemAccessType(code); -} - -inline DexMemAccessType IGetQuickOrIPutQuickMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionIGetQuickOrIPutQuick(code)); - switch (code) { - case Instruction::IGET_QUICK: case Instruction::IPUT_QUICK: - return kDexMemAccessWord; - case Instruction::IGET_WIDE_QUICK: case Instruction::IPUT_WIDE_QUICK: - return kDexMemAccessWide; - case Instruction::IGET_OBJECT_QUICK: case Instruction::IPUT_OBJECT_QUICK: - return kDexMemAccessObject; - case Instruction::IGET_BOOLEAN_QUICK: case Instruction::IPUT_BOOLEAN_QUICK: - return kDexMemAccessBoolean; - case Instruction::IGET_BYTE_QUICK: case Instruction::IPUT_BYTE_QUICK: - return kDexMemAccessByte; - case Instruction::IGET_CHAR_QUICK: case Instruction::IPUT_CHAR_QUICK: - return kDexMemAccessChar; - case Instruction::IGET_SHORT_QUICK: case Instruction::IPUT_SHORT_QUICK: - return kDexMemAccessShort; - default: - LOG(FATAL) << code; - UNREACHABLE(); - } -} - -constexpr DexMemAccessType SGetOrSPutMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionSGetOrSPut(code)); - return (code >= Instruction::SPUT) ? SPutMemAccessType(code) : SGetMemAccessType(code); -} - -constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) { - DCHECK(IsInstructionAGetOrAPut(code)); - return (code >= Instruction::APUT) ? APutMemAccessType(code) : AGetMemAccessType(code); -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_ diff --git a/runtime/dex/invoke_type.h b/runtime/dex/invoke_type.h deleted file mode 100644 index 726d269a3e..0000000000 --- a/runtime/dex/invoke_type.h +++ /dev/null @@ -1,38 +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. - */ - -#ifndef ART_RUNTIME_DEX_INVOKE_TYPE_H_ -#define ART_RUNTIME_DEX_INVOKE_TYPE_H_ - -#include <iosfwd> - -namespace art { - -enum InvokeType : uint32_t { - kStatic, // <<static>> - kDirect, // <<direct>> - kVirtual, // <<virtual>> - kSuper, // <<super>> - kInterface, // <<interface>> - kPolymorphic, // <<polymorphic>> - kMaxInvokeType = kPolymorphic -}; - -std::ostream& operator<<(std::ostream& os, const InvokeType& rhs); - -} // namespace art - -#endif // ART_RUNTIME_DEX_INVOKE_TYPE_H_ diff --git a/runtime/dex/modifiers.cc b/runtime/dex/modifiers.cc deleted file mode 100644 index 30daefb172..0000000000 --- a/runtime/dex/modifiers.cc +++ /dev/null @@ -1,58 +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 <string> - -#include "modifiers.h" - -namespace art { - -std::string PrettyJavaAccessFlags(uint32_t access_flags) { - std::string result; - if ((access_flags & kAccPublic) != 0) { - result += "public "; - } - if ((access_flags & kAccProtected) != 0) { - result += "protected "; - } - if ((access_flags & kAccPrivate) != 0) { - result += "private "; - } - if ((access_flags & kAccFinal) != 0) { - result += "final "; - } - if ((access_flags & kAccStatic) != 0) { - result += "static "; - } - if ((access_flags & kAccAbstract) != 0) { - result += "abstract "; - } - if ((access_flags & kAccInterface) != 0) { - result += "interface "; - } - if ((access_flags & kAccTransient) != 0) { - result += "transient "; - } - if ((access_flags & kAccVolatile) != 0) { - result += "volatile "; - } - if ((access_flags & kAccSynchronized) != 0) { - result += "synchronized "; - } - return result; -} - -} // namespace art diff --git a/runtime/dex/modifiers.h b/runtime/dex/modifiers.h deleted file mode 100644 index 2998f602d4..0000000000 --- a/runtime/dex/modifiers.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#ifndef ART_RUNTIME_DEX_MODIFIERS_H_ -#define ART_RUNTIME_DEX_MODIFIERS_H_ - -#include <stdint.h> - -namespace art { - -static constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic -static constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic -static constexpr uint32_t kAccProtected = 0x0004; // field, method, ic -static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic -static constexpr uint32_t kAccFinal = 0x0010; // class, field, method, ic -static constexpr uint32_t kAccSynchronized = 0x0020; // method (only allowed on natives) -static constexpr uint32_t kAccSuper = 0x0020; // class (not used in dex) -static constexpr uint32_t kAccVolatile = 0x0040; // field -static constexpr uint32_t kAccBridge = 0x0040; // method (1.5) -static constexpr uint32_t kAccTransient = 0x0080; // field -static constexpr uint32_t kAccVarargs = 0x0080; // method (1.5) -static constexpr uint32_t kAccNative = 0x0100; // method -static constexpr uint32_t kAccInterface = 0x0200; // class, ic -static constexpr uint32_t kAccAbstract = 0x0400; // class, method, ic -static constexpr uint32_t kAccStrict = 0x0800; // method -static constexpr uint32_t kAccSynthetic = 0x1000; // class, field, method, ic -static constexpr uint32_t kAccAnnotation = 0x2000; // class, ic (1.5) -static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5) - -static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16) - -// The following flags are used to insert hidden API access flags into boot -// class path dex files. They are decoded by DexFile::ClassDataItemIterator and -// removed from the access flags before used by the runtime. -static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native) -static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native) - -static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init> -static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only) -static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only) -// Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its -// declaring class. This flag may only be applied to methods. -static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime) -// Used by a method to denote that its execution does not need to go through slow path interpreter. -static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native) -// Used by a class to denote that the verifier has attempted to check it at least once. -static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) -// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent -// that it was copied from its declaring class into another class. All methods marked kAccMiranda -// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ -// array of a concrete class will also have this bit set. -static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime) -static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native) -static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime) -// Native method flags are set when linking the methods based on the presence of the -// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility. -// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods. -static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only) -static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only) - -// Set by the JIT when clearing profiling infos to denote that a method was previously warm. -static constexpr uint32_t kAccPreviouslyWarm = 0x00800000; // method (runtime) - -// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know -// if any particular method needs to be a default conflict. Used to figure out at runtime if -// invoking this method will throw an exception. -static constexpr uint32_t kAccDefaultConflict = 0x01000000; // method (runtime) - -// Set by the verifier for a method we do not want the compiler to compile. -static constexpr uint32_t kAccCompileDontBother = 0x02000000; // method (runtime) - -// Set by the verifier for a method that could not be verified to follow structured locking. -static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (runtime) - -// Set by the class linker for a method that has only one implementation for a -// virtual call. -static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime) - -static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method - -// Not currently used, except for intrinsic methods where these bits -// are part of the intrinsic ordinal. -static constexpr uint32_t kAccMayBeUnusedBits = 0x40000000; - -// Set by the compiler driver when compiling boot classes with instrinsic methods. -static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime) - -// Special runtime-only flags. -// Interface and all its super-interfaces with default methods have been recursively initialized. -static constexpr uint32_t kAccRecursivelyInitialized = 0x20000000; -// Interface declares some default method. -static constexpr uint32_t kAccHasDefaultMethod = 0x40000000; -// class/ancestor overrides finalize() -static constexpr uint32_t kAccClassIsFinalizable = 0x80000000; - -// Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags -// which overlap are not valid when kAccIntrinsic is set. -static constexpr uint32_t kAccIntrinsicBits = kAccMayBeUnusedBits | kAccHiddenApiBits | - kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict | - kAccPreviouslyWarm; - -// Valid (meaningful) bits for a field. -static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected | - kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum; - -// Valid (meaningful) bits for a method. -static constexpr uint32_t kAccValidMethodFlags = kAccPublic | kAccPrivate | kAccProtected | - kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | - kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized; -static_assert(((kAccIntrinsic | kAccIntrinsicBits) & kAccValidMethodFlags) == 0, - "Intrinsic bits and valid dex file method access flags must not overlap."); - -// Valid (meaningful) bits for a class (not interface). -// Note 1. These are positive bits. Other bits may have to be zero. -// Note 2. Inner classes can expose more access flags to Java programs. That is handled by libcore. -static constexpr uint32_t kAccValidClassFlags = kAccPublic | kAccFinal | kAccSuper | - kAccAbstract | kAccSynthetic | kAccEnum; - -// Valid (meaningful) bits for an interface. -// Note 1. Annotations are interfaces. -// Note 2. These are positive bits. Other bits may have to be zero. -// Note 3. Inner classes can expose more access flags to Java programs. That is handled by libcore. -static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface | - kAccAbstract | kAccSynthetic | kAccAnnotation; - -static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected; - -// Returns a human-readable version of the Java part of the access flags, e.g., "private static " -// (note the trailing whitespace). -std::string PrettyJavaAccessFlags(uint32_t access_flags); - -} // namespace art - -#endif // ART_RUNTIME_DEX_MODIFIERS_H_ - diff --git a/runtime/dex/standard_dex_file.cc b/runtime/dex/standard_dex_file.cc deleted file mode 100644 index f7317eb997..0000000000 --- a/runtime/dex/standard_dex_file.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "standard_dex_file.h" - -#include "base/casts.h" -#include "code_item_accessors-inl.h" -#include "dex_file-inl.h" -#include "leb128.h" - -namespace art { - -const uint8_t StandardDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' }; -const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersions] - [StandardDexFile::kDexVersionLen] = { - {'0', '3', '5', '\0'}, - // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex - // files with that version number would erroneously be accepted and run. - {'0', '3', '7', '\0'}, - // Dex version 038: Android "O" and beyond. - {'0', '3', '8', '\0'}, - // Dex verion 039: Beyond Android "O". - {'0', '3', '9', '\0'}, -}; - -void StandardDexFile::WriteMagic(uint8_t* magic) { - std::copy_n(kDexMagic, kDexMagicSize, magic); -} - -void StandardDexFile::WriteCurrentVersion(uint8_t* magic) { - std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1], - kDexVersionLen, - magic + kDexMagicSize); -} - -bool StandardDexFile::IsMagicValid(const uint8_t* magic) { - return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); -} - -bool StandardDexFile::IsVersionValid(const uint8_t* magic) { - const uint8_t* version = &magic[sizeof(kDexMagic)]; - for (uint32_t i = 0; i < kNumDexVersions; i++) { - if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) { - return true; - } - } - return false; -} - -bool StandardDexFile::IsMagicValid() const { - return IsMagicValid(header_->magic_); -} - -bool StandardDexFile::IsVersionValid() const { - return IsVersionValid(header_->magic_); -} - -bool StandardDexFile::SupportsDefaultMethods() const { - return GetDexVersion() >= DexFile::kDefaultMethodsVersion; -} - -uint32_t StandardDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { - DCHECK(IsInDataSection(&item)); - return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) - - reinterpret_cast<uintptr_t>(&item); -} - -} // namespace art diff --git a/runtime/dex/standard_dex_file.h b/runtime/dex/standard_dex_file.h deleted file mode 100644 index e0e9f2f11c..0000000000 --- a/runtime/dex/standard_dex_file.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_ -#define ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_ - -#include <iosfwd> - -#include "dex_file.h" - -namespace art { - -class OatDexFile; - -// Standard dex file. This is the format that is packaged in APKs and produced by tools. -class StandardDexFile : public DexFile { - public: - class Header : public DexFile::Header { - // Same for now. - }; - - struct CodeItem : public DexFile::CodeItem { - static constexpr size_t kAlignment = 4; - - private: - CodeItem() = default; - - uint16_t registers_size_; // the number of registers used by this code - // (locals + parameters) - uint16_t ins_size_; // the number of words of incoming arguments to the method - // that this code is for - uint16_t outs_size_; // the number of words of outgoing argument space required - // by this code for method invocation - uint16_t tries_size_; // the number of try_items for this instance. If non-zero, - // then these appear as the tries array just after the - // insns in this instance. - uint32_t debug_info_off_; // Holds file offset to debug info stream. - - uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units - uint16_t insns_[1]; // actual array of bytecode. - - ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); - friend class CodeItemDataAccessor; - friend class CodeItemDebugInfoAccessor; - friend class CodeItemInstructionAccessor; - friend class DexWriter; - friend class StandardDexFile; - DISALLOW_COPY_AND_ASSIGN(CodeItem); - }; - - // Write the standard dex specific magic. - static void WriteMagic(uint8_t* magic); - - // Write the current version, note that the input is the address of the magic. - static void WriteCurrentVersion(uint8_t* magic); - - static const uint8_t kDexMagic[kDexMagicSize]; - static constexpr size_t kNumDexVersions = 4; - static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; - - // Returns true if the byte string points to the magic value. - static bool IsMagicValid(const uint8_t* magic); - virtual bool IsMagicValid() const OVERRIDE; - - // Returns true if the byte string after the magic is the correct value. - static bool IsVersionValid(const uint8_t* magic); - virtual bool IsVersionValid() const OVERRIDE; - - virtual bool SupportsDefaultMethods() const OVERRIDE; - - uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; - - virtual size_t GetDequickenedSize() const OVERRIDE { - return Size(); - } - - private: - StandardDexFile(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - DexFileContainer* container) - : DexFile(base, - size, - /*data_begin*/ base, - /*data_size*/ size, - location, - location_checksum, - oat_dex_file, - container, - /*is_compact_dex*/ false) {} - - friend class DexFileLoader; - friend class DexFileVerifierTest; - - ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor - friend class OptimizingUnitTestHelper; // for constructor - - DISALLOW_COPY_AND_ASSIGN(StandardDexFile); -}; - -} // namespace art - -#endif // ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_ diff --git a/runtime/dex/utf-inl.h b/runtime/dex/utf-inl.h deleted file mode 100644 index 4f626a8580..0000000000 --- a/runtime/dex/utf-inl.h +++ /dev/null @@ -1,99 +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. - */ - -#ifndef ART_RUNTIME_DEX_UTF_INL_H_ -#define ART_RUNTIME_DEX_UTF_INL_H_ - -#include "utf.h" - -namespace art { - -inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) { - return static_cast<uint16_t>(maybe_pair >> 16); -} - -inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) { - return static_cast<uint16_t>(maybe_pair & 0x0000FFFF); -} - -inline uint32_t GetUtf16FromUtf8(const char** utf8_data_in) { - const uint8_t one = *(*utf8_data_in)++; - if ((one & 0x80) == 0) { - // one-byte encoding - return one; - } - - const uint8_t two = *(*utf8_data_in)++; - if ((one & 0x20) == 0) { - // two-byte encoding - return ((one & 0x1f) << 6) | (two & 0x3f); - } - - const uint8_t three = *(*utf8_data_in)++; - if ((one & 0x10) == 0) { - return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f); - } - - // Four byte encodings need special handling. We'll have - // to convert them into a surrogate pair. - const uint8_t four = *(*utf8_data_in)++; - - // Since this is a 4 byte UTF-8 sequence, it will lie between - // U+10000 and U+1FFFFF. - // - // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The - // spec says they're invalid but nobody appears to check for them. - const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12) - | ((three & 0x3f) << 6) | (four & 0x3f); - - uint32_t surrogate_pair = 0; - // Step two: Write out the high (leading) surrogate to the bottom 16 bits - // of the of the 32 bit type. - surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff; - // Step three : Write out the low (trailing) surrogate to the top 16 bits. - surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16; - - return surrogate_pair; -} - -inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, - const char* utf8_2) { - uint32_t c1, c2; - do { - c1 = *utf8_1; - c2 = *utf8_2; - // Did we reach a terminating character? - if (c1 == 0) { - return (c2 == 0) ? 0 : -1; - } else if (c2 == 0) { - return 1; - } - - c1 = GetUtf16FromUtf8(&utf8_1); - c2 = GetUtf16FromUtf8(&utf8_2); - } while (c1 == c2); - - const uint32_t leading_surrogate_diff = GetLeadingUtf16Char(c1) - GetLeadingUtf16Char(c2); - if (leading_surrogate_diff != 0) { - return static_cast<int>(leading_surrogate_diff); - } - - return GetTrailingUtf16Char(c1) - GetTrailingUtf16Char(c2); -} - -} // namespace art - -#endif // ART_RUNTIME_DEX_UTF_INL_H_ diff --git a/runtime/dex/utf.cc b/runtime/dex/utf.cc deleted file mode 100644 index 772a610140..0000000000 --- a/runtime/dex/utf.cc +++ /dev/null @@ -1,321 +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 "utf.h" - -#include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> - -#include "base/casts.h" -#include "utf-inl.h" - -namespace art { - -using android::base::StringAppendF; -using android::base::StringPrintf; - -// This is used only from debugger and test code. -size_t CountModifiedUtf8Chars(const char* utf8) { - return CountModifiedUtf8Chars(utf8, strlen(utf8)); -} - -/* - * This does not validate UTF8 rules (nor did older code). But it gets the right answer - * for valid UTF-8 and that's fine because it's used only to size a buffer for later - * conversion. - * - * Modified UTF-8 consists of a series of bytes up to 21 bit Unicode code points as follows: - * U+0001 - U+007F 0xxxxxxx - * U+0080 - U+07FF 110xxxxx 10xxxxxx - * U+0800 - U+FFFF 1110xxxx 10xxxxxx 10xxxxxx - * U+10000 - U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * - * U+0000 is encoded using the 2nd form to avoid nulls inside strings (this differs from - * standard UTF-8). - * The four byte encoding converts to two utf16 characters. - */ -size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) { - DCHECK_LE(byte_count, strlen(utf8)); - size_t len = 0; - const char* end = utf8 + byte_count; - for (; utf8 < end; ++utf8) { - int ic = *utf8; - len++; - if (LIKELY((ic & 0x80) == 0)) { - // One-byte encoding. - continue; - } - // Two- or three-byte encoding. - utf8++; - if ((ic & 0x20) == 0) { - // Two-byte encoding. - continue; - } - utf8++; - if ((ic & 0x10) == 0) { - // Three-byte encoding. - continue; - } - - // Four-byte encoding: needs to be converted into a surrogate - // pair. - utf8++; - len++; - } - return len; -} - -// This is used only from debugger and test code. -void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) { - while (*utf8_data_in != '\0') { - const uint32_t ch = GetUtf16FromUtf8(&utf8_data_in); - const uint16_t leading = GetLeadingUtf16Char(ch); - const uint16_t trailing = GetTrailingUtf16Char(ch); - - *utf16_data_out++ = leading; - if (trailing != 0) { - *utf16_data_out++ = trailing; - } - } -} - -void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, size_t out_chars, - const char* utf8_data_in, size_t in_bytes) { - const char *in_start = utf8_data_in; - const char *in_end = utf8_data_in + in_bytes; - uint16_t *out_p = utf16_data_out; - - if (LIKELY(out_chars == in_bytes)) { - // Common case where all characters are ASCII. - for (const char *p = in_start; p < in_end;) { - // Safe even if char is signed because ASCII characters always have - // the high bit cleared. - *out_p++ = dchecked_integral_cast<uint16_t>(*p++); - } - return; - } - - // String contains non-ASCII characters. - for (const char *p = in_start; p < in_end;) { - const uint32_t ch = GetUtf16FromUtf8(&p); - const uint16_t leading = GetLeadingUtf16Char(ch); - const uint16_t trailing = GetTrailingUtf16Char(ch); - - *out_p++ = leading; - if (trailing != 0) { - *out_p++ = trailing; - } - } -} - -void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count, - const uint16_t* utf16_in, size_t char_count) { - if (LIKELY(byte_count == char_count)) { - // Common case where all characters are ASCII. - const uint16_t *utf16_end = utf16_in + char_count; - for (const uint16_t *p = utf16_in; p < utf16_end;) { - *utf8_out++ = dchecked_integral_cast<char>(*p++); - } - return; - } - - // String contains non-ASCII characters. - while (char_count--) { - const uint16_t ch = *utf16_in++; - if (ch > 0 && ch <= 0x7f) { - *utf8_out++ = ch; - } else { - // Char_count == 0 here implies we've encountered an unpaired - // surrogate and we have no choice but to encode it as 3-byte UTF - // sequence. Note that unpaired surrogates can occur as a part of - // "normal" operation. - if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) { - const uint16_t ch2 = *utf16_in; - - // Check if the other half of the pair is within the expected - // range. If it isn't, we will have to emit both "halves" as - // separate 3 byte sequences. - if (ch2 >= 0xdc00 && ch2 <= 0xdfff) { - utf16_in++; - char_count--; - const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00; - *utf8_out++ = (code_point >> 18) | 0xf0; - *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80; - *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80; - *utf8_out++ = (code_point & 0x3f) | 0x80; - continue; - } - } - - if (ch > 0x07ff) { - // Three byte encoding. - *utf8_out++ = (ch >> 12) | 0xe0; - *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80; - *utf8_out++ = (ch & 0x3f) | 0x80; - } else /*(ch > 0x7f || ch == 0)*/ { - // Two byte encoding. - *utf8_out++ = (ch >> 6) | 0xc0; - *utf8_out++ = (ch & 0x3f) | 0x80; - } - } - } -} - -int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) { - uint32_t hash = 0; - while (utf16_length != 0u) { - const uint32_t pair = GetUtf16FromUtf8(&utf8); - const uint16_t first = GetLeadingUtf16Char(pair); - hash = hash * 31 + first; - --utf16_length; - const uint16_t second = GetTrailingUtf16Char(pair); - if (second != 0) { - hash = hash * 31 + second; - DCHECK_NE(utf16_length, 0u); - --utf16_length; - } - } - return static_cast<int32_t>(hash); -} - -uint32_t ComputeModifiedUtf8Hash(const char* chars) { - uint32_t hash = 0; - while (*chars != '\0') { - hash = hash * 31 + *chars++; - } - return static_cast<int32_t>(hash); -} - -int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16, - size_t utf16_length) { - for (;;) { - if (*utf8 == '\0') { - return (utf16_length == 0) ? 0 : -1; - } else if (utf16_length == 0) { - return 1; - } - - const uint32_t pair = GetUtf16FromUtf8(&utf8); - - // First compare the leading utf16 char. - const uint16_t lhs = GetLeadingUtf16Char(pair); - const uint16_t rhs = *utf16++; - --utf16_length; - if (lhs != rhs) { - return lhs > rhs ? 1 : -1; - } - - // Then compare the trailing utf16 char. First check if there - // are any characters left to consume. - const uint16_t lhs2 = GetTrailingUtf16Char(pair); - if (lhs2 != 0) { - if (utf16_length == 0) { - return 1; - } - - const uint16_t rhs2 = *utf16++; - --utf16_length; - if (lhs2 != rhs2) { - return lhs2 > rhs2 ? 1 : -1; - } - } - } -} - -size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) { - size_t result = 0; - const uint16_t *end = chars + char_count; - while (chars < end) { - const uint16_t ch = *chars++; - if (LIKELY(ch != 0 && ch < 0x80)) { - result++; - continue; - } - if (ch < 0x800) { - result += 2; - continue; - } - if (ch >= 0xd800 && ch < 0xdc00) { - if (chars < end) { - const uint16_t ch2 = *chars; - // If we find a properly paired surrogate, we emit it as a 4 byte - // UTF sequence. If we find an unpaired leading or trailing surrogate, - // we emit it as a 3 byte sequence like would have done earlier. - if (ch2 >= 0xdc00 && ch2 < 0xe000) { - chars++; - result += 4; - continue; - } - } - } - result += 3; - } - return result; -} - -static inline constexpr bool NeedsEscaping(uint16_t ch) { - return (ch < ' ' || ch > '~'); -} - -std::string PrintableChar(uint16_t ch) { - std::string result; - result += '\''; - if (NeedsEscaping(ch)) { - StringAppendF(&result, "\\u%04x", ch); - } else { - result += static_cast<std::string::value_type>(ch); - } - result += '\''; - return result; -} - -std::string PrintableString(const char* utf) { - std::string result; - result += '"'; - const char* p = utf; - size_t char_count = CountModifiedUtf8Chars(p); - for (size_t i = 0; i < char_count; ++i) { - uint32_t ch = GetUtf16FromUtf8(&p); - if (ch == '\\') { - result += "\\\\"; - } else if (ch == '\n') { - result += "\\n"; - } else if (ch == '\r') { - result += "\\r"; - } else if (ch == '\t') { - result += "\\t"; - } else { - const uint16_t leading = GetLeadingUtf16Char(ch); - - if (NeedsEscaping(leading)) { - StringAppendF(&result, "\\u%04x", leading); - } else { - result += static_cast<std::string::value_type>(leading); - } - - const uint32_t trailing = GetTrailingUtf16Char(ch); - if (trailing != 0) { - // All high surrogates will need escaping. - StringAppendF(&result, "\\u%04x", trailing); - } - } - } - result += '"'; - return result; -} - -} // namespace art diff --git a/runtime/dex/utf.h b/runtime/dex/utf.h deleted file mode 100644 index 4adfc4af8c..0000000000 --- a/runtime/dex/utf.h +++ /dev/null @@ -1,135 +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. - */ - -#ifndef ART_RUNTIME_DEX_UTF_H_ -#define ART_RUNTIME_DEX_UTF_H_ - -#include "base/macros.h" - -#include <stddef.h> -#include <stdint.h> - -#include <string> - -/* - * All UTF-8 in art is actually modified UTF-8. Mostly, this distinction - * doesn't matter. - * - * See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details. - */ -namespace art { - -/* - * Returns the number of UTF-16 characters in the given modified UTF-8 string. - */ -size_t CountModifiedUtf8Chars(const char* utf8); -size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count); - -/* - * Returns the number of modified UTF-8 bytes needed to represent the given - * UTF-16 string. - */ -size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count); - -/* - * Convert from Modified UTF-8 to UTF-16. - */ -void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in); -void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, size_t out_chars, - const char* utf8_in, size_t in_bytes); - -/* - * Compare two modified UTF-8 strings as UTF-16 code point values in a non-locale sensitive manner - */ -ALWAYS_INLINE int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, - const char* utf8_2); - -/* - * Compare a null-terminated modified UTF-8 string with a UTF-16 string (not null-terminated) - * as code point values in a non-locale sensitive manner. - */ -int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16, - size_t utf16_length); - -/* - * Convert from UTF-16 to Modified UTF-8. Note that the output is _not_ - * NUL-terminated. You probably need to call CountUtf8Bytes before calling - * this anyway, so if you want a NUL-terminated string, you know where to - * put the NUL byte. - */ -void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count, - const uint16_t* utf16_in, size_t char_count); - -/* - * The java.lang.String hashCode() algorithm. - */ -template<typename MemoryType> -int32_t ComputeUtf16Hash(const MemoryType* chars, size_t char_count) { - uint32_t hash = 0; - while (char_count--) { - hash = hash * 31 + *chars++; - } - return static_cast<int32_t>(hash); -} - -int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length); - -// Compute a hash code of a modified UTF-8 string. Not the standard java hash since it returns a -// uint32_t and hashes individual chars instead of codepoint words. -uint32_t ComputeModifiedUtf8Hash(const char* chars); - -/* - * Retrieve the next UTF-16 character or surrogate pair from a UTF-8 string. - * single byte, 2-byte and 3-byte UTF-8 sequences result in a single UTF-16 - * character (possibly one half of a surrogate) whereas 4-byte UTF-8 sequences - * result in a surrogate pair. Use GetLeadingUtf16Char and GetTrailingUtf16Char - * to process the return value of this function. - * - * Advances "*utf8_data_in" to the start of the next character. - * - * WARNING: If a string is corrupted by dropping a '\0' in the middle - * of a multi byte sequence, you can end up overrunning the buffer with - * reads (and possibly with the writes if the length was computed and - * cached before the damage). For performance reasons, this function - * assumes that the string being parsed is known to be valid (e.g., by - * already being verified). Most strings we process here are coming - * out of dex files or other internal translations, so the only real - * risk comes from the JNI NewStringUTF call. - */ -uint32_t GetUtf16FromUtf8(const char** utf8_data_in); - -/** - * Gets the leading UTF-16 character from a surrogate pair, or the sole - * UTF-16 character from the return value of GetUtf16FromUtf8. - */ -ALWAYS_INLINE uint16_t GetLeadingUtf16Char(uint32_t maybe_pair); - -/** - * Gets the trailing UTF-16 character from a surrogate pair, or 0 otherwise - * from the return value of GetUtf16FromUtf8. - */ -ALWAYS_INLINE uint16_t GetTrailingUtf16Char(uint32_t maybe_pair); - -// Returns a printable (escaped) version of a character. -std::string PrintableChar(uint16_t ch); - -// Returns an ASCII string corresponding to the given UTF-8 string. -// Java escapes are used for non-ASCII characters. -std::string PrintableString(const char* utf8); - -} // namespace art - -#endif // ART_RUNTIME_DEX_UTF_H_ diff --git a/runtime/dex/utf_test.cc b/runtime/dex/utf_test.cc deleted file mode 100644 index d2f22d16ef..0000000000 --- a/runtime/dex/utf_test.cc +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2015 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 "utf.h" - -#include <map> -#include <vector> - -#include "gtest/gtest.h" -#include "utf-inl.h" - -namespace art { - -class UtfTest : public testing::Test {}; - -TEST_F(UtfTest, GetLeadingUtf16Char) { - EXPECT_EQ(0xffff, GetLeadingUtf16Char(0xeeeeffff)); -} - -TEST_F(UtfTest, GetTrailingUtf16Char) { - EXPECT_EQ(0xffff, GetTrailingUtf16Char(0xffffeeee)); - EXPECT_EQ(0, GetTrailingUtf16Char(0x0000aaaa)); -} - -#define EXPECT_ARRAY_POSITION(expected, end, start) \ - EXPECT_EQ(static_cast<uintptr_t>(expected), \ - reinterpret_cast<uintptr_t>(end) - reinterpret_cast<uintptr_t>(start)); - -// A test string containing one, two, three and four byte UTF-8 sequences. -static const uint8_t kAllSequences[] = { - 0x24, - 0xc2, 0xa2, - 0xe2, 0x82, 0xac, - 0xf0, 0x9f, 0x8f, 0xa0, - 0x00 -}; - -// A test string that contains a UTF-8 encoding of a surrogate pair -// (code point = U+10400). -static const uint8_t kSurrogateEncoding[] = { - 0xed, 0xa0, 0x81, - 0xed, 0xb0, 0x80, - 0x00 -}; - -TEST_F(UtfTest, GetUtf16FromUtf8) { - const char* const start = reinterpret_cast<const char*>(kAllSequences); - const char* ptr = start; - uint32_t pair = 0; - - // Single byte sequence. - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0x24, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(1, ptr, start); - - // Two byte sequence. - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0xa2, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(3, ptr, start); - - // Three byte sequence. - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0x20ac, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(6, ptr, start); - - // Four byte sequence - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0xd83c, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0xdfe0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(10, ptr, start); - - // Null terminator. - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(11, ptr, start); -} - -TEST_F(UtfTest, GetUtf16FromUtf8_SurrogatesPassThrough) { - const char* const start = reinterpret_cast<const char *>(kSurrogateEncoding); - const char* ptr = start; - uint32_t pair = 0; - - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0xd801, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(3, ptr, start); - - pair = GetUtf16FromUtf8(&ptr); - EXPECT_EQ(0xdc00, GetLeadingUtf16Char(pair)); - EXPECT_EQ(0, GetTrailingUtf16Char(pair)); - EXPECT_ARRAY_POSITION(6, ptr, start); -} - -TEST_F(UtfTest, CountModifiedUtf8Chars) { - EXPECT_EQ(5u, CountModifiedUtf8Chars(reinterpret_cast<const char *>(kAllSequences))); - EXPECT_EQ(2u, CountModifiedUtf8Chars(reinterpret_cast<const char *>(kSurrogateEncoding))); -} - -static void AssertConversion(const std::vector<uint16_t>& input, - const std::vector<uint8_t>& expected) { - ASSERT_EQ(expected.size(), CountUtf8Bytes(&input[0], input.size())); - - std::vector<uint8_t> output(expected.size()); - ConvertUtf16ToModifiedUtf8(reinterpret_cast<char*>(&output[0]), expected.size(), - &input[0], input.size()); - EXPECT_EQ(expected, output); -} - -TEST_F(UtfTest, CountAndConvertUtf8Bytes) { - // Surrogate pairs will be converted into 4 byte sequences. - AssertConversion({ 0xd801, 0xdc00 }, { 0xf0, 0x90, 0x90, 0x80 }); - - // Three byte encodings that are below & above the leading surrogate - // range respectively. - AssertConversion({ 0xdef0 }, { 0xed, 0xbb, 0xb0 }); - AssertConversion({ 0xdcff }, { 0xed, 0xb3, 0xbf }); - // Two byte encoding. - AssertConversion({ 0x0101 }, { 0xc4, 0x81 }); - - // Two byte special case : 0 must use an overlong encoding. - AssertConversion({ 0x0101, 0x0000 }, { 0xc4, 0x81, 0xc0, 0x80 }); - - // One byte encoding. - AssertConversion({ 'h', 'e', 'l', 'l', 'o' }, { 0x68, 0x65, 0x6c, 0x6c, 0x6f }); - - AssertConversion({ - 0xd802, 0xdc02, // Surrogate pair. - 0xdef0, 0xdcff, // Three byte encodings. - 0x0101, 0x0000, // Two byte encodings. - 'p' , 'p' // One byte encoding. - }, { - 0xf0, 0x90, 0xa0, 0x82, - 0xed, 0xbb, 0xb0, 0xed, 0xb3, 0xbf, - 0xc4, 0x81, 0xc0, 0x80, - 0x70, 0x70 - }); -} - -TEST_F(UtfTest, CountAndConvertUtf8Bytes_UnpairedSurrogate) { - // Unpaired trailing surrogate at the end of input. - AssertConversion({ 'h', 'e', 0xd801 }, { 'h', 'e', 0xed, 0xa0, 0x81 }); - // Unpaired (or incorrectly paired) surrogates in the middle of the input. - const std::map<std::vector<uint16_t>, std::vector<uint8_t>> prefixes { - {{ 'h' }, { 'h' }}, - {{ 0 }, { 0xc0, 0x80 }}, - {{ 0x81 }, { 0xc2, 0x81 }}, - {{ 0x801 }, { 0xe0, 0xa0, 0x81 }}, - }; - const std::map<std::vector<uint16_t>, std::vector<uint8_t>> suffixes { - {{ 'e' }, { 'e' }}, - {{ 0 }, { 0xc0, 0x80 }}, - {{ 0x7ff }, { 0xdf, 0xbf }}, - {{ 0xffff }, { 0xef, 0xbf, 0xbf }}, - }; - const std::map<std::vector<uint16_t>, std::vector<uint8_t>> tests { - {{ 0xd801 }, { 0xed, 0xa0, 0x81 }}, - {{ 0xdc00 }, { 0xed, 0xb0, 0x80 }}, - {{ 0xd801, 0xd801 }, { 0xed, 0xa0, 0x81, 0xed, 0xa0, 0x81 }}, - {{ 0xdc00, 0xdc00 }, { 0xed, 0xb0, 0x80, 0xed, 0xb0, 0x80 }}, - }; - for (const auto& prefix : prefixes) { - const std::vector<uint16_t>& prefix_in = prefix.first; - const std::vector<uint8_t>& prefix_out = prefix.second; - for (const auto& test : tests) { - const std::vector<uint16_t>& test_in = test.first; - const std::vector<uint8_t>& test_out = test.second; - for (const auto& suffix : suffixes) { - const std::vector<uint16_t>& suffix_in = suffix.first; - const std::vector<uint8_t>& suffix_out = suffix.second; - std::vector<uint16_t> in = prefix_in; - in.insert(in.end(), test_in.begin(), test_in.end()); - in.insert(in.end(), suffix_in.begin(), suffix_in.end()); - std::vector<uint8_t> out = prefix_out; - out.insert(out.end(), test_out.begin(), test_out.end()); - out.insert(out.end(), suffix_out.begin(), suffix_out.end()); - AssertConversion(in, out); - } - } - } -} - -// Old versions of functions, here to compare answers with optimized versions. - -size_t CountModifiedUtf8Chars_reference(const char* utf8) { - size_t len = 0; - int ic; - while ((ic = *utf8++) != '\0') { - len++; - if ((ic & 0x80) == 0) { - // one-byte encoding - continue; - } - // two- or three-byte encoding - utf8++; - if ((ic & 0x20) == 0) { - // two-byte encoding - continue; - } - utf8++; - if ((ic & 0x10) == 0) { - // three-byte encoding - continue; - } - - // four-byte encoding: needs to be converted into a surrogate - // pair. - utf8++; - len++; - } - return len; -} - -static size_t CountUtf8Bytes_reference(const uint16_t* chars, size_t char_count) { - size_t result = 0; - while (char_count--) { - const uint16_t ch = *chars++; - if (ch > 0 && ch <= 0x7f) { - ++result; - } else if (ch >= 0xd800 && ch <= 0xdbff) { - if (char_count > 0) { - const uint16_t ch2 = *chars; - // If we find a properly paired surrogate, we emit it as a 4 byte - // UTF sequence. If we find an unpaired leading or trailing surrogate, - // we emit it as a 3 byte sequence like would have done earlier. - if (ch2 >= 0xdc00 && ch2 <= 0xdfff) { - chars++; - char_count--; - - result += 4; - } else { - result += 3; - } - } else { - // This implies we found an unpaired trailing surrogate at the end - // of a string. - result += 3; - } - } else if (ch > 0x7ff) { - result += 3; - } else { - result += 2; - } - } - return result; -} - -static void ConvertUtf16ToModifiedUtf8_reference(char* utf8_out, const uint16_t* utf16_in, - size_t char_count) { - while (char_count--) { - const uint16_t ch = *utf16_in++; - if (ch > 0 && ch <= 0x7f) { - *utf8_out++ = ch; - } else { - // Char_count == 0 here implies we've encountered an unpaired - // surrogate and we have no choice but to encode it as 3-byte UTF - // sequence. Note that unpaired surrogates can occur as a part of - // "normal" operation. - if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) { - const uint16_t ch2 = *utf16_in; - - // Check if the other half of the pair is within the expected - // range. If it isn't, we will have to emit both "halves" as - // separate 3 byte sequences. - if (ch2 >= 0xdc00 && ch2 <= 0xdfff) { - utf16_in++; - char_count--; - const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00; - *utf8_out++ = (code_point >> 18) | 0xf0; - *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80; - *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80; - *utf8_out++ = (code_point & 0x3f) | 0x80; - continue; - } - } - - if (ch > 0x07ff) { - // Three byte encoding. - *utf8_out++ = (ch >> 12) | 0xe0; - *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80; - *utf8_out++ = (ch & 0x3f) | 0x80; - } else /*(ch > 0x7f || ch == 0)*/ { - // Two byte encoding. - *utf8_out++ = (ch >> 6) | 0xc0; - *utf8_out++ = (ch & 0x3f) | 0x80; - } - } - } -} - -// Exhaustive test of converting a single code point to UTF-16, then UTF-8, and back again. - -static void codePointToSurrogatePair(uint32_t code_point, uint16_t &first, uint16_t &second) { - first = (code_point >> 10) + 0xd7c0; - second = (code_point & 0x03ff) + 0xdc00; -} - -static void testConversions(uint16_t *buf, int char_count) { - char bytes_test[8] = { 0 }, bytes_reference[8] = { 0 }; - uint16_t out_buf_test[4] = { 0 }, out_buf_reference[4] = { 0 }; - int byte_count_test, byte_count_reference; - int char_count_test, char_count_reference; - - // Calculate the number of utf-8 bytes for the utf-16 chars. - byte_count_reference = CountUtf8Bytes_reference(buf, char_count); - byte_count_test = CountUtf8Bytes(buf, char_count); - EXPECT_EQ(byte_count_reference, byte_count_test); - - // Convert the utf-16 string to utf-8 bytes. - ConvertUtf16ToModifiedUtf8_reference(bytes_reference, buf, char_count); - ConvertUtf16ToModifiedUtf8(bytes_test, byte_count_test, buf, char_count); - for (int i = 0; i < byte_count_test; ++i) { - EXPECT_EQ(bytes_reference[i], bytes_test[i]); - } - - // Calculate the number of utf-16 chars from the utf-8 bytes. - bytes_reference[byte_count_reference] = 0; // Reference function needs null termination. - char_count_reference = CountModifiedUtf8Chars_reference(bytes_reference); - char_count_test = CountModifiedUtf8Chars(bytes_test, byte_count_test); - EXPECT_EQ(char_count, char_count_reference); - EXPECT_EQ(char_count, char_count_test); - - // Convert the utf-8 bytes back to utf-16 chars. - // Does not need copied _reference version of the function because the original - // function with the old API is retained for debug/testing code. - ConvertModifiedUtf8ToUtf16(out_buf_reference, bytes_reference); - ConvertModifiedUtf8ToUtf16(out_buf_test, char_count_test, bytes_test, byte_count_test); - for (int i = 0; i < char_count_test; ++i) { - EXPECT_EQ(buf[i], out_buf_reference[i]); - EXPECT_EQ(buf[i], out_buf_test[i]); - } -} - -TEST_F(UtfTest, ExhaustiveBidirectionalCodePointCheck) { - for (int codePoint = 0; codePoint <= 0x10ffff; ++codePoint) { - uint16_t buf[4] = { 0 }; - if (codePoint <= 0xffff) { - if (codePoint >= 0xd800 && codePoint <= 0xdfff) { - // According to the Unicode standard, no character will ever - // be assigned to these code points, and they cannot be encoded - // into either utf-16 or utf-8. - continue; - } - buf[0] = 'h'; - buf[1] = codePoint; - buf[2] = 'e'; - testConversions(buf, 2); - testConversions(buf, 3); - testConversions(buf + 1, 1); - testConversions(buf + 1, 2); - } else { - buf[0] = 'h'; - codePointToSurrogatePair(codePoint, buf[1], buf[2]); - buf[3] = 'e'; - testConversions(buf, 2); - testConversions(buf, 3); - testConversions(buf, 4); - testConversions(buf + 1, 1); - testConversions(buf + 1, 2); - testConversions(buf + 1, 3); - } - } -} - -} // namespace art diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 037d1fb49c..8ce7921287 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -49,7 +49,8 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, CompilerFilter::Filter filter, bool relocate, bool pic, - bool with_alternate_image) { + bool with_alternate_image, + const char* compilation_reason) { std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA)); std::string dalvik_cache_tmp = dalvik_cache + ".redirected"; std::string oat_location = oat_location_in; @@ -89,6 +90,10 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, args.push_back("--boot-image=" + GetImageLocation2()); } + if (compilation_reason != nullptr) { + args.push_back("--compilation-reason=" + std::string(compilation_reason)); + } + std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; @@ -155,13 +160,15 @@ void DexoptTest::GenerateOdexForTest(const std::string& dex_location, void DexoptTest::GeneratePicOdexForTest(const std::string& dex_location, const std::string& odex_location, - CompilerFilter::Filter filter) { + CompilerFilter::Filter filter, + const char* compilation_reason) { GenerateOatForTest(dex_location, odex_location, filter, /*relocate*/false, /*pic*/true, - /*with_alternate_image*/false); + /*with_alternate_image*/false, + compilation_reason); } void DexoptTest::GenerateOatForTest(const char* dex_location, diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h index 5f0eafd8eb..6e8dc097d5 100644 --- a/runtime/dexopt_test.h +++ b/runtime/dexopt_test.h @@ -46,7 +46,8 @@ class DexoptTest : public Dex2oatEnvironmentTest { CompilerFilter::Filter filter, bool relocate, bool pic, - bool with_alternate_image); + bool with_alternate_image, + const char* compilation_reason = nullptr); // Generate a non-PIC odex file for the purposes of test. // The generated odex file will be un-relocated. @@ -56,7 +57,8 @@ class DexoptTest : public Dex2oatEnvironmentTest { void GeneratePicOdexForTest(const std::string& dex_location, const std::string& odex_location, - CompilerFilter::Filter filter); + CompilerFilter::Filter filter, + const char* compilation_reason = nullptr); // Generate an oat file for the given dex location in its oat location (under // the dalvik cache). diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 9ef7d426df..404c5357bf 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -99,14 +99,13 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, << "This must be due to playing wrongly with class loaders"; } - inlined_method = klass->FindClassMethod(dex_cache, method_index, kRuntimePointerSize); + inlined_method = class_linker->FindResolvedMethod(klass, dex_cache, class_loader, method_index); if (inlined_method == nullptr) { LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor << " does not have " << dex_file->GetMethodName(method_id) << dex_file->GetMethodSignature(method_id) << " declared. " << "This must be due to duplicate classes or playing wrongly with class loaders"; } - dex_cache->SetResolvedMethod(method_index, inlined_method, kRuntimePointerSize); return inlined_method; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index c5157ce9f4..a8c328fbc7 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -319,7 +319,7 @@ class QuickArgumentVisitor { // 'this' object is the 1st argument. They also have the same frame layout as the // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the // 1st GPR. - static mirror::Object* GetProxyThisObject(ArtMethod** sp) + static StackReference<mirror::Object>* GetProxyThisObjectReference(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK((*sp)->IsProxyMethod()); CHECK_GT(kNumQuickGprArgs, 0u); @@ -327,7 +327,7 @@ class QuickArgumentVisitor { size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset + GprIndexToGprOffset(kThisGprIndex); uint8_t* this_arg_address = reinterpret_cast<uint8_t*>(sp) + this_arg_offset; - return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address)->AsMirrorPtr(); + return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address); } static ArtMethod* GetCallingMethod(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -647,7 +647,11 @@ class QuickArgumentVisitor { // allows to use the QuickArgumentVisitor constants without moving all the code in its own module. extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { - return QuickArgumentVisitor::GetProxyThisObject(sp); + return QuickArgumentVisitor::GetProxyThisObjectReference(sp)->AsMirrorPtr(); +} +extern "C" StackReference<mirror::Object>* artQuickGetProxyThisObjectReference(ArtMethod** sp) + REQUIRES_SHARED(Locks::mutator_lock_) { + return QuickArgumentVisitor::GetProxyThisObjectReference(sp); } // Visits arguments on the stack placing them into the shadow frame. diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 72eb8274c8..f8d8271335 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -41,7 +41,7 @@ namespace art { namespace gc { namespace accounting { -// Internal representation is StackReference<T>, so this only works with mirror::Object or it's +// Internal representation is StackReference<T>, so this only works with mirror::Object or its // subclasses. template <typename T> class AtomicStack { diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index df9ee8c219..354b9e1dbd 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -62,7 +62,8 @@ inline bool SpaceBitmap<kAlignment>::Test(const mirror::Object* obj) const { return (bitmap_begin_[OffsetToIndex(offset)].LoadRelaxed() & OffsetToMask(offset)) != 0; } -template<size_t kAlignment> template<typename Visitor> +template<size_t kAlignment> +template<typename Visitor> inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin, uintptr_t visit_end, Visitor&& visitor) const { @@ -121,6 +122,7 @@ inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin, uintptr_t w = bitmap_begin_[i].LoadRelaxed(); if (w != 0) { const uintptr_t ptr_base = IndexToOffset(i) + heap_begin_; + // Iterate on the bits set in word `w`, from the least to the most significant bit. do { const size_t shift = CTZ(w); mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment); @@ -147,6 +149,7 @@ inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin, right_edge &= ((static_cast<uintptr_t>(1) << bit_end) - 1); if (right_edge != 0) { const uintptr_t ptr_base = IndexToOffset(index_end) + heap_begin_; + // Iterate on the bits set in word `right_edge`, from the least to the most significant bit. do { const size_t shift = CTZ(right_edge); mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment); @@ -157,7 +160,8 @@ inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin, #endif } -template<size_t kAlignment> template<typename Visitor> +template<size_t kAlignment> +template<typename Visitor> void SpaceBitmap<kAlignment>::Walk(Visitor&& visitor) { CHECK(bitmap_begin_ != nullptr); @@ -177,7 +181,8 @@ void SpaceBitmap<kAlignment>::Walk(Visitor&& visitor) { } } -template<size_t kAlignment> template<bool kSetBit> +template<size_t kAlignment> +template<bool kSetBit> inline bool SpaceBitmap<kAlignment>::Modify(const mirror::Object* obj) { uintptr_t addr = reinterpret_cast<uintptr_t>(obj); DCHECK_GE(addr, heap_begin_); diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 237ee80ba0..0247564a8c 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -33,7 +33,12 @@ using android::base::StringPrintf; template<size_t kAlignment> size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) { + // Number of space (heap) bytes covered by one bitmap word. + // (Word size in bytes = `sizeof(intptr_t)`, which is expected to be + // 4 on a 32-bit architecture and 8 on a 64-bit one.) const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT; + // Calculate the number of words required to cover a space (heap) + // having a size of `capacity` bytes. return (RoundUp(capacity, kBytesCoveredPerWord) / kBytesCoveredPerWord) * sizeof(intptr_t); } @@ -74,7 +79,8 @@ SpaceBitmap<kAlignment>::~SpaceBitmap() {} template<size_t kAlignment> SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::Create( const std::string& name, uint8_t* heap_begin, size_t heap_capacity) { - // Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord. + // Round up since `heap_capacity` is not necessarily a multiple of `kAlignment * kBitsPerIntPtrT` + // (we represent one word as an `intptr_t`). const size_t bitmap_size = ComputeBitmapSize(heap_capacity); std::string error_msg; std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), nullptr, bitmap_size, @@ -116,7 +122,7 @@ template<size_t kAlignment> void SpaceBitmap<kAlignment>::ClearRange(const mirror::Object* begin, const mirror::Object* end) { uintptr_t begin_offset = reinterpret_cast<uintptr_t>(begin) - heap_begin_; uintptr_t end_offset = reinterpret_cast<uintptr_t>(end) - heap_begin_; - // Align begin and end to word boundaries. + // Align begin and end to bitmap word boundaries. while (begin_offset < end_offset && OffsetBitIndex(begin_offset) != 0) { Clear(reinterpret_cast<mirror::Object*>(heap_begin_ + begin_offset)); begin_offset += kAlignment; @@ -125,6 +131,7 @@ void SpaceBitmap<kAlignment>::ClearRange(const mirror::Object* begin, const mirr end_offset -= kAlignment; Clear(reinterpret_cast<mirror::Object*>(heap_begin_ + end_offset)); } + // Bitmap word boundaries. const uintptr_t start_index = OffsetToIndex(begin_offset); const uintptr_t end_index = OffsetToIndex(end_offset); ZeroAndReleasePages(reinterpret_cast<uint8_t*>(&bitmap_begin_[start_index]), diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 2f33bac902..437aecc2b1 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -55,6 +55,10 @@ class SpaceBitmap { ~SpaceBitmap(); + // Return the bitmap word index corresponding to memory offset (relative to + // `HeapBegin()`) `offset`. + // See also SpaceBitmap::OffsetBitIndex. + // // <offset> is the difference from .base to a pointer address. // <index> is the index of .bits that contains the bit representing // <offset>. @@ -62,24 +66,32 @@ class SpaceBitmap { return offset / kAlignment / kBitsPerIntPtrT; } + // Return the memory offset (relative to `HeapBegin()`) corresponding to + // bitmap word index `index`. template<typename T> static constexpr T IndexToOffset(T index) { return static_cast<T>(index * kAlignment * kBitsPerIntPtrT); } + // Return the bit within the bitmap word index corresponding to + // memory offset (relative to `HeapBegin()`) `offset`. + // See also SpaceBitmap::OffsetToIndex. ALWAYS_INLINE static constexpr uintptr_t OffsetBitIndex(uintptr_t offset) { return (offset / kAlignment) % kBitsPerIntPtrT; } + // Return the word-wide bit mask corresponding to `OffsetBitIndex(offset)`. // Bits are packed in the obvious way. static constexpr uintptr_t OffsetToMask(uintptr_t offset) { return static_cast<size_t>(1) << OffsetBitIndex(offset); } + // Set the bit corresponding to `obj` in the bitmap and return the previous value of that bit. bool Set(const mirror::Object* obj) ALWAYS_INLINE { return Modify<true>(obj); } + // Clear the bit corresponding to `obj` in the bitmap and return the previous value of that bit. bool Clear(const mirror::Object* obj) ALWAYS_INLINE { return Modify<false>(obj); } @@ -90,9 +102,14 @@ class SpaceBitmap { // Fill the bitmap with zeroes. Returns the bitmap's memory to the system as a side-effect. void Clear(); - // Clear a covered by the bitmap using madvise if possible. + // Clear a range covered by the bitmap using madvise if possible. void ClearRange(const mirror::Object* begin, const mirror::Object* end); + // Test whether `obj` is part of the bitmap (i.e. return whether the bit + // corresponding to `obj` has been set in the bitmap). + // + // Precondition: `obj` is within the range of pointers that this bitmap could + // potentially cover (i.e. `this->HasAddress(obj)` is true) bool Test(const mirror::Object* obj) const; // Return true iff <obj> is within the range of pointers that this bitmap could potentially cover, @@ -204,6 +221,8 @@ class SpaceBitmap { const void* heap_begin, size_t heap_capacity); + // Change the value of the bit corresponding to `obj` in the bitmap + // to `kSetBit` and return the previous value of that bit. template<bool kSetBit> bool Modify(const mirror::Object* obj); diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc index bd5f77ebb1..1ca3fd6d12 100644 --- a/runtime/gc/accounting/space_bitmap_test.cc +++ b/runtime/gc/accounting/space_bitmap_test.cc @@ -74,7 +74,7 @@ TEST_F(SpaceBitmapTest, ScanRange) { } } // Try every possible starting bit in the first word. Then for each starting bit, try each - // possible length up to a maximum of kBitsPerWord * 2 - 1 bits. + // possible length up to a maximum of `kBitsPerIntPtrT * 2 - 1` bits. // This handles all the cases, having runs which start and end on the same word, and different // words. for (size_t i = 0; i < static_cast<size_t>(kBitsPerIntPtrT); ++i) { diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h index 85a656ec51..d739ed2867 100644 --- a/runtime/gc/collector/concurrent_copying-inl.h +++ b/runtime/gc/collector/concurrent_copying-inl.h @@ -52,7 +52,8 @@ inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion( // we can avoid an expensive CAS. // For the baker case, an object is marked if either the mark bit marked or the bitmap bit is // set. - success = ref->AtomicSetReadBarrierState(ReadBarrier::WhiteState(), ReadBarrier::GrayState()); + success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(), + /* rb_state */ ReadBarrier::GrayState()); } else { success = !bitmap->AtomicTestAndSet(ref); } @@ -86,8 +87,8 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { return ref; } // This may or may not succeed, which is ok because the object may already be gray. - bool success = ref->AtomicSetReadBarrierState(ReadBarrier::WhiteState(), - ReadBarrier::GrayState()); + bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(), + /* rb_state */ ReadBarrier::GrayState()); if (success) { MutexLock mu(Thread::Current(), immune_gray_stack_lock_); immune_gray_stack_.push_back(ref); @@ -133,6 +134,8 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, // It isn't marked yet. Mark it by copying it to the to-space. to_ref = Copy(from_ref, holder, offset); } + // The copy should either be in a to-space region, or in the + // non-moving space, if it could not fit in a to-space region. DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref)) << "from_ref=" << from_ref << " to_ref=" << to_ref; return to_ref; diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 1e0c0b16e4..3770085c07 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -347,8 +347,9 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor // This must come before the revoke. size_t thread_local_objects = thread->GetThreadLocalObjectsAllocated(); concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread); - reinterpret_cast<Atomic<size_t>*>(&concurrent_copying_->from_space_num_objects_at_first_pause_)-> - FetchAndAddSequentiallyConsistent(thread_local_objects); + reinterpret_cast<Atomic<size_t>*>( + &concurrent_copying_->from_space_num_objects_at_first_pause_)-> + FetchAndAddSequentiallyConsistent(thread_local_objects); } else { concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread); } @@ -440,7 +441,7 @@ class ConcurrentCopying::FlipCallback : public Closure { if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) { cc->GrayAllNewlyDirtyImmuneObjects(); if (kIsDebugBuild) { - // Check that all non-gray immune objects only refernce immune objects. + // Check that all non-gray immune objects only reference immune objects. cc->VerifyGrayImmuneObjects(); } } @@ -1534,7 +1535,8 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { !IsInToSpace(referent)))) { // Leave this reference gray in the queue so that GetReferent() will trigger a read barrier. We // will change it to white later in ReferenceQueue::DequeuePendingReference(). - DCHECK(to_ref->AsReference()->GetPendingNext() != nullptr) << "Left unenqueued ref gray " << to_ref; + DCHECK(to_ref->AsReference()->GetPendingNext() != nullptr) + << "Left unenqueued ref gray " << to_ref; } else { // We may occasionally leave a reference white in the queue if its referent happens to be // concurrently marked after the Scan() call above has enqueued the Reference, in which case the @@ -1552,7 +1554,7 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { #endif if (add_to_live_bytes) { - // Add to the live bytes per unevacuated from space. Note this code is always run by the + // Add to the live bytes per unevacuated from-space. Note this code is always run by the // GC-running thread (no synchronization required). DCHECK(region_space_bitmap_->Test(to_ref)); size_t obj_size = to_ref->SizeOf<kDefaultVerifyFlags>(); @@ -1759,13 +1761,14 @@ void ConcurrentCopying::ReclaimPhase() { } CHECK_LE(to_objects, from_objects); CHECK_LE(to_bytes, from_bytes); - // cleared_bytes and cleared_objects may be greater than the from space equivalents since - // ClearFromSpace may clear empty unevac regions. + // Cleared bytes and objects, populated by the call to RegionSpace::ClearFromSpace below. uint64_t cleared_bytes; uint64_t cleared_objects; { TimingLogger::ScopedTiming split4("ClearFromSpace", GetTimings()); region_space_->ClearFromSpace(&cleared_bytes, &cleared_objects); + // `cleared_bytes` and `cleared_objects` may be greater than the from space equivalents since + // RegionSpace::ClearFromSpace may clear empty unevac regions. CHECK_GE(cleared_bytes, from_bytes); CHECK_GE(cleared_objects, from_objects); } @@ -1774,17 +1777,20 @@ void ConcurrentCopying::ReclaimPhase() { if (kVerboseMode) { LOG(INFO) << "RecordFree:" << " from_bytes=" << from_bytes << " from_objects=" << from_objects - << " unevac_from_bytes=" << unevac_from_bytes << " unevac_from_objects=" << unevac_from_objects + << " unevac_from_bytes=" << unevac_from_bytes + << " unevac_from_objects=" << unevac_from_objects << " to_bytes=" << to_bytes << " to_objects=" << to_objects << " freed_bytes=" << freed_bytes << " freed_objects=" << freed_objects << " from_space size=" << region_space_->FromSpaceSize() << " unevac_from_space size=" << region_space_->UnevacFromSpaceSize() << " to_space size=" << region_space_->ToSpaceSize(); - LOG(INFO) << "(before) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); + LOG(INFO) << "(before) num_bytes_allocated=" + << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); } RecordFree(ObjectBytePair(freed_objects, freed_bytes)); if (kVerboseMode) { - LOG(INFO) << "(after) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); + LOG(INFO) << "(after) num_bytes_allocated=" + << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); } } @@ -1806,27 +1812,82 @@ void ConcurrentCopying::ReclaimPhase() { } } -// Assert the to-space invariant. +std::string ConcurrentCopying::DumpReferenceInfo(mirror::Object* ref, + const char* ref_name, + std::string indent) { + std::ostringstream oss; + oss << indent << heap_->GetVerification()->DumpObjectInfo(ref, ref_name) << '\n'; + if (ref != nullptr) { + if (kUseBakerReadBarrier) { + oss << indent << ref_name << "->GetMarkBit()=" << ref->GetMarkBit() << '\n'; + oss << indent << ref_name << "->GetReadBarrierState()=" << ref->GetReadBarrierState() << '\n'; + } + } + if (region_space_->HasAddress(ref)) { + oss << indent << "Region containing " << ref_name << ":" << '\n'; + region_space_->DumpRegionForObject(oss, ref); + if (region_space_bitmap_ != nullptr) { + oss << indent << "region_space_bitmap_->Test(" << ref_name << ")=" + << std::boolalpha << region_space_bitmap_->Test(ref) << std::noboolalpha; + } + } + return oss.str(); +} + +std::string ConcurrentCopying::DumpHeapReference(mirror::Object* obj, + MemberOffset offset, + mirror::Object* ref) { + std::ostringstream oss; + std::string indent = " "; + oss << indent << "Invalid reference: ref=" << ref + << " referenced from: object=" << obj << " offset= " << offset << '\n'; + // Information about `obj`. + oss << DumpReferenceInfo(obj, "obj", indent) << '\n'; + // Information about `ref`. + oss << DumpReferenceInfo(ref, "ref", indent); + return oss.str(); +} + void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) { - CHECK_EQ(heap_->collector_type_, kCollectorTypeCC); + CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_); if (is_asserting_to_space_invariant_) { - using RegionType = space::RegionSpace::RegionType; - space::RegionSpace::RegionType type = region_space_->GetRegionType(ref); - if (type == RegionType::kRegionTypeToSpace) { - // OK. - return; - } else if (type == RegionType::kRegionTypeUnevacFromSpace) { - CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; - } else if (UNLIKELY(type == RegionType::kRegionTypeFromSpace)) { - // Not OK. Do extra logging. - if (obj != nullptr) { - LogFromSpaceRefHolder(obj, offset); + if (region_space_->HasAddress(ref)) { + // Check to-space invariant in region space (moving space). + using RegionType = space::RegionSpace::RegionType; + space::RegionSpace::RegionType type = region_space_->GetRegionType(ref); + if (type == RegionType::kRegionTypeToSpace) { + // OK. + return; + } else if (type == RegionType::kRegionTypeUnevacFromSpace) { + if (!IsMarkedInUnevacFromSpace(ref)) { + LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:"; + LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref); + } + CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; + } else { + // Not OK: either a from-space ref or a reference in an unused region. + // Do extra logging. + if (type == RegionType::kRegionTypeFromSpace) { + LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:"; + } else { + LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":"; + } + LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref); + if (obj != nullptr) { + LogFromSpaceRefHolder(obj, offset); + } + ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); + LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:"; + region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); + LOG(FATAL) << "Invalid reference " << ref + << " referenced from object " << obj << " at offset " << offset; } - ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); - CHECK(false) << "Found from-space ref " << ref << " " << ref->PrettyTypeOf(); } else { + // Check to-space invariant in non-moving space. AssertToSpaceInvariantInNonMovingSpace(obj, ref); } } @@ -1857,39 +1918,66 @@ class RootPrinter { } }; +std::string ConcurrentCopying::DumpGcRoot(mirror::Object* ref) { + std::ostringstream oss; + std::string indent = " "; + oss << indent << "Invalid GC root: ref=" << ref << '\n'; + // Information about `ref`. + oss << DumpReferenceInfo(ref, "ref", indent); + return oss.str(); +} + void ConcurrentCopying::AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref) { - CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_); + CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_); if (is_asserting_to_space_invariant_) { - if (region_space_->IsInToSpace(ref)) { - // OK. - return; - } else if (region_space_->IsInUnevacFromSpace(ref)) { - CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; - } else if (region_space_->IsInFromSpace(ref)) { - // Not OK. Do extra logging. - if (gc_root_source == nullptr) { - // No info. - } else if (gc_root_source->HasArtField()) { - ArtField* field = gc_root_source->GetArtField(); - LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " " - << ArtField::PrettyField(field); - RootPrinter root_printer; - field->VisitRoots(root_printer); - } else if (gc_root_source->HasArtMethod()) { - ArtMethod* method = gc_root_source->GetArtMethod(); - LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " " - << ArtMethod::PrettyMethod(method); - RootPrinter root_printer; - method->VisitRoots(root_printer, kRuntimePointerSize); + if (region_space_->HasAddress(ref)) { + // Check to-space invariant in region space (moving space). + using RegionType = space::RegionSpace::RegionType; + space::RegionSpace::RegionType type = region_space_->GetRegionType(ref); + if (type == RegionType::kRegionTypeToSpace) { + // OK. + return; + } else if (type == RegionType::kRegionTypeUnevacFromSpace) { + if (!IsMarkedInUnevacFromSpace(ref)) { + LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:"; + LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref); + } + CHECK(IsMarkedInUnevacFromSpace(ref)) << ref; + } else { + // Not OK: either a from-space ref or a reference in an unused region. + // Do extra logging. + if (type == RegionType::kRegionTypeFromSpace) { + LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:"; + } else { + LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":"; + } + LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref); + if (gc_root_source == nullptr) { + // No info. + } else if (gc_root_source->HasArtField()) { + ArtField* field = gc_root_source->GetArtField(); + LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " " + << ArtField::PrettyField(field); + RootPrinter root_printer; + field->VisitRoots(root_printer); + } else if (gc_root_source->HasArtMethod()) { + ArtMethod* method = gc_root_source->GetArtMethod(); + LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " " + << ArtMethod::PrettyMethod(method); + RootPrinter root_printer; + method->VisitRoots(root_printer, kRuntimePointerSize); + } + ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); + LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:"; + region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); + LOG(FATAL) << "Invalid reference " << ref; } - ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); - region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); - MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); - CHECK(false) << "Found from-space ref " << ref << " " << ref->PrettyTypeOf(); } else { - AssertToSpaceInvariantInNonMovingSpace(nullptr, ref); + // Check to-space invariant in non-moving space. + AssertToSpaceInvariantInNonMovingSpace(/* obj */ nullptr, ref); } } } @@ -1944,7 +2032,8 @@ void ConcurrentCopying::LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj, mirror::Object* ref) { - // In a non-moving spaces. Check that the ref is marked. + CHECK(!region_space_->HasAddress(ref)) << "obj=" << obj << " ref=" << ref; + // In a non-moving space. Check that the ref is marked. if (immune_spaces_.ContainsObject(ref)) { if (kUseBakerReadBarrier) { // Immune object may not be gray if called from the GC. @@ -1968,11 +2057,13 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o (is_los && los_bitmap->Test(ref))) { // OK. } else { - // If ref is on the allocation stack, then it may not be + // If `ref` is on the allocation stack, then it may not be // marked live, but considered marked/alive (but not // necessarily on the live stack). - CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack. " - << "obj=" << obj << " ref=" << ref; + CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack." + << " obj=" << obj + << " ref=" << ref + << " is_los=" << std::boolalpha << is_los << std::noboolalpha; } } } @@ -2013,7 +2104,6 @@ class ConcurrentCopying::RefFieldsVisitor { ConcurrentCopying* const collector_; }; -// Scan ref fields of an object. inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) { // Avoid all read barriers during visit references to help performance. @@ -2032,7 +2122,6 @@ inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { } } -// Process a field. inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) { DCHECK_EQ(Thread::Current(), thread_running_gc_); mirror::Object* ref = obj->GetFieldObject< @@ -2053,7 +2142,7 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) // It was updated by the mutator. break; } - // Use release cas to make sure threads reading the reference see contents of copied objects. + // Use release CAS to make sure threads reading the reference see contents of copied objects. } while (!obj->CasFieldWeakReleaseObjectWithoutWriteBarrier<false, false, kVerifyNone>( offset, expected_ref, @@ -2428,6 +2517,9 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) { to_ref = nullptr; } } else { + // At this point, `from_ref` should not be in the region space + // (i.e. within an "unused" region). + DCHECK(!region_space_->HasAddress(from_ref)) << from_ref; // from_ref is in a non-moving space. if (immune_spaces_.ContainsObject(from_ref)) { // An immune object is alive. @@ -2598,7 +2690,12 @@ void ConcurrentCopying::FinishPhase() { DCHECK(rb_mark_bit_stack_ != nullptr); const auto* limit = rb_mark_bit_stack_->End(); for (StackReference<mirror::Object>* it = rb_mark_bit_stack_->Begin(); it != limit; ++it) { - CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0)); + CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0)) + << "rb_mark_bit_stack_->Begin()" << rb_mark_bit_stack_->Begin() << '\n' + << "rb_mark_bit_stack_->End()" << rb_mark_bit_stack_->End() << '\n' + << "rb_mark_bit_stack_->IsFull()" + << std::boolalpha << rb_mark_bit_stack_->IsFull() << std::noboolalpha << '\n' + << DumpReferenceInfo(it->AsMirrorPtr(), "*it"); } rb_mark_bit_stack_->Reset(); } diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 8b4b58e7b1..c58dd44648 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -100,8 +100,10 @@ class ConcurrentCopying : public GarbageCollector { space::RegionSpace* RegionSpace() { return region_space_; } + // Assert the to-space invariant for a heap reference `ref` held in `obj` at offset `offset`. void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); + // Assert the to-space invariant for a GC root reference `ref`. void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); bool IsInToSpace(mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -109,6 +111,7 @@ class ConcurrentCopying : public GarbageCollector { return IsMarked(ref) == ref; } template<bool kGrayImmuneObject = true, bool kFromGCThread = false> + // Mark object `from_ref`, copying it to the to-space if needed. ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref, mirror::Object* holder = nullptr, MemberOffset offset = MemberOffset(0)) @@ -148,8 +151,10 @@ class ConcurrentCopying : public GarbageCollector { MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); + // Scan the reference fields of object `to_ref`. void Scan(mirror::Object* to_ref) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); + // Process a field. void Process(mirror::Object* obj, MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_ , !skipped_blocks_lock_, !immune_gray_stack_lock_); @@ -232,6 +237,16 @@ class ConcurrentCopying : public GarbageCollector { void ComputeUnevacFromSpaceLiveRatio(); void LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_); + // Dump information about reference `ref` and return it as a string. + // Use `ref_name` to name the reference in messages. Each message is prefixed with `indent`. + std::string DumpReferenceInfo(mirror::Object* ref, const char* ref_name, std::string indent = "") + REQUIRES_SHARED(Locks::mutator_lock_); + // Dump information about heap reference `ref`, referenced from object `obj` at offset `offset`, + // and return it as a string. + std::string DumpHeapReference(mirror::Object* obj, MemberOffset offset, mirror::Object* ref) + REQUIRES_SHARED(Locks::mutator_lock_); + // Dump information about GC root `ref` and return it as a string. + std::string DumpGcRoot(mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); void AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj, mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_); void ReenableWeakRefAccess(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); @@ -266,8 +281,20 @@ class ConcurrentCopying : public GarbageCollector { space::RegionSpace* region_space_; // The underlying region space. std::unique_ptr<Barrier> gc_barrier_; std::unique_ptr<accounting::ObjectStack> gc_mark_stack_; + + // The read-barrier mark-bit stack. Stores object references whose + // mark bit has been set by ConcurrentCopying::MarkFromReadBarrier, + // so that this bit can be reset at the end of the collection in + // ConcurrentCopying::FinishPhase. The mark bit of an object can be + // used by mutator read barrier code to quickly test whether that + // object has been already marked. std::unique_ptr<accounting::ObjectStack> rb_mark_bit_stack_; + // Thread-unsafe Boolean value hinting that `rb_mark_bit_stack_` is + // full. A thread-safe test of whether the read-barrier mark-bit + // stack is full is implemented by `rb_mark_bit_stack_->AtomicPushBack(ref)` + // (see use case in ConcurrentCopying::MarkFromReadBarrier). bool rb_mark_bit_stack_full_; + std::vector<mirror::Object*> false_gray_stack_ GUARDED_BY(mark_stack_lock_); Mutex mark_stack_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::vector<accounting::ObjectStack*> revoked_mark_stacks_ diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index fdfe949265..9ab965ec78 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -191,7 +191,7 @@ void MarkSweep::PausePhase() { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // Re-mark root set. ReMarkRoots(); - // Scan dirty objects, this is only required if we are not doing concurrent GC. + // Scan dirty objects, this is only required if we are doing concurrent GC. RecursiveMarkDirtyObjects(true, accounting::CardTable::kCardDirty); } { @@ -259,8 +259,30 @@ void MarkSweep::MarkingPhase() { BindBitmaps(); FindDefaultSpaceBitmap(); // Process dirty cards and add dirty cards to mod union tables. - // If the GC type is non sticky, then we just clear the cards instead of ageing them. - heap_->ProcessCards(GetTimings(), false, true, GetGcType() != kGcTypeSticky); + // If the GC type is non sticky, then we just clear the cards of the + // alloc space instead of aging them. + // + // Note that it is fine to clear the cards of the alloc space here, + // in the case of a concurrent (non-sticky) mark-sweep GC (whose + // marking phase _is_ performed concurrently with mutator threads + // running and possibly dirtying cards), as the whole alloc space + // will be traced in that case, starting *after* this call to + // Heap::ProcessCards (see calls to MarkSweep::MarkRoots and + // MarkSweep::MarkReachableObjects). References held by objects on + // cards that became dirty *after* the actual marking work started + // will be marked in the pause (see MarkSweep::PausePhase), in a + // *non-concurrent* way to prevent races with mutator threads. + // + // TODO: Do we need some sort of fence between the call to + // Heap::ProcessCard and the calls to MarkSweep::MarkRoot / + // MarkSweep::MarkReachableObjects below to make sure write + // operations in the card table clearing the alloc space's dirty + // cards (during the call to Heap::ProcessCard) are not reordered + // *after* marking actually starts? + heap_->ProcessCards(GetTimings(), + /* use_rem_sets */ false, + /* process_alloc_space_cards */ true, + /* clear_alloc_space_cards */ GetGcType() != kGcTypeSticky); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); MarkRoots(self); MarkReachableObjects(); @@ -1287,7 +1309,7 @@ void MarkSweep::Sweep(bool swap_bitmaps) { CHECK_GE(live_stack_freeze_size_, GetHeap()->GetLiveStack()->Size()); { TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings()); - // Mark everything allocated since the last as GC live so that we can sweep concurrently, + // Mark everything allocated since the last GC as live so that we can sweep concurrently, // knowing that new allocations won't be marked as live. accounting::ObjectStack* live_stack = heap_->GetLiveStack(); heap_->MarkAllocStackAsLive(live_stack); diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index b66095fc2a..d93bd89835 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -63,7 +63,7 @@ void StickyMarkSweep::MarkReachableObjects() { void StickyMarkSweep::MarkConcurrentRoots(VisitRootFlags flags) { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Visit all runtime roots and clear dirty flags including class loader. This is done to prevent - // incorrect class unloading since the GC does not card mark when storing store the class during + // incorrect class unloading since the GC does not card mark when storing the class during // object allocation. Doing this for each allocation would be slow. // Since the card is not dirty, it means the object may not get scanned. This can cause class // unloading to occur even though the class and class loader are reachable through the object's diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 84671c34b5..17913fc2dc 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -434,6 +434,7 @@ Heap::Heap(size_t initial_size, // Create other spaces based on whether or not we have a moving GC. if (foreground_collector_type_ == kCollectorTypeCC) { CHECK(separate_non_moving_space); + // Reserve twice the capacity, to allow evacuating every region for explicit GCs. MemMap* region_space_mem_map = space::RegionSpace::CreateMemMap(kRegionSpaceName, capacity_ * 2, request_begin); @@ -517,7 +518,7 @@ Heap::Heap(size_t initial_size, // Since we don't know where in the low_4gb the app image will be located, make the card table // cover the whole low_4gb. TODO: Extend the card table in AddSpace. UNUSED(heap_capacity); - // Start at 64 KB, we can be sure there are no spaces mapped this low since the address range is + // Start at 4 KB, we can be sure there are no spaces mapped this low since the address range is // reserved by the kernel. static constexpr size_t kMinHeapAddress = 4 * KB; card_table_.reset(accounting::CardTable::Create(reinterpret_cast<uint8_t*>(kMinHeapAddress), @@ -1536,7 +1537,7 @@ void Heap::RecordFree(uint64_t freed_objects, int64_t freed_bytes) { void Heap::RecordFreeRevoke() { // Subtract num_bytes_freed_revoke_ from num_bytes_allocated_ to cancel out the - // the ahead-of-time, bulk counting of bytes allocated in rosalloc thread-local buffers. + // ahead-of-time, bulk counting of bytes allocated in rosalloc thread-local buffers. // If there's a concurrent revoke, ok to not necessarily reset num_bytes_freed_revoke_ // all the way to zero exactly as the remainder will be subtracted at the next GC. size_t bytes_freed = num_bytes_freed_revoke_.LoadSequentiallyConsistent(); @@ -1758,7 +1759,7 @@ void Heap::SetTargetHeapUtilization(float target) { size_t Heap::GetObjectsAllocated() const { Thread* const self = Thread::Current(); ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated); - // Prevent GC running during GetObjectsALlocated since we may get a checkpoint request that tells + // Prevent GC running during GetObjectsAllocated since we may get a checkpoint request that tells // us to suspend while we are doing SuspendAll. b/35232978 gc::ScopedGCCriticalSection gcs(Thread::Current(), gc::kGcCauseGetObjectsAllocated, @@ -1899,10 +1900,10 @@ HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() { MutexLock mu(self, *gc_complete_lock_); // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(kGcCauseHomogeneousSpaceCompact, self); - // Homogeneous space compaction is a copying transition, can't run it if the moving GC disable count - // is non zero. - // If the collector type changed to something which doesn't benefit from homogeneous space compaction, - // exit. + // Homogeneous space compaction is a copying transition, can't run it if the moving GC disable + // count is non zero. + // If the collector type changed to something which doesn't benefit from homogeneous space + // compaction, exit. if (disable_moving_gc_count_ != 0 || IsMovingGc(collector_type_) || !main_space_->CanMoveObjects()) { return kErrorReject; @@ -3445,8 +3446,8 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran, TraceHeapSize(bytes_allocated); uint64_t target_size; collector::GcType gc_type = collector_ran->GetGcType(); - const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for - // foreground. + // Use the multiplier to grow more for foreground. + const double multiplier = HeapGrowthMultiplier(); const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier); const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier); if (gc_type != collector::kGcTypeSticky) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index faa6195259..7fb634fcf7 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -551,7 +551,7 @@ class Heap { return total_memory - std::min(total_memory, byte_allocated); } - // get the space that corresponds to an object's address. Current implementation searches all + // Get the space that corresponds to an object's address. Current implementation searches all // spaces in turn. If fail_ok is false then failing to find a space will cause an abort. // TODO: consider using faster data structure like binary tree. space::ContinuousSpace* FindContinuousSpaceFromObject(ObjPtr<mirror::Object>, bool fail_ok) const @@ -1293,8 +1293,9 @@ class Heap { // Parallel GC data structures. std::unique_ptr<ThreadPool> thread_pool_; - // For a GC cycle, a bitmap that is set corresponding to the + // A bitmap that is set corresponding to the known live objects since the last GC cycle. std::unique_ptr<accounting::HeapBitmap> live_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); + // A bitmap that is set corresponding to the marked objects in the current GC cycle. std::unique_ptr<accounting::HeapBitmap> mark_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); // Mark stack that we reuse to avoid re-allocating the mark stack. @@ -1312,7 +1313,7 @@ class Heap { AllocatorType current_allocator_; const AllocatorType current_non_moving_allocator_; - // Which GCs we run in order when we an allocation fails. + // Which GCs we run in order when an allocation fails. std::vector<collector::GcType> gc_plan_; // Bump pointer spaces. @@ -1320,6 +1321,7 @@ class Heap { // Temp space is the space which the semispace collector copies to. space::BumpPointerSpace* temp_space_; + // Region space, used by the concurrent collector. space::RegionSpace* region_space_; // Minimum free guarantees that you always have at least min_free_ free bytes after growing for diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 9d8e5d23eb..6d426c2dd0 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -81,7 +81,6 @@ class ZygoteHeapTest : public CommonRuntimeTest { void SetUpRuntimeOptions(RuntimeOptions* options) { CommonRuntimeTest::SetUpRuntimeOptions(options); options->push_back(std::make_pair("-Xzygote", nullptr)); - options->push_back(std::make_pair("-Xno-hidden-api-checks", nullptr)); } }; diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h index e74e9b169f..410931cbe5 100644 --- a/runtime/gc/space/region_space-inl.h +++ b/runtime/gc/space/region_space-inl.h @@ -24,26 +24,30 @@ namespace art { namespace gc { namespace space { -inline mirror::Object* RegionSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) { +inline mirror::Object* RegionSpace::Alloc(Thread* self ATTRIBUTE_UNUSED, + size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) { num_bytes = RoundUp(num_bytes, kAlignment); return AllocNonvirtual<false>(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated); } -inline mirror::Object* RegionSpace::AllocThreadUnsafe(Thread* self, size_t num_bytes, - size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) { +inline mirror::Object* RegionSpace::AllocThreadUnsafe(Thread* self, + size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) { Locks::mutator_lock_->AssertExclusiveHeld(self); return Alloc(self, num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated); } template<bool kForEvac> -inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) { +inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) { DCHECK_ALIGNED(num_bytes, kAlignment); mirror::Object* obj; if (LIKELY(num_bytes <= kRegionSize)) { @@ -79,8 +83,7 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by } } else { // Large object. - obj = AllocLarge<kForEvac>(num_bytes, bytes_allocated, usable_size, - bytes_tl_bulk_allocated); + obj = AllocLarge<kForEvac>(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated); if (LIKELY(obj != nullptr)) { return obj; } @@ -88,9 +91,10 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by return nullptr; } -inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) { +inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) { DCHECK(IsAllocated() && IsInToSpace()); DCHECK_ALIGNED(num_bytes, kAlignment); uint8_t* old_top; @@ -238,9 +242,9 @@ inline mirror::Object* RegionSpace::GetNextObject(mirror::Object* obj) { template<bool kForEvac> inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, - size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) { + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) { DCHECK_ALIGNED(num_bytes, kAlignment); DCHECK_GT(num_bytes, kRegionSize); size_t num_regs = RoundUp(num_bytes, kRegionSize) / kRegionSize; @@ -254,7 +258,7 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, return nullptr; } } - // Find a large enough contiguous free regions. + // Find a large enough set of contiguous free regions. size_t left = 0; while (left + num_regs - 1 < num_regions_) { bool found = true; @@ -270,7 +274,7 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, } } if (found) { - // right points to the one region past the last free region. + // `right` points to the one region past the last free region. DCHECK_EQ(left + num_regs, right); Region* first_reg = ®ions_[left]; DCHECK(first_reg->IsFree()); @@ -345,7 +349,7 @@ inline size_t RegionSpace::Region::BytesAllocated() const { DCHECK_EQ(begin_, Top()); return 0; } else { - DCHECK(IsAllocated()) << static_cast<uint>(state_); + DCHECK(IsAllocated()) << "state=" << state_; DCHECK_LE(begin_, Top()); size_t bytes; if (is_a_tlab_) { @@ -358,6 +362,20 @@ inline size_t RegionSpace::Region::BytesAllocated() const { } } +inline size_t RegionSpace::Region::ObjectsAllocated() const { + if (IsLarge()) { + DCHECK_LT(begin_ + kRegionSize, Top()); + DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + return 1; + } else if (IsLargeTail()) { + DCHECK_EQ(begin_, Top()); + DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + return 0; + } else { + DCHECK(IsAllocated()) << "state=" << state_; + return objects_allocated_; + } +} } // namespace space } // namespace gc diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index a505f32c49..8d94c86701 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -27,7 +27,7 @@ namespace space { // If a region has live objects whose size is less than this percent // value of the region size, evaculate the region. -static constexpr uint kEvaculateLivePercentThreshold = 75U; +static constexpr uint kEvacuateLivePercentThreshold = 75U; // If we protect the cleared regions. // Only protect for target builds to prevent flaky test failures (b/63131961). @@ -158,14 +158,14 @@ size_t RegionSpace::ToSpaceSize() { inline bool RegionSpace::Region::ShouldBeEvacuated() { DCHECK((IsAllocated() || IsLarge()) && IsInToSpace()); - // if the region was allocated after the start of the - // previous GC or the live ratio is below threshold, evacuate - // it. + // The region should be evacuated if: + // - the region was allocated after the start of the previous GC (newly allocated region); or + // - the live ratio is below threshold (`kEvacuateLivePercentThreshold`). bool result; if (is_newly_allocated_) { result = true; } else { - bool is_live_percent_valid = live_bytes_ != static_cast<size_t>(-1); + bool is_live_percent_valid = (live_bytes_ != static_cast<size_t>(-1)); if (is_live_percent_valid) { DCHECK(IsInToSpace()); DCHECK(!IsLargeTail()); @@ -177,10 +177,10 @@ inline bool RegionSpace::Region::ShouldBeEvacuated() { // Side node: live_percent == 0 does not necessarily mean // there's no live objects due to rounding (there may be a // few). - result = live_bytes_ * 100U < kEvaculateLivePercentThreshold * bytes_allocated; + result = (live_bytes_ * 100U < kEvacuateLivePercentThreshold * bytes_allocated); } else { DCHECK(IsLarge()); - result = live_bytes_ == 0U; + result = (live_bytes_ == 0U); } } else { result = false; @@ -198,7 +198,10 @@ void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table, bool forc rb_table->SetAll(); } MutexLock mu(Thread::Current(), region_lock_); - size_t num_expected_large_tails = 0; + // Counter for the number of expected large tail regions following a large region. + size_t num_expected_large_tails = 0U; + // Flag to store whether the previously seen large region has been evacuated. + // This is used to apply the same evacuation policy to related large tail regions. bool prev_large_evacuated = false; VerifyNonFreeRegionLimit(); const size_t iter_limit = kUseTableLookupReadBarrier @@ -260,7 +263,8 @@ static void ZeroAndProtectRegion(uint8_t* begin, uint8_t* end) { } } -void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_objects) { +void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, + /* out */ uint64_t* cleared_objects) { DCHECK(cleared_bytes != nullptr); DCHECK(cleared_objects != nullptr); *cleared_bytes = 0; @@ -272,18 +276,32 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje // Update max of peak non free region count before reclaiming evacuated regions. max_peak_num_non_free_regions_ = std::max(max_peak_num_non_free_regions_, num_non_free_regions_); - // Combine zeroing and releasing pages to reduce how often madvise is called. This helps - // reduce contention on the mmap semaphore. b/62194020 - // clear_region adds a region to the current block. If the region is not adjacent, the - // clear block is zeroed, released, and a new block begins. + + // Lambda expression `clear_region` clears a region and adds a region to the + // "clear block". + // + // As we sweep regions to clear them, we maintain a "clear block", composed of + // adjacent cleared regions and whose bounds are `clear_block_begin` and + // `clear_block_end`. When processing a new region which is not adjacent to + // the clear block (discontinuity in cleared regions), the clear block + // is zeroed and released and the clear block is reset (to the most recent + // cleared region). + // + // This is done in order to combine zeroing and releasing pages to reduce how + // often madvise is called. This helps reduce contention on the mmap semaphore + // (see b/62194020). uint8_t* clear_block_begin = nullptr; uint8_t* clear_block_end = nullptr; auto clear_region = [&clear_block_begin, &clear_block_end](Region* r) { r->Clear(/*zero_and_release_pages*/false); if (clear_block_end != r->Begin()) { + // Region `r` is not adjacent to the current clear block; zero and release + // pages within the current block and restart a new clear block at the + // beginning of region `r`. ZeroAndProtectRegion(clear_block_begin, clear_block_end); clear_block_begin = r->Begin(); } + // Add region `r` to the clear block. clear_block_end = r->End(); }; for (size_t i = 0; i < std::min(num_regions_, non_free_region_index_limit_); ++i) { @@ -334,12 +352,22 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje ++regions_to_clear_bitmap; } + // Optimization: If the live bytes are *all* live in a region + // then the live-bit information for these objects is superfluous: + // - We can determine that these objects are all live by using + // Region::AllAllocatedBytesAreLive (which just checks whether + // `LiveBytes() == static_cast<size_t>(Top() - Begin())`. + // - We can visit the objects in this region using + // RegionSpace::GetNextObject, i.e. without resorting to the + // live bits (see RegionSpace::WalkInternal). + // Therefore, we can clear the bits for these objects in the + // (live) region space bitmap (and release the corresponding pages). GetLiveBitmap()->ClearRange( reinterpret_cast<mirror::Object*>(r->Begin()), reinterpret_cast<mirror::Object*>(r->Begin() + regions_to_clear_bitmap * kRegionSize)); - // Skip over extra regions we cleared the bitmaps: we don't need to clear them, as they - // are unevac region sthat are live. - // Subtract one for the for loop. + // Skip over extra regions for which we cleared the bitmaps: we shall not clear them, + // as they are unevac regions that are live. + // Subtract one for the for-loop. i += regions_to_clear_bitmap - 1; } } @@ -432,7 +460,13 @@ void RegionSpace::ClampGrowthLimit(size_t new_capacity) { void RegionSpace::Dump(std::ostream& os) const { os << GetName() << " " - << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit()); + << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit()); +} + +void RegionSpace::DumpRegionForObject(std::ostream& os, mirror::Object* obj) { + CHECK(HasAddress(obj)); + MutexLock mu(Thread::Current(), region_lock_); + RefToRegionUnlocked(obj)->Dump(os); } void RegionSpace::DumpRegions(std::ostream& os) { @@ -526,13 +560,18 @@ void RegionSpace::AssertAllThreadLocalBuffersAreRevoked() { } void RegionSpace::Region::Dump(std::ostream& os) const { - os << "Region[" << idx_ << "]=" << reinterpret_cast<void*>(begin_) << "-" - << reinterpret_cast<void*>(Top()) + os << "Region[" << idx_ << "]=" + << reinterpret_cast<void*>(begin_) + << "-" << reinterpret_cast<void*>(Top()) << "-" << reinterpret_cast<void*>(end_) - << " state=" << static_cast<uint>(state_) << " type=" << static_cast<uint>(type_) + << " state=" << state_ + << " type=" << type_ << " objects_allocated=" << objects_allocated_ - << " alloc_time=" << alloc_time_ << " live_bytes=" << live_bytes_ - << " is_newly_allocated=" << is_newly_allocated_ << " is_a_tlab=" << is_a_tlab_ << " thread=" << thread_ << "\n"; + << " alloc_time=" << alloc_time_ + << " live_bytes=" << live_bytes_ + << " is_newly_allocated=" << std::boolalpha << is_newly_allocated_ << std::noboolalpha + << " is_a_tlab=" << std::boolalpha << is_a_tlab_ << std::noboolalpha + << " thread=" << thread_ << '\n'; } size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size) { diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index 55c2772129..c3b7ff72ef 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -46,24 +46,33 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { static MemMap* CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin); static RegionSpace* Create(const std::string& name, MemMap* mem_map); - // Allocate num_bytes, returns null if the space is full. - mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, size_t* bytes_tl_bulk_allocated) + // Allocate `num_bytes`, returns null if the space is full. + mirror::Object* Alloc(Thread* self, + size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) OVERRIDE REQUIRES(!region_lock_); // Thread-unsafe allocation for when mutators are suspended, used by the semispace collector. - mirror::Object* AllocThreadUnsafe(Thread* self, size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, size_t* bytes_tl_bulk_allocated) + mirror::Object* AllocThreadUnsafe(Thread* self, + size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) OVERRIDE REQUIRES(Locks::mutator_lock_) REQUIRES(!region_lock_); // The main allocation routine. template<bool kForEvac> - ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) + ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_); - // Allocate/free large objects (objects that are larger than the region size.) + // Allocate/free large objects (objects that are larger than the region size). template<bool kForEvac> - mirror::Object* AllocLarge(size_t num_bytes, size_t* bytes_allocated, size_t* usable_size, - size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_); + mirror::Object* AllocLarge(size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_); template<bool kForEvac> void FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) REQUIRES(!region_lock_); @@ -101,6 +110,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void Dump(std::ostream& os) const; void DumpRegions(std::ostream& os) REQUIRES(!region_lock_); + // Dump region containing object `obj`. Precondition: `obj` is in the region space. + void DumpRegionForObject(std::ostream& os, mirror::Object* obj) REQUIRES(!region_lock_); void DumpNonFreeRegions(std::ostream& os) REQUIRES(!region_lock_); size_t RevokeThreadLocalBuffers(Thread* thread) REQUIRES(!region_lock_); @@ -174,7 +185,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { template <typename Visitor> ALWAYS_INLINE void WalkToSpace(Visitor&& visitor) REQUIRES(Locks::mutator_lock_) { - WalkInternal<true>(visitor); + WalkInternal<true /* kToSpaceOnly */>(visitor); } accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE { @@ -228,13 +239,16 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { return RegionType::kRegionTypeNone; } + // Determine which regions to evacuate and tag them as + // from-space. Tag the rest as unevacuated from-space. void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all) REQUIRES(!region_lock_); size_t FromSpaceSize() REQUIRES(!region_lock_); size_t UnevacFromSpaceSize() REQUIRES(!region_lock_); size_t ToSpaceSize() REQUIRES(!region_lock_); - void ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_objects) REQUIRES(!region_lock_); + void ClearFromSpace(/* out */ uint64_t* cleared_bytes, /* out */ uint64_t* cleared_objects) + REQUIRES(!region_lock_); void AddLiveBytes(mirror::Object* ref, size_t alloc_size) { Region* reg = RefToRegionUnlocked(ref); @@ -301,12 +315,13 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void Clear(bool zero_and_release_pages); - ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated); + ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated); bool IsFree() const { - bool is_free = state_ == RegionState::kRegionStateFree; + bool is_free = (state_ == RegionState::kRegionStateFree); if (is_free) { DCHECK(IsInNoSpace()); DCHECK_EQ(begin_, Top()); @@ -319,9 +334,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void Unfree(RegionSpace* region_space, uint32_t alloc_time) REQUIRES(region_space->region_lock_); + // Given a free region, declare it non-free (allocated) and large. void UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time) REQUIRES(region_space->region_lock_); + // Given a free region, declare it non-free (allocated) and large tail. void UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time) REQUIRES(region_space->region_lock_); @@ -339,7 +356,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { // Large allocated. bool IsLarge() const { - bool is_large = state_ == RegionState::kRegionStateLarge; + bool is_large = (state_ == RegionState::kRegionStateLarge); if (is_large) { DCHECK_LT(begin_ + kRegionSize, Top()); } @@ -348,7 +365,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { // Large-tail allocated. bool IsLargeTail() const { - bool is_large_tail = state_ == RegionState::kRegionStateLargeTail; + bool is_large_tail = (state_ == RegionState::kRegionStateLargeTail); if (is_large_tail) { DCHECK_EQ(begin_, Top()); } @@ -379,23 +396,33 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { return type_ == RegionType::kRegionTypeNone; } + // Set this region as evacuated from-space. At the end of the + // collection, RegionSpace::ClearFromSpace will clear and reclaim + // the space used by this region, and tag it as unallocated/free. void SetAsFromSpace() { DCHECK(!IsFree() && IsInToSpace()); type_ = RegionType::kRegionTypeFromSpace; live_bytes_ = static_cast<size_t>(-1); } + // Set this region as unevacuated from-space. At the end of the + // collection, RegionSpace::ClearFromSpace will preserve the space + // used by this region, and tag it as to-space (see + // Region::SetUnevacFromSpaceAsToSpace below). void SetAsUnevacFromSpace() { DCHECK(!IsFree() && IsInToSpace()); type_ = RegionType::kRegionTypeUnevacFromSpace; live_bytes_ = 0U; } + // Set this region as to-space. Used by RegionSpace::ClearFromSpace. + // This is only valid if it is currently an unevac from-space region. void SetUnevacFromSpaceAsToSpace() { DCHECK(!IsFree() && IsInUnevacFromSpace()); type_ = RegionType::kRegionTypeToSpace; } + // Return whether this region should be evacuated. Used by RegionSpace::SetFromSpace. ALWAYS_INLINE bool ShouldBeEvacuated(); void AddLiveBytes(size_t live_bytes) { @@ -418,20 +445,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { size_t BytesAllocated() const; - size_t ObjectsAllocated() const { - if (IsLarge()) { - DCHECK_LT(begin_ + kRegionSize, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); - return 1; - } else if (IsLargeTail()) { - DCHECK_EQ(begin_, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); - return 0; - } else { - DCHECK(IsAllocated()) << static_cast<uint>(state_); - return objects_allocated_; - } - } + size_t ObjectsAllocated() const; uint8_t* Begin() const { return begin_; @@ -467,12 +481,17 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { private: size_t idx_; // The region's index in the region space. uint8_t* begin_; // The begin address of the region. + // Note that `top_` can be higher than `end_` in the case of a + // large region, where an allocated object spans multiple regions + // (large region + one or more large tail regions). Atomic<uint8_t*> top_; // The current position of the allocation. uint8_t* end_; // The end address of the region. RegionState state_; // The region state (see RegionState). RegionType type_; // The region type (see RegionType). Atomic<size_t> objects_allocated_; // The number of objects allocated. uint32_t alloc_time_; // The allocation time of the region. + // Note that newly allocated and evacuated regions use -1 as + // special value for `live_bytes_`. size_t live_bytes_; // The live bytes. Used to compute the live percent. bool is_newly_allocated_; // True if it's allocated after the last collection. bool is_a_tlab_; // True if it's a tlab. @@ -488,12 +507,12 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { Region* RefToRegionUnlocked(mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS { // For a performance reason (this is frequently called via - // IsInFromSpace() etc.) we avoid taking a lock here. Note that - // since we only change a region from to-space to from-space only - // during a pause (SetFromSpace()) and from from-space to free - // (after GC is done) as long as ref is a valid reference into an - // allocated region, it's safe to access the region state without - // the lock. + // RegionSpace::IsInFromSpace, etc.) we avoid taking a lock here. + // Note that since we only change a region from to-space to (evac) + // from-space during a pause (in RegionSpace::SetFromSpace) and + // from (evac) from-space to free (after GC is done), as long as + // `ref` is a valid reference into an allocated region, it's safe + // to access the region state without the lock. return RefToRegionLocked(ref); } @@ -508,6 +527,13 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { return reg; } + // Return the object location following `obj` in the region space + // (i.e., the object location at `obj + obj->SizeOf()`). + // + // Note that + // - unless the region containing `obj` is fully used; and + // - `obj` is not the last object of that region; + // the returned location is not guaranteed to be a valid object. mirror::Object* GetNextObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); @@ -524,6 +550,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { VerifyNonFreeRegionLimit(); } + // Implementation of this invariant: + // for all `i >= non_free_region_index_limit_`, `regions_[i].IsFree()` is true. void VerifyNonFreeRegionLimit() REQUIRES(region_lock_) { if (kIsDebugBuild && non_free_region_index_limit_ < num_regions_) { for (size_t i = non_free_region_index_limit_; i < num_regions_; ++i) { @@ -549,14 +577,18 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { // regions are in non-free. size_t max_peak_num_non_free_regions_; + // The pointer to the region array. std::unique_ptr<Region[]> regions_ GUARDED_BY(region_lock_); - // The pointer to the region array. + // The upper-bound index of the non-free regions. Used to avoid scanning all regions in - // SetFromSpace(). Invariant: for all i >= non_free_region_index_limit_, regions_[i].IsFree() is - // true. + // RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace. + // + // Invariant (verified by RegionSpace::VerifyNonFreeRegionLimit): + // for all `i >= non_free_region_index_limit_`, `regions_[i].IsFree()` is true. size_t non_free_region_index_limit_ GUARDED_BY(region_lock_); - Region* current_region_; // The region that's being allocated currently. - Region* evac_region_; // The region that's being evacuated to currently. + + Region* current_region_; // The region currently used for allocation. + Region* evac_region_; // The region currently used for evacuation. Region full_region_; // The dummy/sentinel region that looks full. // Mark bitmap used by the GC. diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc index 2c6afa7eb8..a8bd7b816e 100644 --- a/runtime/gc/space/space.cc +++ b/runtime/gc/space/space.cc @@ -107,7 +107,6 @@ collector::ObjectBytePair ContinuousMemMapAllocSpace::Sweep(bool swap_bitmaps) { return scc.freed; } -// Returns the old mark bitmap. void ContinuousMemMapAllocSpace::BindLiveToMarkBitmap() { CHECK(!HasBoundBitmaps()); accounting::ContinuousSpaceBitmap* live_bitmap = GetLiveBitmap(); @@ -125,7 +124,7 @@ bool ContinuousMemMapAllocSpace::HasBoundBitmaps() const { void ContinuousMemMapAllocSpace::UnBindBitmaps() { CHECK(HasBoundBitmaps()); - // At this point, the temp_bitmap holds our old mark bitmap. + // At this point, `temp_bitmap_` holds our old mark bitmap. accounting::ContinuousSpaceBitmap* new_bitmap = temp_bitmap_.release(); Runtime::Current()->GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap_.get(), new_bitmap); CHECK_EQ(mark_bitmap_.release(), live_bitmap_.get()); diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 6b76048cb1..12bccb35e7 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -389,7 +389,11 @@ class MemMapSpace : public ContinuousSpace { } protected: - MemMapSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end, uint8_t* limit, + MemMapSpace(const std::string& name, + MemMap* mem_map, + uint8_t* begin, + uint8_t* end, + uint8_t* limit, GcRetentionPolicy gc_retention_policy) : ContinuousSpace(name, gc_retention_policy, begin, end, limit), mem_map_(mem_map) { @@ -420,7 +424,10 @@ class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace { } bool HasBoundBitmaps() const REQUIRES(Locks::heap_bitmap_lock_); + // Make the mark bitmap an alias of the live bitmap. Save the current mark bitmap into + // `temp_bitmap_`, so that we can restore it later in ContinuousMemMapAllocSpace::UnBindBitmaps. void BindLiveToMarkBitmap() REQUIRES(Locks::heap_bitmap_lock_); + // Unalias the mark bitmap from the live bitmap and restore the old mark bitmap. void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_); // Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping. void SwapBitmaps(); diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h index 4d10de8237..08231017e7 100644 --- a/runtime/gc/space/zygote_space.h +++ b/runtime/gc/space/zygote_space.h @@ -26,7 +26,7 @@ namespace gc { namespace space { -// An zygote space is a space which you cannot allocate into or free from. +// A zygote space is a space which you cannot allocate into or free from. class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace { public: // Returns the remaining storage in the out_map field. diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc index d99b37762f..fb5db1147f 100644 --- a/runtime/gc/verification.cc +++ b/runtime/gc/verification.cc @@ -140,7 +140,7 @@ bool Verification::IsValidClass(const void* addr) const { if (!IsValidHeapObjectAddress(k1)) { return false; } - // k should be class class, take the class again to verify. + // `k1` should be class class, take the class again to verify. // Note that this check may not be valid for the no image space since the class class might move // around from moving GC. mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>(); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 05e68e66dd..d7e5e18b9e 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -31,6 +31,23 @@ enum Action { kDeny }; +enum AccessMethod { + kReflection, + kJNI +}; + +inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { + switch (value) { + case kReflection: + os << "reflection"; + break; + case kJNI: + os << "JNI"; + break; + } + return os; +} + inline Action GetMemberAction(uint32_t access_flags) { switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) { case HiddenApiAccessFlags::kWhitelist: @@ -45,19 +62,25 @@ inline Action GetMemberAction(uint32_t access_flags) { } // Issue a warning about field access. -inline void WarnAboutMemberAccess(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { +inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method) + REQUIRES_SHARED(Locks::mutator_lock_) { std::string tmp; LOG(WARNING) << "Accessing hidden field " << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->" - << field->GetName() << ":" << field->GetTypeDescriptor(); + << field->GetName() << ":" << field->GetTypeDescriptor() + << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags()) + << ", " << access_method << ")"; } // Issue a warning about method access. -inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { +inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) + REQUIRES_SHARED(Locks::mutator_lock_) { std::string tmp; LOG(WARNING) << "Accessing hidden method " << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->" - << method->GetName() << method->GetSignature().ToString(); + << method->GetName() << method->GetSignature().ToString() + << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags()) + << ", " << access_method << ")"; } // Returns true if access to `member` should be denied to the caller of the @@ -69,7 +92,8 @@ inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::muta template<typename T> inline bool ShouldBlockAccessToMember(T* member, Thread* self, - std::function<bool(Thread*)> fn_caller_in_boot) + std::function<bool(Thread*)> fn_caller_in_boot, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); Runtime* runtime = Runtime::Current(); @@ -92,25 +116,33 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } - // Member is hidden and we are not in the boot class path. Act accordingly. + // Member is hidden and we are not in the boot class path. + + // Print a log message with information about this class member access. + // We do this regardless of whether we block the access or not. + WarnAboutMemberAccess(member, access_method); + + // Block access if on blacklist. if (action == kDeny) { return true; - } else { - DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - - // Allow access to this member but print a warning. Depending on a runtime - // flag, we might move the member into whitelist and skip the warning the - // next time the member is used. - if (runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); - } - WarnAboutMemberAccess(member); - if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - Runtime::Current()->SetPendingHiddenApiWarning(true); - } - return false; } + + // Allow access to this member but print a warning. + DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + + // Depending on a runtime flag, we might move the member into whitelist and + // skip the warning the next time the member is accessed. + if (runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } + + // If this action requires a UI warning, set the appropriate flag. + if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { + Runtime::Current()->SetPendingHiddenApiWarning(true); + } + + return false; } // Returns true if access to member with `access_flags` should be denied to `caller`. diff --git a/runtime/hidden_api_access_flags.h b/runtime/hidden_api_access_flags.h index c328f965d2..6a88c12be5 100644 --- a/runtime/hidden_api_access_flags.h +++ b/runtime/hidden_api_access_flags.h @@ -146,6 +146,24 @@ class HiddenApiAccessFlags { }; }; +inline std::ostream& operator<<(std::ostream& os, HiddenApiAccessFlags::ApiList value) { + switch (value) { + case HiddenApiAccessFlags::kWhitelist: + os << "whitelist"; + break; + case HiddenApiAccessFlags::kLightGreylist: + os << "light greylist"; + break; + case HiddenApiAccessFlags::kDarkGreylist: + os << "dark greylist"; + break; + case HiddenApiAccessFlags::kBlacklist: + os << "blacklist"; + break; + } + return os; +} + } // namespace art diff --git a/runtime/image.cc b/runtime/image.cc index 8e3615ffcf..99406229a5 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '4', '\0' }; // Math.pow() intrinsic. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '5', '\0' }; // Bitstring type check off. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 24cedb093b..0ae6dbfa88 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -269,7 +269,20 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) } } else { CHECK_NE(return_pc, 0U); - CHECK(!reached_existing_instrumentation_frames_); + if (UNLIKELY(reached_existing_instrumentation_frames_)) { + std::string thread_name; + GetThread()->GetThreadName(thread_name); + uint32_t dex_pc = dex::kDexNoIndex; + if (last_return_pc_ != 0 && + GetCurrentOatQuickMethodHeader() != nullptr) { + dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_); + } + LOG(FATAL) << "While walking " << thread_name << " found existing instrumentation frames." + << " method is " << GetMethod()->PrettyMethod() + << " return_pc is " << std::hex << return_pc + << " dex pc: " << dex_pc; + UNREACHABLE(); + } InstrumentationStackFrame instrumentation_frame( m->IsRuntimeMethod() ? nullptr : GetThisObject(), m, diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 12b8c38bbb..a8ab626a62 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -31,6 +31,7 @@ #include "mirror/class.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" +#include "mirror/var_handle.h" #include "reflection-inl.h" #include "reflection.h" #include "stack.h" @@ -723,263 +724,149 @@ bool DoMethodHandleInvoke(Thread* self, } } -static bool UnimplementedSignaturePolymorphicMethod(Thread* self ATTRIBUTE_UNUSED, - ShadowFrame& shadow_frame ATTRIBUTE_UNUSED, - const Instruction* inst ATTRIBUTE_UNUSED, - uint16_t inst_data ATTRIBUTE_UNUSED, - JValue* result ATTRIBUTE_UNUSED) +static bool DoVarHandleInvokeChecked(Thread* self, + Handle<mirror::VarHandle> var_handle, + Handle<mirror::MethodType> callsite_type, + mirror::VarHandle::AccessMode access_mode, + ShadowFrame& shadow_frame, + InstructionOperands* operands, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - UNIMPLEMENTED(FATAL) << "TODO(oth): b/65872996"; - return false; -} - -bool DoVarHandleCompareAndExchange(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleCompareAndExchangeAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleCompareAndExchangeRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleCompareAndSet(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGet(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndAdd(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndAddAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndAddRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseAnd(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseAndAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseAndRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseOr(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseOrAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseOrRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseXor(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseXorAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndBitwiseXorRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndSet(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndSetAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetAndSetRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetOpaque(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} - -bool DoVarHandleGetVolatile(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); + // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType() + // which is only required if we need to convert argument and/or + // return types. + StackHandleScope<1> hs(self); + Handle<mirror::MethodType> accessor_type(hs.NewHandle( + var_handle->GetMethodTypeForAccessMode(self, access_mode))); + const size_t num_vregs = accessor_type->NumberOfVRegs(); + const int num_params = accessor_type->GetPTypes()->GetLength(); + ShadowFrameAllocaUniquePtr accessor_frame = + CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); + ShadowFrameGetter getter(shadow_frame, operands); + static const uint32_t kFirstDestinationReg = 0; + ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); + if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + return false; + } + RangeInstructionOperands accessor_operands(kFirstDestinationReg, + kFirstDestinationReg + num_vregs); + if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + return false; + } + return ConvertReturnValue(callsite_type, accessor_type, result); } -bool DoVarHandleSet(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} +static bool DoVarHandleInvokeCommon(Thread* self, + ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result, + mirror::VarHandle::AccessMode access_mode) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Make sure to check for async exceptions + if (UNLIKELY(self->ObserveAsyncException())) { + return false; + } -bool DoVarHandleSetOpaque(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} + bool is_var_args = inst->HasVarArgs(); + const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); + ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC)); + if (receiver.IsNull()) { + ThrowNullPointerExceptionFromDexPC(); + return false; + } -bool DoVarHandleSetRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} + StackHandleScope<2> hs(self); + Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr()))); + if (!var_handle->IsAccessModeSupported(access_mode)) { + ThrowUnsupportedOperationException(); + return false; + } -bool DoVarHandleSetVolatile(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} + const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::MethodType> callsite_type(hs.NewHandle( + class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod()))); + // This implies we couldn't resolve one or more types in this VarHandle. + if (UNLIKELY(callsite_type == nullptr)) { + CHECK(self->IsExceptionPending()); + return false; + } -bool DoVarHandleWeakCompareAndSet(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} + if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) { + ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), + callsite_type.Get()); + return false; + } -bool DoVarHandleWeakCompareAndSetAcquire(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); + if (is_var_args) { + uint32_t args[Instruction::kMaxVarArgRegs]; + inst->GetVarArgs(args, inst_data); + VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc()); + NoReceiverInstructionOperands operands(&all_operands); + return DoVarHandleInvokeChecked(self, + var_handle, + callsite_type, + access_mode, + shadow_frame, + &operands, + result); + } else { + RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc()); + NoReceiverInstructionOperands operands(&all_operands); + return DoVarHandleInvokeChecked(self, + var_handle, + callsite_type, + access_mode, + shadow_frame, + &operands, + result); + } } -bool DoVarHandleWeakCompareAndSetPlain(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); +#define DO_VAR_HANDLE_ACCESSOR(_access_mode) \ +bool DoVarHandle ## _access_mode(Thread* self, \ + ShadowFrame& shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { \ + const auto access_mode = mirror::VarHandle::AccessMode::k ## _access_mode; \ + return DoVarHandleInvokeCommon(self, shadow_frame, inst, inst_data, result, access_mode); \ } -bool DoVarHandleWeakCompareAndSetRelease(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result); -} +DO_VAR_HANDLE_ACCESSOR(CompareAndExchange) +DO_VAR_HANDLE_ACCESSOR(CompareAndExchangeAcquire) +DO_VAR_HANDLE_ACCESSOR(CompareAndExchangeRelease) +DO_VAR_HANDLE_ACCESSOR(CompareAndSet) +DO_VAR_HANDLE_ACCESSOR(Get) +DO_VAR_HANDLE_ACCESSOR(GetAcquire) +DO_VAR_HANDLE_ACCESSOR(GetAndAdd) +DO_VAR_HANDLE_ACCESSOR(GetAndAddAcquire) +DO_VAR_HANDLE_ACCESSOR(GetAndAddRelease) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseAnd) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseAndAcquire) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseAndRelease) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseOr) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseOrAcquire) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseOrRelease) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseXor) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseXorAcquire) +DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseXorRelease) +DO_VAR_HANDLE_ACCESSOR(GetAndSet) +DO_VAR_HANDLE_ACCESSOR(GetAndSetAcquire) +DO_VAR_HANDLE_ACCESSOR(GetAndSetRelease) +DO_VAR_HANDLE_ACCESSOR(GetOpaque) +DO_VAR_HANDLE_ACCESSOR(GetVolatile) +DO_VAR_HANDLE_ACCESSOR(Set) +DO_VAR_HANDLE_ACCESSOR(SetOpaque) +DO_VAR_HANDLE_ACCESSOR(SetRelease) +DO_VAR_HANDLE_ACCESSOR(SetVolatile) +DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSet) +DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSetAcquire) +DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSetPlain) +DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSetRelease) + +#undef DO_VAR_HANDLE_ACCESSOR template<bool is_range> bool DoInvokePolymorphic(Thread* self, @@ -1049,7 +936,8 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, // The first parameter is a MethodHandles lookup instance. { - Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass())); + Handle<mirror::Class> lookup_class = + hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass()); ObjPtr<mirror::MethodHandlesLookup> lookup = mirror::MethodHandlesLookup::Create(self, lookup_class); if (lookup.IsNull()) { diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 8180222e22..39a1db85d4 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -176,7 +176,8 @@ static inline bool DoInvoke(Thread* self, } const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - ObjPtr<mirror::Object> receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC); + ObjPtr<mirror::Object> receiver = + (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC); ArtMethod* sf_method = shadow_frame.GetMethod(); ArtMethod* const called_method = FindMethodFromCode<type, do_access_check>( method_idx, &receiver, sf_method, self); @@ -645,7 +646,7 @@ EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kVirtual); // invoke-virtual // Explicitly instantiate all DoInvokeVirtualQuick functions. #define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ + template REQUIRES_SHARED(Locks::mutator_lock_) \ bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \ const Instruction* inst, uint16_t inst_data, \ JValue* result) diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S index df4bcc66f3..7c7c527ef4 100644 --- a/runtime/interpreter/mterp/arm/entry.S +++ b/runtime/interpreter/mterp/arm/entry.S @@ -56,7 +56,7 @@ ENTRY ExecuteMterpImpl VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S index 64ab9efa19..1f15f870ea 100644 --- a/runtime/interpreter/mterp/arm/header.S +++ b/runtime/interpreter/mterp/arm/header.S @@ -93,6 +93,8 @@ unspecified registers or condition codes. /* During bringup, we'll use the shadow frame model instead of rFP */ /* single-purpose registers, given names for clarity */ #define rPC r4 +#define CFI_DEX 4 // DWARF register number of the register holding dex-pc (xPC). +#define CFI_TMP 0 // DWARF register number of the first argument register (r0). #define rFP r5 #define rSELF r6 #define rINST r7 diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S index 8d61210be8..cf38a2992d 100644 --- a/runtime/interpreter/mterp/arm64/entry.S +++ b/runtime/interpreter/mterp/arm64/entry.S @@ -46,7 +46,7 @@ ENTRY ExecuteMterpImpl add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode - .cfi_register DPC_PSEUDO_REG, xPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 9261b770d6..f0bf8ca34e 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -95,6 +95,8 @@ codes. /* During bringup, we'll use the shadow frame model instead of xFP */ /* single-purpose registers, given names for clarity */ #define xPC x20 +#define CFI_DEX 20 // DWARF register number of the register holding dex-pc (xPC). +#define CFI_TMP 0 // DWARF register number of the first argument register (r0). #define xFP x21 #define xSELF x22 #define xINST x23 diff --git a/runtime/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/mterp/cfi_asm_support.h index a97e153993..0df4eb4f81 100644 --- a/runtime/interpreter/mterp/cfi_asm_support.h +++ b/runtime/interpreter/mterp/cfi_asm_support.h @@ -18,14 +18,30 @@ #define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ /* - * To keep track of the Dalvik PC, give assign it a magic register number that - * won't be confused with a pysical register. Then, standard .cfi directives - * will track the location of it so that it may be extracted during a stack - * unwind. + * Define the DEX PC (memory address of the currently interpreted bytecode) + * within the CFI stream of the current function (stored in .eh_frame). + * This allows libunwind to detect that the frame is in the interpreter, + * and to resolve the memory address into human readable Java method name. + * The CFI instruction is recognised by the magic bytes in the expression + * (we push magic "DEX1" constant on the DWARF stack and drop it again). * - * The Dalvik PC will be in either a physical registor, or the frame. - * Encoded from the ASCII string " DEX" -> 0x20 0x44 0x45 0x58 + * As with any other CFI opcode, the expression needs to be associated with + * a register. Any caller-save register will do as those are unused in CFI. + * Better solution would be to store the expression in Android-specific + * DWARF register (CFI registers don't have to correspond to real hardware + * registers), however, gdb handles any unknown registers very poorly. + * Similarly, we could also use some of the user-defined opcodes defined + * in the DWARF specification, but gdb doesn't support those either. + * + * The DEX PC is generally advanced in the middle of the bytecode handler, + * which will result in the reported DEX PC to be off by an instruction. + * Therefore the macro allows adding/subtracting an offset to compensate. + * TODO: Add the offsets to handlers to get line-accurate DEX PC reporting. */ -#define DPC_PSEUDO_REG 0x20444558 +#define CFI_DEFINE_DEX_PC_WITH_OFFSET(tmpReg, dexReg, dexOffset) .cfi_escape \ + 0x16 /* DW_CFA_val_expression */, tmpReg, 0x09 /* size */, \ + 0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = "DEX1" */ \ + 0x13 /* DW_OP_drop */, \ + 0x92 /* DW_OP_bregx */, dexReg, (dexOffset & 0x7F) /* 1-byte SLEB128 */ #endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ diff --git a/runtime/interpreter/mterp/gen_mterp.py b/runtime/interpreter/mterp/gen_mterp.py index 1c9af30d0a..40d99df037 100755 --- a/runtime/interpreter/mterp/gen_mterp.py +++ b/runtime/interpreter/mterp/gen_mterp.py @@ -22,7 +22,7 @@ import sys, string, re, time from string import Template -interp_defs_file = "../../dex_instruction_list.h" # need opcode list +interp_defs_file = "../../dex/dex_instruction_list.h" # need opcode list kNumPackedOpcodes = 256 splitops = False diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S index 41b5d5650d..d342354969 100644 --- a/runtime/interpreter/mterp/mips/entry.S +++ b/runtime/interpreter/mterp/mips/entry.S @@ -54,7 +54,7 @@ ExecuteMterpImpl: EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC() diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S index 0f7a6f1116..014628f415 100644 --- a/runtime/interpreter/mterp/mips/header.S +++ b/runtime/interpreter/mterp/mips/header.S @@ -61,6 +61,8 @@ /* single-purpose registers, given names for clarity */ #define rPC s0 +#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0). +#define CFI_TMP 4 // DWARF register number of the first argument register (a0). #define rFP s1 #define rSELF s2 #define rIBASE s3 diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S index 841a817569..ed965aa201 100644 --- a/runtime/interpreter/mterp/mips64/entry.S +++ b/runtime/interpreter/mterp/mips64/entry.S @@ -73,7 +73,7 @@ ExecuteMterpImpl: dlsa rREFS, v0, rFP, 2 lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) dlsa rPC, v0, a1, 1 - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S index 2b550cb533..4947aff38e 100644 --- a/runtime/interpreter/mterp/mips64/header.S +++ b/runtime/interpreter/mterp/mips64/header.S @@ -90,6 +90,8 @@ The following registers have fixed assignments: /* During bringup, we'll use the shadow frame model instead of rFP */ /* single-purpose registers, given names for clarity */ #define rPC s0 +#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0). +#define CFI_TMP 4 // DWARF register number of the first argument register (a0). #define rFP s1 #define rSELF s2 #define rINST s3 diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index f3c1124ec4..5c1a13b9d6 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -100,6 +100,8 @@ unspecified registers or condition codes. /* During bringup, we'll use the shadow frame model instead of rFP */ /* single-purpose registers, given names for clarity */ #define rPC r4 +#define CFI_DEX 4 // DWARF register number of the register holding dex-pc (xPC). +#define CFI_TMP 0 // DWARF register number of the first argument register (r0). #define rFP r5 #define rSELF r6 #define rINST r7 @@ -375,7 +377,7 @@ ENTRY ExecuteMterpImpl VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 347d54f705..72446ba082 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -102,6 +102,8 @@ codes. /* During bringup, we'll use the shadow frame model instead of xFP */ /* single-purpose registers, given names for clarity */ #define xPC x20 +#define CFI_DEX 20 // DWARF register number of the register holding dex-pc (xPC). +#define CFI_TMP 0 // DWARF register number of the first argument register (r0). #define xFP x21 #define xSELF x22 #define xINST x23 @@ -405,7 +407,7 @@ ENTRY ExecuteMterpImpl add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode - .cfi_register DPC_PSEUDO_REG, xPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 1687afa58a..d5861b28a7 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -68,6 +68,8 @@ /* single-purpose registers, given names for clarity */ #define rPC s0 +#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0). +#define CFI_TMP 4 // DWARF register number of the first argument register (a0). #define rFP s1 #define rSELF s2 #define rIBASE s3 @@ -788,7 +790,7 @@ ExecuteMterpImpl: EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC() diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 559c72bb0c..5224df92be 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -97,6 +97,8 @@ The following registers have fixed assignments: /* During bringup, we'll use the shadow frame model instead of rFP */ /* single-purpose registers, given names for clarity */ #define rPC s0 +#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0). +#define CFI_TMP 4 // DWARF register number of the first argument register (a0). #define rFP s1 #define rSELF s2 #define rINST s3 @@ -408,7 +410,7 @@ ExecuteMterpImpl: dlsa rREFS, v0, rFP, 2 lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) dlsa rPC, v0, a1, 1 - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 0613c9d12e..f98fa5b74f 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -164,6 +164,8 @@ unspecified registers or condition codes. /* single-purpose registers, given names for clarity */ #define rSELF IN_ARG0(%esp) #define rPC %esi +#define CFI_DEX 6 // DWARF register number of the register holding dex-pc (esi). +#define CFI_TMP 0 // DWARF register number of the first argument register (eax). #define rFP %edi #define rINST %ebx #define rINSTw %bx @@ -380,7 +382,7 @@ SYMBOL(ExecuteMterpImpl): leal (rFP, %eax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax lea (%ecx, %eax, 2), rPC - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Set up for backwards branches & osr profiling */ diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index aa91db3b61..d82a2d2eb0 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -164,6 +164,8 @@ unspecified registers or condition codes. /* single-purpose registers, given names for clarity */ #define rSELF SELF_SPILL(%rsp) #define rPC %r12 +#define CFI_DEX 12 // DWARF register number of the register holding dex-pc (rPC). +#define CFI_TMP 5 // DWARF register number of the first argument register (rdi). #define rFP %r13 #define rINST %ebx #define rINSTq %rbx @@ -363,7 +365,7 @@ SYMBOL(ExecuteMterpImpl): leaq (rFP, %rax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax leaq (IN_ARG1, %rax, 2), rPC - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S index 10ca8366de..324637bf9a 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -61,7 +61,7 @@ SYMBOL(ExecuteMterpImpl): leal (rFP, %eax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax lea (%ecx, %eax, 2), rPC - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Set up for backwards branches & osr profiling */ diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 0e585e86f0..2e3bbdf6f7 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -157,6 +157,8 @@ unspecified registers or condition codes. /* single-purpose registers, given names for clarity */ #define rSELF IN_ARG0(%esp) #define rPC %esi +#define CFI_DEX 6 // DWARF register number of the register holding dex-pc (esi). +#define CFI_TMP 0 // DWARF register number of the first argument register (eax). #define rFP %edi #define rINST %ebx #define rINSTw %bx diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S index d85ef7fe24..2f69226206 100644 --- a/runtime/interpreter/mterp/x86_64/entry.S +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -58,7 +58,7 @@ SYMBOL(ExecuteMterpImpl): leaq (rFP, %rax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax leaq (IN_ARG1, %rax, 2), rPC - .cfi_register DPC_PSEUDO_REG, rPC + CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0) EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index a3ef8953ca..eabaade4e7 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -157,6 +157,8 @@ unspecified registers or condition codes. /* single-purpose registers, given names for clarity */ #define rSELF SELF_SPILL(%rsp) #define rPC %r12 +#define CFI_DEX 12 // DWARF register number of the register holding dex-pc (rPC). +#define CFI_TMP 5 // DWARF register number of the first argument register (rdi). #define rFP %r13 #define rINST %ebx #define rINSTq %rbx diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 85acc71377..f8dd8293ca 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -235,6 +235,20 @@ void UnstartedRuntime::UnstartedClassForNameLong( UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.forName"); } +void UnstartedRuntime::UnstartedClassGetPrimitiveClass( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + ObjPtr<mirror::String> class_name = GetClassName(self, shadow_frame, arg_offset); + ObjPtr<mirror::Class> klass = mirror::Class::GetPrimitiveClass(class_name); + if (UNLIKELY(klass == nullptr)) { + DCHECK(self->IsExceptionPending()); + AbortTransactionOrFail(self, + "Class.getPrimitiveClass() failed: %s", + self->GetException()->GetDetailMessage()->ToModifiedUtf8().c_str()); + return; + } + result->SetL(klass); +} + void UnstartedRuntime::UnstartedClassClassForName( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.classForName"); @@ -738,12 +752,6 @@ void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass( } } -void UnstartedRuntime::UnstartedVoidLookupType( - Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, JValue* result, - size_t arg_offset ATTRIBUTE_UNUSED) { - result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V')); -} - // Arraycopy emulation. // Note: we can't use any fast copy functions, as they are not available under transaction. diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h index c029e07432..3cc598aed7 100644 --- a/runtime/interpreter/unstarted_runtime_list.h +++ b/runtime/interpreter/unstarted_runtime_list.h @@ -23,6 +23,7 @@ V(CharacterToUpperCase, "int java.lang.Character.toUpperCase(int)") \ V(ClassForName, "java.lang.Class java.lang.Class.forName(java.lang.String)") \ V(ClassForNameLong, "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") \ + V(ClassGetPrimitiveClass, "java.lang.Class java.lang.Class.getPrimitiveClass(java.lang.String)") \ V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \ V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \ V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \ @@ -36,7 +37,6 @@ V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \ V(ConstructorNewInstance0, "java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[])") \ V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \ - V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \ V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \ V(SystemArraycopyByte, "void java.lang.System.arraycopy(byte[], int, byte[], int, int)") \ V(SystemArraycopyChar, "void java.lang.System.arraycopy(char[], int, char[], int, int)") \ diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h index 8c81c2565d..ac20afecd4 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/java_vm_ext.h @@ -225,7 +225,7 @@ class JavaVMExt : public JavaVM { // Extra checking. bool check_jni_; - bool force_copy_; + const bool force_copy_; const bool tracing_enabled_; // Extra diagnostics. diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc index d68430f3ac..481aff91f8 100644 --- a/runtime/jdwp/jdwp_adb.cc +++ b/runtime/jdwp/jdwp_adb.cc @@ -38,8 +38,7 @@ * domain stream socket (@jdwp-control) that is opened by the * ADB daemon. * - * 2/ it then sends the current process PID as a string of 4 hexadecimal - * chars (no terminating zero) + * 2/ it then sends the current process PID as an int32_t. * * 3/ then, it uses recvmsg to receive file descriptors from the * daemon. each incoming file descriptor is a pass-through to @@ -225,7 +224,6 @@ bool JdwpAdbState::Accept() { if (ControlSock() == -1) { int sleep_ms = 500; const int sleep_max_ms = 2*1000; - char buff[5]; int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (sock < 0) { @@ -247,8 +245,7 @@ bool JdwpAdbState::Accept() { } } - snprintf(buff, sizeof(buff), "%04x", getpid()); - buff[4] = 0; + int32_t pid = getpid(); for (;;) { /* @@ -277,9 +274,9 @@ bool JdwpAdbState::Accept() { #endif /* now try to send our pid to the ADB daemon */ - ret = TEMP_FAILURE_RETRY(send(control_sock, buff, 4, 0)); - if (ret == 4) { - VLOG(jdwp) << StringPrintf("PID sent as '%.*s' to ADB", 4, buff); + ret = TEMP_FAILURE_RETRY(send(control_sock, &pid, sizeof(pid), 0)); + if (ret == sizeof(pid)) { + VLOG(jdwp) << "PID " << pid << " sent to ADB"; break; } diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index de4d02edaf..dcb4a20b5f 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -168,9 +168,10 @@ bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, return data->AddMethod(flags, method_idx); } -bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) { +bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods, + MethodHotness::Flag flags) { for (const ProfileMethodInfo& method : methods) { - if (!AddMethod(method)) { + if (!AddMethod(method, flags)) { return false; } } @@ -644,15 +645,26 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, uint32_t dex_checksum, uint16_t method_index, uint32_t num_method_ids, - const OfflineProfileMethodInfo& pmi) { + const OfflineProfileMethodInfo& pmi, + MethodHotness::Flag flags) { DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location), dex_checksum, num_method_ids); - if (data == nullptr) { // checksum mismatch + if (data == nullptr) { + // The data is null if there is a mismatch in the checksum or number of method ids. return false; } + // Add the method. InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index); + if (inline_cache == nullptr) { + // Happens if the method index is outside the range (i.e. is greater then the number + // of methods in the dex file). This should not happen during normal execution, + // But tools (e.g. boot image aggregation tools) and tests stress this behaviour. + return false; + } + + data->SetMethodHotness(method_index, flags); if (pmi.inline_caches == nullptr) { // If we don't have inline caches return success right away. @@ -691,12 +703,16 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, return true; } -bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { +bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi, MethodHotness::Flag flags) { DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file); if (data == nullptr) { // checksum mismatch return false; } InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.index); + if (inline_cache == nullptr) { + return false; + } + data->SetMethodHotness(pmi.ref.index, flags); for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) { if (cache.is_missing_types) { @@ -811,6 +827,9 @@ bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer, uint16_t method_index = last_method_index + diff_with_last_method_index; last_method_index = method_index; InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index); + if (inline_cache == nullptr) { + return false; + } if (!ReadInlineCache(buffer, number_of_dex_files, dex_profile_index_remap, @@ -1521,6 +1540,9 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other, for (const auto& other_method_it : other_dex_data->method_map) { uint16_t other_method_index = other_method_it.first; InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index); + if (inline_cache == nullptr) { + return false; + } const auto& other_inline_cache = other_method_it.second; for (const auto& other_ic_it : other_inline_cache) { uint16_t other_dex_pc = other_ic_it.first; @@ -1955,6 +1977,10 @@ bool ProfileCompilationInfo::IsEmpty() const { ProfileCompilationInfo::InlineCacheMap* ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) { + if (method_index >= num_method_ids) { + LOG(ERROR) << "Invalid method index " << method_index << ". num_method_ids=" << num_method_ids; + return nullptr; + } return &(method_map.FindOrAdd( method_index, InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)))->second); @@ -1967,12 +1993,8 @@ bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, s return false; } - if ((flags & MethodHotness::kFlagStartup) != 0) { - method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true); - } - if ((flags & MethodHotness::kFlagPostStartup) != 0) { - method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true); - } + SetMethodHotness(index, flags); + if ((flags & MethodHotness::kFlagHot) != 0) { method_map.FindOrAdd( index, @@ -1981,6 +2003,17 @@ bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, s return true; } +void ProfileCompilationInfo::DexFileData::SetMethodHotness(size_t index, + MethodHotness::Flag flags) { + DCHECK_LT(index, num_method_ids); + if ((flags & MethodHotness::kFlagStartup) != 0) { + method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true); + } + if ((flags & MethodHotness::kFlagPostStartup) != 0) { + method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true); + } +} + ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo( uint32_t dex_method_index) const { MethodHotness ret; diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 1973f3f09e..5488a9e81e 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -241,7 +241,7 @@ class ProfileCompilationInfo { ~ProfileCompilationInfo(); // Add the given methods to the current profile object. - bool AddMethods(const std::vector<ProfileMethodInfo>& methods); + bool AddMethods(const std::vector<ProfileMethodInfo>& methods, MethodHotness::Flag flags); // Add the given classes to the current profile object. bool AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes); @@ -278,7 +278,7 @@ class ProfileCompilationInfo { bool AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref); // Add a method to the profile using its online representation (containing runtime structures). - bool AddMethod(const ProfileMethodInfo& pmi); + bool AddMethod(const ProfileMethodInfo& pmi, MethodHotness::Flag flags); // Bulk add sampled methods and/or hot methods for a single dex, fast since it only has one // GetOrAddDexFileData call. @@ -500,6 +500,7 @@ class ProfileCompilationInfo { } } + void SetMethodHotness(size_t index, MethodHotness::Flag flags); MethodHotness GetHotnessInfo(uint32_t dex_method_index) const; // The allocator used to allocate new inline cache maps. @@ -559,7 +560,8 @@ class ProfileCompilationInfo { uint32_t dex_checksum, uint16_t method_index, uint32_t num_method_ids, - const OfflineProfileMethodInfo& pmi); + const OfflineProfileMethodInfo& pmi, + MethodHotness::Flag flags); // Add a class index to the profile. bool AddClassIndex(const std::string& dex_location, diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 4ac11ee422..e6917956ae 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -80,7 +80,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { uint16_t method_index, const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, ProfileCompilationInfo* info) { - return info->AddMethod(dex_location, checksum, method_index, kMaxMethodIds, pmi); + return info->AddMethod( + dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup); } bool AddClass(const std::string& dex_location, @@ -99,7 +100,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { bool SaveProfilingInfo( const std::string& filename, const std::vector<ArtMethod*>& methods, - const std::set<DexCacheResolvedClasses>& resolved_classes) { + const std::set<DexCacheResolvedClasses>& resolved_classes, + Hotness::Flag flags) { ProfileCompilationInfo info; std::vector<ProfileMethodInfo> profile_methods; ScopedObjectAccess soa(Thread::Current()); @@ -107,7 +109,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { profile_methods.emplace_back( MethodReference(method->GetDexFile(), method->GetDexMethodIndex())); } - if (!info.AddMethods(profile_methods) || !info.AddClasses(resolved_classes)) { + if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) { return false; } if (info.GetNumberOfMethods() != profile_methods.size()) { @@ -130,6 +132,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { bool SaveProfilingInfoWithFakeInlineCaches( const std::string& filename, const std::vector<ArtMethod*>& methods, + Hotness::Flag flags, /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) { ProfileCompilationInfo info; std::vector<ProfileMethodInfo> profile_methods; @@ -170,7 +173,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { profile_methods_map->Put(method, pmi); } - if (!info.AddMethods(profile_methods) || info.GetNumberOfMethods() != profile_methods.size()) { + if (!info.AddMethods(profile_methods, flags) + || info.GetNumberOfMethods() != profile_methods.size()) { return false; } return info.Save(filename, nullptr); @@ -345,7 +349,8 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { // Save virtual methods from Main. std::set<DexCacheResolvedClasses> resolved_classes; std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); - ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes)); + ASSERT_TRUE(SaveProfilingInfo( + profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup)); // Check that what we saved is in the profile. ProfileCompilationInfo info1; @@ -354,14 +359,16 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { { ScopedObjectAccess soa(self); for (ArtMethod* m : main_methods) { - ASSERT_TRUE(info1.GetMethodHotness( - MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot()); + Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsPostStartup()); } } // Save virtual methods from Second. std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;"); - ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes)); + ASSERT_TRUE(SaveProfilingInfo( + profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup)); // Check that what we saved is in the profile (methods form Main and Second). ProfileCompilationInfo info2; @@ -371,12 +378,14 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { { ScopedObjectAccess soa(self); for (ArtMethod* m : main_methods) { - ASSERT_TRUE( - info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot()); + Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsPostStartup()); } for (ArtMethod* m : second_methods) { - ASSERT_TRUE( - info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot()); + Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsStartup()); } } } @@ -730,7 +739,7 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map; ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( - profile.GetFilename(), main_methods, &profile_methods_map)); + profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map)); // Check that what we saved is in the profile. ProfileCompilationInfo info; @@ -739,8 +748,9 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { { ScopedObjectAccess soa(self); for (ArtMethod* m : main_methods) { - ASSERT_TRUE( - info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot()); + Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsStartup()); const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi = info.GetMethod(m->GetDexFile()->GetLocation(), diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 8f0ac33594..53f48644f2 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -511,7 +511,7 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number uint64_t last_save_number_of_methods = info.GetNumberOfMethods(); uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses(); - info.AddMethods(profile_methods); + info.AddMethods(profile_methods, ProfileCompilationInfo::MethodHotness::kFlagPostStartup); auto profile_cache_it = profile_cache_.find(filename); if (profile_cache_it != profile_cache_.end()) { info.MergeWith(*(profile_cache_it->second)); diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h index 0e8fd03057..291ac48e86 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -96,6 +96,15 @@ class JNIEnvExt : public JNIEnv { } Thread* GetSelf() const { return self_; } + uint32_t GetCritical() const { return critical_; } + void SetCritical(uint32_t new_critical) { critical_ = new_critical; } + uint64_t GetCriticalStartUs() const { return critical_start_us_; } + void SetCriticalStartUs(uint64_t new_critical_start_us) { + critical_start_us_ = new_critical_start_us; + } + const JNINativeInterface* GetUncheckedFunctions() const { + return unchecked_functions_; + } JavaVMExt* GetVm() const { return vm_; } bool IsRuntimeDeleted() const { return runtime_deleted_; } @@ -190,9 +199,7 @@ class JNIEnvExt : public JNIEnv { // If we are a JNI env for a daemon thread with a deleted runtime. bool runtime_deleted_; - friend class CheckJNI; friend class JNI; - friend class ScopedCheck; friend class ScopedJniEnvLocalRefState; friend class Thread; ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 666fb98354..cd4d9543be 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -90,7 +90,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath); + return hiddenapi::ShouldBlockAccessToMember( + member, self, IsCallerInBootClassPath, hiddenapi::kJNI); } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/lock_word.h b/runtime/lock_word.h index fac1a7597d..e89beb6d41 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -34,7 +34,9 @@ class Monitor; /* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of * the state. The four possible states are fat locked, thin/unlocked, hash code, and forwarding - * address. When the lock word is in the "thin" state and its bits are formatted as follows: + * address. + * + * When the lock word is in the "thin" state and its bits are formatted as follows: * * |33|2|2|222222221111|1111110000000000| * |10|9|8|765432109876|5432109876543210| @@ -59,7 +61,7 @@ class Monitor; * |11|0| ForwardingAddress | * * The `r` bit stores the read barrier state. - * The `m` bit stores the mark state. + * The `m` bit stores the mark bit state. */ class LockWord { public: diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 2701ec66a4..9b21e1d9bf 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -346,7 +346,7 @@ inline bool ConvertAndCopyArgumentsFromCallerFrame( return false; } - ShadowFrameGetter getter(operands, caller_frame); + ShadowFrameGetter getter(caller_frame, operands); ShadowFrameSetter setter(callee_frame, first_dest_reg); return PerformConversions<ShadowFrameGetter, ShadowFrameSetter>(self, callsite_type, diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 6ffd1a81fc..3b1bf2ee66 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -130,8 +130,10 @@ bool PerformConversions(Thread* self, // arguments while performing standard argument conversions. class ShadowFrameGetter { public: - ShadowFrameGetter(const InstructionOperands* const operands, const ShadowFrame& shadow_frame) - : operands_(operands), operand_index_(0), shadow_frame_(shadow_frame) {} + ShadowFrameGetter(const ShadowFrame& shadow_frame, + const InstructionOperands* const operands, + size_t operand_index = 0u) + : shadow_frame_(shadow_frame), operands_(operands), operand_index_(operand_index) {} ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { return shadow_frame_.GetVReg(Next()); @@ -151,26 +153,24 @@ class ShadowFrameGetter { operand_index_ += 1; return next; } + uint32_t NextLong() { const uint32_t next = operands_->GetOperand(operand_index_); operand_index_ += 2; return next; } - const InstructionOperands* const operands_; - size_t operand_index_; // the next register operand to read from frame const ShadowFrame& shadow_frame_; + const InstructionOperands* const operands_; // the set of register operands to read + size_t operand_index_; // the next register operand to read from frame }; // A convenience class that allows values to be written to a given shadow frame, // starting at location |first_dst_reg|. class ShadowFrameSetter { public: - ShadowFrameSetter(ShadowFrame* shadow_frame, - size_t first_dst_reg) : - shadow_frame_(shadow_frame), - arg_index_(first_dst_reg) { - } + ShadowFrameSetter(ShadowFrame* shadow_frame, size_t first_dst_reg) + : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {} ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame_->SetVReg(arg_index_++, value); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 36388eb3aa..86d538ec80 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -550,7 +550,7 @@ inline bool Class::IsSubClass(ObjPtr<Class> klass) { current = current->GetSuperClass(); } while (current != nullptr); - if (kIsDebugBuild) { + if (kIsDebugBuild && kBitstringSubtypeCheckEnabled) { ObjPtr<mirror::Class> dis(this); SubtypeCheckInfo::Result sc_result = SubtypeCheck<ObjPtr<Class>>::IsSubtypeOf(dis, klass); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 8a7defd362..5d730ce0b0 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -72,6 +72,42 @@ void Class::VisitRoots(RootVisitor* visitor) { java_lang_Class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); } +ObjPtr<mirror::Class> Class::GetPrimitiveClass(ObjPtr<mirror::String> name) { + const char* expected_name = nullptr; + ClassLinker::ClassRoot class_root = ClassLinker::kJavaLangObject; // Invalid. + if (name != nullptr && name->GetLength() >= 2) { + // Perfect hash for the expected values: from the second letters of the primitive types, + // only 'y' has the bit 0x10 set, so use it to change 'b' to 'B'. + char hash = name->CharAt(0) ^ ((name->CharAt(1) & 0x10) << 1); + switch (hash) { + case 'b': expected_name = "boolean"; class_root = ClassLinker::kPrimitiveBoolean; break; + case 'B': expected_name = "byte"; class_root = ClassLinker::kPrimitiveByte; break; + case 'c': expected_name = "char"; class_root = ClassLinker::kPrimitiveChar; break; + case 'd': expected_name = "double"; class_root = ClassLinker::kPrimitiveDouble; break; + case 'f': expected_name = "float"; class_root = ClassLinker::kPrimitiveFloat; break; + case 'i': expected_name = "int"; class_root = ClassLinker::kPrimitiveInt; break; + case 'l': expected_name = "long"; class_root = ClassLinker::kPrimitiveLong; break; + case 's': expected_name = "short"; class_root = ClassLinker::kPrimitiveShort; break; + case 'v': expected_name = "void"; class_root = ClassLinker::kPrimitiveVoid; break; + default: break; + } + } + if (expected_name != nullptr && name->Equals(expected_name)) { + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->GetClassRoot(class_root); + DCHECK(klass != nullptr); + return klass; + } else { + Thread* self = Thread::Current(); + if (name == nullptr) { + // Note: ThrowNullPointerException() requires a message which we deliberately want to omit. + self->ThrowNewException("Ljava/lang/NullPointerException;", /* msg */ nullptr); + } else { + self->ThrowNewException("Ljava/lang/ClassNotFoundException;", name->ToModifiedUtf8().c_str()); + } + return nullptr; + } +} + ClassExt* Class::EnsureExtDataPresent(Thread* self) { ObjPtr<ClassExt> existing(GetExtData()); if (!existing.IsNull()) { @@ -156,9 +192,19 @@ void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self self->AssertPendingException(); } - { + if (kBitstringSubtypeCheckEnabled) { + // FIXME: This looks broken with respect to aborted transactions. ObjPtr<mirror::Class> h_this_ptr = h_this.Get(); SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(h_this_ptr, new_status); + } else { + // The ClassStatus is always in the 4 most-significant bits of status_. + static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32"); + uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize); + if (Runtime::Current()->IsActiveTransaction()) { + h_this->SetField32Volatile<true>(StatusOffset(), new_status_value); + } else { + h_this->SetField32Volatile<false>(StatusOffset(), new_status_value); + } } // Setting the object size alloc fast path needs to be after the status write so that if the diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ced7c7c908..b9a31e54b7 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -81,7 +81,7 @@ class MANAGED Class FINAL : public Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> ClassStatus GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid including "subtype_check_bits_and_status.h" to get the field. - // The ClassStatus is always in the 4 most-significant of status_. + // The ClassStatus is always in the 4 most-significant bits of status_. return enum_cast<ClassStatus>( static_cast<uint32_t>(GetField32Volatile<kVerifyFlags>(StatusOffset())) >> (32 - 4)); } @@ -1134,6 +1134,10 @@ class MANAGED Class FINAL : public Object { void VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + // Get one of the primitive classes. + static ObjPtr<mirror::Class> GetPrimitiveClass(ObjPtr<mirror::String> name) + REQUIRES_SHARED(Locks::mutator_lock_); + // When class is verified, set the kAccSkipAccessChecks flag on each method. void SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 5757992167..5f00c6e9c7 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -183,7 +183,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( } // Step 4 : Perform argument conversions (if required). - ShadowFrameGetter getter(operands, caller_frame); + ShadowFrameGetter getter(caller_frame, operands); EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength()); if (!PerformConversions<ShadowFrameGetter, EmulatedStackFrameAccessor>( self, caller_type, callee_type, &getter, &setter, num_method_params)) { diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 6e2a07c9e0..7fdaa32751 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -160,7 +160,8 @@ inline bool Object::VerifierInstanceOf(ObjPtr<Class> klass) { template<VerifyObjectFlags kVerifyFlags> inline bool Object::InstanceOf(ObjPtr<Class> klass) { DCHECK(klass != nullptr); - DCHECK(GetClass<kVerifyNone>() != nullptr); + DCHECK(GetClass<kVerifyNone>() != nullptr) + << "this=" << std::hex << reinterpret_cast<uintptr_t>(this) << std::dec; return klass->IsAssignableFrom(GetClass<kVerifyFlags>()); } @@ -935,6 +936,195 @@ inline bool Object::CasFieldWeakReleaseObjectWithoutWriteBarrier( return success; } +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline ObjPtr<Object> Object::CompareAndExchangeFieldObject(MemberOffset field_offset, + ObjPtr<Object> old_value, + ObjPtr<Object> new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + if (kVerifyFlags & kVerifyWrites) { + VerifyObject(new_value); + } + if (kVerifyFlags & kVerifyReads) { + VerifyObject(old_value); + } + uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value)); + uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value)); + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); + bool success = atomic_addr->CompareAndExchangeStrongSequentiallyConsistent(&old_ref, new_ref); + ObjPtr<Object> witness_value(PtrCompression<kPoisonHeapReferences, Object>::Decompress(old_ref)); + if (kIsDebugBuild) { + // Ensure caller has done read barrier on the reference field so it's in the to-space. + ReadBarrier::AssertToSpaceInvariant(witness_value.Ptr()); + } + if (kTransactionActive && success) { + Runtime::Current()->RecordWriteFieldReference(this, field_offset, witness_value, true); + } + if (kVerifyFlags & kVerifyReads) { + VerifyObject(witness_value); + } + return witness_value; +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline ObjPtr<Object> Object::ExchangeFieldObject(MemberOffset field_offset, + ObjPtr<Object> new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + if (kVerifyFlags & kVerifyWrites) { + VerifyObject(new_value); + } + uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value)); + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); + uint32_t old_ref = atomic_addr->ExchangeSequentiallyConsistent(new_ref); + ObjPtr<Object> old_value(PtrCompression<kPoisonHeapReferences, Object>::Decompress(old_ref)); + if (kIsDebugBuild) { + // Ensure caller has done read barrier on the reference field so it's in the to-space. + ReadBarrier::AssertToSpaceInvariant(old_value.Ptr()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); + } + if (kVerifyFlags & kVerifyReads) { + VerifyObject(old_value); + } + return old_value; +} + +template<typename T, VerifyObjectFlags kVerifyFlags> +inline void Object::GetPrimitiveFieldViaAccessor(MemberOffset field_offset, Accessor<T>* accessor) { + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + T* addr = reinterpret_cast<T*>(raw_addr); + accessor->Access(addr); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline void Object::UpdateFieldBooleanViaAccessor(MemberOffset field_offset, + Accessor<uint8_t>* accessor) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + static const bool kIsVolatile = true; + uint8_t old_value = GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset); + Runtime::Current()->RecordWriteFieldBoolean(this, field_offset, old_value, kIsVolatile); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + uint8_t* addr = raw_addr; + accessor->Access(addr); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline void Object::UpdateFieldByteViaAccessor(MemberOffset field_offset, + Accessor<int8_t>* accessor) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + static const bool kIsVolatile = true; + int8_t old_value = GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset); + Runtime::Current()->RecordWriteFieldByte(this, field_offset, old_value, kIsVolatile); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + int8_t* addr = reinterpret_cast<int8_t*>(raw_addr); + accessor->Access(addr); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline void Object::UpdateFieldCharViaAccessor(MemberOffset field_offset, + Accessor<uint16_t>* accessor) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + static const bool kIsVolatile = true; + uint16_t old_value = GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset); + Runtime::Current()->RecordWriteFieldChar(this, field_offset, old_value, kIsVolatile); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + uint16_t* addr = reinterpret_cast<uint16_t*>(raw_addr); + accessor->Access(addr); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline void Object::UpdateFieldShortViaAccessor(MemberOffset field_offset, + Accessor<int16_t>* accessor) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + static const bool kIsVolatile = true; + int16_t old_value = GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset); + Runtime::Current()->RecordWriteFieldShort(this, field_offset, old_value, kIsVolatile); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + int16_t* addr = reinterpret_cast<int16_t*>(raw_addr); + accessor->Access(addr); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline void Object::UpdateField32ViaAccessor(MemberOffset field_offset, + Accessor<int32_t>* accessor) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + static const bool kIsVolatile = true; + int32_t old_value = GetField32<kVerifyFlags, kIsVolatile>(field_offset); + Runtime::Current()->RecordWriteField32(this, field_offset, old_value, kIsVolatile); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + int32_t* addr = reinterpret_cast<int32_t*>(raw_addr); + accessor->Access(addr); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline void Object::UpdateField64ViaAccessor(MemberOffset field_offset, + Accessor<int64_t>* accessor) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + static const bool kIsVolatile = true; + int64_t old_value = GetField64<kVerifyFlags, kIsVolatile>(field_offset); + Runtime::Current()->RecordWriteField64(this, field_offset, old_value, kIsVolatile); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); + int64_t* addr = reinterpret_cast<int64_t*>(raw_addr); + accessor->Access(addr); +} + template<bool kIsStatic, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h index d81fff0a22..126cb04cf1 100644 --- a/runtime/mirror/object-readbarrier-inl.h +++ b/runtime/mirror/object-readbarrier-inl.h @@ -187,7 +187,7 @@ inline bool Object::AtomicSetMarkBit(uint32_t expected_mark_bit, uint32_t mark_b expected_lw = lw; new_lw = lw; new_lw.SetMarkBitState(mark_bit); - // Since this is only set from the mutator, we can use the non release Cas. + // Since this is only set from the mutator, we can use the non-release CAS. } while (!CasLockWordWeakRelaxed(expected_lw, new_lw)); return true; } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 25b86a527d..816ac69b29 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -347,6 +347,21 @@ class MANAGED LOCKABLE Object { ObjPtr<Object> old_value, ObjPtr<Object> new_value) REQUIRES_SHARED(Locks::mutator_lock_); + + template<bool kTransactionActive, + bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ObjPtr<Object> CompareAndExchangeFieldObject(MemberOffset field_offset, + ObjPtr<Object> old_value, + ObjPtr<Object> new_value) + REQUIRES_SHARED(Locks::mutator_lock_); + + template<bool kTransactionActive, + bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ObjPtr<Object> ExchangeFieldObject(MemberOffset field_offset, ObjPtr<Object> new_value) + REQUIRES_SHARED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> @@ -592,6 +607,52 @@ class MANAGED LOCKABLE Object { field_offset, reinterpret_cast64<int64_t>(new_value)); } } + + // Base class for accessors used to describe accesses performed by VarHandle methods. + template <typename T> + class Accessor { + public: + virtual ~Accessor() { + static_assert(std::is_arithmetic<T>::value, "unsupported type"); + } + virtual void Access(T* field_address) = 0; + }; + + // Getter method that exposes the raw address of a primitive value-type field to an Accessor + // instance. This are used by VarHandle accessor methods to read fields with a wider range of + // memory orderings than usually required. + template<typename T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void GetPrimitiveFieldViaAccessor(MemberOffset field_offset, Accessor<T>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Update methods that expose the raw address of a primitive value-type to an Accessor instance + // that will attempt to update the field. These are used by VarHandle accessor methods to + // atomically update fields with a wider range of memory orderings than usually required. + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void UpdateFieldBooleanViaAccessor(MemberOffset field_offset, Accessor<uint8_t>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void UpdateFieldByteViaAccessor(MemberOffset field_offset, Accessor<int8_t>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void UpdateFieldCharViaAccessor(MemberOffset field_offset, Accessor<uint16_t>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void UpdateFieldShortViaAccessor(MemberOffset field_offset, Accessor<int16_t>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void UpdateField32ViaAccessor(MemberOffset field_offset, Accessor<int32_t>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void UpdateField64ViaAccessor(MemberOffset field_offset, Accessor<int64_t>* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); + // TODO fix thread safety analysis broken by the use of template. This should be // REQUIRES_SHARED(Locks::mutator_lock_). template <bool kVisitNativeRoots = true, diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index 3f4a28ce9d..85d06f03fe 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -16,14 +16,23 @@ #include "var_handle.h" +#include "array-inl.h" +#include "art_field-inl.h" #include "class-inl.h" #include "class_linker.h" #include "gc_root-inl.h" +#include "jni_internal.h" +#include "jvalue-inl.h" +#include "method_handles.h" #include "method_type.h" +#include "well_known_classes.h" namespace art { namespace mirror { +static constexpr bool kTransactionActive = true; +static constexpr bool kTransactionInactive = !kTransactionActive; + namespace { struct VarHandleAccessorToAccessModeEntry { @@ -158,32 +167,64 @@ AccessModeTemplate GetAccessModeTemplate(VarHandle::AccessMode access_mode) { } } -// Returns the number of parameters associated with an -// AccessModeTemplate and the supplied coordinate types. -int32_t GetParameterCount(AccessModeTemplate access_mode_template, - ObjPtr<Class> coordinateType0, - ObjPtr<Class> coordinateType1) { - int32_t index = 0; - if (!coordinateType0.IsNull()) { - index++; - if (!coordinateType1.IsNull()) { - index++; - } - } - +int32_t GetNumberOfVarTypeParameters(AccessModeTemplate access_mode_template) { switch (access_mode_template) { case AccessModeTemplate::kGet: - return index; + return 0; case AccessModeTemplate::kSet: case AccessModeTemplate::kGetAndUpdate: - return index + 1; + return 1; case AccessModeTemplate::kCompareAndSet: case AccessModeTemplate::kCompareAndExchange: - return index + 2; + return 2; } UNREACHABLE(); } +// Returns the number of parameters associated with an +// AccessModeTemplate and the supplied coordinate types. +int32_t GetNumberOfParameters(AccessModeTemplate access_mode_template, + ObjPtr<Class> coordinateType0, + ObjPtr<Class> coordinateType1) { + int32_t count = 0; + if (!coordinateType0.IsNull()) { + count++; + if (!coordinateType1.IsNull()) { + count++; + } + } + return count + GetNumberOfVarTypeParameters(access_mode_template); +} + +void ThrowNullPointerExceptionForCoordinate() REQUIRES_SHARED(Locks::mutator_lock_) { + ThrowNullPointerException("Attempt to access memory on a null object"); +} + +bool CheckElementIndex(Primitive::Type type, + int32_t relative_index, + int32_t start, + int32_t limit) REQUIRES_SHARED(Locks::mutator_lock_) { + int64_t index = start + relative_index; + int64_t max_index = limit - Primitive::ComponentSize(type); + if (index < start || index > max_index) { + ThrowIndexOutOfBoundsException(index, limit - start); + return false; + } + return true; +} + +bool CheckElementIndex(Primitive::Type type, int32_t index, int32_t range_limit) + REQUIRES_SHARED(Locks::mutator_lock_) { + return CheckElementIndex(type, index, 0, range_limit); +} + +// Returns true if access_mode only entails a memory read. False if +// access_mode may write to memory. +bool IsReadOnlyAccessMode(VarHandle::AccessMode access_mode) { + AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); + return access_mode_template == AccessModeTemplate::kGet; +} + // Writes the parameter types associated with the AccessModeTemplate // into an array. The parameter types are derived from the specified // variable type and coordinate types. Returns the number of @@ -248,6 +289,1123 @@ ObjectArray<Class>* NewArrayOfClasses(Thread* self, int count) return ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, count); } +// Method to insert a read barrier for accessors to reference fields. +inline void ReadBarrierForVarHandleAccess(ObjPtr<Object> obj, MemberOffset field_offset) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (kUseReadBarrier) { + // We need to ensure that the reference stored in the field is a to-space one before attempting + // the CompareAndSet/CompareAndExchange/Exchange operation otherwise it will fail incorrectly + // if obj is in the process of being moved. + uint8_t* raw_field_addr = reinterpret_cast<uint8_t*>(obj.Ptr()) + field_offset.SizeValue(); + auto field_addr = reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr); + // Note that the read barrier load does NOT need to be volatile. + static constexpr bool kIsVolatile = false; + static constexpr bool kAlwaysUpdateField = true; + ReadBarrier::Barrier<mirror::Object, kIsVolatile, kWithReadBarrier, kAlwaysUpdateField>( + obj.Ptr(), + MemberOffset(field_offset), + field_addr); + } +} + +inline MemberOffset GetMemberOffset(jfieldID field_id) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* const field = jni::DecodeArtField(field_id); + return field->GetOffset(); +} + +// +// Helper methods for storing results from atomic operations into +// JValue instances. +// + +inline void StoreResult(uint8_t value, JValue* result) { + result->SetZ(value); +} + +inline void StoreResult(int8_t value, JValue* result) { + result->SetB(value); +} + +inline void StoreResult(uint16_t value, JValue* result) { + result->SetC(value); +} + +inline void StoreResult(int16_t value, JValue* result) { + result->SetS(value); +} + +inline void StoreResult(int32_t value, JValue* result) { + result->SetI(value); +} + +inline void StoreResult(int64_t value, JValue* result) { + result->SetJ(value); +} + +inline void StoreResult(float value, JValue* result) { + result->SetF(value); +} + +inline void StoreResult(double value, JValue* result) { + result->SetD(value); +} + +inline void StoreResult(ObjPtr<Object> value, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + result->SetL(value); +} + +// +// Helper class for byte-swapping value that has been stored in a JValue. +// + +template <typename T> +class JValueByteSwapper FINAL { + public: + static void ByteSwap(JValue* value); + static void MaybeByteSwap(bool byte_swap, JValue* value) { + if (byte_swap) { + ByteSwap(value); + } + } +}; + +template <> +void JValueByteSwapper<uint16_t>::ByteSwap(JValue* value) { + value->SetC(BSWAP(value->GetC())); +} + +template <> +void JValueByteSwapper<int16_t>::ByteSwap(JValue* value) { + value->SetS(BSWAP(value->GetS())); +} + +template <> +void JValueByteSwapper<int32_t>::ByteSwap(JValue* value) { + value->SetI(BSWAP(value->GetI())); +} + +template <> +void JValueByteSwapper<int64_t>::ByteSwap(JValue* value) { + value->SetJ(BSWAP(value->GetJ())); +} + +// +// Accessor implementations, shared across all VarHandle types. +// + +template <typename T, std::memory_order MO> +class AtomicGetAccessor : public Object::Accessor<T> { + public: + explicit AtomicGetAccessor(JValue* result) : result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + StoreResult(atom->load(MO), result_); + } + + private: + JValue* result_; +}; + +template <typename T, std::memory_order MO> +class AtomicSetAccessor : public Object::Accessor<T> { + public: + explicit AtomicSetAccessor(T new_value) : new_value_(new_value) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + atom->store(new_value_, MO); + } + + private: + T new_value_; +}; + +template <typename T> using GetAccessor = AtomicGetAccessor<T, std::memory_order_relaxed>; + +template <typename T> using SetAccessor = AtomicSetAccessor<T, std::memory_order_relaxed>; + +template <typename T> +using GetVolatileAccessor = AtomicGetAccessor<T, std::memory_order_seq_cst>; + +template <typename T> +using SetVolatileAccessor = AtomicSetAccessor<T, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MOS, std::memory_order MOF> +class AtomicStrongCompareAndSetAccessor : public Object::Accessor<T> { + public: + AtomicStrongCompareAndSetAccessor(T expected_value, T desired_value, JValue* result) + : expected_value_(expected_value), desired_value_(desired_value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + bool success = atom->compare_exchange_strong(expected_value_, desired_value_, MOS, MOF); + StoreResult(success ? JNI_TRUE : JNI_FALSE, result_); + } + + private: + T expected_value_; + T desired_value_; + JValue* result_; +}; + +template<typename T> +using CompareAndSetAccessor = + AtomicStrongCompareAndSetAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MOS, std::memory_order MOF> +class AtomicStrongCompareAndExchangeAccessor : public Object::Accessor<T> { + public: + AtomicStrongCompareAndExchangeAccessor(T expected_value, T desired_value, JValue* result) + : expected_value_(expected_value), desired_value_(desired_value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + atom->compare_exchange_strong(expected_value_, desired_value_, MOS, MOF); + StoreResult(expected_value_, result_); + } + + private: + T expected_value_; + T desired_value_; + JValue* result_; +}; + +template <typename T> +using CompareAndExchangeAccessor = + AtomicStrongCompareAndExchangeAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MOS, std::memory_order MOF> +class AtomicWeakCompareAndSetAccessor : public Object::Accessor<T> { + public: + AtomicWeakCompareAndSetAccessor(T expected_value, T desired_value, JValue* result) + : expected_value_(expected_value), desired_value_(desired_value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + bool success = atom->compare_exchange_weak(expected_value_, desired_value_, MOS, MOF); + StoreResult(success ? JNI_TRUE : JNI_FALSE, result_); + } + + private: + T expected_value_; + T desired_value_; + JValue* result_; +}; + +template <typename T> +using WeakCompareAndSetAccessor = + AtomicWeakCompareAndSetAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MO> +class AtomicGetAndSetAccessor : public Object::Accessor<T> { + public: + AtomicGetAndSetAccessor(T new_value, JValue* result) : new_value_(new_value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + T old_value = atom->exchange(new_value_, MO); + StoreResult(old_value, result_); + } + + private: + T new_value_; + JValue* result_; +}; + +template <typename T> +using GetAndSetAccessor = AtomicGetAndSetAccessor<T, std::memory_order_seq_cst>; + +template <typename T, bool kIsFloat, std::memory_order MO> +class AtomicGetAndAddOperator { + public: + static T Apply(T* addr, T addend) { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + return atom->fetch_add(addend, MO); + } +}; + +template <typename T, std::memory_order MO> +class AtomicGetAndAddOperator<T, /* kIsFloat */ true, MO> { + public: + static T Apply(T* addr, T addend) { + // c++11 does not have std::atomic<T>::fetch_and_add for floating + // point types, so we effect one with a compare and swap. + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + T old_value = atom->load(std::memory_order_relaxed); + T new_value; + do { + new_value = old_value + addend; + } while (!atom->compare_exchange_weak(old_value, new_value, MO, std::memory_order_relaxed)); + return old_value; + } +}; + +template <typename T, std::memory_order MO> +class AtomicGetAndAddAccessor : public Object::Accessor<T> { + public: + AtomicGetAndAddAccessor(T addend, JValue* result) : addend_(addend), result_(result) {} + + void Access(T* addr) OVERRIDE { + constexpr bool kIsFloatingPoint = std::is_floating_point<T>::value; + T old_value = AtomicGetAndAddOperator<T, kIsFloatingPoint, MO>::Apply(addr, addend_); + StoreResult(old_value, result_); + } + + private: + T addend_; + JValue* result_; +}; + +template <typename T> +using GetAndAddAccessor = AtomicGetAndAddAccessor<T, std::memory_order_seq_cst>; + +// Accessor specifically for memory views where the caller can specify +// the byte-ordering. Addition only works outside of the byte-swapped +// memory view because of the direction of carries. +template <typename T, std::memory_order MO> +class AtomicGetAndAddWithByteSwapAccessor : public Object::Accessor<T> { + public: + AtomicGetAndAddWithByteSwapAccessor(T value, JValue* result) : value_(value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* const atom = reinterpret_cast<std::atomic<T>*>(addr); + T current_value = atom->load(std::memory_order_relaxed); + T sum; + do { + sum = BSWAP(current_value) + value_; + // NB current_value is a pass-by-reference argument in the call to + // atomic<T>::compare_exchange_weak(). + } while (!atom->compare_exchange_weak(current_value, + BSWAP(sum), + MO, + std::memory_order_relaxed)); + StoreResult(BSWAP(current_value), result_); + } + + private: + T value_; + JValue* result_; +}; + +template <typename T> +using GetAndAddWithByteSwapAccessor = + AtomicGetAndAddWithByteSwapAccessor<T, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MO> +class AtomicGetAndBitwiseOrAccessor : public Object::Accessor<T> { + public: + AtomicGetAndBitwiseOrAccessor(T value, JValue* result) : value_(value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + T old_value = atom->fetch_or(value_, MO); + StoreResult(old_value, result_); + } + + private: + T value_; + JValue* result_; +}; + +template <typename T> +using GetAndBitwiseOrAccessor = AtomicGetAndBitwiseOrAccessor<T, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MO> +class AtomicGetAndBitwiseAndAccessor : public Object::Accessor<T> { + public: + AtomicGetAndBitwiseAndAccessor(T value, JValue* result) : value_(value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + T old_value = atom->fetch_and(value_, MO); + StoreResult(old_value, result_); + } + + private: + T value_; + JValue* result_; +}; + +template <typename T> +using GetAndBitwiseAndAccessor = + AtomicGetAndBitwiseAndAccessor<T, std::memory_order_seq_cst>; + +template <typename T, std::memory_order MO> +class AtomicGetAndBitwiseXorAccessor : public Object::Accessor<T> { + public: + AtomicGetAndBitwiseXorAccessor(T value, JValue* result) : value_(value), result_(result) {} + + void Access(T* addr) OVERRIDE { + std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); + T old_value = atom->fetch_xor(value_, MO); + StoreResult(old_value, result_); + } + + private: + T value_; + JValue* result_; +}; + +template <typename T> +using GetAndBitwiseXorAccessor = AtomicGetAndBitwiseXorAccessor<T, std::memory_order_seq_cst>; + +// +// Unreachable access modes. +// + +NO_RETURN void UnreachableAccessMode(const char* access_mode, const char* type_name) { + LOG(FATAL) << "Unreachable access mode :" << access_mode << " for type " << type_name; + UNREACHABLE(); +} + +#define UNREACHABLE_ACCESS_MODE(ACCESS_MODE, TYPE) \ +template<> void ACCESS_MODE ## Accessor<TYPE>::Access(TYPE*) { \ + UnreachableAccessMode(#ACCESS_MODE, #TYPE); \ +} + +// The boolean primitive type is not numeric (boolean == std::uint8_t). +UNREACHABLE_ACCESS_MODE(GetAndAdd, uint8_t) + +// The floating point types do not support bitwise operations. +UNREACHABLE_ACCESS_MODE(GetAndBitwiseOr, float) +UNREACHABLE_ACCESS_MODE(GetAndBitwiseAnd, float) +UNREACHABLE_ACCESS_MODE(GetAndBitwiseXor, float) +UNREACHABLE_ACCESS_MODE(GetAndBitwiseOr, double) +UNREACHABLE_ACCESS_MODE(GetAndBitwiseAnd, double) +UNREACHABLE_ACCESS_MODE(GetAndBitwiseXor, double) + +// A helper class for object field accesses for floats and +// doubles. The object interface deals with Field32 and Field64. The +// former is used for both integers and floats, the latter for longs +// and doubles. This class provides the necessary coercion. +template <typename T, typename U> +class TypeAdaptorAccessor : public Object::Accessor<T> { + public: + explicit TypeAdaptorAccessor(Object::Accessor<U>* inner_accessor) + : inner_accessor_(inner_accessor) {} + + void Access(T* addr) OVERRIDE { + static_assert(sizeof(T) == sizeof(U), "bad conversion"); + inner_accessor_->Access(reinterpret_cast<U*>(addr)); + } + + private: + Object::Accessor<U>* inner_accessor_; +}; + +template <typename T> +class FieldAccessViaAccessor { + public: + typedef Object::Accessor<T> Accessor; + + // Apply an Accessor to get a field in an object. + static void Get(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + obj->GetPrimitiveFieldViaAccessor(field_offset, accessor); + } + + // Apply an Accessor to update a field in an object. + static void Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_); +}; + +template <> +inline void FieldAccessViaAccessor<float>::Get(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + TypeAdaptorAccessor<int32_t, float> float_to_int_accessor(accessor); + obj->GetPrimitiveFieldViaAccessor(field_offset, &float_to_int_accessor); +} + +template <> +inline void FieldAccessViaAccessor<double>::Get(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + TypeAdaptorAccessor<int64_t, double> double_to_int_accessor(accessor); + obj->GetPrimitiveFieldViaAccessor(field_offset, &double_to_int_accessor); +} + +template <> +void FieldAccessViaAccessor<uint8_t>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateFieldBooleanViaAccessor<kTransactionActive>(field_offset, accessor); + } else { + obj->UpdateFieldBooleanViaAccessor<kTransactionInactive>(field_offset, accessor); + } +} + +template <> +void FieldAccessViaAccessor<int8_t>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateFieldByteViaAccessor<kTransactionActive>(field_offset, accessor); + } else { + obj->UpdateFieldByteViaAccessor<kTransactionInactive>(field_offset, accessor); + } +} + +template <> +void FieldAccessViaAccessor<uint16_t>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateFieldCharViaAccessor<kTransactionActive>(field_offset, accessor); + } else { + obj->UpdateFieldCharViaAccessor<kTransactionInactive>(field_offset, accessor); + } +} + +template <> +void FieldAccessViaAccessor<int16_t>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateFieldShortViaAccessor<kTransactionActive>(field_offset, accessor); + } else { + obj->UpdateFieldShortViaAccessor<kTransactionInactive>(field_offset, accessor); + } +} + +template <> +void FieldAccessViaAccessor<int32_t>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateField32ViaAccessor<kTransactionActive>(field_offset, accessor); + } else { + obj->UpdateField32ViaAccessor<kTransactionInactive>(field_offset, accessor); + } +} + +template <> +void FieldAccessViaAccessor<int64_t>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateField64ViaAccessor<kTransactionActive>(field_offset, accessor); + } else { + obj->UpdateField64ViaAccessor<kTransactionInactive>(field_offset, accessor); + } +} + +template <> +void FieldAccessViaAccessor<float>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + TypeAdaptorAccessor<int32_t, float> float_to_int_accessor(accessor); + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateField32ViaAccessor<kTransactionActive>(field_offset, &float_to_int_accessor); + } else { + obj->UpdateField32ViaAccessor<kTransactionInactive>(field_offset, &float_to_int_accessor); + } +} + +template <> +void FieldAccessViaAccessor<double>::Update(ObjPtr<Object> obj, + MemberOffset field_offset, + Accessor* accessor) + REQUIRES_SHARED(Locks::mutator_lock_) { + TypeAdaptorAccessor<int64_t, double> double_to_int_accessor(accessor); + if (Runtime::Current()->IsActiveTransaction()) { + obj->UpdateField64ViaAccessor<kTransactionActive>(field_offset, &double_to_int_accessor); + } else { + obj->UpdateField64ViaAccessor<kTransactionInactive>(field_offset, &double_to_int_accessor); + } +} + +// Helper class that gets values from a shadow frame with appropriate type coercion. +template <typename T> +class ValueGetter { + public: + static T Get(ShadowFrameGetter* getter) REQUIRES_SHARED(Locks::mutator_lock_) { + static_assert(sizeof(T) <= sizeof(uint32_t), "Bad size"); + uint32_t raw_value = getter->Get(); + return static_cast<T>(raw_value); + } +}; + +template <> +int64_t ValueGetter<int64_t>::Get(ShadowFrameGetter* getter) { + return getter->GetLong(); +} + +template <> +float ValueGetter<float>::Get(ShadowFrameGetter* getter) { + uint32_t raw_value = getter->Get(); + return *reinterpret_cast<float*>(&raw_value); +} + +template <> +double ValueGetter<double>::Get(ShadowFrameGetter* getter) { + int64_t raw_value = getter->GetLong(); + return *reinterpret_cast<double*>(&raw_value); +} + +template <> +ObjPtr<Object> ValueGetter<ObjPtr<Object>>::Get(ShadowFrameGetter* getter) { + return getter->GetReference(); +} + +// Class for accessing fields of Object instances +template <typename T> +class FieldAccessor { + public: + static bool Dispatch(VarHandle::AccessMode access_mode, + ObjPtr<Object> obj, + MemberOffset field_offset, + ShadowFrameGetter* getter, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); +}; + +// Dispatch implementation for primitive fields. +template <typename T> +bool FieldAccessor<T>::Dispatch(VarHandle::AccessMode access_mode, + ObjPtr<Object> obj, + MemberOffset field_offset, + ShadowFrameGetter* getter, + JValue* result) { + switch (access_mode) { + case VarHandle::AccessMode::kGet: { + GetAccessor<T> accessor(result); + FieldAccessViaAccessor<T>::Get(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kSet: { + T new_value = ValueGetter<T>::Get(getter); + SetAccessor<T> accessor(new_value); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kGetAcquire: + case VarHandle::AccessMode::kGetOpaque: + case VarHandle::AccessMode::kGetVolatile: { + GetVolatileAccessor<T> accessor(result); + FieldAccessViaAccessor<T>::Get(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kSetOpaque: + case VarHandle::AccessMode::kSetRelease: + case VarHandle::AccessMode::kSetVolatile: { + T new_value = ValueGetter<T>::Get(getter); + SetVolatileAccessor<T> accessor(new_value); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kCompareAndSet: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + CompareAndSetAccessor<T> accessor(expected_value, desired_value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kCompareAndExchange: + case VarHandle::AccessMode::kCompareAndExchangeAcquire: + case VarHandle::AccessMode::kCompareAndExchangeRelease: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kWeakCompareAndSet: + case VarHandle::AccessMode::kWeakCompareAndSetAcquire: + case VarHandle::AccessMode::kWeakCompareAndSetPlain: + case VarHandle::AccessMode::kWeakCompareAndSetRelease: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kGetAndSet: + case VarHandle::AccessMode::kGetAndSetAcquire: + case VarHandle::AccessMode::kGetAndSetRelease: { + T new_value = ValueGetter<T>::Get(getter); + GetAndSetAccessor<T> accessor(new_value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kGetAndAdd: + case VarHandle::AccessMode::kGetAndAddAcquire: + case VarHandle::AccessMode::kGetAndAddRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndAddAccessor<T> accessor(value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseOr: + case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: + case VarHandle::AccessMode::kGetAndBitwiseOrRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndBitwiseOrAccessor<T> accessor(value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseAnd: + case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: + case VarHandle::AccessMode::kGetAndBitwiseAndRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndBitwiseAndAccessor<T> accessor(value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseXor: + case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: + case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndBitwiseXorAccessor<T> accessor(value, result); + FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); + break; + } + } + return true; +} + +// Dispatch implementation for reference fields. +template <> +bool FieldAccessor<ObjPtr<Object>>::Dispatch(VarHandle::AccessMode access_mode, + ObjPtr<Object> obj, + MemberOffset field_offset, + ShadowFrameGetter* getter, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + // To keep things simple, use the minimum strongest existing + // field accessor for Object fields. This may be the most + // straightforward strategy in general for the interpreter. + switch (access_mode) { + case VarHandle::AccessMode::kGet: { + StoreResult(obj->GetFieldObject<Object>(field_offset), result); + break; + } + case VarHandle::AccessMode::kSet: { + ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter); + if (Runtime::Current()->IsActiveTransaction()) { + obj->SetFieldObject<kTransactionActive>(field_offset, new_value); + } else { + obj->SetFieldObject<kTransactionInactive>(field_offset, new_value); + } + break; + } + case VarHandle::AccessMode::kGetAcquire: + case VarHandle::AccessMode::kGetOpaque: + case VarHandle::AccessMode::kGetVolatile: { + StoreResult(obj->GetFieldObjectVolatile<Object>(field_offset), result); + break; + } + case VarHandle::AccessMode::kSetOpaque: + case VarHandle::AccessMode::kSetRelease: + case VarHandle::AccessMode::kSetVolatile: { + ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter); + if (Runtime::Current()->IsActiveTransaction()) { + obj->SetFieldObjectVolatile<kTransactionActive>(field_offset, new_value); + } else { + obj->SetFieldObjectVolatile<kTransactionInactive>(field_offset, new_value); + } + break; + } + case VarHandle::AccessMode::kCompareAndSet: { + ReadBarrierForVarHandleAccess(obj, field_offset); + ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter); + ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter); + bool cas_result; + if (Runtime::Current()->IsActiveTransaction()) { + cas_result = obj->CasFieldStrongSequentiallyConsistentObject<kTransactionActive>( + field_offset, + expected_value, + desired_value); + } else { + cas_result = obj->CasFieldStrongSequentiallyConsistentObject<kTransactionInactive>( + field_offset, + expected_value, + desired_value); + } + StoreResult(cas_result, result); + break; + } + case VarHandle::AccessMode::kWeakCompareAndSet: + case VarHandle::AccessMode::kWeakCompareAndSetAcquire: + case VarHandle::AccessMode::kWeakCompareAndSetPlain: + case VarHandle::AccessMode::kWeakCompareAndSetRelease: { + ReadBarrierForVarHandleAccess(obj, field_offset); + ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter); + ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter); + bool cas_result; + if (Runtime::Current()->IsActiveTransaction()) { + cas_result = obj->CasFieldWeakSequentiallyConsistentObject<kTransactionActive>( + field_offset, + expected_value, + desired_value); + } else { + cas_result = obj->CasFieldWeakSequentiallyConsistentObject<kTransactionInactive>( + field_offset, + expected_value, + desired_value); + } + StoreResult(cas_result, result); + break; + } + case VarHandle::AccessMode::kCompareAndExchange: + case VarHandle::AccessMode::kCompareAndExchangeAcquire: + case VarHandle::AccessMode::kCompareAndExchangeRelease: { + ReadBarrierForVarHandleAccess(obj, field_offset); + ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter); + ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter); + ObjPtr<Object> witness_value; + if (Runtime::Current()->IsActiveTransaction()) { + witness_value = obj->CompareAndExchangeFieldObject<kTransactionActive>( + field_offset, + expected_value, + desired_value); + } else { + witness_value = obj->CompareAndExchangeFieldObject<kTransactionInactive>( + field_offset, + expected_value, + desired_value); + } + StoreResult(witness_value, result); + break; + } + case VarHandle::AccessMode::kGetAndSet: + case VarHandle::AccessMode::kGetAndSetAcquire: + case VarHandle::AccessMode::kGetAndSetRelease: { + ReadBarrierForVarHandleAccess(obj, field_offset); + ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter); + ObjPtr<Object> old_value; + if (Runtime::Current()->IsActiveTransaction()) { + old_value = obj->ExchangeFieldObject<kTransactionActive>(field_offset, new_value); + } else { + old_value = obj->ExchangeFieldObject<kTransactionInactive>(field_offset, new_value); + } + StoreResult(old_value, result); + break; + } + case VarHandle::AccessMode::kGetAndAdd: + case VarHandle::AccessMode::kGetAndAddAcquire: + case VarHandle::AccessMode::kGetAndAddRelease: + case VarHandle::AccessMode::kGetAndBitwiseOr: + case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: + case VarHandle::AccessMode::kGetAndBitwiseOrRelease: + case VarHandle::AccessMode::kGetAndBitwiseAnd: + case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: + case VarHandle::AccessMode::kGetAndBitwiseAndRelease: + case VarHandle::AccessMode::kGetAndBitwiseXor: + case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: + case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { + size_t index = static_cast<size_t>(access_mode); + const char* access_mode_name = kAccessorToAccessMode[index].method_name; + UnreachableAccessMode(access_mode_name, "Object"); + } + } + return true; +} + +// Class for accessing primitive array elements. +template <typename T> +class PrimitiveArrayElementAccessor { + public: + static T* GetElementAddress(ObjPtr<Array> target_array, int target_element) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto primitive_array = ObjPtr<PrimitiveArray<T>>::DownCast(target_array); + DCHECK(primitive_array->CheckIsValidIndex(target_element)); + return &primitive_array->GetData()[target_element]; + } + + static bool Dispatch(VarHandle::AccessMode access_mode, + ObjPtr<Array> target_array, + int target_element, + ShadowFrameGetter* getter, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + T* element_address = GetElementAddress(target_array, target_element); + switch (access_mode) { + case VarHandle::AccessMode::kGet: { + GetAccessor<T> accessor(result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kSet: { + T new_value = ValueGetter<T>::Get(getter); + SetAccessor<T> accessor(new_value); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAcquire: + case VarHandle::AccessMode::kGetOpaque: + case VarHandle::AccessMode::kGetVolatile: { + GetVolatileAccessor<T> accessor(result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kSetOpaque: + case VarHandle::AccessMode::kSetRelease: + case VarHandle::AccessMode::kSetVolatile: { + T new_value = ValueGetter<T>::Get(getter); + SetVolatileAccessor<T> accessor(new_value); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kCompareAndSet: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + CompareAndSetAccessor<T> accessor(expected_value, desired_value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kCompareAndExchange: + case VarHandle::AccessMode::kCompareAndExchangeAcquire: + case VarHandle::AccessMode::kCompareAndExchangeRelease: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kWeakCompareAndSet: + case VarHandle::AccessMode::kWeakCompareAndSetAcquire: + case VarHandle::AccessMode::kWeakCompareAndSetPlain: + case VarHandle::AccessMode::kWeakCompareAndSetRelease: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAndSet: + case VarHandle::AccessMode::kGetAndSetAcquire: + case VarHandle::AccessMode::kGetAndSetRelease: { + T new_value = ValueGetter<T>::Get(getter); + GetAndSetAccessor<T> accessor(new_value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAndAdd: + case VarHandle::AccessMode::kGetAndAddAcquire: + case VarHandle::AccessMode::kGetAndAddRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndAddAccessor<T> accessor(value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseOr: + case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: + case VarHandle::AccessMode::kGetAndBitwiseOrRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndBitwiseOrAccessor<T> accessor(value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseAnd: + case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: + case VarHandle::AccessMode::kGetAndBitwiseAndRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndBitwiseAndAccessor<T> accessor(value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseXor: + case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: + case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { + T value = ValueGetter<T>::Get(getter); + GetAndBitwiseXorAccessor<T> accessor(value, result); + accessor.Access(element_address); + break; + } + } + return true; + } +}; + +// Class for accessing primitive array elements. +template <typename T> +class ByteArrayViewAccessor { + public: + static inline bool IsAccessAligned(int8_t* data, int data_index) { + static_assert(IsPowerOfTwo(sizeof(T)), "unexpected size"); + static_assert(std::is_arithmetic<T>::value, "unexpected type"); + uintptr_t alignment_mask = sizeof(T) - 1; + uintptr_t address = reinterpret_cast<uintptr_t>(data + data_index); + return (address & alignment_mask) == 0; + } + + static inline void MaybeByteSwap(bool byte_swap, T* const value) { + if (byte_swap) { + *value = BSWAP(*value); + } + } + + static bool Dispatch(const VarHandle::AccessMode access_mode, + int8_t* const data, + const int data_index, + const bool byte_swap, + ShadowFrameGetter* const getter, + JValue* const result) + REQUIRES_SHARED(Locks::mutator_lock_) { + const bool is_aligned = IsAccessAligned(data, data_index); + if (!is_aligned) { + switch (access_mode) { + case VarHandle::AccessMode::kGet: { + T value; + memcpy(&value, data + data_index, sizeof(T)); + MaybeByteSwap(byte_swap, &value); + StoreResult(value, result); + return true; + } + case VarHandle::AccessMode::kSet: { + T new_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &new_value); + memcpy(data + data_index, &new_value, sizeof(T)); + return true; + } + default: + // No other access modes support unaligned access. + ThrowIllegalStateException("Unaligned access not supported"); + return false; + } + } + + T* const element_address = reinterpret_cast<T*>(data + data_index); + CHECK(IsAccessAligned(reinterpret_cast<int8_t*>(element_address), 0)); + switch (access_mode) { + case VarHandle::AccessMode::kGet: { + GetAccessor<T> accessor(result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + case VarHandle::AccessMode::kSet: { + T new_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &new_value); + SetAccessor<T> accessor(new_value); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAcquire: + case VarHandle::AccessMode::kGetOpaque: + case VarHandle::AccessMode::kGetVolatile: { + GetVolatileAccessor<T> accessor(result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + case VarHandle::AccessMode::kSetOpaque: + case VarHandle::AccessMode::kSetRelease: + case VarHandle::AccessMode::kSetVolatile: { + T new_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &new_value); + SetVolatileAccessor<T> accessor(new_value); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kCompareAndSet: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &expected_value); + MaybeByteSwap(byte_swap, &desired_value); + CompareAndSetAccessor<T> accessor(expected_value, desired_value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kCompareAndExchange: + case VarHandle::AccessMode::kCompareAndExchangeAcquire: + case VarHandle::AccessMode::kCompareAndExchangeRelease: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &expected_value); + MaybeByteSwap(byte_swap, &desired_value); + CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + case VarHandle::AccessMode::kWeakCompareAndSet: + case VarHandle::AccessMode::kWeakCompareAndSetAcquire: + case VarHandle::AccessMode::kWeakCompareAndSetPlain: + case VarHandle::AccessMode::kWeakCompareAndSetRelease: { + T expected_value = ValueGetter<T>::Get(getter); + T desired_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &expected_value); + MaybeByteSwap(byte_swap, &desired_value); + WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result); + accessor.Access(element_address); + break; + } + case VarHandle::AccessMode::kGetAndSet: + case VarHandle::AccessMode::kGetAndSetAcquire: + case VarHandle::AccessMode::kGetAndSetRelease: { + T new_value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &new_value); + GetAndSetAccessor<T> accessor(new_value, result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + case VarHandle::AccessMode::kGetAndAdd: + case VarHandle::AccessMode::kGetAndAddAcquire: + case VarHandle::AccessMode::kGetAndAddRelease: { + T value = ValueGetter<T>::Get(getter); + if (byte_swap) { + GetAndAddWithByteSwapAccessor<T> accessor(value, result); + accessor.Access(element_address); + } else { + GetAndAddAccessor<T> accessor(value, result); + accessor.Access(element_address); + } + break; + } + case VarHandle::AccessMode::kGetAndBitwiseOr: + case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: + case VarHandle::AccessMode::kGetAndBitwiseOrRelease: { + T value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &value); + GetAndBitwiseOrAccessor<T> accessor(value, result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseAnd: + case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: + case VarHandle::AccessMode::kGetAndBitwiseAndRelease: { + T value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &value); + GetAndBitwiseAndAccessor<T> accessor(value, result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + case VarHandle::AccessMode::kGetAndBitwiseXor: + case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: + case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { + T value = ValueGetter<T>::Get(getter); + MaybeByteSwap(byte_swap, &value); + GetAndBitwiseXorAccessor<T> accessor(value, result); + accessor.Access(element_address); + JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); + break; + } + } + return true; + } +}; + } // namespace Class* VarHandle::GetVarType() { @@ -267,35 +1425,38 @@ int32_t VarHandle::GetAccessModesBitMask() { } bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) { - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - + StackHandleScope<3> hs(Thread::Current()); + Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType())); + Handle<VarHandle> vh(hs.NewHandle(this)); + Handle<Class> var_type(hs.NewHandle(vh->GetVarType())); AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); - // Check return types first. - ObjPtr<Class> var_type = GetVarType(); - ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type); - ObjPtr<Class> void_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V'); - ObjPtr<Class> mt_rtype = method_type->GetRType(); - - // If the mt_rtype is void, the result of the operation will be discarded (okay). - if (mt_rtype != void_type && mt_rtype != vh_rtype) { - return false; + + // Check return type first. + if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) { + // The result of the operation will be discarded. The return type + // of the VarHandle is immaterial. + } else { + ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get())); + if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) { + return false; + } } // Check the number of parameters matches. ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters]; const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, access_mode_template, - var_type, + var_type.Get(), GetCoordinateType0(), GetCoordinateType1()); if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) { return false; } - // Check the parameter types match. + // Check the parameter types are compatible. ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes(); for (int32_t i = 0; i < vh_ptypes_count; ++i) { - if (mt_ptypes->Get(i) != vh_ptypes[i].Ptr()) { + if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) { return false; } } @@ -311,8 +1472,9 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, StackHandleScope<3> hs(self); Handle<VarHandle> vh = hs.NewHandle(var_handle); Handle<Class> rtype = hs.NewHandle(GetReturnType(access_mode_template, vh->GetVarType())); - const int32_t ptypes_count = - GetParameterCount(access_mode_template, vh->GetCoordinateType0(), vh->GetCoordinateType1()); + const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, + vh->GetCoordinateType0(), + vh->GetCoordinateType1()); Handle<ObjectArray<Class>> ptypes = hs.NewHandle(NewArrayOfClasses(self, ptypes_count)); if (ptypes == nullptr) { return nullptr; @@ -334,6 +1496,29 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode acces return GetMethodTypeForAccessMode(self, this, access_mode); } +bool VarHandle::Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) { + Class* klass = GetClass(); + if (klass == FieldVarHandle::StaticClass()) { + auto vh = reinterpret_cast<FieldVarHandle*>(this); + return vh->Access(access_mode, shadow_frame, operands, result); + } else if (klass == ArrayElementVarHandle::StaticClass()) { + auto vh = reinterpret_cast<ArrayElementVarHandle*>(this); + return vh->Access(access_mode, shadow_frame, operands, result); + } else if (klass == ByteArrayViewVarHandle::StaticClass()) { + auto vh = reinterpret_cast<ByteArrayViewVarHandle*>(this); + return vh->Access(access_mode, shadow_frame, operands, result); + } else if (klass == ByteBufferViewVarHandle::StaticClass()) { + auto vh = reinterpret_cast<ByteBufferViewVarHandle*>(this); + return vh->Access(access_mode, shadow_frame, operands, result); + } else { + LOG(FATAL) << "Unknown varhandle kind"; + UNREACHABLE(); + } +} + const char* VarHandle::GetReturnTypeDescriptor(const char* accessor_name) { AccessMode access_mode; if (!GetAccessModeByMethodName(accessor_name, &access_mode)) { @@ -369,6 +1554,10 @@ bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* a return true; } +Class* VarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { + return static_class_.Read(); +} + void VarHandle::SetClass(Class* klass) { CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; CHECK(klass != nullptr); @@ -391,6 +1580,57 @@ ArtField* FieldVarHandle::GetField() { return reinterpret_cast<ArtField*>(opaque_field); } +bool FieldVarHandle::Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) { + ShadowFrameGetter getter(*shadow_frame, operands); + ArtField* field = GetField(); + ObjPtr<Object> obj; + if (field->IsStatic()) { + DCHECK_LE(operands->GetNumberOfOperands(), + 2u * (Primitive::Is64BitType(GetVarType()->GetPrimitiveType()) ? 2u : 1u)); + obj = field->GetDeclaringClass(); + } else { + DCHECK_GE(operands->GetNumberOfOperands(), 1u); + DCHECK_LE(operands->GetNumberOfOperands(), + 1u + 2u * (Primitive::Is64BitType(GetVarType()->GetPrimitiveType()) ? 2u : 1u)); + obj = getter.GetReference(); + if (obj.IsNull()) { + ThrowNullPointerExceptionForCoordinate(); + return false; + } + } + DCHECK(!obj.IsNull()); + + const MemberOffset offset = field->GetOffset(); + const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); + switch (primitive_type) { + case Primitive::Type::kPrimNot: + return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimBoolean: + return FieldAccessor<uint8_t>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimByte: + return FieldAccessor<int8_t>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimChar: + return FieldAccessor<uint16_t>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimShort: + return FieldAccessor<int16_t>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimInt: + return FieldAccessor<int32_t>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimFloat: + return FieldAccessor<float>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimLong: + return FieldAccessor<int64_t>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimDouble: + return FieldAccessor<double>::Dispatch(access_mode, obj, offset, &getter, result); + case Primitive::kPrimVoid: + break; + } + LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; + UNREACHABLE(); +} + Class* FieldVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { return static_class_.Read(); } @@ -412,6 +1652,94 @@ void FieldVarHandle::VisitRoots(RootVisitor* visitor) { GcRoot<Class> FieldVarHandle::static_class_; +bool ArrayElementVarHandle::Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) { + ShadowFrameGetter getter(*shadow_frame, operands); + + // The target array is the first co-ordinate type preceeding var type arguments. + ObjPtr<Object> raw_array(getter.GetReference()); + if (raw_array == nullptr) { + ThrowNullPointerExceptionForCoordinate(); + return false; + } + + ObjPtr<Array> target_array(raw_array->AsArray()); + + // The target array element is the second co-ordinate type preceeding var type arguments. + const int target_element = getter.Get(); + if (!target_array->CheckIsValidIndex(target_element)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return false; + } + + const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); + switch (primitive_type) { + case Primitive::Type::kPrimNot: { + MemberOffset target_element_offset = + target_array->AsObjectArray<Object>()->OffsetOfElement(target_element); + return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode, + target_array, + target_element_offset, + &getter, + result); + } + case Primitive::Type::kPrimBoolean: + return PrimitiveArrayElementAccessor<uint8_t>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimByte: + return PrimitiveArrayElementAccessor<int8_t>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimChar: + return PrimitiveArrayElementAccessor<uint16_t>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimShort: + return PrimitiveArrayElementAccessor<int16_t>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimInt: + return PrimitiveArrayElementAccessor<int32_t>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimLong: + return PrimitiveArrayElementAccessor<int64_t>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimFloat: + return PrimitiveArrayElementAccessor<float>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimDouble: + return PrimitiveArrayElementAccessor<double>::Dispatch(access_mode, + target_array, + target_element, + &getter, + result); + case Primitive::Type::kPrimVoid: + break; + } + LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; + UNREACHABLE(); +} + Class* ArrayElementVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { return static_class_.Read(); } @@ -437,6 +1765,90 @@ bool ByteArrayViewVarHandle::GetNativeByteOrder() { return GetFieldBoolean(NativeByteOrderOffset()); } +bool ByteArrayViewVarHandle::Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) { + ShadowFrameGetter getter(*shadow_frame, operands); + + // The byte array is the first co-ordinate type preceeding var type arguments. + ObjPtr<Object> raw_byte_array(getter.GetReference()); + if (raw_byte_array == nullptr) { + ThrowNullPointerExceptionForCoordinate(); + return false; + } + + ObjPtr<ByteArray> byte_array(raw_byte_array->AsByteArray()); + + // The offset in the byte array element is the second co-ordinate type. + const int32_t data_offset = getter.Get(); + + // Bounds check requested access. + const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); + if (!CheckElementIndex(primitive_type, data_offset, byte_array->GetLength())) { + return false; + } + + int8_t* const data = byte_array->GetData(); + bool byte_swap = !GetNativeByteOrder(); + switch (primitive_type) { + case Primitive::Type::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimVoid: + // These are not supported for byte array views and not instantiable. + break; + case Primitive::kPrimChar: + return ByteArrayViewAccessor<uint16_t>::Dispatch(access_mode, + data, + data_offset, + byte_swap, + &getter, + result); + case Primitive::kPrimShort: + return ByteArrayViewAccessor<int16_t>::Dispatch(access_mode, + data, + data_offset, + byte_swap, + &getter, + result); + case Primitive::kPrimInt: + return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, + data, + data_offset, + byte_swap, + &getter, + result); + case Primitive::kPrimFloat: + // Treated as a bitwise representation. See javadoc comments for + // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). + return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, + data, + data_offset, + byte_swap, + &getter, + result); + case Primitive::kPrimLong: + return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, + data, + data_offset, + byte_swap, + &getter, + result); + case Primitive::kPrimDouble: + // Treated as a bitwise representation. See javadoc comments for + // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). + return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, + data, + data_offset, + byte_swap, + &getter, + result); + } + LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; + UNREACHABLE(); +} + Class* ByteArrayViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { return static_class_.Read(); } @@ -462,6 +1874,122 @@ bool ByteBufferViewVarHandle::GetNativeByteOrder() { return GetFieldBoolean(NativeByteOrderOffset()); } +bool ByteBufferViewVarHandle::Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) { + ShadowFrameGetter getter(*shadow_frame, operands); + + // The byte buffer is the first co-ordinate argument preceeding var type arguments. + ObjPtr<Object> byte_buffer(getter.GetReference()); + if (byte_buffer == nullptr) { + ThrowNullPointerExceptionForCoordinate(); + return false; + } + + // The byte index for access is the second co-ordinate + // argument. This is relative to the offset field of the ByteBuffer. + const int32_t byte_index = getter.Get(); + + // Check access_mode is compatible with ByteBuffer's read-only property. + bool is_read_only = byte_buffer->GetFieldBoolean( + GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_isReadOnly)); + if (is_read_only && !IsReadOnlyAccessMode(access_mode)) { + ThrowReadOnlyBufferException(); + return false; + } + + // The native_address is only set for ByteBuffer instances backed by native memory. + const int64_t native_address = + byte_buffer->GetField64(GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_address)); + + // Determine offset and limit for accesses. + int32_t byte_buffer_offset; + if (native_address == 0l) { + // Accessing a heap allocated byte buffer. + byte_buffer_offset = byte_buffer->GetField32( + GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_offset)); + } else { + // Accessing direct memory. + byte_buffer_offset = 0; + } + const int32_t byte_buffer_limit = byte_buffer->GetField32( + GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_limit)); + + const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); + if (!CheckElementIndex(primitive_type, byte_index, byte_buffer_offset, byte_buffer_limit)) { + return false; + } + const int32_t checked_offset32 = byte_buffer_offset + byte_index; + + int8_t* data; + if (native_address == 0) { + ObjPtr<ByteArray> heap_byte_array = byte_buffer->GetFieldObject<ByteArray>( + GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_hb)); + data = heap_byte_array->GetData(); + } else { + data = reinterpret_cast<int8_t*>(static_cast<uint32_t>(native_address)); + } + + bool byte_swap = !GetNativeByteOrder(); + switch (primitive_type) { + case Primitive::kPrimChar: + return ByteArrayViewAccessor<uint16_t>::Dispatch(access_mode, + data, + checked_offset32, + byte_swap, + &getter, + result); + case Primitive::kPrimShort: + return ByteArrayViewAccessor<int16_t>::Dispatch(access_mode, + data, + checked_offset32, + byte_swap, + &getter, + result); + case Primitive::kPrimInt: + return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, + data, + checked_offset32, + byte_swap, + &getter, + result); + case Primitive::kPrimFloat: + // Treated as a bitwise representation. See javadoc comments for + // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). + return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, + data, + checked_offset32, + byte_swap, + &getter, + result); + case Primitive::kPrimLong: + return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, + data, + checked_offset32, + byte_swap, + &getter, + result); + case Primitive::kPrimDouble: + // Treated as a bitwise representation. See javadoc comments for + // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). + return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, + data, + checked_offset32, + byte_swap, + &getter, + result); + case Primitive::Type::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimVoid: + // These are not supported for byte array views and not instantiable. + break; + } + LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; + UNREACHABLE(); +} + Class* ByteBufferViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { return static_class_.Read(); } diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index 7b48669bba..6565af7f06 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -18,18 +18,24 @@ #define ART_RUNTIME_MIRROR_VAR_HANDLE_H_ #include "handle.h" +#include "interpreter/shadow_frame.h" #include "gc_root.h" +#include "jvalue.h" #include "object.h" namespace art { template<class T> class Handle; +class InstructionOperands; + struct VarHandleOffsets; struct FieldVarHandleOffsets; struct ArrayElementVarHandleOffsets; struct ByteArrayViewVarHandleOffsets; struct ByteBufferViewVarHandleOffsets; +class ShadowFrameGetter; + namespace mirror { class MethodType; @@ -44,6 +50,10 @@ class MANAGED VarHandle : public Object { // (array, index, old, new). static constexpr int kMaxAccessorParameters = 4; + // The maximum number of VarType parameters a VarHandle accessor + // method can take. + static constexpr size_t kMaxVarTypeParameters = 2; + // Enumeration of the possible access modes. This mirrors the enum // in java.lang.invoke.VarHandle. enum class AccessMode : uint32_t { @@ -101,11 +111,16 @@ class MANAGED VarHandle : public Object { // supported operation so the MethodType can be used when raising a // WrongMethodTypeException exception. MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } + bool Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Gets the variable type that is operated on by this VarHandle instance. + Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_); // Gets the return type descriptor for a named accessor method, // nullptr if accessor_method is not supported. @@ -115,12 +130,13 @@ class MANAGED VarHandle : public Object { // VarHandle access method, such as "setOpaque". Returns false otherwise. static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode); + + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); private: - Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_); Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_); Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_); @@ -163,6 +179,12 @@ class MANAGED VarHandle : public Object { // The corresponding managed class in libart java.lang.invoke.FieldVarHandle. class MANAGED FieldVarHandle : public VarHandle { public: + bool Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + ArtField* GetField() REQUIRES_SHARED(Locks::mutator_lock_); static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); @@ -190,6 +212,12 @@ class MANAGED FieldVarHandle : public VarHandle { // The corresponding managed class in libart java.lang.invoke.ArrayElementVarHandle. class MANAGED ArrayElementVarHandle : public VarHandle { public: + bool Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); @@ -207,6 +235,12 @@ class MANAGED ArrayElementVarHandle : public VarHandle { // The corresponding managed class in libart java.lang.invoke.ByteArrayViewVarHandle. class MANAGED ByteArrayViewVarHandle : public VarHandle { public: + bool Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); @@ -234,10 +268,13 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { // The corresponding managed class in libart java.lang.invoke.ByteBufferViewVarHandle. class MANAGED ByteBufferViewVarHandle : public VarHandle { public: - bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); + bool Access(AccessMode access_mode, + ShadowFrame* shadow_frame, + InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); - static ByteBufferViewVarHandle* Create(Thread* const self, bool native_byte_order) - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); @@ -245,6 +282,21 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); private: + bool AccessHeapBuffer(AccessMode access_mode, + ObjPtr<Object> byte_buffer, + int buffer_offset, + ObjPtr<ByteArray> heap_byte_array, + ShadowFrameGetter* getter, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + + bool AccessFixedMemory(AccessMode access_mode, + ObjPtr<Object> byte_buffer, + int buffer_offset, + ShadowFrameGetter* getter, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + static MemberOffset NativeByteOrderOffset() { return MemberOffset(OFFSETOF_MEMBER(ByteBufferViewVarHandle, native_byte_order_)); } diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index e844fd4436..d9fa07f207 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -327,7 +327,7 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) { EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I"))); EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V"))); EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V"))); } // Check compatibility - "GetAndUpdate" pattern @@ -336,7 +336,7 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) { EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I"))); EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); } // Check synthesized method types match expected forms. @@ -461,8 +461,8 @@ TEST_F(VarHandleTest, StaticFieldVarHandle) { EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I"))); EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)D"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); + EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V"))); } // Check compatibility - "GetAndUpdate" pattern diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 6ea9a7ad62..b49209c4cf 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -549,6 +549,55 @@ static jstring DexFile_getDexFileStatus(JNIEnv* env, return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str()); } +// Return an array specifying the optimization status of the given file. +// The array specification is [compiler_filter, compiler_reason]. +static jobjectArray DexFile_getDexFileOptimizationStatus(JNIEnv* env, + jclass, + jstring javaFilename, + jstring javaInstructionSet) { + ScopedUtfChars filename(env, javaFilename); + if (env->ExceptionCheck()) { + return nullptr; + } + + ScopedUtfChars instruction_set(env, javaInstructionSet); + if (env->ExceptionCheck()) { + return nullptr; + } + + const InstructionSet target_instruction_set = GetInstructionSetFromString( + instruction_set.c_str()); + if (target_instruction_set == InstructionSet::kNone) { + ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str())); + env->ThrowNew(iae.get(), message.c_str()); + return nullptr; + } + + std::string compilation_filter; + std::string compilation_reason; + OatFileAssistant::GetOptimizationStatus( + filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason); + + ScopedLocalRef<jstring> j_compilation_filter(env, env->NewStringUTF(compilation_filter.c_str())); + if (j_compilation_filter.get() == nullptr) { + return nullptr; + } + ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str())); + if (j_compilation_reason.get() == nullptr) { + return nullptr; + } + + // Now create output array and copy the set into it. + jobjectArray result = env->NewObjectArray(2, + WellKnownClasses::java_lang_String, + nullptr); + env->SetObjectArrayElement(result, 0, j_compilation_filter.get()); + env->SetObjectArrayElement(result, 1, j_compilation_reason.get()); + + return result; +} + static jint DexFile_getDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename, @@ -801,7 +850,9 @@ static JNINativeMethod gMethods[] = { "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(DexFile, getDexFileOutputPaths, "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), - NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J") + NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"), + NATIVE_METHOD(DexFile, getDexFileOptimizationStatus, + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;") }; void register_dalvik_system_DexFile(JNIEnv* env) { diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 57a429cf1e..505b745200 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -405,18 +405,15 @@ static void PreloadDexCachesResolveMethod(ObjPtr<mirror::DexCache> dex_cache, ui } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + ObjPtr<mirror::Class> klass = class_linker->LookupResolvedType( method_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } - ArtMethod* method = klass->IsInterface() - ? klass->FindInterfaceMethod(dex_cache, method_idx, kRuntimePointerSize) - : klass->FindClassMethod(dex_cache, method_idx, kRuntimePointerSize); - if (method == nullptr) { - return; - } - dex_cache->SetResolvedMethod(method_idx, method, kRuntimePointerSize); + // Call FindResolvedMethod to populate the dex cache. + class_linker->FindResolvedMethod(klass, dex_cache, /* class_loader */ nullptr, method_idx); } struct DexCacheStats { diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 648a464b6e..12400e26ea 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -173,7 +173,7 @@ enum { DEBUG_JAVA_DEBUGGABLE = 1 << 8, DISABLE_VERIFIER = 1 << 9, ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, - DISABLE_HIDDEN_API_CHECKS = 1 << 11, + ENABLE_HIDDEN_API_CHECKS = 1 << 11, DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12, }; @@ -282,7 +282,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - bool do_hidden_api_checks = true; + bool do_hidden_api_checks = false; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -294,9 +294,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES; } - if ((runtime_flags & DISABLE_HIDDEN_API_CHECKS) != 0) { - do_hidden_api_checks = false; - runtime_flags &= ~DISABLE_HIDDEN_API_CHECKS; + if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) { + do_hidden_api_checks = true; + runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS; } if (runtime_flags != 0) { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 2091a27ffd..e518553292 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -97,7 +97,8 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath); + return hiddenapi::ShouldBlockAccessToMember( + member, self, IsCallerInBootClassPath, hiddenapi::kReflection); } // Returns true if a class member should be discoverable with reflection given @@ -175,6 +176,12 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean return soa.AddLocalReference<jclass>(c.Get()); } +static jclass Class_getPrimitiveClass(JNIEnv* env, jclass, jstring name) { + ScopedFastNativeObjectAccess soa(env); + ObjPtr<mirror::Class> klass = mirror::Class::GetPrimitiveClass(soa.Decode<mirror::String>(name)); + return soa.AddLocalReference<jclass>(klass); +} + static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); @@ -868,6 +875,7 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Class, getInnerClassFlags, "(I)I"), FAST_NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"), FAST_NATIVE_METHOD(Class, getInterfacesInternal, "()[Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Class, getPrimitiveClass, "(Ljava/lang/String;)Ljava/lang/Class;"), FAST_NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"), FAST_NATIVE_METHOD(Class, getPublicDeclaredFields, "()[Ljava/lang/reflect/Field;"), FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"), diff --git a/runtime/native/java_lang_Void.cc b/runtime/native/java_lang_Void.cc deleted file mode 100644 index af83dd1a79..0000000000 --- a/runtime/native/java_lang_Void.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "java_lang_Void.h" - -#include "nativehelper/jni_macros.h" - -#include "class_linker-inl.h" -#include "jni_internal.h" -#include "native_util.h" -#include "runtime.h" -#include "scoped_fast_native_object_access-inl.h" - -namespace art { - -static jclass Void_lookupType(JNIEnv* env, jclass) { - ScopedFastNativeObjectAccess soa(env); - return soa.AddLocalReference<jclass>( - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kPrimitiveVoid)); -} - -static JNINativeMethod gMethods[] = { - FAST_NATIVE_METHOD(Void, lookupType, "()Ljava/lang/Class;"), -}; - -void register_java_lang_Void(JNIEnv* env) { - REGISTER_NATIVE_METHODS("java/lang/Void"); -} - -} // namespace art diff --git a/runtime/native/java_lang_Void.h b/runtime/native/java_lang_Void.h deleted file mode 100644 index 8777d8068c..0000000000 --- a/runtime/native/java_lang_Void.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_ -#define ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_ - -#include <jni.h> - -namespace art { - -void register_java_lang_Void(JNIEnv* env); - -} // namespace art - -#endif // ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_ diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 099d77edaa..4d4bab764c 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -345,6 +345,9 @@ void DumpNativeStack(std::ostream& os, } else { os << it->map.name; } + if (it->map.offset != 0) { + os << StringPrintf(" (offset %" PRIx64 ")", it->map.offset); + } os << " ("; if (!it->func_name.empty()) { os << it->func_name; diff --git a/runtime/oat.h b/runtime/oat.h index 8f81010a06..af14b3e601 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -45,6 +45,7 @@ class PACKED(4) OatHeader { static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPathKey = "bootclasspath"; static constexpr const char* kConcurrentCopying = "concurrent-copying"; + static constexpr const char* kCompilationReasonKey = "compilation-reason"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index dc4bae3415..0852da5644 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -633,6 +633,15 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { const uint8_t* dex_file_pointer = nullptr; if (UNLIKELY(dex_file_offset == 0U)) { if (uncompressed_dex_files_ == nullptr) { + // Do not support mixed-mode oat files. + if (i > 0) { + *error_msg = StringPrintf("In oat file '%s', unsupported uncompressed-dex-file for dex " + "file %zu (%s)", + GetLocation().c_str(), + i, + dex_file_location.c_str()); + return false; + } uncompressed_dex_files_.reset(new std::vector<std::unique_ptr<const DexFile>>()); // No dex files, load it from location. const ArtDexFileLoader dex_file_loader; @@ -652,9 +661,31 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } } + // The oat file may be out of date wrt/ the dex-file location. We need to be defensive + // here and ensure that at least the number of dex files still matches. + // Note: actual checksum comparisons are the duty of the OatFileAssistant and will be + // done after loading the OatFile. + if (uncompressed_dex_files_->size() != dex_file_count) { + *error_msg = StringPrintf("In oat file '%s', expected %u uncompressed dex files, but " + "found %zu in '%s'", + GetLocation().c_str(), + dex_file_count, + uncompressed_dex_files_->size(), + dex_file_location.c_str()); + return false; + } } dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin(); } else { + // Do not support mixed-mode oat files. + if (uncompressed_dex_files_ != nullptr) { + *error_msg = StringPrintf("In oat file '%s', unsupported embedded dex-file for dex file " + "%zu (%s)", + GetLocation().c_str(), + i, + dex_file_location.c_str()); + return false; + } if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u of %zu but the size of dex file header is %zu", @@ -1893,6 +1924,10 @@ std::string OatFile::GetClassLoaderContext() const { return GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey); } +const char* OatFile::GetCompilationReason() const { + return GetOatHeader().GetStoreValueByKey(OatHeader::kCompilationReasonKey); +} + OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 46c692e568..802adc3a36 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -130,6 +130,8 @@ class OatFile { std::string GetClassLoaderContext() const; + const char* GetCompilationReason() const; + const std::string& GetLocation() const { return location_; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 15a5954396..0170073e29 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1262,4 +1262,27 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { } return std::unique_ptr<OatFile>(); } + +// TODO(calin): we could provide a more refined status here +// (e.g. run from uncompressed apk, run with vdex but not oat etc). It will allow us to +// track more experiments but adds extra complexity. +void OatFileAssistant::GetOptimizationStatus( + const std::string& filename, + InstructionSet isa, + std::string* out_compilation_filter, + std::string* out_compilation_reason) { + // Try to load the oat file as we would do at runtime. + OatFileAssistant oat_file_assistant(filename.c_str(), isa, true /* load_executable */); + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + + if (oat_file == nullptr) { + *out_compilation_filter = "run-from-apk"; + *out_compilation_reason = "unknown"; + } else { + *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter()); + const char* reason = oat_file->GetCompilationReason(); + *out_compilation_reason = reason == nullptr ? "unknown" : reason; + } +} + } // namespace art diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index a6140304c2..a184807b73 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -226,6 +226,20 @@ class OatFileAssistant { // dex file. The returned description is for debugging purposes only. std::string GetStatusDump(); + // Computes the optimization status of the given dex file. The result is + // returned via the two output parameters. + // - out_compilation_filter: the level of optimizations (compiler filter) + // - out_compilation_reason: the optimization reason. The reason might + // be "unknown" if the compiler artifacts were not annotated during optimizations. + // + // This method will try to mimic the runtime effect of loading the dex file. + // For example, if there is no usable oat file, the compiler filter will be set + // to "run-from-apk". + static void GetOptimizationStatus(const std::string& filename, + InstructionSet isa, + std::string* out_compilation_filter, + std::string* out_compilation_reason); + // Open and returns an image space associated with the oat file. static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 50f5e7a0d5..72f7d02892 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -43,7 +43,27 @@ namespace art { static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4] static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr; -class OatFileAssistantTest : public DexoptTest {}; +class OatFileAssistantTest : public DexoptTest { + public: + void VerifyOptimizationStatus(const std::string& file, + const std::string& expected_filter, + const std::string& expected_reason) { + std::string compilation_filter; + std::string compilation_reason; + OatFileAssistant::GetOptimizationStatus( + file, kRuntimeISA, &compilation_filter, &compilation_reason); + + ASSERT_EQ(expected_filter, compilation_filter); + ASSERT_EQ(expected_reason, compilation_reason); + } + + void VerifyOptimizationStatus(const std::string& file, + CompilerFilter::Filter expected_filter, + const std::string& expected_reason) { + VerifyOptimizationStatus( + file, CompilerFilter::NameOfFilter(expected_filter), expected_reason); + } +}; class OatFileAssistantNoDex2OatTest : public DexoptTest { public: @@ -107,6 +127,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); + + VerifyOptimizationStatus(dex_location, "run-from-apk", "unknown"); } // Case: We have no DEX file and no OAT file. @@ -136,7 +158,7 @@ TEST_F(OatFileAssistantTest, OdexUpToDate) { std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar"; std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex"; Copy(GetDexSrc1(), dex_location); - GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install"); // For the use of oat location by making the dex parent not writable. OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); @@ -154,6 +176,8 @@ TEST_F(OatFileAssistantTest, OdexUpToDate) { EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); + + VerifyOptimizationStatus(dex_location, CompilerFilter::kSpeed, "install"); } // Case: We have a DEX file and a PIC ODEX file, but no OAT file. We load the dex @@ -221,6 +245,8 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); + + VerifyOptimizationStatus(dex_location, CompilerFilter::kSpeed, "unknown"); } // Case: Passing valid file descriptors of updated odex/vdex filesalong with @@ -381,6 +407,8 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { // Make sure we don't crash in this case when we dump the status. We don't // care what the actual dumped value is. oat_file_assistant.GetStatusDump(); + + VerifyOptimizationStatus(dex_location, "run-from-apk", "unknown"); } // Case: We have a DEX file and empty VDEX and ODEX files. diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index 8d864018ab..89812f370f 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -88,4 +88,46 @@ TEST_F(OatFileTest, LoadOat) { EXPECT_EQ(odex_file->GetVdexFile()->Begin(), odex_file->VdexBegin()); } +TEST_F(OatFileTest, ChangingMultiDexUncompressed) { + std::string dex_location = GetScratchDir() + "/MultiDexUncompressed.jar"; + + Copy(GetTestDexFileName("MultiDexUncompressed"), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken); + + std::string oat_location; + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( + dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + + // Ensure we can load that file. Just a precondition. + { + std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex_location.c_str(), + &error_msg)); + ASSERT_TRUE(odex_file != nullptr); + ASSERT_EQ(2u, odex_file->GetOatDexFiles().size()); + } + + // Now replace the source. + Copy(GetTestDexFileName("MainUncompressed"), dex_location); + + // And try to load again. + std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex_location.c_str(), + &error_msg)); + EXPECT_TRUE(odex_file == nullptr); + EXPECT_NE(std::string::npos, error_msg.find("expected 2 uncompressed dex files, but found 1")) + << error_msg; +} + } // namespace art diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 92eb703338..c7f73d14db 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -330,8 +330,10 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xtarget-sdk-version:_") .WithType<int>() .IntoKey(M::TargetSdkVersion) - .Define("-Xno-hidden-api-checks") - .IntoKey(M::NoHiddenApiChecks) + .Define("-Xhidden-api-checks") + .IntoKey(M::HiddenApiChecks) + .Define("-Xuse-stderr-logger") + .IntoKey(M::UseStderrLogger) .Ignore({ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa", "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b26b09c156..d0aec116a4 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -105,6 +105,7 @@ #include "mirror/method_type.h" #include "mirror/stack_trace_element.h" #include "mirror/throwable.h" +#include "mirror/var_handle.h" #include "monitor.h" #include "native/dalvik_system_DexFile.h" #include "native/dalvik_system_VMDebug.h" @@ -119,7 +120,6 @@ #include "native/java_lang_Thread.h" #include "native/java_lang_Throwable.h" #include "native/java_lang_VMClassLoader.h" -#include "native/java_lang_Void.h" #include "native/java_lang_invoke_MethodHandleImpl.h" #include "native/java_lang_ref_FinalizerReference.h" #include "native/java_lang_ref_Reference.h" @@ -265,7 +265,7 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - do_hidden_api_checks_(true), + do_hidden_api_checks_(false), pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), @@ -1082,10 +1082,16 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. env_snapshot_.TakeSnapshot(); - RuntimeArgumentMap runtime_options(std::move(runtime_options_in)); + using Opt = RuntimeArgumentMap; + Opt runtime_options(std::move(runtime_options_in)); ScopedTrace trace(__FUNCTION__); CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize); + // Early override for logging output. + if (runtime_options.Exists(Opt::UseStderrLogger)) { + android::base::SetLogger(android::base::StderrLogger); + } + MemMap::Init(); // Try to reserve a dedicated fault page. This is allocated for clobbered registers and sentinels. @@ -1112,7 +1118,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } } - using Opt = RuntimeArgumentMap; VLOG(startup) << "Runtime::Init -verbose:startup enabled"; QuasiAtomic::Startup(); @@ -1176,14 +1181,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // by default and we only enable them if: // (a) runtime was started with a flag that enables the checks, or // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). - // TODO(dbrazdil): Turn the NoHiddenApiChecks negative flag into a positive one - // to clean up this logic. - if (kIsTargetBuild && IsAotCompiler() && !runtime_options.Exists(Opt::NoHiddenApiChecks)) { - // dex2oat on target without -Xno-hidden-api-checks. - do_hidden_api_checks_ = !IsCompilingBootImage(); - } else { - do_hidden_api_checks_ = false; - } + do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks); DCHECK(!is_zygote_ || !do_hidden_api_checks_) << "Zygote should not be started with hidden API checks"; @@ -1747,7 +1745,6 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { register_java_lang_Thread(env); register_java_lang_Throwable(env); register_java_lang_VMClassLoader(env); - register_java_lang_Void(env); register_java_util_concurrent_atomic_AtomicLong(env); register_libcore_util_CharsetUtils(env); register_org_apache_harmony_dalvik_ddmc_DdmServer(env); @@ -1934,6 +1931,11 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { mirror::EmulatedStackFrame::VisitRoots(visitor); mirror::ClassExt::VisitRoots(visitor); mirror::CallSite::VisitRoots(visitor); + mirror::VarHandle::VisitRoots(visitor); + mirror::FieldVarHandle::VisitRoots(visitor); + mirror::ArrayElementVarHandle::VisitRoots(visitor); + mirror::ByteArrayViewVarHandle::VisitRoots(visitor); + mirror::ByteBufferViewVarHandle::VisitRoots(visitor); // Visit all the primitive array types classes. mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor); // BooleanArray mirror::PrimitiveArray<int8_t>::VisitRoots(visitor); // ByteArray diff --git a/runtime/runtime.h b/runtime/runtime.h index 7ab9be5c5b..c7f650ea3f 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -325,7 +325,7 @@ class Runtime { // instead. void VisitImageRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Visit all of the roots we can do safely do concurrently. + // Visit all of the roots we can safely visit concurrently. void VisitConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags = kVisitRootFlagAllRoots) REQUIRES(!Locks::classlinker_classes_lock_, !Locks::trace_lock_) diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index e78d952c1c..dba648e785 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -119,7 +119,7 @@ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \ RUNTIME_OPTIONS_KEY (verifier::VerifyMode, \ Verify, verifier::VerifyMode::kEnable) RUNTIME_OPTIONS_KEY (int, TargetSdkVersion, Runtime::kUnsetSdkVersion) -RUNTIME_OPTIONS_KEY (Unit, NoHiddenApiChecks) +RUNTIME_OPTIONS_KEY (Unit, HiddenApiChecks) RUNTIME_OPTIONS_KEY (std::string, NativeBridge) RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10) RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) @@ -150,5 +150,6 @@ RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullpt RUNTIME_OPTIONS_KEY (bool, SlowDebug, false) RUNTIME_OPTIONS_KEY (unsigned int, GlobalRefAllocStackTraceLimit, 0) // 0 = off +RUNTIME_OPTIONS_KEY (Unit, UseStderrLogger) #undef RUNTIME_OPTIONS_KEY diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 54d2f00106..3b1d5f8c4a 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -24,6 +24,9 @@ #include "mirror/class.h" #include "runtime.h" +// Build flag for the bitstring subtype check runtime hooks. +constexpr bool kBitstringSubtypeCheckEnabled = false; + /** * Any node in a tree can have its path (from the root to the node) represented as a string by * concatenating the path of the parent to that of the current node. diff --git a/runtime/thread.cc b/runtime/thread.cc index 9dc92f3788..2ee7f9deda 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -663,8 +663,8 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer); stack_size = FixStackSize(stack_size); - // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing to - // assign it. + // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing + // to assign it. env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, reinterpret_cast<jlong>(child_thread)); @@ -839,7 +839,8 @@ Thread* Thread::Attach(const char* thread_name, if (create_peer) { self->CreatePeer(thread_name, as_daemon, thread_group); if (self->IsExceptionPending()) { - // We cannot keep the exception around, as we're deleting self. Try to be helpful and log it. + // We cannot keep the exception around, as we're deleting self. Try to be helpful and log + // it. { ScopedObjectAccess soa(self); LOG(ERROR) << "Exception creating thread peer:"; @@ -3435,6 +3436,9 @@ bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const { return object != nullptr && object->GetLockOwnerThreadId() == GetThreadId(); } +extern "C" StackReference<mirror::Object>* artQuickGetProxyThisObjectReference(ArtMethod** sp) + REQUIRES_SHARED(Locks::mutator_lock_); + // RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor). template <typename RootVisitor, bool kPrecise = false> class ReferenceMapVisitor : public StackVisitor { @@ -3535,7 +3539,7 @@ class ReferenceMapVisitor : public StackVisitor { if (!m->IsNative() && !m->IsRuntimeMethod() && (!m->IsProxyMethod() || m->IsConstructor())) { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); DCHECK(method_header->IsOptimized()); - auto* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>( + StackReference<mirror::Object>* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>( reinterpret_cast<uintptr_t>(cur_quick_frame)); uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); CodeInfo code_info = method_header->GetOptimizedCodeInfo(); @@ -3550,7 +3554,7 @@ class ReferenceMapVisitor : public StackVisitor { BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, map); for (size_t i = 0; i < number_of_bits; ++i) { if (stack_mask.LoadBit(i)) { - auto* ref_addr = vreg_base + i; + StackReference<mirror::Object>* ref_addr = vreg_base + i; mirror::Object* ref = ref_addr->AsMirrorPtr(); if (ref != nullptr) { mirror::Object* new_ref = ref; @@ -3579,6 +3583,19 @@ class ReferenceMapVisitor : public StackVisitor { } } } + } else if (!m->IsStatic() && !m->IsRuntimeMethod() && m->IsProxyMethod()) { + // If this is a non-static proxy method, visit its target (`this` object). + DCHECK(!m->IsNative()); + StackReference<mirror::Object>* ref_addr = + artQuickGetProxyThisObjectReference(cur_quick_frame); + mirror::Object* ref = ref_addr->AsMirrorPtr(); + if (ref != nullptr) { + mirror::Object* new_ref = ref; + visitor_(&new_ref, -1, this); + if (ref != new_ref) { + ref_addr->Assign(new_ref); + } + } } } @@ -3773,9 +3790,9 @@ void Thread::VisitRoots(RootVisitor* visitor) { void Thread::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { if ((flags & VisitRootFlags::kVisitRootFlagPrecise) != 0) { - VisitRoots<true>(visitor); + VisitRoots</* kPrecise */ true>(visitor); } else { - VisitRoots<false>(visitor); + VisitRoots</* kPrecise */ false>(visitor); } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index afb3224944..66e578f312 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3899,21 +3899,13 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( } ObjPtr<mirror::Class> klass = klass_type.GetClass(); const RegType& referrer = GetDeclaringClass(); - auto* cl = Runtime::Current()->GetClassLinker(); - auto pointer_size = cl->GetImagePointerSize(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + PointerSize pointer_size = class_linker->GetImagePointerSize(); ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx, pointer_size); if (res_method == nullptr) { - // Try to find the method with the appropriate lookup for the klass type (interface or not). - // If this lookup does not match `method_type`, errors shall be reported below. - if (klass->IsInterface()) { - res_method = klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size); - } else { - res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size); - } - if (res_method != nullptr) { - dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size); - } + res_method = class_linker->FindResolvedMethod( + klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx); } // Record result of method resolution attempt. The klass resolution has recorded whether @@ -4432,6 +4424,8 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name); } else if (klass == mirror::VarHandle::StaticClass()) { expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name); + // TODO: add compiler support for VarHandle accessor methods (b/71781600) + Fail(VERIFY_ERROR_FORCE_INTERPRETER); } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor(); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 5564684c4f..30aefede20 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -278,7 +278,7 @@ RegTypeCache::RegTypeCache(bool can_load_classes, ScopedArenaAllocator& allocato klass_entries_(allocator.Adapter(kArenaAllocVerifier)), can_load_classes_(can_load_classes), allocator_(allocator) { - DCHECK(can_suspend || !can_load_classes) << "Cannot load classes is suspension is disabled!"; + DCHECK(can_suspend || !can_load_classes) << "Cannot load classes if suspension is disabled!"; if (kIsDebugBuild && can_suspend) { Thread::Current()->AssertThreadSuspensionIsAllowable(gAborting == 0); } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 5fe10f5c12..67ea64be74 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -72,6 +72,7 @@ jclass WellKnownClasses::java_lang_System; jclass WellKnownClasses::java_lang_Thread; jclass WellKnownClasses::java_lang_ThreadGroup; jclass WellKnownClasses::java_lang_Throwable; +jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; @@ -142,6 +143,11 @@ jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; jfieldID WellKnownClasses::java_lang_reflect_Executable_artMethod; jfieldID WellKnownClasses::java_lang_reflect_Proxy_h; +jfieldID WellKnownClasses::java_nio_ByteBuffer_address; +jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; +jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly; +jfieldID WellKnownClasses::java_nio_ByteBuffer_limit; +jfieldID WellKnownClasses::java_nio_ByteBuffer_offset; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; jfieldID WellKnownClasses::java_util_ArrayList_array; @@ -277,7 +283,27 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { } #undef STRING_INIT_LIST +class ScopedHiddenApiExemption { + public: + explicit ScopedHiddenApiExemption(Runtime* runtime) + : runtime_(runtime), + initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) { + runtime_->SetHiddenApiChecksEnabled(false); + } + + ~ScopedHiddenApiExemption() { + runtime_->SetHiddenApiChecksEnabled(initially_enabled_); + } + + private: + Runtime* runtime_; + const bool initially_enabled_; + DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); +}; + void WellKnownClasses::Init(JNIEnv* env) { + ScopedHiddenApiExemption hiddenapi_exemption(Runtime::Current()); + dalvik_annotation_optimization_CriticalNative = CacheClass(env, "dalvik/annotation/optimization/CriticalNative"); dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative"); @@ -318,6 +344,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread = CacheClass(env, "java/lang/Thread"); java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup"); java_lang_Throwable = CacheClass(env, "java/lang/Throwable"); + java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer"); java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); @@ -379,6 +406,11 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;"); java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;"); java_lang_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J"); + java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J"); + java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B"); + java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z"); + java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I"); + java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I"); java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J"); java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;"); @@ -462,6 +494,7 @@ void WellKnownClasses::Clear() { java_lang_Throwable = nullptr; java_util_ArrayList = nullptr; java_util_Collections = nullptr; + java_nio_ByteBuffer = nullptr; java_nio_DirectByteBuffer = nullptr; libcore_reflect_AnnotationFactory = nullptr; libcore_reflect_AnnotationMember = nullptr; @@ -530,6 +563,11 @@ void WellKnownClasses::Clear() { java_lang_Throwable_stackTrace = nullptr; java_lang_Throwable_stackState = nullptr; java_lang_Throwable_suppressedExceptions = nullptr; + java_nio_ByteBuffer_address = nullptr; + java_nio_ByteBuffer_hb = nullptr; + java_nio_ByteBuffer_isReadOnly = nullptr; + java_nio_ByteBuffer_limit = nullptr; + java_nio_ByteBuffer_offset = nullptr; java_nio_DirectByteBuffer_capacity = nullptr; java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr; java_util_ArrayList_array = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 9e0b079b7b..d5d7033132 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,6 +85,7 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; + static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; static jclass libcore_reflect_AnnotationMember; @@ -153,8 +154,14 @@ struct WellKnownClasses { static jfieldID java_lang_Throwable_stackTrace; static jfieldID java_lang_Throwable_stackState; static jfieldID java_lang_Throwable_suppressedExceptions; + static jfieldID java_nio_ByteBuffer_address; + static jfieldID java_nio_ByteBuffer_hb; + static jfieldID java_nio_ByteBuffer_isReadOnly; + static jfieldID java_nio_ByteBuffer_limit; + static jfieldID java_nio_ByteBuffer_offset; static jfieldID java_nio_DirectByteBuffer_capacity; static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; + static jfieldID java_util_ArrayList_array; static jfieldID java_util_ArrayList_size; static jfieldID java_util_Collections_EMPTY_LIST; |