diff options
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r-- | runtime/class_linker.cc | 757 |
1 files changed, 608 insertions, 149 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ed833c4335..be9310abd8 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -336,6 +336,10 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Use the pointer size from the runtime since we are probably creating the image. image_pointer_size_ = InstructionSetPointerSize(runtime->GetInstructionSet()); + if (!ValidPointerSize(image_pointer_size_)) { + *error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_); + return false; + } // java_lang_Class comes first, it's needed for AllocClass // The GC can't handle an object with a null class since we can't get the size of this object. @@ -489,7 +493,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b return false; } AppendToBootClassPath(self, *dex_file); - opened_dex_files_.push_back(std::move(dex_file)); + boot_dex_files_.push_back(std::move(dex_file)); } // now we can use FindSystemClass @@ -878,6 +882,7 @@ struct TrampolineCheckData { ArtMethod* m; bool error; }; + static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS { if (obj->IsClass()) { mirror::Class* klass = obj->AsClass(); @@ -896,8 +901,8 @@ static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_AN } } -bool ClassLinker::InitFromImage(std::string* error_msg) { - VLOG(startup) << "ClassLinker::InitFromImage entering"; +bool ClassLinker::InitFromBootImage(std::string* error_msg) { + VLOG(startup) << __FUNCTION__ << " entering"; CHECK(!init_done_); Runtime* const runtime = Runtime::Current(); @@ -906,6 +911,21 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces(); CHECK(!spaces.empty()); image_pointer_size_ = spaces[0]->GetImageHeader().GetPointerSize(); + if (!ValidPointerSize(image_pointer_size_)) { + *error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_); + return false; + } + if (!runtime->IsAotCompiler()) { + // Only the Aot compiler supports having an image with a different pointer size than the + // runtime. This happens on the host for compiling 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. + if (image_pointer_size_ != sizeof(void*)) { + *error_msg = StringPrintf("Runtime must use current image pointer size: %zu vs %zu", + image_pointer_size_, + sizeof(void*)); + return false; + } + } dex_cache_boot_image_class_lookup_required_ = true; std::vector<const OatFile*> oat_files = runtime->GetOatFileManager().RegisterImageOatFiles(spaces); @@ -957,19 +977,10 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { } } - StackHandleScopeCollection handles(self); - std::vector<Handle<mirror::ObjectArray<mirror::DexCache>>> dex_caches_vector; - for (gc::space::ImageSpace* space : spaces) { - Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(handles.NewHandle( - space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches)-> - AsObjectArray<mirror::DexCache>())); - dex_caches_vector.push_back(dex_caches); - } - - Handle<mirror::ObjectArray<mirror::Class>> class_roots(handles.NewHandle( - spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> - AsObjectArray<mirror::Class>())); - class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); + class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>( + down_cast<mirror::ObjectArray<mirror::Class>*>( + spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); + mirror::Class::SetClassClass(class_roots_.Read()->Get(kJavaLangClass)); // Special case of setting up the String class early so that we can test arbitrary objects // as being Strings or not @@ -982,162 +993,595 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { runtime->SetSentinel(heap->AllocNonMovableObject<true>( self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); - uint32_t dex_file_count = 0; - for (const OatFile* oat_file : oat_files) { - dex_file_count += oat_file->GetOatHeader().GetDexFileCount(); + // 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::Field::SetClass(GetClassRoot(kJavaLangReflectField)); + mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); + mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); + mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); + mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); + mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); + mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); + 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)); + + for (gc::space::ImageSpace* image_space : spaces) { + // Boot class loader, use a null handle. + std::vector<std::unique_ptr<const DexFile>> dex_files; + if (!AddImageSpace(image_space, + ScopedNullHandle<mirror::ClassLoader>(), + /*dex_elements*/nullptr, + /*dex_location*/nullptr, + /*out*/&dex_files, + error_msg)) { + return false; + } + // Append opened dex files at the end. + boot_dex_files_.insert(boot_dex_files_.end(), + std::make_move_iterator(dex_files.begin()), + std::make_move_iterator(dex_files.end())); } - uint32_t dex_caches_count = 0; - for (auto dex_caches : dex_caches_vector) { - dex_caches_count += dex_caches->GetLength(); + FinishInit(self); + + VLOG(startup) << __FUNCTION__ << " exiting"; + return true; +} + +static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_) { + return class_loader == nullptr || + class_loader->GetClass() == + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); +} + +static mirror::String* GetDexPathListElementName(ScopedObjectAccessUnchecked& soa, + mirror::Object* element) + SHARED_REQUIRES(Locks::mutator_lock_) { + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + ArtField* const dex_file_name_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_fileName); + DCHECK(dex_file_field != nullptr); + DCHECK(dex_file_name_field != nullptr); + DCHECK(element != nullptr); + CHECK_EQ(dex_file_field->GetDeclaringClass(), element->GetClass()) << PrettyTypeOf(element); + mirror::Object* dex_file = dex_file_field->GetObject(element); + if (dex_file == nullptr) { + return nullptr; } - if (dex_file_count != dex_caches_count) { - *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " - "image"; - return false; + mirror::Object* const name_object = dex_file_name_field->GetObject(dex_file); + if (name_object != nullptr) { + return name_object->AsString(); } - for (auto dex_caches : dex_caches_vector) { - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - StackHandleScope<1> hs2(self); - Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i))); - const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); - const OatFile::OatDexFile* oat_dex_file = nullptr; - for (const OatFile* oat_file : oat_files) { - const OatFile::OatDexFile* oat_dex = - oat_file->GetOatDexFile(dex_file_location.c_str(), nullptr, false); - if (oat_dex != nullptr) { - DCHECK(oat_dex_file == nullptr); - oat_dex_file = oat_dex; + return nullptr; +} + +static bool FlattenPathClassLoader(mirror::ClassLoader* class_loader, + std::list<mirror::String*>* out_dex_file_names, + std::string* error_msg) + SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(out_dex_file_names != nullptr); + DCHECK(error_msg != nullptr); + ScopedObjectAccessUnchecked soa(Thread::Current()); + ArtField* const dex_path_list_field = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList); + ArtField* const dex_elements_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements); + CHECK(dex_path_list_field != nullptr); + CHECK(dex_elements_field != nullptr); + while (!IsBootClassLoader(soa, class_loader)) { + if (class_loader->GetClass() != + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { + *error_msg = StringPrintf("Unknown class loader type %s", PrettyTypeOf(class_loader).c_str()); + // Unsupported class loader. + return false; + } + mirror::Object* dex_path_list = dex_path_list_field->GetObject(class_loader); + if (dex_path_list != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + mirror::Object* dex_elements_obj = dex_elements_field->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) { + mirror::ObjectArray<mirror::Object>* dex_elements = + dex_elements_obj->AsObjectArray<mirror::Object>(); + // Reverse order since we insert the parent at the front. + for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) { + mirror::Object* const element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + *error_msg = StringPrintf("Null dex element at index %d", i); + return false; + } + mirror::String* const name = GetDexPathListElementName(soa, element); + if (name == nullptr) { + *error_msg = StringPrintf("Null name for dex element at index %d", i); + return false; + } + out_dex_file_names->push_front(name); } } + } + class_loader = class_loader->GetParent(); + } + return true; +} - if (oat_dex_file == nullptr) { - *error_msg = StringPrintf("Failed finding oat dex file for %s", - dex_file_location.c_str()); - return false; +class FixupArtMethodArrayVisitor : public ArtMethodVisitor { + public: + explicit FixupArtMethodArrayVisitor(const ImageHeader& header) : header_(header) {} + + virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { + GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*)); + const bool is_miranda = method->IsMiranda(); + if (resolved_types != nullptr) { + bool in_image_space = false; + if (kIsDebugBuild || is_miranda) { + in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( + reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin()); } - std::string inner_error_msg; - std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); - if (dex_file == nullptr) { - *error_msg = StringPrintf("Failed to open dex file %s error '%s'", - dex_file_location.c_str(), - inner_error_msg.c_str()); - return false; + // Must be in image space for non-miranda method. + DCHECK(is_miranda || in_image_space) + << resolved_types << " is not in image starting at " + << reinterpret_cast<void*>(header_.GetImageBegin()); + if (!is_miranda || in_image_space) { + // Go through the array so that we don't need to do a slow map lookup. + method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types), + sizeof(void*)); } - - if (kSanityCheckObjects) { - SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), - dex_cache->NumResolvedMethods(), - image_pointer_size_, - spaces); + } + ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*)); + if (resolved_methods != nullptr) { + bool in_image_space = false; + if (kIsDebugBuild || is_miranda) { + in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( + reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin()); + } + // Must be in image space for non-miranda method. + DCHECK(is_miranda || in_image_space) + << resolved_methods << " is not in image starting at " + << reinterpret_cast<void*>(header_.GetImageBegin()); + if (!is_miranda || in_image_space) { + // Go through the array so that we don't need to do a slow map lookup. + method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods), + sizeof(void*)); } + } + } - if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { - *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", - dex_file_location.c_str(), - dex_file->GetLocationChecksum(), - oat_dex_file->GetDexFileLocationChecksum()); - return false; + private: + const ImageHeader& header_; +}; + +class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { + public: + explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {} + + virtual void Visit(ArtMethod* method) + SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_) { + mirror::Class* klass = method->GetDeclaringClass(); + if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + CHECK_EQ(table_->LookupByDescriptor(klass), klass) << PrettyClass(klass); + } + } + + private: + ClassTable* const table_; +}; + +void ClassLinker::UpdateAppImageClassLoadersAndDexCaches( + gc::space::ImageSpace* space, + Handle<mirror::ClassLoader> class_loader, + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches, + bool added_class_table) { + Thread* const self = Thread::Current(); + gc::Heap* const heap = Runtime::Current()->GetHeap(); + const ImageHeader& header = space->GetImageHeader(); + // Add image classes into the class table for the class loader, and fixup the dex caches and + // class loader fields. + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* table = InsertClassTableForClassLoader(class_loader.Get()); + // TODO: Store class table in the image to avoid manually adding the classes. + for (int32_t i = 0, num_dex_caches = dex_caches->GetLength(); i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and + // copy over the arrays. + DCHECK(dex_file != nullptr); + const size_t num_strings = dex_file->NumStringIds(); + const size_t num_types = dex_file->NumTypeIds(); + const size_t num_methods = dex_file->NumMethodIds(); + const size_t num_fields = dex_file->NumFieldIds(); + CHECK_EQ(num_strings, dex_cache->NumStrings()); + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); + CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); + if (dex_file->GetOatDexFile() != nullptr && + dex_file->GetOatDexFile()->GetDexCacheArrays() != nullptr) { + DexCacheArraysLayout layout(image_pointer_size_, dex_file); + uint8_t* const raw_arrays = dex_file->GetOatDexFile()->GetDexCacheArrays(); + // The space is not yet visible to the GC, we can avoid the read barriers and use + // std::copy_n. + if (num_strings != 0u) { + GcRoot<mirror::String>* const strings = + reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) { + DCHECK(strings[j].IsNull()); + } + std::copy_n(dex_cache->GetStrings(), num_strings, strings); + dex_cache->SetStrings(strings); } - AppendToBootClassPath(*dex_file.get(), dex_cache); - opened_dex_files_.push_back(std::move(dex_file)); + if (num_types != 0u) { + GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes(); + GcRoot<mirror::Class>* const types = + reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { + DCHECK(types[j].IsNull()); + } + std::copy_n(image_resolved_types, num_types, types); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + // This leaves random garbage at the start of the dex cache array, but nobody should ever + // read from it again. + *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types; + dex_cache->SetResolvedTypes(types); + } + if (num_methods != 0u) { + ArtMethod** const methods = reinterpret_cast<ArtMethod**>( + raw_arrays + layout.MethodsOffset()); + ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); + for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { + DCHECK(methods[j] == nullptr); + } + std::copy_n(image_resolved_methods, num_methods, methods); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods; + dex_cache->SetResolvedMethods(methods); + } + if (num_fields != 0u) { + ArtField** const fields = reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) { + DCHECK(fields[j] == nullptr); + } + std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); + dex_cache->SetResolvedFields(fields); + } } + { + WriterMutexLock mu2(self, dex_lock_); + // Make sure to do this after we update the arrays since we store the resolved types array + // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the + // BSS. + mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self, + *dex_file, + /*allow_failure*/true); + CHECK(existing_dex_cache == nullptr); + StackHandleScope<1> hs3(self); + RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); + } + GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); + if (!added_class_table) { + for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read<kWithoutReadBarrier>(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + // Update the class loader from the one in the image class loader to the one that loaded + // the app image. + klass->SetClassLoader(class_loader.Get()); + // If there are multiple dex caches, there may be the same class multiple times + // in different dex caches. Check for this since inserting will add duplicates + // otherwise. + if (num_dex_caches > 1) { + mirror::Class* existing = table->LookupByDescriptor(klass); + if (existing != nullptr) { + DCHECK_EQ(existing, klass) << PrettyClass(klass); + } else { + table->Insert(klass); + } + } else { + table->Insert(klass); + } + // Double checked VLOG to avoid overhead. + if (VLOG_IS_ON(image)) { + VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus(); + if (!klass->IsArrayClass()) { + VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation(); + } + VLOG(image) << "Direct methods"; + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } + VLOG(image) << "Virtual methods"; + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } + } + } + } + } + if (kIsDebugBuild) { + for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read<kWithoutReadBarrier>(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + if (kIsDebugBuild) { + DCHECK_EQ(table->LookupByDescriptor(klass), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + } + } + DCHECK_EQ(klass->GetClassLoader(), class_loader.Get()); + if (kIsDebugBuild) { + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } + } + VLOG(image) << "Virtual methods"; + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } + } + } + } + } + } + } + { + FixupArtMethodArrayVisitor visitor(header); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor, space->Begin(), sizeof(void*)); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); } + if (kIsDebugBuild) { + ClassTable* const class_table = class_loader.Get()->GetClassTable(); + VerifyClassInTableArtMethodVisitor visitor2(class_table); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor2, space->Begin(), sizeof(void*)); + } +} - if (!ValidPointerSize(image_pointer_size_)) { - *error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_); +bool ClassLinker::AddImageSpace( + gc::space::ImageSpace* space, + Handle<mirror::ClassLoader> class_loader, + jobjectArray dex_elements, + const char* dex_location, + std::vector<std::unique_ptr<const DexFile>>* out_dex_files, + std::string* error_msg) { + DCHECK(out_dex_files != nullptr); + DCHECK(error_msg != nullptr); + const uint64_t start_time = NanoTime(); + const bool app_image = class_loader.Get() != nullptr; + const ImageHeader& header = space->GetImageHeader(); + mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches); + DCHECK(dex_caches_object != nullptr); + Runtime* const runtime = Runtime::Current(); + gc::Heap* const heap = runtime->GetHeap(); + Thread* const self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches( + hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>())); + Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( + header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>())); + const OatFile* oat_file = space->GetOatFile(); + std::unordered_set<mirror::ClassLoader*> image_class_loaders; + // Check that the image is what we are expecting. + if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) { + *error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu", + static_cast<size_t>(space->GetImageHeader().GetPointerSize()), + image_pointer_size_); + return false; + } + DCHECK(class_roots.Get() != nullptr); + if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) { + *error_msg = StringPrintf("Expected %d class roots but got %d", + class_roots->GetLength(), + static_cast<int32_t>(kClassRootsMax)); + return false; + } + // Check against existing class roots to make sure they match the ones in the boot image. + for (size_t i = 0; i < kClassRootsMax; i++) { + if (class_roots->Get(i) != GetClassRoot(static_cast<ClassRoot>(i))) { + *error_msg = "App image class roots must have pointer equality with runtime ones."; + return false; + } + } + if (oat_file->GetOatHeader().GetDexFileCount() != + static_cast<uint32_t>(dex_caches->GetLength())) { + *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " + "image"; return false; } - // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live - // bitmap walk. - if (!runtime->IsAotCompiler()) { - // Only the Aot 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. - if (image_pointer_size_ != sizeof(void*)) { - *error_msg = StringPrintf("Runtime must use current image pointer size: %zu vs %zu", - image_pointer_size_ , - sizeof(void*)); + StackHandleScope<1> hs2(self); + MutableHandle<mirror::DexCache> h_dex_cache(hs2.NewHandle<mirror::DexCache>(nullptr)); + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + h_dex_cache.Assign(dex_caches->Get(i)); + std::string dex_file_location(h_dex_cache->GetLocation()->ToModifiedUtf8()); + // TODO: Only store qualified paths. + // If non qualified, qualify it. + if (dex_file_location.find('/') == std::string::npos) { + std::string dex_location_path = dex_location; + const size_t pos = dex_location_path.find_last_of('/'); + CHECK_NE(pos, std::string::npos); + dex_location_path = dex_location_path.substr(0, pos + 1); // Keep trailing '/' + dex_file_location = dex_location_path + dex_file_location; + } + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(), + nullptr); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("Failed finding oat dex file for %s %s", + oat_file->GetLocation().c_str(), + dex_file_location.c_str()); + return false; + } + std::string inner_error_msg; + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); + if (dex_file == nullptr) { + *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'", + dex_file_location.c_str(), + oat_file->GetLocation().c_str(), + inner_error_msg.c_str()); + return false; + } + + if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", + dex_file_location.c_str(), + dex_file->GetLocationChecksum(), + oat_dex_file->GetDexFileLocationChecksum()); return false; } + + if (app_image) { + // The current dex file field is bogus, overwrite it so that we can get the dex file in the + // loop below. + h_dex_cache->SetDexFile(dex_file.get()); + // Check that each class loader resolved the same way. + // TODO: Store image class loaders as image roots. + GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes(); + for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) { + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + mirror::ClassLoader* image_class_loader = klass->GetClassLoader(); + image_class_loaders.insert(image_class_loader); + } + } + } else { + if (kSanityCheckObjects) { + SanityCheckArtMethodPointerArray(h_dex_cache->GetResolvedMethods(), + h_dex_cache->NumResolvedMethods(), + image_pointer_size_, + heap->GetBootImageSpaces()); + } + // Register dex files, keep track of existing ones that are conflicts. + AppendToBootClassPath(*dex_file.get(), h_dex_cache); + } + out_dex_files->push_back(std::move(dex_file)); + } + + if (app_image) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + // Check that the class loader resolves the same way as the ones in the image. + // Image class loader [A][B][C][image dex files] + // Class loader = [???][dex_elements][image dex files] + // Need to ensure that [???][dex_elements] == [A][B][C]. + // For each class loader, PathClassLoader, the laoder checks the parent first. Also the logic + // for PathClassLoader does this by looping through the array of dex files. To ensure they + // resolve the same way, simply flatten the hierarchy in the way the resolution order would be, + // and check that the dex file names are the same. + for (mirror::ClassLoader* image_class_loader : image_class_loaders) { + std::list<mirror::String*> image_dex_file_names; + std::string temp_error_msg; + if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) { + *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'", + temp_error_msg.c_str()); + return false; + } + std::list<mirror::String*> loader_dex_file_names; + if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) { + *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'", + temp_error_msg.c_str()); + return false; + } + // Add the temporary dex path list elements at the end. + auto* elements = soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements); + for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) { + mirror::Object* element = elements->GetWithoutChecks(i); + if (element != nullptr) { + // If we are somewhere in the middle of the array, there may be nulls at the end. + loader_dex_file_names.push_back(GetDexPathListElementName(soa, element)); + } + } + // Ignore the number of image dex files since we are adding those to the class loader anyways. + CHECK_GE(static_cast<size_t>(image_dex_file_names.size()), + static_cast<size_t>(dex_caches->GetLength())); + size_t image_count = image_dex_file_names.size() - dex_caches->GetLength(); + // Check that the dex file names match. + bool equal = image_count == loader_dex_file_names.size(); + if (equal) { + auto it1 = image_dex_file_names.begin(); + auto it2 = loader_dex_file_names.begin(); + for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) { + equal = equal && (*it1)->Equals(*it2); + } + } + if (!equal) { + *error_msg = "Rejecting application image due to class loader mismatch"; + return false; + } + } } if (kSanityCheckObjects) { - for (auto dex_caches : dex_caches_vector) { - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - auto* dex_cache = dex_caches->Get(i); - for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { - auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); - if (field != nullptr) { - CHECK(field->GetDeclaringClass()->GetClass() != nullptr); - } + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + auto* dex_cache = dex_caches->Get(i); + for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { + auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); + if (field != nullptr) { + CHECK(field->GetDeclaringClass()->GetClass() != nullptr); } } } - heap->VisitObjects(SanityCheckObjectsCallback, nullptr); + if (!app_image) { + heap->VisitObjects(SanityCheckObjectsCallback, nullptr); + } } // Set entry point to interpreter if in InterpretOnly mode. if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { - for (gc::space::ImageSpace* space : spaces) { - const ImageHeader& header = space->GetImageHeader(); - const ImageSection& methods = header.GetMethodsSection(); - SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_); - methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); - } - } - - // 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::Field::SetClass(GetClassRoot(kJavaLangReflectField)); - mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); - mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); - mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); - mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); - mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); - mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); - 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)); - - size_t class_tables_added = 0; - for (gc::space::ImageSpace* space : spaces) { - const ImageHeader& header = space->GetImageHeader(); - const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable); - if (section.Size() > 0u) { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const class_table = InsertClassTableForClassLoader(nullptr); - class_table->ReadFromMemory(space->Begin() + section.Offset()); - ++class_tables_added; + const ImageSection& methods = header.GetMethodsSection(); + SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_); + methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); + } + + const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable); + bool added_class_table = false; + if (app_image) { + GetOrCreateAllocatorForClassLoader(class_loader.Get()); // Make sure we have a linear alloc. + } + if (class_table_section.Size() > 0u) { + const uint64_t start_time2 = NanoTime(); + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); + class_table->ReadFromMemory(space->Begin() + class_table_section.Offset()); + if (app_image) { + class_table->SetClassLoader(class_loader.Get()); + } else { + dex_cache_boot_image_class_lookup_required_ = false; } + VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); + added_class_table = true; } - if (class_tables_added != 0) { - // Either all of the image spaces have an empty class section or none do. In the case where - // an image space has no classes, it will still have a non-empty class section that contains - // metadata. - CHECK_EQ(spaces.size(), class_tables_added) - << "Expected non-empty class section for each image space."; - dex_cache_boot_image_class_lookup_required_ = false; + if (app_image) { + UpdateAppImageClassLoadersAndDexCaches(space, class_loader, dex_caches, added_class_table); } - - FinishInit(self); - - VLOG(startup) << "ClassLinker::InitFromImage exiting"; - + VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); return true; } @@ -1527,14 +1971,6 @@ ClassPathEntry FindInClassPath(const char* descriptor, return ClassPathEntry(nullptr, nullptr); } -static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - mirror::ClassLoader* class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) { - return class_loader == nullptr || - class_loader->GetClass() == - soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); -} - bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, @@ -1820,6 +2256,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, // inserted before we allocate / fill in these fields. LoadClass(self, dex_file, dex_class_def, klass); if (self->IsExceptionPending()) { + VLOG(class_linker) << self->GetException()->Dump(); // An exception occured during load, set status to erroneous while holding klass' lock in case // notification is necessary. if (!klass->IsErroneous()) { @@ -2487,7 +2924,20 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, Thread* const self = Thread::Current(); dex_lock_.AssertExclusiveHeld(self); CHECK(dex_cache.Get() != nullptr) << dex_file.GetLocation(); - CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation())) + // For app images, the dex cache location may be a suffix of the dex file location since the + // dex file location is an absolute path. + const size_t dex_cache_length = dex_cache->GetLocation()->GetLength(); + CHECK_GT(dex_cache_length, 0u) << dex_file.GetLocation(); + std::string dex_file_location = dex_file.GetLocation(); + CHECK_GE(dex_file_location.length(), dex_cache_length) + << dex_cache->GetLocation()->ToModifiedUtf8() << " " << dex_file.GetLocation(); + // Take suffix. + const std::string dex_file_suffix = dex_file_location.substr( + dex_file_location.length() - dex_cache_length, + dex_cache_length); + // Example dex_cache location is SettingsProvider.apk and + // dex file location is /system/priv-app/SettingsProvider/SettingsProvider.apk + CHECK(dex_cache->GetLocation()->Equals(dex_file_suffix)) << dex_cache->GetLocation()->ToModifiedUtf8() << " " << dex_file.GetLocation(); // Clean up pass to remove null dex caches. // Null dex caches can occur due to class unloading and we are lazily removing null entries. @@ -6931,10 +7381,13 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi ArtField* cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->GetType<false>()); + ArtField* file_name_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_fileName); + DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->GetType<false>()); + // Fill the elements array. int32_t index = 0; for (const DexFile* dex_file : dex_files) { - StackHandleScope<3> hs2(self); + StackHandleScope<4> hs2(self); // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the // oat file but we can leave it null. @@ -6949,6 +7402,11 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi DCHECK(h_dex_file.Get() != nullptr); cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get()); + Handle<mirror::String> h_file_name = hs2.NewHandle( + mirror::String::AllocFromModifiedUtf8(self, dex_file->GetLocation().c_str())); + DCHECK(h_file_name.Get() != nullptr); + file_name_field->SetObject<false>(h_dex_file.Get(), h_file_name.Get()); + Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self)); DCHECK(h_element.Get() != nullptr); element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get()); @@ -7048,6 +7506,7 @@ void ClassLinker::CleanupClassLoaders() { if (class_loader != nullptr) { ++it; } else { + VLOG(class_linker) << "Freeing class loader"; DeleteClassLoader(self, data); it = class_loaders_.erase(it); } |