| /* |
| * 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 "class_linker.h" |
| |
| #include <fcntl.h> |
| #include <sys/file.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <deque> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/casts.h" |
| #include "base/logging.h" |
| #include "base/scoped_flock.h" |
| #include "base/stl_util.h" |
| #include "base/unix_file/fd_file.h" |
| #include "class_linker-inl.h" |
| #include "compiler_callbacks.h" |
| #include "debugger.h" |
| #include "dex_file-inl.h" |
| #include "gc_root-inl.h" |
| #include "gc/accounting/card_table-inl.h" |
| #include "gc/accounting/heap_bitmap.h" |
| #include "gc/heap.h" |
| #include "gc/space/image_space.h" |
| #include "handle_scope.h" |
| #include "intern_table.h" |
| #include "interpreter/interpreter.h" |
| #include "leb128.h" |
| #include "method_helper-inl.h" |
| #include "oat.h" |
| #include "oat_file.h" |
| #include "object_lock.h" |
| #include "mirror/art_field-inl.h" |
| #include "mirror/art_method-inl.h" |
| #include "mirror/class.h" |
| #include "mirror/class-inl.h" |
| #include "mirror/class_loader.h" |
| #include "mirror/dex_cache-inl.h" |
| #include "mirror/iftable-inl.h" |
| #include "mirror/object-inl.h" |
| #include "mirror/object_array-inl.h" |
| #include "mirror/proxy.h" |
| #include "mirror/reference-inl.h" |
| #include "mirror/stack_trace_element.h" |
| #include "mirror/string-inl.h" |
| #include "os.h" |
| #include "runtime.h" |
| #include "entrypoints/entrypoint_utils.h" |
| #include "ScopedLocalRef.h" |
| #include "scoped_thread_state_change.h" |
| #include "handle_scope-inl.h" |
| #include "thread.h" |
| #include "utils.h" |
| #include "verifier/method_verifier.h" |
| #include "well_known_classes.h" |
| |
| namespace art { |
| |
| static void ThrowNoClassDefFoundError(const char* fmt, ...) |
| __attribute__((__format__(__printf__, 1, 2))) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); |
| static void ThrowNoClassDefFoundError(const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| Thread* self = Thread::Current(); |
| ThrowLocation throw_location = self->GetCurrentLocationForThrow(); |
| self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args); |
| va_end(args); |
| } |
| |
| static void ThrowEarlierClassFailure(mirror::Class* c) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| // The class failed to initialize on a previous attempt, so we want to throw |
| // a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we |
| // failed in verification, in which case v2 5.4.1 says we need to re-throw |
| // the previous error. |
| if (!Runtime::Current()->IsCompiler()) { // Give info if this occurs at runtime. |
| LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c); |
| } |
| |
| CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus(); |
| Thread* self = Thread::Current(); |
| ThrowLocation throw_location = self->GetCurrentLocationForThrow(); |
| if (c->GetVerifyErrorClass() != nullptr) { |
| // TODO: change the verifier to store an _instance_, with a useful detail message? |
| std::string temp; |
| self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp), |
| PrettyDescriptor(c).c_str()); |
| } else { |
| self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;", |
| PrettyDescriptor(c).c_str()); |
| } |
| } |
| |
| static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* self = Thread::Current(); |
| JNIEnv* env = self->GetJniEnv(); |
| |
| ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred()); |
| CHECK(cause.get() != nullptr); |
| |
| env->ExceptionClear(); |
| bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error); |
| env->Throw(cause.get()); |
| |
| // We only wrap non-Error exceptions; an Error can just be used as-is. |
| if (!is_error) { |
| ThrowLocation throw_location = self->GetCurrentLocationForThrow(); |
| self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;", |
| nullptr); |
| } |
| } |
| |
| const char* ClassLinker::class_roots_descriptors_[] = { |
| "Ljava/lang/Class;", |
| "Ljava/lang/Object;", |
| "[Ljava/lang/Class;", |
| "[Ljava/lang/Object;", |
| "Ljava/lang/String;", |
| "Ljava/lang/DexCache;", |
| "Ljava/lang/ref/Reference;", |
| "Ljava/lang/reflect/ArtField;", |
| "Ljava/lang/reflect/ArtMethod;", |
| "Ljava/lang/reflect/Proxy;", |
| "[Ljava/lang/String;", |
| "[Ljava/lang/reflect/ArtField;", |
| "[Ljava/lang/reflect/ArtMethod;", |
| "Ljava/lang/ClassLoader;", |
| "Ljava/lang/Throwable;", |
| "Ljava/lang/ClassNotFoundException;", |
| "Ljava/lang/StackTraceElement;", |
| "Z", |
| "B", |
| "C", |
| "D", |
| "F", |
| "I", |
| "J", |
| "S", |
| "V", |
| "[Z", |
| "[B", |
| "[C", |
| "[D", |
| "[F", |
| "[I", |
| "[J", |
| "[S", |
| "[Ljava/lang/StackTraceElement;", |
| }; |
| |
| ClassLinker::ClassLinker(InternTable* intern_table) |
| // dex_lock_ is recursive as it may be used in stack dumping. |
| : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), |
| dex_cache_image_class_lookup_required_(false), |
| failed_dex_cache_class_lookups_(0), |
| class_roots_(nullptr), |
| array_iftable_(nullptr), |
| find_array_class_cache_next_victim_(0), |
| init_done_(false), |
| log_new_dex_caches_roots_(false), |
| log_new_class_table_roots_(false), |
| intern_table_(intern_table), |
| portable_resolution_trampoline_(nullptr), |
| quick_resolution_trampoline_(nullptr), |
| portable_imt_conflict_trampoline_(nullptr), |
| quick_imt_conflict_trampoline_(nullptr), |
| quick_generic_jni_trampoline_(nullptr), |
| quick_to_interpreter_bridge_trampoline_(nullptr), |
| image_pointer_size_(sizeof(void*)) { |
| CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax)); |
| memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*)); |
| } |
| |
| // To set a value for generic JNI. May be necessary in compiler tests. |
| extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); |
| extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); |
| extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); |
| extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); |
| |
| void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) { |
| VLOG(startup) << "ClassLinker::Init"; |
| CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it."; |
| |
| CHECK(!init_done_); |
| |
| // java_lang_Class comes first, it's needed for AllocClass |
| Thread* self = Thread::Current(); |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| // The GC can't handle an object with a null class since we can't get the size of this object. |
| heap->IncrementDisableMovingGC(self); |
| StackHandleScope<64> hs(self); // 64 is picked arbitrarily. |
| Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>( |
| heap->AllocNonMovableObject<true>(self, nullptr, |
| mirror::Class::ClassClassSize(), |
| VoidFunctor())))); |
| CHECK(java_lang_Class.Get() != nullptr); |
| mirror::Class::SetClassClass(java_lang_Class.Get()); |
| java_lang_Class->SetClass(java_lang_Class.Get()); |
| if (kUseBakerOrBrooksReadBarrier) { |
| java_lang_Class->AssertReadBarrierPointer(); |
| } |
| java_lang_Class->SetClassSize(mirror::Class::ClassClassSize()); |
| heap->DecrementDisableMovingGC(self); |
| // AllocClass(mirror::Class*) can now be used |
| |
| // Class[] is used for reflection support. |
| Handle<mirror::Class> class_array_class(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Class>::ClassSize()))); |
| class_array_class->SetComponentType(java_lang_Class.Get()); |
| |
| // java_lang_Object comes next so that object_array_class can be created. |
| Handle<mirror::Class> java_lang_Object(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize()))); |
| CHECK(java_lang_Object.Get() != nullptr); |
| // backfill Object as the super class of Class. |
| java_lang_Class->SetSuperClass(java_lang_Object.Get()); |
| java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self); |
| |
| // Object[] next to hold class roots. |
| Handle<mirror::Class> object_array_class(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Object>::ClassSize()))); |
| object_array_class->SetComponentType(java_lang_Object.Get()); |
| |
| // Setup the char (primitive) class to be used for char[]. |
| Handle<mirror::Class> char_class(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::Class::PrimitiveClassSize()))); |
| |
| // Setup the char[] class to be used for String. |
| Handle<mirror::Class> char_array_class(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), |
| mirror::Array::ClassSize()))); |
| char_array_class->SetComponentType(char_class.Get()); |
| mirror::CharArray::SetArrayClass(char_array_class.Get()); |
| |
| // Setup String. |
| Handle<mirror::Class> java_lang_String(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize()))); |
| mirror::String::SetClass(java_lang_String.Get()); |
| java_lang_String->SetObjectSize(mirror::String::InstanceSize()); |
| java_lang_String->SetStatus(mirror::Class::kStatusResolved, self); |
| |
| // Setup Reference. |
| Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize()))); |
| mirror::Reference::SetClass(java_lang_ref_Reference.Get()); |
| java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); |
| java_lang_ref_Reference->SetStatus(mirror::Class::kStatusResolved, self); |
| |
| // Create storage for root classes, save away our work so far (requires descriptors). |
| class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class> >( |
| mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(), |
| kClassRootsMax)); |
| CHECK(!class_roots_.IsNull()); |
| SetClassRoot(kJavaLangClass, java_lang_Class.Get()); |
| SetClassRoot(kJavaLangObject, java_lang_Object.Get()); |
| SetClassRoot(kClassArrayClass, class_array_class.Get()); |
| SetClassRoot(kObjectArrayClass, object_array_class.Get()); |
| SetClassRoot(kCharArrayClass, char_array_class.Get()); |
| SetClassRoot(kJavaLangString, java_lang_String.Get()); |
| SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get()); |
| |
| // Setup the primitive type classes. |
| SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); |
| SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); |
| SetClassRoot(kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); |
| SetClassRoot(kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); |
| SetClassRoot(kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); |
| SetClassRoot(kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat)); |
| SetClassRoot(kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); |
| SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); |
| |
| // Create array interface entries to populate once we can load system classes. |
| array_iftable_ = GcRoot<mirror::IfTable>(AllocIfTable(self, 2)); |
| |
| // Create int array type for AllocDexCache (done in AppendToBootClassPath). |
| Handle<mirror::Class> int_array_class(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize()))); |
| int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt)); |
| mirror::IntArray::SetArrayClass(int_array_class.Get()); |
| SetClassRoot(kIntArrayClass, int_array_class.Get()); |
| |
| // now that these are registered, we can use AllocClass() and AllocObjectArray |
| |
| // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. |
| Handle<mirror::Class> java_lang_DexCache(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize()))); |
| SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); |
| java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); |
| java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self); |
| |
| // Constructor, Field, Method, and AbstractMethod are necessary so |
| // that FindClass can link members. |
| Handle<mirror::Class> java_lang_reflect_ArtField(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::ArtField::ClassSize()))); |
| CHECK(java_lang_reflect_ArtField.Get() != nullptr); |
| java_lang_reflect_ArtField->SetObjectSize(mirror::ArtField::InstanceSize()); |
| SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.Get()); |
| java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self); |
| mirror::ArtField::SetClass(java_lang_reflect_ArtField.Get()); |
| |
| Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize()))); |
| CHECK(java_lang_reflect_ArtMethod.Get() != nullptr); |
| java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*))); |
| SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get()); |
| java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self); |
| mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get()); |
| |
| // Set up array classes for string, field, method |
| Handle<mirror::Class> object_array_string(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), |
| mirror::ObjectArray<mirror::String>::ClassSize()))); |
| object_array_string->SetComponentType(java_lang_String.Get()); |
| SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get()); |
| |
| Handle<mirror::Class> object_array_art_method(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), |
| mirror::ObjectArray<mirror::ArtMethod>::ClassSize()))); |
| object_array_art_method->SetComponentType(java_lang_reflect_ArtMethod.Get()); |
| SetClassRoot(kJavaLangReflectArtMethodArrayClass, object_array_art_method.Get()); |
| |
| Handle<mirror::Class> object_array_art_field(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), |
| mirror::ObjectArray<mirror::ArtField>::ClassSize()))); |
| object_array_art_field->SetComponentType(java_lang_reflect_ArtField.Get()); |
| SetClassRoot(kJavaLangReflectArtFieldArrayClass, object_array_art_field.Get()); |
| |
| // Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create |
| // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses |
| // these roots. |
| CHECK_NE(0U, boot_class_path.size()); |
| for (const DexFile* dex_file : boot_class_path) { |
| CHECK(dex_file != nullptr); |
| AppendToBootClassPath(*dex_file); |
| } |
| |
| // now we can use FindSystemClass |
| |
| // run char class through InitializePrimitiveClass to finish init |
| InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar); |
| SetClassRoot(kPrimitiveChar, char_class.Get()); // needs descriptor |
| |
| // Create runtime resolution and imt conflict methods. Also setup the default imt. |
| Runtime* runtime = Runtime::Current(); |
| runtime->SetResolutionMethod(runtime->CreateResolutionMethod()); |
| runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod()); |
| runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod()); |
| runtime->SetDefaultImt(runtime->CreateDefaultImt(this)); |
| |
| // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that |
| // we do not need friend classes or a publicly exposed setter. |
| quick_generic_jni_trampoline_ = reinterpret_cast<void*>(art_quick_generic_jni_trampoline); |
| if (!runtime->IsCompiler()) { |
| // We need to set up the generic trampolines since we don't have an image. |
| quick_resolution_trampoline_ = reinterpret_cast<void*>(art_quick_resolution_trampoline); |
| quick_imt_conflict_trampoline_ = reinterpret_cast<void*>(art_quick_imt_conflict_trampoline); |
| quick_to_interpreter_bridge_trampoline_ = reinterpret_cast<void*>(art_quick_to_interpreter_bridge); |
| } |
| |
| // Object, String and DexCache need to be rerun through FindSystemClass to finish init |
| java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self); |
| mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;"); |
| CHECK_EQ(java_lang_Object.Get(), Object_class); |
| CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize()); |
| java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self); |
| mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;"); |
| std::ostringstream os1, os2; |
| java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail); |
| String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail); |
| CHECK_EQ(java_lang_String.Get(), String_class) << os1.str() << "\n\n" << os2.str(); |
| CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize()); |
| java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self); |
| mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;"); |
| CHECK_EQ(java_lang_String.Get(), String_class); |
| CHECK_EQ(java_lang_DexCache.Get(), DexCache_class); |
| CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize()); |
| |
| // Setup the primitive array type classes - can't be done until Object has a vtable. |
| SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z")); |
| mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); |
| |
| SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B")); |
| mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); |
| |
| mirror::Class* found_char_array_class = FindSystemClass(self, "[C"); |
| CHECK_EQ(char_array_class.Get(), found_char_array_class); |
| |
| SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S")); |
| mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); |
| |
| mirror::Class* found_int_array_class = FindSystemClass(self, "[I"); |
| CHECK_EQ(int_array_class.Get(), found_int_array_class); |
| |
| SetClassRoot(kLongArrayClass, FindSystemClass(self, "[J")); |
| mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); |
| |
| SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F")); |
| mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); |
| |
| SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D")); |
| mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); |
| |
| mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;"); |
| CHECK_EQ(class_array_class.Get(), found_class_array_class); |
| |
| mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;"); |
| CHECK_EQ(object_array_class.Get(), found_object_array_class); |
| |
| // Setup the single, global copy of "iftable". |
| mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;"); |
| CHECK(java_lang_Cloneable != nullptr); |
| mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;"); |
| CHECK(java_io_Serializable != nullptr); |
| // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to |
| // crawl up and explicitly list all of the supers as well. |
| { |
| mirror::IfTable* array_iftable = array_iftable_.Read(); |
| array_iftable->SetInterface(0, java_lang_Cloneable); |
| array_iftable->SetInterface(1, java_io_Serializable); |
| } |
| |
| // Sanity check Class[] and Object[]'s interfaces. |
| CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, class_array_class, 0)); |
| CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, class_array_class, 1)); |
| CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, object_array_class, 0)); |
| CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, object_array_class, 1)); |
| // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their |
| // dex_cache_ fields and register them in class_table_. |
| mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;"); |
| CHECK_EQ(java_lang_Class.Get(), Class_class); |
| |
| java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self); |
| mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;"); |
| CHECK_EQ(java_lang_reflect_ArtMethod.Get(), Art_method_class); |
| |
| java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self); |
| mirror::Class* Art_field_class = FindSystemClass(self, "Ljava/lang/reflect/ArtField;"); |
| CHECK_EQ(java_lang_reflect_ArtField.Get(), Art_field_class); |
| |
| mirror::Class* String_array_class = |
| FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]); |
| CHECK_EQ(object_array_string.Get(), String_array_class); |
| |
| mirror::Class* Art_method_array_class = |
| FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]); |
| CHECK_EQ(object_array_art_method.Get(), Art_method_array_class); |
| |
| mirror::Class* Art_field_array_class = |
| FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]); |
| CHECK_EQ(object_array_art_field.Get(), Art_field_array_class); |
| |
| // End of special init trickery, subsequent classes may be loaded via FindSystemClass. |
| |
| // Create java.lang.reflect.Proxy root. |
| mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;"); |
| SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy); |
| |
| // java.lang.ref classes need to be specially flagged, but otherwise are normal classes |
| // finish initializing Reference class |
| java_lang_ref_Reference->SetStatus(mirror::Class::kStatusNotReady, self); |
| mirror::Class* Reference_class = FindSystemClass(self, "Ljava/lang/ref/Reference;"); |
| CHECK_EQ(java_lang_ref_Reference.Get(), Reference_class); |
| CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize()); |
| CHECK_EQ(java_lang_ref_Reference->GetClassSize(), mirror::Reference::ClassSize()); |
| mirror::Class* java_lang_ref_FinalizerReference = |
| FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); |
| java_lang_ref_FinalizerReference->SetAccessFlags( |
| java_lang_ref_FinalizerReference->GetAccessFlags() | |
| kAccClassIsReference | kAccClassIsFinalizerReference); |
| mirror::Class* java_lang_ref_PhantomReference = |
| FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); |
| java_lang_ref_PhantomReference->SetAccessFlags( |
| java_lang_ref_PhantomReference->GetAccessFlags() | |
| kAccClassIsReference | kAccClassIsPhantomReference); |
| mirror::Class* java_lang_ref_SoftReference = |
| FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); |
| java_lang_ref_SoftReference->SetAccessFlags( |
| java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference); |
| mirror::Class* java_lang_ref_WeakReference = |
| FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); |
| java_lang_ref_WeakReference->SetAccessFlags( |
| java_lang_ref_WeakReference->GetAccessFlags() | |
| kAccClassIsReference | kAccClassIsWeakReference); |
| |
| // Setup the ClassLoader, verifying the object_size_. |
| mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;"); |
| CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), mirror::ClassLoader::InstanceSize()); |
| SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader); |
| |
| // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and |
| // java.lang.StackTraceElement as a convenience. |
| SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); |
| mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); |
| SetClassRoot(kJavaLangClassNotFoundException, |
| FindSystemClass(self, "Ljava/lang/ClassNotFoundException;")); |
| SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;")); |
| SetClassRoot(kJavaLangStackTraceElementArrayClass, |
| FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); |
| mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); |
| |
| // Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly |
| // initialized. |
| { |
| const DexFile& dex_file = java_lang_Object->GetDexFile(); |
| const DexFile::StringId* void_string_id = dex_file.FindStringId("V"); |
| CHECK(void_string_id != nullptr); |
| uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id); |
| const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index); |
| CHECK(void_type_id != nullptr); |
| uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id); |
| // Now we resolve void type so the dex cache contains it. We use java.lang.Object class |
| // as referrer so the used dex cache is core's one. |
| mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get()); |
| CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid)); |
| self->AssertNoPendingException(); |
| } |
| |
| FinishInit(self); |
| |
| VLOG(startup) << "ClassLinker::InitFromCompiler exiting"; |
| } |
| |
| void ClassLinker::FinishInit(Thread* self) { |
| VLOG(startup) << "ClassLinker::FinishInit entering"; |
| |
| // Let the heap know some key offsets into java.lang.ref instances |
| // Note: we hard code the field indexes here rather than using FindInstanceField |
| // as the types of the field can't be resolved prior to the runtime being |
| // fully initialized |
| mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference); |
| mirror::Class* java_lang_ref_FinalizerReference = |
| FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); |
| |
| mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); |
| CHECK_STREQ(pendingNext->GetName(), "pendingNext"); |
| CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); |
| |
| mirror::ArtField* queue = java_lang_ref_Reference->GetInstanceField(1); |
| CHECK_STREQ(queue->GetName(), "queue"); |
| CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;"); |
| |
| mirror::ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2); |
| CHECK_STREQ(queueNext->GetName(), "queueNext"); |
| CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); |
| |
| mirror::ArtField* referent = java_lang_ref_Reference->GetInstanceField(3); |
| CHECK_STREQ(referent->GetName(), "referent"); |
| CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;"); |
| |
| mirror::ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2); |
| CHECK_STREQ(zombie->GetName(), "zombie"); |
| CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;"); |
| |
| // ensure all class_roots_ are initialized |
| for (size_t i = 0; i < kClassRootsMax; i++) { |
| ClassRoot class_root = static_cast<ClassRoot>(i); |
| mirror::Class* klass = GetClassRoot(class_root); |
| CHECK(klass != nullptr); |
| DCHECK(klass->IsArrayClass() || klass->IsPrimitive() || klass->GetDexCache() != nullptr); |
| // note SetClassRoot does additional validation. |
| // if possible add new checks there to catch errors early |
| } |
| |
| CHECK(!array_iftable_.IsNull()); |
| |
| // disable the slow paths in FindClass and CreatePrimitiveClass now |
| // that Object, Class, and Object[] are setup |
| init_done_ = true; |
| |
| VLOG(startup) << "ClassLinker::FinishInit exiting"; |
| } |
| |
| void ClassLinker::RunRootClinits() { |
| Thread* self = Thread::Current(); |
| for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) { |
| mirror::Class* c = GetClassRoot(ClassRoot(i)); |
| if (!c->IsArrayClass() && !c->IsPrimitive()) { |
| StackHandleScope<1> hs(self); |
| Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i)))); |
| EnsureInitialized(h_class, true, true); |
| self->AssertNoPendingException(); |
| } |
| } |
| } |
| |
| bool ClassLinker::GenerateOatFile(const char* dex_filename, |
| int oat_fd, |
| const char* oat_cache_filename, |
| std::string* error_msg) { |
| Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC. |
| std::string dex2oat(Runtime::Current()->GetCompilerExecutable()); |
| |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| std::string boot_image_option("--boot-image="); |
| if (heap->GetImageSpace() == nullptr) { |
| // TODO If we get a dex2dex compiler working we could maybe use that, OTOH since we are likely |
| // out of space anyway it might not matter. |
| *error_msg = StringPrintf("Cannot create oat file for '%s' because we are running " |
| "without an image.", dex_filename); |
| return false; |
| } |
| boot_image_option += heap->GetImageSpace()->GetImageLocation(); |
| |
| std::string dex_file_option("--dex-file="); |
| dex_file_option += dex_filename; |
| |
| std::string oat_fd_option("--oat-fd="); |
| StringAppendF(&oat_fd_option, "%d", oat_fd); |
| |
| std::string oat_location_option("--oat-location="); |
| oat_location_option += oat_cache_filename; |
| |
| std::vector<std::string> argv; |
| argv.push_back(dex2oat); |
| argv.push_back("--runtime-arg"); |
| argv.push_back("-classpath"); |
| argv.push_back("--runtime-arg"); |
| argv.push_back(Runtime::Current()->GetClassPathString()); |
| |
| Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); |
| |
| if (!Runtime::Current()->IsVerificationEnabled()) { |
| argv.push_back("--compiler-filter=verify-none"); |
| } |
| |
| if (Runtime::Current()->MustRelocateIfPossible()) { |
| argv.push_back("--runtime-arg"); |
| argv.push_back("-Xrelocate"); |
| } else { |
| argv.push_back("--runtime-arg"); |
| argv.push_back("-Xnorelocate"); |
| } |
| |
| if (!kIsTargetBuild) { |
| argv.push_back("--host"); |
| } |
| |
| argv.push_back(boot_image_option); |
| argv.push_back(dex_file_option); |
| argv.push_back(oat_fd_option); |
| argv.push_back(oat_location_option); |
| const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions(); |
| for (size_t i = 0; i < compiler_options.size(); ++i) { |
| argv.push_back(compiler_options[i].c_str()); |
| } |
| |
| if (!Exec(argv, error_msg)) { |
| // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly |
| // died. Ignore unlink failure, propagate the original error. |
| TEMP_FAILURE_RETRY(unlink(oat_cache_filename)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) { |
| WriterMutexLock mu(Thread::Current(), dex_lock_); |
| if (kIsDebugBuild) { |
| for (size_t i = 0; i < oat_files_.size(); ++i) { |
| CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation(); |
| } |
| } |
| VLOG(class_linker) << "Registering " << oat_file->GetLocation(); |
| oat_files_.push_back(oat_file); |
| return oat_file; |
| } |
| |
| OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { |
| VLOG(startup) << "ClassLinker::GetImageOatFile entering"; |
| OatFile* oat_file = space->ReleaseOatFile(); |
| CHECK_EQ(RegisterOatFile(oat_file), oat_file); |
| VLOG(startup) << "ClassLinker::GetImageOatFile exiting"; |
| return *oat_file; |
| } |
| |
| const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFileForDexFile(const DexFile& dex_file) { |
| const char* dex_location = dex_file.GetLocation().c_str(); |
| uint32_t dex_location_checksum = dex_file.GetLocationChecksum(); |
| return FindOpenedOatDexFile(nullptr, dex_location, &dex_location_checksum); |
| } |
| |
| const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_location, |
| const char* dex_location, |
| const uint32_t* dex_location_checksum) { |
| ReaderMutexLock mu(Thread::Current(), dex_lock_); |
| for (const OatFile* oat_file : oat_files_) { |
| DCHECK(oat_file != nullptr); |
| |
| if (oat_location != nullptr) { |
| if (oat_file->GetLocation() != oat_location) { |
| continue; |
| } |
| } |
| |
| const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, |
| dex_location_checksum, |
| false); |
| if (oat_dex_file != nullptr) { |
| return oat_dex_file; |
| } |
| } |
| return nullptr; |
| } |
| |
| |
| // Loads all multi dex files from the given oat file returning true on success. |
| // |
| // Parameters: |
| // oat_file - the oat file to load from |
| // dex_location - the dex location used to generate the oat file |
| // dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files) |
| // generated - whether or not the oat_file existed before or was just (re)generated |
| // error_msgs - any error messages will be appended here |
| // dex_files - the loaded dex_files will be appended here (only if the loading succeeds) |
| static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, |
| const char* dex_location, |
| const uint32_t* dex_location_checksum, |
| bool generated, |
| std::vector<std::string>* error_msgs, |
| std::vector<const DexFile*>* dex_files) { |
| if (oat_file == nullptr) { |
| return false; |
| } |
| |
| size_t old_size = dex_files->size(); // To rollback on error. |
| |
| bool success = true; |
| for (size_t i = 0; success; ++i) { |
| std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location); |
| const char* next_name = next_name_str.c_str(); |
| |
| uint32_t next_location_checksum; |
| uint32_t* next_location_checksum_pointer = &next_location_checksum; |
| std::string error_msg; |
| if ((i == 0) && (strcmp(next_name, dex_location) == 0)) { |
| // When i=0 the multidex name should be the same as the location name. We already have the |
| // checksum it so we don't need to recompute it. |
| if (dex_location_checksum == nullptr) { |
| next_location_checksum_pointer = nullptr; |
| } else { |
| next_location_checksum = *dex_location_checksum; |
| } |
| } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) { |
| DCHECK_EQ(false, i == 0 && generated); |
| next_location_checksum_pointer = nullptr; |
| } |
| |
| const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false); |
| |
| if (oat_dex_file == nullptr) { |
| if (i == 0 && generated) { |
| std::string error_msg; |
| error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out " |
| " file'%s'", dex_location, next_location_checksum, |
| oat_file->GetLocation().c_str()); |
| error_msgs->push_back(error_msg); |
| } |
| break; // Not found, done. |
| } |
| |
| // Checksum test. Test must succeed when generated. |
| success = !generated; |
| if (next_location_checksum_pointer != nullptr) { |
| success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum(); |
| } |
| |
| if (success) { |
| const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); |
| if (dex_file == nullptr) { |
| success = false; |
| error_msgs->push_back(error_msg); |
| } else { |
| dex_files->push_back(dex_file); |
| } |
| } |
| |
| // When we generated the file, we expect success, or something is terribly wrong. |
| CHECK_EQ(false, generated && !success) |
| << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str() |
| << std::hex << " dex_location_checksum=" << next_location_checksum |
| << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum(); |
| } |
| |
| if (dex_files->size() == old_size) { |
| success = false; // We did not even find classes.dex |
| } |
| |
| if (success) { |
| return true; |
| } else { |
| // Free all the dex files we have loaded. |
| auto it = dex_files->begin() + old_size; |
| auto it_end = dex_files->end(); |
| for (; it != it_end; it++) { |
| delete *it; |
| } |
| dex_files->erase(dex_files->begin() + old_size, it_end); |
| |
| return false; |
| } |
| } |
| |
| // Multidex files make it possible that some, but not all, dex files can be broken/outdated. This |
| // complicates the loading process, as we should not use an iterative loading process, because that |
| // would register the oat file and dex files that come before the broken one. Instead, check all |
| // multidex ahead of time. |
| bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location, |
| std::vector<std::string>* error_msgs, |
| std::vector<const DexFile*>* dex_files) { |
| // 1) Check whether we have an open oat file. |
| // This requires a dex checksum, use the "primary" one. |
| uint32_t dex_location_checksum; |
| uint32_t* dex_location_checksum_pointer = &dex_location_checksum; |
| bool have_checksum = true; |
| std::string checksum_error_msg; |
| if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) { |
| // This happens for pre-opted files since the corresponding dex files are no longer on disk. |
| dex_location_checksum_pointer = nullptr; |
| have_checksum = false; |
| } |
| |
| bool needs_registering = false; |
| |
| const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location, |
| dex_location_checksum_pointer); |
| std::unique_ptr<const OatFile> open_oat_file( |
| oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr); |
| |
| // 2) If we do not have an open one, maybe there's one on disk already. |
| |
| // In case the oat file is not open, we play a locking game here so |
| // that if two different processes race to load and register or generate |
| // (or worse, one tries to open a partial generated file) we will be okay. |
| // This is actually common with apps that use DexClassLoader to work |
| // around the dex method reference limit and that have a background |
| // service running in a separate process. |
| ScopedFlock scoped_flock; |
| |
| if (open_oat_file.get() == nullptr) { |
| if (oat_location != nullptr) { |
| // Can only do this if we have a checksum, else error. |
| if (!have_checksum) { |
| error_msgs->push_back(checksum_error_msg); |
| return false; |
| } |
| |
| std::string error_msg; |
| |
| // We are loading or creating one in the future. Time to set up the file lock. |
| if (!scoped_flock.Init(oat_location, &error_msg)) { |
| error_msgs->push_back(error_msg); |
| return false; |
| } |
| |
| // TODO Caller specifically asks for this oat_location. We should honor it. Probably? |
| open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum, |
| oat_location, &error_msg)); |
| |
| if (open_oat_file.get() == nullptr) { |
| std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s", |
| dex_location, oat_location, error_msg.c_str()); |
| VLOG(class_linker) << compound_msg; |
| error_msgs->push_back(compound_msg); |
| } |
| } else { |
| // TODO: What to lock here? |
| bool obsolete_file_cleanup_failed; |
| open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location, |
| dex_location_checksum_pointer, |
| kRuntimeISA, error_msgs, |
| &obsolete_file_cleanup_failed)); |
| // There's no point in going forward and eventually try to regenerate the |
| // file if we couldn't remove the obsolete one. Mostly likely we will fail |
| // with the same error when trying to write the new file. |
| // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS). |
| if (obsolete_file_cleanup_failed) { |
| return false; |
| } |
| } |
| needs_registering = true; |
| } |
| |
| // 3) If we have an oat file, check all contained multidex files for our dex_location. |
| // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument. |
| bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, |
| dex_location_checksum_pointer, |
| false, error_msgs, dex_files); |
| if (success) { |
| const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it. |
| if (needs_registering) { |
| // We opened the oat file, so we must register it. |
| RegisterOatFile(oat_file); |
| } |
| // If the file isn't executable we failed patchoat but did manage to get the dex files. |
| return oat_file->IsExecutable(); |
| } else { |
| if (needs_registering) { |
| // We opened it, delete it. |
| open_oat_file.reset(); |
| } else { |
| open_oat_file.release(); // Do not delete open oat files. |
| } |
| } |
| |
| // 4) If it's not the case (either no oat file or mismatches), regenerate and load. |
| |
| // Need a checksum, fail else. |
| if (!have_checksum) { |
| error_msgs->push_back(checksum_error_msg); |
| return false; |
| } |
| |
| // Look in cache location if no oat_location is given. |
| std::string cache_location; |
| if (oat_location == nullptr) { |
| // Use the dalvik cache. |
| const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA))); |
| cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str()); |
| oat_location = cache_location.c_str(); |
| } |
| |
| bool has_flock = true; |
| // Definitely need to lock now. |
| if (!scoped_flock.HasFile()) { |
| std::string error_msg; |
| if (!scoped_flock.Init(oat_location, &error_msg)) { |
| error_msgs->push_back(error_msg); |
| has_flock = false; |
| } |
| } |
| |
| if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) { |
| // Create the oat file. |
| open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(), |
| oat_location, error_msgs)); |
| } |
| |
| // Failed, bail. |
| if (open_oat_file.get() == nullptr) { |
| std::string error_msg; |
| // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress. |
| DexFile::Open(dex_location, dex_location, &error_msg, dex_files); |
| error_msgs->push_back(error_msg); |
| return false; |
| } |
| |
| // Try to load again, but stronger checks. |
| success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, |
| dex_location_checksum_pointer, |
| true, error_msgs, dex_files); |
| if (success) { |
| RegisterOatFile(open_oat_file.release()); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location, |
| uint32_t dex_location_checksum, |
| const char* oat_location, |
| std::string* error_msg) { |
| std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr, |
| !Runtime::Current()->IsCompiler(), |
| error_msg)); |
| if (oat_file.get() == nullptr) { |
| *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location, |
| error_msg->c_str()); |
| return nullptr; |
| } |
| Runtime* runtime = Runtime::Current(); |
| const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); |
| if (image_space != nullptr) { |
| const ImageHeader& image_header = image_space->GetImageHeader(); |
| uint32_t expected_image_oat_checksum = image_header.GetOatChecksum(); |
| uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum(); |
| if (expected_image_oat_checksum != actual_image_oat_checksum) { |
| *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of " |
| "0x%x, found 0x%x", oat_location, expected_image_oat_checksum, |
| actual_image_oat_checksum); |
| return nullptr; |
| } |
| |
| uintptr_t expected_image_oat_offset = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()); |
| uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(); |
| if (expected_image_oat_offset != actual_image_oat_offset) { |
| *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %" |
| PRIuPTR ", found %ud", oat_location, expected_image_oat_offset, |
| actual_image_oat_offset); |
| return nullptr; |
| } |
| int32_t expected_patch_delta = image_header.GetPatchDelta(); |
| int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta(); |
| if (expected_patch_delta != actual_patch_delta) { |
| *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, " |
| " found %d", oat_location, expected_patch_delta, actual_patch_delta); |
| return nullptr; |
| } |
| } |
| |
| const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, |
| &dex_location_checksum); |
| if (oat_dex_file == nullptr) { |
| *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location, |
| dex_location); |
| return nullptr; |
| } |
| uint32_t expected_dex_checksum = dex_location_checksum; |
| uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum(); |
| if (expected_dex_checksum != actual_dex_checksum) { |
| *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, " |
| "found 0x%x", oat_location, expected_dex_checksum, |
| actual_dex_checksum); |
| return nullptr; |
| } |
| std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg)); |
| if (dex_file.get() != nullptr) { |
| return oat_file.release(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location, |
| int fd, const char* oat_location, |
| std::vector<std::string>* error_msgs) { |
| // Generate the output oat file for the dex file |
| VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location; |
| std::string error_msg; |
| if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) { |
| CHECK(!error_msg.empty()); |
| error_msgs->push_back(error_msg); |
| return nullptr; |
| } |
| std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr, |
| !Runtime::Current()->IsCompiler(), |
| &error_msg)); |
| if (oat_file.get() == nullptr) { |
| std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s", |
| oat_location, error_msg.c_str()); |
| error_msgs->push_back(compound_msg); |
| return nullptr; |
| } |
| |
| return oat_file.release(); |
| } |
| |
| bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file, |
| const InstructionSet instruction_set) { |
| Runtime* runtime = Runtime::Current(); |
| const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); |
| if (image_space == nullptr) { |
| return false; |
| } |
| uint32_t image_oat_checksum = 0; |
| if (instruction_set == kRuntimeISA) { |
| const ImageHeader& image_header = image_space->GetImageHeader(); |
| image_oat_checksum = image_header.GetOatChecksum(); |
| } else { |
| std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie( |
| image_space->GetImageLocation().c_str(), instruction_set)); |
| image_oat_checksum = image_header->GetOatChecksum(); |
| } |
| return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum; |
| } |
| |
| bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file, |
| const InstructionSet instruction_set, |
| std::string* error_msg) { |
| Runtime* runtime = Runtime::Current(); |
| const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); |
| if (image_space == nullptr) { |
| *error_msg = "No image space for verification against"; |
| return false; |
| } |
| |
| // If the requested instruction set is the same as the current runtime, |
| // we can use the checksums directly. If it isn't, we'll have to read the |
| // image header from the image for the right instruction set. |
| uint32_t image_oat_checksum = 0; |
| uintptr_t image_oat_data_begin = 0; |
| int32_t image_patch_delta = 0; |
| if (instruction_set == runtime->GetInstructionSet()) { |
| const ImageHeader& image_header = image_space->GetImageHeader(); |
| image_oat_checksum = image_header.GetOatChecksum(); |
| image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()); |
| image_patch_delta = image_header.GetPatchDelta(); |
| } else { |
| std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie( |
| image_space->GetImageLocation().c_str(), instruction_set)); |
| image_oat_checksum = image_header->GetOatChecksum(); |
| image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()); |
| image_patch_delta = image_header->GetPatchDelta(); |
| } |
| const OatHeader& oat_header = oat_file->GetOatHeader(); |
| bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum); |
| |
| // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks. |
| if (!oat_file->IsPic()) { |
| ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta) |
| && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin); |
| } |
| if (!ret) { |
| *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)", |
| oat_file->GetLocation().c_str(), |
| oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), |
| oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), |
| oat_file->GetOatHeader().GetImagePatchDelta(), |
| image_oat_checksum, image_oat_data_begin, image_patch_delta); |
| } |
| return ret; |
| } |
| |
| bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file, |
| const char* dex_location, |
| uint32_t dex_location_checksum, |
| const InstructionSet instruction_set, |
| std::string* error_msg) { |
| if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) { |
| return false; |
| } |
| |
| const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, |
| &dex_location_checksum); |
| if (oat_dex_file == nullptr) { |
| *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x", |
| oat_file->GetLocation().c_str(), dex_location, dex_location_checksum); |
| for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { |
| *error_msg += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x", |
| oat_file->GetLocation().c_str(), |
| oat_dex_file->GetDexFileLocation().c_str(), |
| oat_dex_file->GetDexFileLocationChecksum()); |
| } |
| return false; |
| } |
| |
| if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) { |
| *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)", |
| oat_file->GetLocation().c_str(), |
| oat_dex_file->GetDexFileLocationChecksum(), |
| dex_location, dex_location_checksum); |
| return false; |
| } |
| return true; |
| } |
| |
| bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file, |
| const char* dex_location, |
| const uint32_t* dex_location_checksum, |
| std::string* error_msg) { |
| CHECK(oat_file != nullptr); |
| CHECK(dex_location != nullptr); |
| std::unique_ptr<const DexFile> dex_file; |
| if (dex_location_checksum == nullptr) { |
| // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is |
| // up-to-date. This is the common case in user builds for jar's and apk's in the /system |
| // directory. |
| const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, nullptr); |
| if (oat_dex_file == nullptr) { |
| *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat " |
| "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location, |
| error_msg->c_str()); |
| return false; |
| } |
| dex_file.reset(oat_dex_file->OpenDexFile(error_msg)); |
| } else { |
| bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum, |
| kRuntimeISA, error_msg); |
| if (!verified) { |
| return false; |
| } |
| dex_file.reset(oat_file->GetOatDexFile(dex_location, |
| dex_location_checksum)->OpenDexFile(error_msg)); |
| } |
| return dex_file.get() != nullptr; |
| } |
| |
| const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation( |
| const char* dex_location, |
| const uint32_t* dex_location_checksum, |
| InstructionSet isa, |
| std::vector<std::string>* error_msgs, |
| bool* obsolete_file_cleanup_failed) { |
| *obsolete_file_cleanup_failed = false; |
| bool already_opened = false; |
| std::string dex_location_str(dex_location); |
| std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa, |
| &already_opened, |
| obsolete_file_cleanup_failed, |
| error_msgs)); |
| std::string error_msg; |
| if (oat_file.get() == nullptr) { |
| error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'", |
| dex_location)); |
| return nullptr; |
| } else if (oat_file->IsExecutable() && |
| !VerifyOatWithDexFile(oat_file.get(), dex_location, |
| dex_location_checksum, &error_msg)) { |
| error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location " |
| "'%s': %s", oat_file->GetLocation().c_str(), dex_location, |
| error_msg.c_str())); |
| return nullptr; |
| } else if (!oat_file->IsExecutable() && |
| Runtime::Current()->GetHeap()->HasImageSpace() && |
| !VerifyOatImageChecksum(oat_file.get(), isa)) { |
| error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for " |
| "dex location '%s'. Image checksum incorrect.", |
| oat_file->GetLocation().c_str(), dex_location)); |
| return nullptr; |
| } else { |
| return oat_file.release(); |
| } |
| } |
| |
| const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) { |
| ReaderMutexLock mu(Thread::Current(), dex_lock_); |
| for (size_t i = 0; i < oat_files_.size(); i++) { |
| const OatFile* oat_file = oat_files_[i]; |
| DCHECK(oat_file != nullptr); |
| if (oat_file->GetLocation() == oat_location) { |
| return oat_file; |
| } |
| } |
| return nullptr; |
| } |
| |
| const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location, |
| InstructionSet isa, |
| bool *already_opened, |
| bool *obsolete_file_cleanup_failed, |
| std::vector<std::string>* error_msgs) { |
| // Find out if we've already opened the file |
| const OatFile* ret = nullptr; |
| std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa)); |
| ret = FindOpenedOatFileFromOatLocation(odex_filename); |
| if (ret != nullptr) { |
| *already_opened = true; |
| return ret; |
| } |
| |
| std::string dalvik_cache; |
| bool have_android_data = false; |
| bool have_dalvik_cache = false; |
| bool is_global_cache = false; |
| GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache, |
| &have_android_data, &have_dalvik_cache, &is_global_cache); |
| std::string cache_filename; |
| if (have_dalvik_cache) { |
| cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str()); |
| ret = FindOpenedOatFileFromOatLocation(cache_filename); |
| if (ret != nullptr) { |
| *already_opened = true; |
| return ret; |
| } |
| } else { |
| // If we need to relocate we should just place odex back where it started. |
| cache_filename = odex_filename; |
| } |
| |
| ret = nullptr; |
| |
| // We know that neither the odex nor the cache'd version is already in use, if it even exists. |
| // |
| // Now we do the following: |
| // 1) Try and open the odex version |
| // 2) If present, checksum-verified & relocated correctly return it |
| // 3) Close the odex version to free up its address space. |
| // 4) Try and open the cache version |
| // 5) If present, checksum-verified & relocated correctly return it |
| // 6) Close the cache version to free up its address space. |
| // 7) If we should relocate: |
| // a) If we have opened and checksum-verified the odex version relocate it to |
| // 'cache_filename' and return it |
| // b) If we have opened and checksum-verified the cache version relocate it in place and return |
| // it. This should not happen often (I think only the run-test's will hit this case). |
| // 8) If the cache-version was present we should delete it since it must be obsolete if we get to |
| // this point. |
| // 9) Return nullptr |
| |
| *already_opened = false; |
| const Runtime* runtime = Runtime::Current(); |
| CHECK(runtime != nullptr); |
| bool executable = !runtime->IsCompiler(); |
| |
| std::string odex_error_msg; |
| bool should_patch_system = false; |
| bool odex_checksum_verified = false; |
| bool have_system_odex = false; |
| { |
| // There is a high probability that both these oat files map similar/the same address |
| // spaces so we must scope them like this so they each gets its turn. |
| std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr, |
| nullptr, |
| executable, &odex_error_msg)); |
| if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa, |
| &odex_checksum_verified, |
| &odex_error_msg)) { |
| error_msgs->push_back(odex_error_msg); |
| return odex_oat_file.release(); |
| } else { |
| if (odex_checksum_verified) { |
| // We can just relocate |
| should_patch_system = true; |
| odex_error_msg = "Image Patches are incorrect"; |
| } |
| if (odex_oat_file.get() != nullptr) { |
| have_system_odex = true; |
| } |
| } |
| } |
| |
| std::string cache_error_msg; |
| bool should_patch_cache = false; |
| bool cache_checksum_verified = false; |
| if (have_dalvik_cache) { |
| std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr, |
| nullptr, |
| executable, &cache_error_msg)); |
| if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa, |
| &cache_checksum_verified, |
| &cache_error_msg)) { |
| error_msgs->push_back(cache_error_msg); |
| return cache_oat_file.release(); |
| } else if (cache_checksum_verified) { |
| // We can just relocate |
| should_patch_cache = true; |
| cache_error_msg = "Image Patches are incorrect"; |
| } |
| } else if (have_android_data) { |
| // dalvik_cache does not exist but android data does. This means we should be able to create |
| // it, so we should try. |
| GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true); |
| } |
| |
| ret = nullptr; |
| std::string error_msg; |
| if (runtime->CanRelocate()) { |
| // Run relocation |
| gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace(); |
| if (space != nullptr) { |
| const std::string& image_location = space->GetImageLocation(); |
| if (odex_checksum_verified && should_patch_system) { |
| ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg); |
| } else if (cache_checksum_verified && should_patch_cache) { |
| CHECK(have_dalvik_cache); |
| ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg); |
| } |
| } else if (have_system_odex) { |
| ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg); |
| } |
| } |
| if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) { |
| // implicitly: were able to fine where the cached version is but we were unable to use it, |
| // either as a destination for relocation or to open a file. We should delete it if it is |
| // there. |
| if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) { |
| std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when " |
| "searching for dex file %s: %s", |
| cache_filename.c_str(), dex_location.c_str(), |
| strerror(errno)); |
| error_msgs->push_back(rm_error_msg); |
| VLOG(class_linker) << rm_error_msg; |
| // Let the caller know that we couldn't remove the obsolete file. |
| // This is a good indication that further writes may fail as well. |
| *obsolete_file_cleanup_failed = true; |
| } |
| } |
| if (ret == nullptr) { |
| VLOG(class_linker) << error_msg; |
| error_msgs->push_back(error_msg); |
| std::string relocation_msg; |
| if (runtime->CanRelocate()) { |
| relocation_msg = StringPrintf(" and relocation failed"); |
| } |
| if (have_dalvik_cache && cache_checksum_verified) { |
| error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s " |
| "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(), |
| cache_filename.c_str(), cache_error_msg.c_str(), |
| relocation_msg.c_str()); |
| } else { |
| error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no " |
| "dalvik_cache availible)%s.", odex_filename.c_str(), |
| odex_error_msg.c_str(), relocation_msg.c_str()); |
| } |
| VLOG(class_linker) << error_msg; |
| error_msgs->push_back(error_msg); |
| } |
| return ret; |
| } |
| |
| const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path, |
| InstructionSet isa, |
| std::string* error_msg) { |
| // We open it non-executable |
| std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg)); |
| if (output.get() == nullptr) { |
| return nullptr; |
| } |
| if (!Runtime::Current()->GetHeap()->HasImageSpace() || |
| VerifyOatImageChecksum(output.get(), isa)) { |
| return output.release(); |
| } else { |
| *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.", |
| oat_path.c_str()); |
| return nullptr; |
| } |
| } |
| |
| const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat, |
| const std::string& output_oat, |
| const std::string& image_location, |
| InstructionSet isa, |
| std::string* error_msg) { |
| Runtime* runtime = Runtime::Current(); |
| DCHECK(runtime != nullptr); |
| if (!runtime->GetHeap()->HasImageSpace()) { |
| // We don't have an image space so there is no point in trying to patchoat. |
| LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are " |
| << "running without an image. Attempting to use oat file for interpretation."; |
| return GetInterpretedOnlyOat(input_oat, isa, error_msg); |
| } |
| if (!runtime->IsDex2OatEnabled()) { |
| // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the |
| // input_oat but make sure we only do interpretation on it's dex files. |
| LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being " |
| << "disabled. Attempting to use oat file for interpretation"; |
| return GetInterpretedOnlyOat(input_oat, isa, error_msg); |
| } |
| Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC. |
| std::string patchoat(runtime->GetPatchoatExecutable()); |
| |
| std::string isa_arg("--instruction-set="); |
| isa_arg += GetInstructionSetString(isa); |
| std::string input_oat_filename_arg("--input-oat-file="); |
| input_oat_filename_arg += input_oat; |
| std::string output_oat_filename_arg("--output-oat-file="); |
| output_oat_filename_arg += output_oat; |
| std::string patched_image_arg("--patched-image-location="); |
| patched_image_arg += image_location; |
| |
| std::vector<std::string> argv; |
| argv.push_back(patchoat); |
| argv.push_back(isa_arg); |
| argv.push_back(input_oat_filename_arg); |
| argv.push_back(output_oat_filename_arg); |
| argv.push_back(patched_image_arg); |
| |
| std::string command_line(Join(argv, ' ')); |
| LOG(INFO) << "Relocate Oat File: " << command_line; |
| bool success = Exec(argv, error_msg); |
| if (success) { |
| std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr, |
| !runtime->IsCompiler(), error_msg)); |
| bool checksum_verified = false; |
| if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified, |
| error_msg)) { |
| return output.release(); |
| } else if (output.get() != nullptr) { |
| *error_msg = StringPrintf("Patching of oat file '%s' succeeded " |
| "but output file '%s' failed verifcation: %s", |
| input_oat.c_str(), output_oat.c_str(), error_msg->c_str()); |
| } else { |
| *error_msg = StringPrintf("Patching of oat file '%s' succeeded " |
| "but was unable to open output file '%s': %s", |
| input_oat.c_str(), output_oat.c_str(), error_msg->c_str()); |
| } |
| } else if (!runtime->IsCompiler()) { |
| // patchoat failed which means we probably don't have enough room to place the output oat file, |
| // instead of failing we should just run the interpreter from the dex files in the input oat. |
| LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file " |
| << "for interpretation. patchoat failure was: " << *error_msg; |
| return GetInterpretedOnlyOat(input_oat, isa, error_msg); |
| } else { |
| *error_msg = StringPrintf("Patching of oat file '%s to '%s' " |
| "failed: %s", input_oat.c_str(), output_oat.c_str(), |
| error_msg->c_str()); |
| } |
| return nullptr; |
| } |
| |
| bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa, |
| bool* checksum_verified, |
| std::string* error_msg) { |
| std::string compound_msg("Oat file failed to verify: "); |
| uint32_t real_image_checksum; |
| void* real_image_oat_offset; |
| int32_t real_patch_delta; |
| const gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); |
| if (image_space == nullptr) { |
| *error_msg = "No image space present"; |
| return false; |
| } |
| if (isa == Runtime::Current()->GetInstructionSet()) { |
| const ImageHeader& image_header = image_space->GetImageHeader(); |
| real_image_checksum = image_header.GetOatChecksum(); |
| real_image_oat_offset = image_header.GetOatDataBegin(); |
| real_patch_delta = image_header.GetPatchDelta(); |
| } else { |
| std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie( |
| image_space->GetImageLocation().c_str(), isa)); |
| real_image_checksum = image_header->GetOatChecksum(); |
| real_image_oat_offset = image_header->GetOatDataBegin(); |
| real_patch_delta = image_header->GetPatchDelta(); |
| } |
| |
| const OatHeader& oat_header = oat_file->GetOatHeader(); |
| |
| uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum(); |
| *checksum_verified = oat_image_checksum == real_image_checksum; |
| if (!*checksum_verified) { |
| compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)", |
| real_image_checksum, oat_image_checksum); |
| } |
| |
| bool offset_verified; |
| bool patch_delta_verified; |
| |
| if (!oat_file->IsPic()) { |
| void* oat_image_oat_offset = |
| reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin()); |
| offset_verified = oat_image_oat_offset == real_image_oat_offset; |
| if (!offset_verified) { |
| compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)", |
| real_image_oat_offset, oat_image_oat_offset); |
| } |
| |
| int32_t oat_patch_delta = oat_header.GetImagePatchDelta(); |
| patch_delta_verified = oat_patch_delta == real_patch_delta; |
| if (!patch_delta_verified) { |
| compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)", |
| real_patch_delta, oat_patch_delta); |
| } |
| } else { |
| // If an oat file is PIC, we ignore offset and patching delta. |
| offset_verified = true; |
| patch_delta_verified = true; |
| } |
| |
| bool ret = (*checksum_verified && offset_verified && patch_delta_verified); |
| if (ret) { |
| *error_msg = compound_msg; |
| } |
| return ret; |
| } |
| |
| const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location, |
| std::string* error_msg) { |
| const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location); |
| if (oat_file != nullptr) { |
| return oat_file; |
| } |
| |
| return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(), |
| error_msg); |
| } |
| |
| static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg); |
| |
| DCHECK(obj != nullptr); |
| DCHECK(class_linker != nullptr); |
| |
| if (obj->IsArtMethod()) { |
| mirror::ArtMethod* method = obj->AsArtMethod(); |
| if (!method->IsNative()) { |
| method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); |
| if (method != Runtime::Current()->GetResolutionMethod()) { |
| method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); |
| #if defined(ART_USE_PORTABLE_COMPILER) |
| method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); |
| #endif |
| } |
| } |
| } |
| } |
| |
| void ClassLinker::InitFromImage() { |
| VLOG(startup) << "ClassLinker::InitFromImage entering"; |
| CHECK(!init_done_); |
| |
| Thread* self = Thread::Current(); |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| gc::space::ImageSpace* space = heap->GetImageSpace(); |
| dex_cache_image_class_lookup_required_ = true; |
| CHECK(space != nullptr); |
| OatFile& oat_file = GetImageOatFile(space); |
| CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U); |
| CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); |
| const char* image_file_location = oat_file.GetOatHeader(). |
| GetStoreValueByKey(OatHeader::kImageLocationKey); |
| CHECK(image_file_location == nullptr || *image_file_location == 0); |
| portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline(); |
| quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline(); |
| portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline(); |
| quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline(); |
| quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline(); |
| quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge(); |
| mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); |
| mirror::ObjectArray<mirror::DexCache>* dex_caches = |
| dex_caches_object->AsObjectArray<mirror::DexCache>(); |
| |
| StackHandleScope<1> hs(self); |
| Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( |
| space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> |
| AsObjectArray<mirror::Class>())); |
| class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); |
| |
| // Special case of setting up the String class early so that we can test arbitrary objects |
| // as being Strings or not |
| mirror::String::SetClass(GetClassRoot(kJavaLangString)); |
| |
| CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(), |
| static_cast<uint32_t>(dex_caches->GetLength())); |
| for (int32_t i = 0; i < dex_caches->GetLength(); i++) { |
| StackHandleScope<1> hs(self); |
| Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_caches->Get(i))); |
| const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); |
| const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(), |
| nullptr); |
| CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location; |
| std::string error_msg; |
| const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); |
| if (dex_file == nullptr) { |
| LOG(FATAL) << "Failed to open dex file " << dex_file_location |
| << " from within oat file " << oat_file.GetLocation() |
| << " error '" << error_msg << "'"; |
| } |
| |
| CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); |
| |
| AppendToBootClassPath(*dex_file, dex_cache); |
| } |
| |
| // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live |
| // bitmap walk. |
| mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod)); |
| size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize(); |
| if (!Runtime::Current()->IsCompiler()) { |
| // Compiler supports having an image with a different pointer size than the runtime. This |
| // happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may |
| // also use 32 bit dex2oat on a system with 64 bit apps. |
| CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*))) |
| << sizeof(void*); |
| } |
| if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) { |
| image_pointer_size_ = 4; |
| } else { |
| CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8)); |
| image_pointer_size_ = 8; |
| } |
| |
| // Set entry point to interpreter if in InterpretOnly mode. |
| if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { |
| ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); |
| heap->VisitObjects(InitFromImageInterpretOnlyCallback, this); |
| } |
| |
| // reinit class_roots_ |
| mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass)); |
| class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); |
| |
| // reinit array_iftable_ from any array class instance, they should be == |
| array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable()); |
| DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); |
| // String class root was set above |
| mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); |
| mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField)); |
| mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); |
| mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); |
| mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); |
| mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); |
| mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); |
| mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass)); |
| mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); |
| mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); |
| mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); |
| mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); |
| |
| FinishInit(self); |
| |
| VLOG(startup) << "ClassLinker::InitFromImage exiting"; |
| } |
| |
| void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) { |
| WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); |
| if ((flags & kVisitRootFlagAllRoots) != 0) { |
| for (GcRoot<mirror::Class>& root : class_table_) { |
| root.VisitRoot(callback, arg, RootInfo(kRootStickyClass)); |
| } |
| for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { |
| root.VisitRoot(callback, arg, RootInfo(kRootStickyClass)); |
| } |
| } else if ((flags & kVisitRootFlagNewRoots) != 0) { |
| for (auto& root : new_class_roots_) { |
| mirror::Class* old_ref = root.Read<kWithoutReadBarrier>(); |
| root.VisitRoot(callback, arg, RootInfo(kRootStickyClass)); |
| mirror::Class* new_ref = root.Read<kWithoutReadBarrier>(); |
| if (UNLIKELY(new_ref != old_ref)) { |
| // Uh ohes, GC moved a root in the log. Need to search the class_table and update the |
| // corresponding object. This is slow, but luckily for us, this may only happen with a |
| // concurrent moving GC. |
| auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref)); |
| DCHECK(it != class_table_.end()); |
| *it = GcRoot<mirror::Class>(new_ref); |
| } |
| } |
| } |
| if ((flags & kVisitRootFlagClearRootLog) != 0) { |
| new_class_roots_.clear(); |
| } |
| if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { |
| log_new_class_table_roots_ = true; |
| } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { |
| log_new_class_table_roots_ = false; |
| } |
| // We deliberately ignore the class roots in the image since we |
| // handle image roots by using the MS/CMS rescanning of dirty cards. |
| } |
| |
| // Keep in sync with InitCallback. Anything we visit, we need to |
| // reinit references to when reinitializing a ClassLinker from a |
| // mapped image. |
| void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) { |
| class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal)); |
| Thread* self = Thread::Current(); |
| { |
| ReaderMutexLock mu(self, dex_lock_); |
| if ((flags & kVisitRootFlagAllRoots) != 0) { |
| for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) { |
| dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal)); |
| } |
| } else if ((flags & kVisitRootFlagNewRoots) != 0) { |
| for (size_t index : new_dex_cache_roots_) { |
| dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal)); |
| } |
| } |
| if ((flags & kVisitRootFlagClearRootLog) != 0) { |
| new_dex_cache_roots_.clear(); |
| } |
| if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { |
| log_new_dex_caches_roots_ = true; |
| } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { |
| log_new_dex_caches_roots_ = false; |
| } |
| } |
| VisitClassRoots(callback, arg, flags); |
| array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal)); |
| DCHECK(!array_iftable_.IsNull()); |
| for (size_t i = 0; i < kFindArrayCacheSize; ++i) { |
| find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal)); |
| } |
| } |
| |
| void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) { |
| if (dex_cache_image_class_lookup_required_) { |
| MoveImageClassesToClassTable(); |
| } |
| // TODO: why isn't this a ReaderMutexLock? |
| WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); |
| for (GcRoot<mirror::Class>& root : class_table_) { |
| if (!visitor(root.Read(), arg)) { |
| return; |
| } |
| } |
| for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { |
| if (!visitor(root.Read(), arg)) { |
| return; |
| } |
| } |
| } |
| |
| static bool GetClassesVisitorSet(mirror::Class* c, void* arg) { |
| std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg); |
| classes->insert(c); |
| return true; |
| } |
| |
| struct GetClassesVisitorArrayArg { |
| Handle<mirror::ObjectArray<mirror::Class>>* classes; |
| int32_t index; |
| bool success; |
| }; |
| |
| static bool GetClassesVisitorArray(mirror::Class* c, void* varg) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg); |
| if (arg->index < (*arg->classes)->GetLength()) { |
| (*arg->classes)->Set(arg->index, c); |
| arg->index++; |
| return true; |
| } else { |
| arg->success = false; |
| return false; |
| } |
| } |
| |
| void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) { |
| // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem |
| // is avoiding duplicates. |
| if (!kMovingClasses) { |
| std::set<mirror::Class*> classes; |
| VisitClasses(GetClassesVisitorSet, &classes); |
| for (mirror::Class* klass : classes) { |
| if (!visitor(klass, arg)) { |
| return; |
| } |
| } |
| } else { |
| Thread* self = Thread::Current(); |
| StackHandleScope<1> hs(self); |
| Handle<mirror::ObjectArray<mirror::Class>> classes = |
| hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); |
| GetClassesVisitorArrayArg local_arg; |
| local_arg.classes = &classes; |
| local_arg.success = false; |
| // We size the array assuming classes won't be added to the class table during the visit. |
| // If this assumption fails we iterate again. |
| while (!local_arg.success) { |
| size_t class_table_size; |
| { |
| ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); |
| class_table_size = class_table_.Size() + pre_zygote_class_table_.Size(); |
| } |
| mirror::Class* class_type = mirror::Class::GetJavaLangClass(); |
| mirror::Class* array_of_class = FindArrayClass(self, &class_type); |
| classes.Assign( |
| mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size)); |
| CHECK(classes.Get() != nullptr); // OOME. |
| local_arg.index = 0; |
| local_arg.success = true; |
| VisitClasses(GetClassesVisitorArray, &local_arg); |
| } |
| for (int32_t i = 0; i < classes->GetLength(); ++i) { |
| // If the class table shrank during creation of the clases array we expect null elements. If |
| // the class table grew then the loop repeats. If classes are created after the loop has |
| // finished then we don't visit. |
| mirror::Class* klass = classes->Get(i); |
| if (klass != nullptr && !visitor(klass, arg)) { |
| return; |
| } |
| } |
| } |
| } |
| |
| ClassLinker::~ClassLinker() { |
| mirror::Class::ResetClass(); |
| mirror::String::ResetClass(); |
| mirror::Reference::ResetClass(); |
| mirror::ArtField::ResetClass(); |
| mirror::ArtMethod::ResetClass(); |
| mirror::BooleanArray::ResetArrayClass(); |
| mirror::ByteArray::ResetArrayClass(); |
| mirror::CharArray::ResetArrayClass(); |
| mirror::DoubleArray::ResetArrayClass(); |
| mirror::FloatArray::ResetArrayClass(); |
| mirror::IntArray::ResetArrayClass(); |
| mirror::LongArray::ResetArrayClass(); |
| mirror::ShortArray::ResetArrayClass(); |
| mirror::Throwable::ResetClass(); |
| mirror::StackTraceElement::ResetClass(); |
| STLDeleteElements(&boot_class_path_); |
| STLDeleteElements(&oat_files_); |
| } |
| |
| mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| StackHandleScope<16> hs(self); |
| Handle<mirror::Class> dex_cache_class(hs.NewHandle(GetClassRoot(kJavaLangDexCache))); |
| Handle<mirror::DexCache> dex_cache( |
| hs.NewHandle(down_cast<mirror::DexCache*>( |
| heap->AllocObject<true>(self, dex_cache_class.Get(), dex_cache_class->GetObjectSize(), |
| VoidFunctor())))); |
| if (dex_cache.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::String> |
| location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str()))); |
| if (location.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::String>> |
| strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds()))); |
| if (strings.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::Class>> |
| types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds()))); |
| if (types.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::ArtMethod>> |
| methods(hs.NewHandle(AllocArtMethodArray(self, dex_file.NumMethodIds()))); |
| if (methods.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::ArtField>> |
| fields(hs.NewHandle(AllocArtFieldArray(self, dex_file.NumFieldIds()))); |
| if (fields.Get() == nullptr) { |
| return nullptr; |
| } |
| dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(), |
| fields.Get()); |
| return dex_cache.Get(); |
| } |
| |
| mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class, |
| uint32_t class_size) { |
| DCHECK_GE(class_size, sizeof(mirror::Class)); |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| mirror::Class::InitializeClassVisitor visitor(class_size); |
| mirror::Object* k = kMovingClasses ? |
| heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) : |
| heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor); |
| if (UNLIKELY(k == nullptr)) { |
| CHECK(self->IsExceptionPending()); // OOME. |
| return nullptr; |
| } |
| return k->AsClass(); |
| } |
| |
| mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { |
| return AllocClass(self, GetClassRoot(kJavaLangClass), class_size); |
| } |
| |
| mirror::ArtField* ClassLinker::AllocArtField(Thread* self) { |
| return down_cast<mirror::ArtField*>( |
| GetClassRoot(kJavaLangReflectArtField)->AllocNonMovableObject(self)); |
| } |
| |
| mirror::ArtMethod* ClassLinker::AllocArtMethod(Thread* self) { |
| return down_cast<mirror::ArtMethod*>( |
| GetClassRoot(kJavaLangReflectArtMethod)->AllocNonMovableObject(self)); |
| } |
| |
| mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray( |
| Thread* self, size_t length) { |
| return mirror::ObjectArray<mirror::StackTraceElement>::Alloc( |
| self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); |
| } |
| |
| mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, |
| mirror::Class* klass) { |
| DCHECK(klass != nullptr); |
| |
| // For temporary classes we must wait for them to be retired. |
| if (init_done_ && klass->IsTemp()) { |
| CHECK(!klass->IsResolved()); |
| if (klass->IsErroneous()) { |
| ThrowEarlierClassFailure(klass); |
| return nullptr; |
| } |
| StackHandleScope<1> hs(self); |
| Handle<mirror::Class> h_class(hs.NewHandle(klass)); |
| ObjectLock<mirror::Class> lock(self, h_class); |
| // Loop and wait for the resolving thread to retire this class. |
| while (!h_class->IsRetired() && !h_class->IsErroneous()) { |
| lock.WaitIgnoringInterrupts(); |
| } |
| if (h_class->IsErroneous()) { |
| ThrowEarlierClassFailure(h_class.Get()); |
| return nullptr; |
| } |
| CHECK(h_class->IsRetired()); |
| // Get the updated class from class table. |
| klass = LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor), |
| h_class.Get()->GetClassLoader()); |
| } |
| |
| // Wait for the class if it has not already been linked. |
| if (!klass->IsResolved() && !klass->IsErroneous()) { |
| StackHandleScope<1> hs(self); |
| HandleWrapper<mirror::Class> h_class(hs.NewHandleWrapper(&klass)); |
| ObjectLock<mirror::Class> lock(self, h_class); |
| // Check for circular dependencies between classes. |
| if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) { |
| ThrowClassCircularityError(h_class.Get()); |
| h_class->SetStatus(mirror::Class::kStatusError, self); |
| return nullptr; |
| } |
| // Wait for the pending initialization to complete. |
| while (!h_class->IsResolved() && !h_class->IsErroneous()) { |
| lock.WaitIgnoringInterrupts(); |
| } |
| } |
| |
| if (klass->IsErroneous()) { |
| ThrowEarlierClassFailure(klass); |
| return nullptr; |
| } |
| // Return the loaded class. No exceptions should be pending. |
| CHECK(klass->IsResolved()) << PrettyClass(klass); |
| self->AssertNoPendingException(); |
| return klass; |
| } |
| |
| typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry; |
| |
| // Search a collection of DexFiles for a descriptor |
| ClassPathEntry FindInClassPath(const char* descriptor, |
| size_t hash, const std::vector<const DexFile*>& class_path) { |
| for (const DexFile* dex_file : class_path) { |
| const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash); |
| if (dex_class_def != nullptr) { |
| return ClassPathEntry(dex_file, dex_class_def); |
| } |
| } |
| return ClassPathEntry(nullptr, nullptr); |
| } |
| |
| mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, |
| Thread* self, const char* descriptor, |
| size_t hash, |
| Handle<mirror::ClassLoader> class_loader) { |
| if (class_loader->GetClass() != |
| soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) || |
| class_loader->GetParent()->GetClass() != |
| soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) { |
| return nullptr; |
| } |
| ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); |
| // Check if this would be found in the parent boot class loader. |
| if (pair.second != nullptr) { |
| mirror::Class* klass = LookupClass(descriptor, hash, nullptr); |
| if (klass != nullptr) { |
| return EnsureResolved(self, descriptor, klass); |
| } |
| klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, |
| *pair.second); |
| if (klass != nullptr) { |
| return klass; |
| } |
| CHECK(self->IsExceptionPending()) << descriptor; |
| self->ClearException(); |
| } else { |
| // RegisterDexFile may allocate dex caches (and cause thread suspension). |
| StackHandleScope<3> hs(self); |
| // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. |
| // We need to get the DexPathList and loop through it. |
| Handle<mirror::ArtField> cookie_field = |
| hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie)); |
| Handle<mirror::ArtField> dex_file_field = |
| hs.NewHandle( |
| soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile)); |
| mirror::Object* dex_path_list = |
| soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> |
| GetObject(class_loader.Get()); |
| if (dex_path_list != nullptr && dex_file_field.Get() != nullptr && |
| cookie_field.Get() != nullptr) { |
| // DexPathList has an array dexElements of Elements[] which each contain a dex file. |
| mirror::Object* dex_elements_obj = |
| soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> |
| GetObject(dex_path_list); |
| // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look |
| // at the mCookie which is a DexFile vector. |
| if (dex_elements_obj != nullptr) { |
| Handle<mirror::ObjectArray<mirror::Object>> dex_elements = |
| hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); |
| for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { |
| mirror::Object* element = dex_elements->GetWithoutChecks(i); |
| if (element == nullptr) { |
| // Should never happen, fall back to java code to throw a NPE. |
| break; |
| } |
| mirror::Object* dex_file = dex_file_field->GetObject(element); |
| if (dex_file != nullptr) { |
| const uint64_t cookie = cookie_field->GetLong(dex_file); |
| auto* dex_files = |
| reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie)); |
| if (dex_files == nullptr) { |
| // This should never happen so log a warning. |
| LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; |
| break; |
| } |
| for (const DexFile* dex_file : *dex_files) { |
| const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash); |
| if (dex_class_def != nullptr) { |
| RegisterDexFile(*dex_file); |
| mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, *dex_file, |
| *dex_class_def); |
| if (klass == nullptr) { |
| CHECK(self->IsExceptionPending()) << descriptor; |
| self->ClearException(); |
| return nullptr; |
| } |
| return klass; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, |
| Handle<mirror::ClassLoader> class_loader) { |
| DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; |
| DCHECK(self != nullptr); |
| self->AssertNoPendingException(); |
| if (descriptor[1] == '\0') { |
| // only the descriptors of primitive types should be 1 character long, also avoid class lookup |
| // for primitive classes that aren't backed by dex files. |
| return FindPrimitiveClass(descriptor[0]); |
| } |
| const size_t hash = ComputeModifiedUtf8Hash(descriptor); |
| // Find the class in the loaded classes table. |
| mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get()); |
| if (klass != nullptr) { |
| return EnsureResolved(self, descriptor, klass); |
| } |
| // Class is not yet loaded. |
| if (descriptor[0] == '[') { |
| return CreateArrayClass(self, descriptor, hash, class_loader); |
| } else if (class_loader.Get() == nullptr) { |
| // The boot class loader, search the boot class path. |
| ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); |
| if (pair.second != nullptr) { |
| return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, |
| *pair.second); |
| } else { |
| // The boot class loader is searched ahead of the application class loader, failures are |
| // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to |
| // trigger the chaining with a proper stack trace. |
| mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); |
| self->SetException(ThrowLocation(), pre_allocated); |
| return nullptr; |
| } |
| } else if (Runtime::Current()->UseCompileTimeClassPath()) { |
| // First try with the bootstrap class loader. |
| if (class_loader.Get() != nullptr) { |
| klass = LookupClass(descriptor, hash, nullptr); |
| if (klass != nullptr) { |
| return EnsureResolved(self, descriptor, klass); |
| } |
| } |
| // If the lookup failed search the boot class path. We don't perform a recursive call to avoid |
| // a NoClassDefFoundError being allocated. |
| ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); |
| if (pair.second != nullptr) { |
| return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, |
| *pair.second); |
| } |
| // Next try the compile time class path. |
| const std::vector<const DexFile*>* class_path; |
| { |
| ScopedObjectAccessUnchecked soa(self); |
| ScopedLocalRef<jobject> jclass_loader(soa.Env(), |
| soa.AddLocalReference<jobject>(class_loader.Get())); |
| class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get()); |
| } |
| pair = FindInClassPath(descriptor, hash, *class_path); |
| if (pair.second != nullptr) { |
| return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second); |
| } |
| } else { |
| ScopedObjectAccessUnchecked soa(self); |
| mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader); |
| if (klass != nullptr) { |
| return klass; |
| } |
| ScopedLocalRef<jobject> class_loader_object(soa.Env(), |
| soa.AddLocalReference<jobject>(class_loader.Get())); |
| std::string class_name_string(DescriptorToDot(descriptor)); |
| ScopedLocalRef<jobject> result(soa.Env(), nullptr); |
| { |
| ScopedThreadStateChange tsc(self, kNative); |
| ScopedLocalRef<jobject> class_name_object(soa.Env(), |
| soa.Env()->NewStringUTF(class_name_string.c_str())); |
| if (class_name_object.get() == nullptr) { |
| DCHECK(self->IsExceptionPending()); // OOME. |
| return nullptr; |
| } |
| CHECK(class_loader_object.get() != nullptr); |
| result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(), |
| WellKnownClasses::java_lang_ClassLoader_loadClass, |
| class_name_object.get())); |
| } |
| if (self->IsExceptionPending()) { |
| // If the ClassLoader threw, pass that exception up. |
| return nullptr; |
| } else if (result.get() == nullptr) { |
| // broken loader - throw NPE to be compatible with Dalvik |
| ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s", |
| class_name_string.c_str()).c_str()); |
| return nullptr; |
| } else { |
| // success, return mirror::Class* |
| return soa.Decode<mirror::Class*>(result.get()); |
| } |
| } |
| |
| ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str()); |
| return nullptr; |
| } |
| |
| mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash, |
| Handle<mirror::ClassLoader> class_loader, |
| const DexFile& dex_file, |
| const DexFile::ClassDef& dex_class_def) { |
| StackHandleScope<3> hs(self); |
| auto klass = hs.NewHandle<mirror::Class>(nullptr); |
| |
| // Load the class from the dex file. |
| if (UNLIKELY(!init_done_)) { |
| // finish up init of hand crafted class_roots_ |
| if (strcmp(descriptor, "Ljava/lang/Object;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangObject)); |
| } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangClass)); |
| } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangString)); |
| } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangRefReference)); |
| } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangDexCache)); |
| } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangReflectArtField)); |
| } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangReflectArtMethod)); |
| } |
| } |
| |
| if (klass.Get() == nullptr) { |
| // Allocate a class with the status of not ready. |
| // Interface object should get the right size here. Regular class will |
| // figure out the right size later and be replaced with one of the right |
| // size when the class becomes resolved. |
| klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); |
| } |
| if (UNLIKELY(klass.Get() == nullptr)) { |
| CHECK(self->IsExceptionPending()); // Expect an OOME. |
| return nullptr; |
| } |
| klass->SetDexCache(FindDexCache(dex_file)); |
| LoadClass(dex_file, dex_class_def, klass, class_loader.Get()); |
| ObjectLock<mirror::Class> lock(self, klass); |
| if (self->IsExceptionPending()) { |
| // An exception occured during load, set status to erroneous while holding klass' lock in case |
| // notification is necessary. |
| if (!klass->IsErroneous()) { |
| klass->SetStatus(mirror::Class::kStatusError, self); |
| } |
| return nullptr; |
| } |
| klass->SetClinitThreadId(self->GetTid()); |
| |
| // Add the newly loaded class to the loaded classes table. |
| mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash); |
| if (existing != nullptr) { |
| // We failed to insert because we raced with another thread. Calling EnsureResolved may cause |
| // this thread to block. |
| return EnsureResolved(self, descriptor, existing); |
| } |
| |
| // Finish loading (if necessary) by finding parents |
| CHECK(!klass->IsLoaded()); |
| if (!LoadSuperAndInterfaces(klass, dex_file)) { |
| // Loading failed. |
| if (!klass->IsErroneous()) { |
| klass->SetStatus(mirror::Class::kStatusError, self); |
| } |
| return nullptr; |
| } |
| CHECK(klass->IsLoaded()); |
| // Link the class (if necessary) |
| CHECK(!klass->IsResolved()); |
| // TODO: Use fast jobjects? |
| auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); |
| |
| mirror::Class* new_class = nullptr; |
| if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) { |
| // Linking failed. |
| if (!klass->IsErroneous()) { |
| klass->SetStatus(mirror::Class::kStatusError, self); |
| } |
| return nullptr; |
| } |
| self->AssertNoPendingException(); |
| CHECK(new_class != nullptr) << descriptor; |
| CHECK(new_class->IsResolved()) << descriptor; |
| |
| Handle<mirror::Class> new_class_h(hs.NewHandle(new_class)); |
| |
| /* |
| * We send CLASS_PREPARE events to the debugger from here. The |
| * definition of "preparation" is creating the static fields for a |
| * class and initializing them to the standard default values, but not |
| * executing any code (that comes later, during "initialization"). |
| * |
| * We did the static preparation in LinkClass. |
| * |
| * The class has been prepared and resolved but possibly not yet verified |
| * at this point. |
| */ |
| Dbg::PostClassPrepare(new_class_h.Get()); |
| |
| return new_class_h.Get(); |
| } |
| |
| uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, |
| const DexFile::ClassDef& dex_class_def) { |
| const byte* class_data = dex_file.GetClassData(dex_class_def); |
| size_t num_ref = 0; |
| size_t num_32 = 0; |
| size_t num_64 = 0; |
| if (class_data != nullptr) { |
| for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { |
| const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex()); |
| const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); |
| char c = descriptor[0]; |
| if (c == 'L' || c == '[') { |
| num_ref++; |
| } else if (c == 'J' || c == 'D') { |
| num_64++; |
| } else { |
| num_32++; |
| } |
| } |
| } |
| return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref); |
| } |
| |
| bool ClassLinker::FindOatClass(const DexFile& dex_file, |
| uint16_t class_def_idx, |
| OatFile::OatClass* oat_class) { |
| DCHECK(oat_class != nullptr); |
| DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); |
| const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file); |
| if (oat_dex_file == nullptr) { |
| return false; |
| } |
| *oat_class = oat_dex_file->GetOatClass(class_def_idx); |
| return true; |
| } |
| |
| static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, |
| uint32_t method_idx) { |
| const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); |
| const byte* class_data = dex_file.GetClassData(class_def); |
| CHECK(class_data != nullptr); |
| ClassDataItemIterator it(dex_file, class_data); |
| // Skip fields |
| while (it.HasNextStaticField()) { |
|