/* * 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_CLASS_LINKER_INL_H_ #define ART_RUNTIME_CLASS_LINKER_INL_H_ #include #include "android-base/thread_annotations.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/mutex.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/dex_file_structs.h" #include "gc_root-inl.h" #include "handle_scope-inl.h" #include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" #include "mirror/object_array-inl.h" #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" #include "well_known_classes.h" namespace art { inline ObjPtr ClassLinker::FindArrayClass(Thread* self, ObjPtr element_class) { for (size_t i = 0; i < kFindArrayCacheSize; ++i) { // Read the cached array class once to avoid races with other threads setting it. ObjPtr array_class = find_array_class_cache_[i].Read(); if (array_class != nullptr && array_class->GetComponentType() == element_class) { return array_class; } } std::string descriptor = "["; std::string temp; descriptor += element_class->GetDescriptor(&temp); StackHandleScope<1> hs(Thread::Current()); Handle class_loader(hs.NewHandle(element_class->GetClassLoader())); ObjPtr array_class = FindClass(self, descriptor.c_str(), class_loader); if (array_class != nullptr) { // Benign races in storing array class and incrementing index. size_t victim_index = find_array_class_cache_next_victim_; find_array_class_cache_[victim_index] = GcRoot(array_class); find_array_class_cache_next_victim_ = (victim_index + 1) % kFindArrayCacheSize; } else { // We should have a NoClassDefFoundError. self->AssertPendingException(); } return array_class; } inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, ArtField* referrer) { Thread::PoisonObjectPointersIfDebug(); DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved = referrer->GetDexCache()->GetResolvedString(string_idx); if (resolved == nullptr) { resolved = DoResolveString(string_idx, referrer->GetDexCache()); } return resolved; } inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved = referrer->GetDexCache()->GetResolvedString(string_idx); if (resolved == nullptr) { resolved = DoResolveString(string_idx, referrer->GetDexCache()); } return resolved; } inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, Handle dex_cache) { Thread::PoisonObjectPointersIfDebug(); DCHECK(!Thread::Current()->IsExceptionPending()); ObjPtr resolved = dex_cache->GetResolvedString(string_idx); if (resolved == nullptr) { resolved = DoResolveString(string_idx, dex_cache); } return resolved; } inline ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, ObjPtr dex_cache) { ObjPtr resolved = dex_cache->GetResolvedString(string_idx); if (resolved == nullptr) { resolved = DoLookupString(string_idx, dex_cache); } return resolved; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, ObjPtr referrer) { if (kObjPtrPoisoning) { StackHandleScope<1> hs(Thread::Current()); HandleWrapperObjPtr referrer_wrapper = hs.NewHandleWrapper(&referrer); Thread::Current()->PoisonObjectPointers(); } DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (resolved_type == nullptr) { resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) { Thread::PoisonObjectPointersIfDebug(); DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, Handle dex_cache, Handle class_loader) { DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); ObjPtr resolved = dex_cache->GetResolvedType(type_idx); if (resolved == nullptr) { resolved = DoResolveType(type_idx, dex_cache, class_loader); } return resolved; } inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, ObjPtr referrer) { // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { type = DoLookupResolvedType(type_idx, referrer); } return type; } inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, ArtField* referrer) { // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass()); } return type; } inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, ArtMethod* referrer) { // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass()); } return type; } inline ObjPtr ClassLinker::LookupResolvedType( dex::TypeIndex type_idx, ObjPtr dex_cache, ObjPtr class_loader) { ObjPtr type = dex_cache->GetResolvedType(type_idx); if (type == nullptr) { type = DoLookupResolvedType(type_idx, dex_cache, class_loader); } return type; } template inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr dex_cache, InvokeType type, ClassGetter class_getter) { switch (type) { case kStatic: case kSuper: case kPolymorphic: break; case kInterface: { // We have to check whether the method id really belongs to an interface (dex static bytecode // constraints A15, A16). Otherwise you must not invoke-interface on it. ObjPtr klass = class_getter(); if (UNLIKELY(!klass->IsInterface())) { if (kThrowOnError) { ThrowIncompatibleClassChangeError(klass, "Found class %s, but interface was expected", klass->PrettyDescriptor().c_str()); } return true; } break; } case kDirect: if (dex_cache->GetDexFile()->SupportsDefaultMethods()) { break; } FALLTHROUGH_INTENDED; case kVirtual: { // Similarly, invoke-virtual (and invoke-direct without default methods) must reference // a non-interface class (dex static bytecode constraint A24, A25). ObjPtr klass = class_getter(); if (UNLIKELY(klass->IsInterface())) { if (kThrowOnError) { ThrowIncompatibleClassChangeError(klass, "Found interface %s, but class was expected", klass->PrettyDescriptor().c_str()); } return true; } break; } default: LOG(FATAL) << "Unreachable - invocation type: " << type; UNREACHABLE(); } return false; } template inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr dex_cache, InvokeType type, uint32_t method_idx, ObjPtr class_loader) { return CheckInvokeClassMismatch( dex_cache, type, [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) { const dex::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); ObjPtr klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); DCHECK(klass != nullptr) << dex_cache->GetDexFile()->PrettyMethod(method_idx); return klass; }); } inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx, ObjPtr dex_cache, ObjPtr class_loader) { PointerSize pointer_size = image_pointer_size_; ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size); if (resolved == nullptr) { const DexFile& dex_file = *dex_cache->GetDexFile(); const dex::MethodId& method_id = dex_file.GetMethodId(method_idx); ObjPtr klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); if (klass != nullptr) { resolved = FindResolvedMethod(klass, dex_cache, class_loader, method_idx); } } return resolved; } template inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) { DCHECK(referrer != nullptr); // Note: The referrer can be a Proxy constructor. In that case, we need to do the // lookup in the context of the original method from where it steals the code. // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); // We do not need the read barrier for getting the DexCache for the initial resolved method // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod( method_idx, image_pointer_size_); if (resolved_method == nullptr) { return nullptr; } DCHECK(!resolved_method->IsRuntimeMethod()); if (kResolveMode == ResolveMode::kCheckICCEAndIAE) { referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_); // Check if the invoke type matches the class type. ObjPtr dex_cache = referrer->GetDexCache(); ObjPtr class_loader = referrer->GetClassLoader(); if (CheckInvokeClassMismatch(dex_cache, type, method_idx, class_loader)) { return nullptr; } // Check access. ObjPtr referring_class = referrer->GetDeclaringClass(); if (!referring_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(), resolved_method, dex_cache, method_idx)) { return nullptr; } // Check if the invoke type matches the method type. if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { return nullptr; } } return resolved_method; } template inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) { DCHECK(referrer != nullptr); // Note: The referrer can be a Proxy constructor. In that case, we need to do the // lookup in the context of the original method from where it steals the code. // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); Thread::PoisonObjectPointersIfDebug(); // We do not need the read barrier for getting the DexCache for the initial resolved method // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod( method_idx, image_pointer_size_); DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod()); if (UNLIKELY(resolved_method == nullptr)) { referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_); ObjPtr declaring_class = referrer->GetDeclaringClass(); StackHandleScope<2> hs(self); Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle h_class_loader(hs.NewHandle(declaring_class->GetClassLoader())); resolved_method = ResolveMethod(method_idx, h_dex_cache, h_class_loader, referrer, type); } else if (kResolveMode == ResolveMode::kCheckICCEAndIAE) { referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_); const dex::MethodId& method_id = referrer->GetDexFile()->GetMethodId(method_idx); ObjPtr cls = LookupResolvedType(method_id.class_idx_, referrer->GetDexCache(), referrer->GetClassLoader()); if (cls == nullptr) { // The verifier breaks the invariant that a resolved method must have its // class in the class table, so resolve the type in case we haven't found it. // b/73760543 StackHandleScope<2> hs(Thread::Current()); Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle h_class_loader(hs.NewHandle(referrer->GetClassLoader())); cls = ResolveType(method_id.class_idx_, h_dex_cache, h_class_loader); if (hs.Self()->IsExceptionPending()) { return nullptr; } } // Check if the invoke type matches the class type. if (CheckInvokeClassMismatch( referrer->GetDexCache(), type, [cls]() { return cls; })) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } // Check access. ObjPtr referring_class = referrer->GetDeclaringClass(); if (!referring_class->CheckResolvedMethodAccess(resolved_method->GetDeclaringClass(), resolved_method, referrer->GetDexCache(), method_idx, type)) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } // Check if the invoke type matches the method type. if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method, referrer); return nullptr; } } // Note: We cannot check here to see whether we added the method to the cache. It // might be an erroneous class, which results in it being hidden from us. return resolved_method; } inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { // We do not need the read barrier for getting the DexCache for the initial resolved field // lookup as both from-space and to-space copies point to the same native resolved fields array. ArtField* field = referrer->GetDexCache()->GetResolvedField( field_idx, image_pointer_size_); if (field == nullptr) { ObjPtr class_loader = referrer->GetDeclaringClass()->GetClassLoader(); field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static); } return field; } inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { Thread::PoisonObjectPointersIfDebug(); // We do not need the read barrier for getting the DexCache for the initial resolved field // lookup as both from-space and to-space copies point to the same native resolved fields array. ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField( field_idx, image_pointer_size_); if (UNLIKELY(resolved_field == nullptr)) { StackHandleScope<2> hs(Thread::Current()); ObjPtr referring_class = referrer->GetDeclaringClass(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static); // Note: We cannot check here to see whether we added the field to the cache. The type // might be an erroneous class, which results in it being hidden from us. } return resolved_field; } template inline void ClassLinker::VisitClassTables(const Visitor& visitor) { Thread* const self = Thread::Current(); WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); for (const ClassLoaderData& data : class_loaders_) { if (data.class_table != nullptr) { visitor(data.class_table); } } } template inline ObjPtr> ClassLinker::GetClassRoots() { ObjPtr> class_roots = class_roots_.Read(); DCHECK(class_roots != nullptr); return class_roots; } template void ClassLinker::VisitKnownDexFiles(Thread* self, Visitor visitor) { ReaderMutexLock rmu(self, *Locks::dex_lock_); std::for_each(dex_caches_.begin(), dex_caches_.end(), [&](DexCacheData& dcd) REQUIRES(Locks::mutator_lock_) { if (dcd.IsValid()) { visitor(dcd.dex_file); } }); } } // namespace art #endif // ART_RUNTIME_CLASS_LINKER_INL_H_