diff options
| -rw-r--r-- | dex2oat/dex2oat.cc | 13 | ||||
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 59 | ||||
| -rw-r--r-- | dex2oat/linker/image_test.h | 1 | ||||
| -rw-r--r-- | dex2oat/linker/image_writer.cc | 50 | ||||
| -rw-r--r-- | dex2oat/linker/image_writer.h | 9 | ||||
| -rw-r--r-- | dex2oat/linker/oat_writer.cc | 5 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 258 |
7 files changed, 273 insertions, 122 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index b634598407..3a24542221 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1930,7 +1930,7 @@ class Dex2Oat final { // ImageWriter, if necessary. // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure // case (when the file will be explicitly erased). - bool WriteOutputFiles() { + bool WriteOutputFiles(jobject class_loader) { TimingLogger::ScopedTiming t("dex2oat Oat", timings_); // Sync the data to the file, in case we did dex2dex transformations. @@ -1965,6 +1965,7 @@ class Dex2Oat final { image_storage_mode_, oat_filenames_, dex_file_oat_index_map_, + class_loader, dirty_image_objects_.get())); // We need to prepare method offsets in the image address space for direct method patching. @@ -2839,11 +2840,12 @@ class ScopedGlobalRef { static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) { dex2oat.LoadClassProfileDescriptors(); + jobject class_loader = dex2oat.Compile(); // Keep the class loader that was used for compilation live for the rest of the compilation // process. - ScopedGlobalRef class_loader(dex2oat.Compile()); + ScopedGlobalRef global_ref(class_loader); - if (!dex2oat.WriteOutputFiles()) { + if (!dex2oat.WriteOutputFiles(class_loader)) { dex2oat.EraseOutputFiles(); return dex2oat::ReturnCode::kOther; } @@ -2883,11 +2885,12 @@ static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) { } static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) { + jobject class_loader = dex2oat.Compile(); // Keep the class loader that was used for compilation live for the rest of the compilation // process. - ScopedGlobalRef class_loader(dex2oat.Compile()); + ScopedGlobalRef global_ref(class_loader); - if (!dex2oat.WriteOutputFiles()) { + if (!dex2oat.WriteOutputFiles(class_loader)) { dex2oat.EraseOutputFiles(); return dex2oat::ReturnCode::kOther; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 1fa21d51fc..97a5f2453e 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1069,7 +1069,8 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { void RunTest(const char* class_loader_context, const char* expected_classpath_key, bool expected_success, - bool use_second_source = false) { + bool use_second_source = false, + bool generate_image = false) { std::string dex_location = GetUsedDexLocation(); std::string odex_location = GetUsedOatLocation(); @@ -1080,6 +1081,9 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { if (class_loader_context != nullptr) { extra_args.push_back(std::string("--class-loader-context=") + class_loader_context); } + if (generate_image) { + extra_args.push_back(std::string("--app-image-file=") + GetUsedImageLocation()); + } auto check_oat = [expected_classpath_key](const OatFile& oat_file) { ASSERT_TRUE(expected_classpath_key != nullptr); const char* classpath = oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey); @@ -1104,6 +1108,10 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { return GetOdexDir() + "/Context.odex"; } + std::string GetUsedImageLocation() { + return GetOdexDir() + "/Context.art"; + } + const char* kEmptyClassPathKey = "PCL[]"; }; @@ -1213,6 +1221,55 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) { RunTest(context.c_str(), expected_classpath_key.c_str(), true); } +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibraryAndImage) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + RunTest(context.c_str(), + expected_classpath_key.c_str(), + /*expected_success=*/ true, + /*use_second_source=*/ false, + /*generate_image=*/ true); +} + +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSameSharedLibrariesAndImage) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]" + + "#PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + + "#PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + RunTest(context.c_str(), + expected_classpath_key.c_str(), + /*expected_success=*/ true, + /*use_second_source=*/ false, + /*generate_image=*/ true); +} + +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrariesDependenciesAndImage) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]" + + "{PCL[" + GetTestDexFileName("Nested") + "]}}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files1) + "]}}"; + RunTest(context.c_str(), + expected_classpath_key.c_str(), + /*expected_success=*/ true, + /*use_second_source=*/ false, + /*generate_image=*/ true); +} + class Dex2oatDeterminism : public Dex2oatTest {}; TEST_F(Dex2oatDeterminism, UnloadCompile) { diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 182f96c614..c90eaddb4c 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -219,6 +219,7 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, storage_mode, oat_filename_vector, dex_file_to_oat_index_map, + /*class_loader=*/ nullptr, /*dirty_image_objects=*/ nullptr)); { { diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 248a4414dc..e59faf1e53 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -146,9 +146,11 @@ static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source, // Separate objects into multiple bins to optimize dirty memory use. static constexpr bool kBinObjects = true; -ObjPtr<mirror::ClassLoader> ImageWriter::GetClassLoader() { - CHECK_EQ(class_loaders_.size(), compiler_options_.IsAppImage() ? 1u : 0u); - return compiler_options_.IsAppImage() ? *class_loaders_.begin() : nullptr; +ObjPtr<mirror::ClassLoader> ImageWriter::GetAppClassLoader() const + REQUIRES_SHARED(Locks::mutator_lock_) { + return compiler_options_.IsAppImage() + ? ObjPtr<mirror::ClassLoader>::DownCast(Thread::Current()->DecodeJObject(app_class_loader_)) + : nullptr; } // Return true if an object is already in an image space. @@ -675,7 +677,7 @@ bool ImageWriter::Write(int image_fd, { // Preload deterministic contents to the dex cache arrays we're going to write. ScopedObjectAccess soa(self); - ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader(); + ObjPtr<mirror::ClassLoader> class_loader = GetAppClassLoader(); std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self); for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) { if (IsInBootImage(dex_cache.Ptr())) { @@ -1470,27 +1472,15 @@ class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor { Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); class_table->Visit(classes_visitor); removed_class_count_ += classes_visitor.Prune(); - - // Record app image class loader. The fake boot class loader should not get registered - // and we should end up with only one class loader for an app and none for boot image. - if (class_loader != nullptr && class_table != nullptr) { - DCHECK(class_loader_ == nullptr); - class_loader_ = class_loader; - } } size_t GetRemovedClassCount() const { return removed_class_count_; } - ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) { - return class_loader_; - } - private: ImageWriter* const image_writer_; size_t removed_class_count_; - ObjPtr<mirror::ClassLoader> class_loader_; }; void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) { @@ -1701,13 +1691,10 @@ void ImageWriter::PruneNonImageClasses() { }); // Remove the undesired classes from the class roots. - ObjPtr<mirror::ClassLoader> class_loader; { PruneClassLoaderClassesVisitor class_loader_visitor(this); VisitClassLoaders(&class_loader_visitor); VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes"; - class_loader = class_loader_visitor.GetClassLoader(); - DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage()); } // Clear references to removed classes from the DexCaches. @@ -1715,7 +1702,7 @@ void ImageWriter::PruneNonImageClasses() { for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) { // Pass the class loader associated with the DexCache. This can either be // the app's `class_loader` or `nullptr` if boot class loader. - PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader); + PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : GetAppClassLoader()); } // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. @@ -2034,18 +2021,17 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, } } else if (obj->IsClassLoader()) { // Register the class loader if it has a class table. - // The fake boot class loader should not get registered and we should end up with only one - // class loader. + // The fake boot class loader should not get registered. mirror::ClassLoader* class_loader = obj->AsClassLoader(); if (class_loader->GetClassTable() != nullptr) { DCHECK(compiler_options_.IsAppImage()); - DCHECK(class_loaders_.empty()); - class_loaders_.insert(class_loader); - ImageInfo& image_info = GetImageInfo(oat_index); - // Note: Avoid locking to prevent lock order violations from root visiting; - // image_info.class_table_ table is only accessed from the image writer - // and class_loader->GetClassTable() is iterated but not modified. - image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); + if (class_loader == GetAppClassLoader()) { + ImageInfo& image_info = GetImageInfo(oat_index); + // Note: Avoid locking to prevent lock order violations from root visiting; + // image_info.class_table_ table is only accessed from the image writer + // and class_loader->GetClassTable() is iterated but not modified. + image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); + } } } AssignImageBinSlot(obj, oat_index); @@ -2323,10 +2309,8 @@ void ImageWriter::CalculateNewObjectOffsets() { ProcessWorkStack(&work_stack); // Store the class loader in the class roots. - CHECK_EQ(class_loaders_.size(), 1u); CHECK_EQ(image_roots.size(), 1u); - CHECK(*class_loaders_.begin() != nullptr); - image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, *class_loaders_.begin()); + image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, GetAppClassLoader()); } // Verify that all objects have assigned image bin slots. @@ -3474,6 +3458,7 @@ ImageWriter::ImageWriter( ImageHeader::StorageMode image_storage_mode, const std::vector<const char*>& oat_filenames, const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map, + jobject class_loader, const HashSet<std::string>* dirty_image_objects) : compiler_options_(compiler_options), global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)), @@ -3482,6 +3467,7 @@ ImageWriter::ImageWriter( image_infos_(oat_filenames.size()), dirty_methods_(0u), clean_methods_(0u), + app_class_loader_(class_loader), boot_image_live_objects_(nullptr), image_storage_mode_(image_storage_mode), oat_filenames_(oat_filenames), diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 8aabaa3a9b..782bbd2fc2 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -81,6 +81,7 @@ class ImageWriter final { ImageHeader::StorageMode image_storage_mode, const std::vector<const char*>& oat_filenames, const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map, + jobject class_loader, const HashSet<std::string>* dirty_image_objects); /* @@ -111,7 +112,7 @@ class ImageWriter final { return true; } - ObjPtr<mirror::ClassLoader> GetClassLoader(); + ObjPtr<mirror::ClassLoader> GetAppClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_); template <typename T> T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) { @@ -771,10 +772,8 @@ class ImageWriter final { // Prune class memoization table to speed up ContainsBootClassLoaderNonImageClass. std::unordered_map<mirror::Class*, bool> prune_class_memo_; - // Class loaders with a class table to write out. There should only be one class loader because - // dex2oat loads the dex files to be compiled into a single class loader. For the boot image, - // null is a valid entry. - std::unordered_set<mirror::ClassLoader*> class_loaders_; + // The application class loader. Null for boot image. + jobject app_class_loader_; // Boot image live objects, null for app image. mirror::ObjectArray<mirror::Object>* boot_image_live_objects_; diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 18528dccee..9aaabc49dd 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1489,7 +1489,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { const std::vector<const DexFile*>* dex_files) : OatDexMethodVisitor(writer, offset), pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), + class_loader_(writer->HasImage() ? writer->image_writer_->GetAppClassLoader() : nullptr), dex_files_(dex_files), class_linker_(Runtime::Current()->GetClassLinker()) {} @@ -1630,7 +1630,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { offset_(relative_offset), dex_file_(nullptr), pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), + class_loader_(writer->HasImage() ? writer->image_writer_->GetAppClassLoader() : nullptr), out_(out), file_offset_(file_offset), class_linker_(Runtime::Current()->GetClassLinker()), @@ -2271,6 +2271,7 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } if (HasImage()) { + ScopedAssertNoThreadSuspension sants("Init image method visitor", Thread::Current()); InitImageMethodVisitor image_visitor(this, offset, dex_files_); success = VisitDexMethods(&image_visitor); image_visitor.Postprocess(); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 639fa7ec92..873d80f0fb 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1094,51 +1094,165 @@ static bool GetDexPathListElementName(ObjPtr<mirror::Object> element, return false; } -static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader, - std::list<ObjPtr<mirror::String>>* out_dex_file_names, - std::string* error_msg) +static bool GetDexFileNames(ScopedObjectAccessUnchecked& soa, + ObjPtr<mirror::ClassLoader> class_loader, + /*out*/std::list<ObjPtr<mirror::String>>* dex_files, + /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(out_dex_file_names != nullptr); - DCHECK(error_msg != nullptr); - ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> handle(hs.NewHandle(class_loader)); - while (!ClassLinker::IsBootClassLoader(soa, class_loader)) { - if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) != - class_loader->GetClass()) { - *error_msg = StringPrintf("Unknown class loader type %s", - class_loader->PrettyTypeOf().c_str()); - // Unsupported class loader. + // Get element names. Sets error to true on failure. + auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (element == nullptr) { + *error_msg = "Null dex element"; + *error = true; // Null element is a critical error. + return false; // Had an error, stop the visit. + } + ObjPtr<mirror::String> name; + if (!GetDexPathListElementName(element, &name)) { + *error_msg = "Invalid dex path list element"; + *error = true; // Invalid element, make it a critical error. + return false; // Stop the visit. + } + if (name != nullptr) { + dex_files->push_front(name); + } + return true; // Continue with the next Element. + }; + bool error = VisitClassLoaderDexElements(soa, + handle, + add_element_names, + /*defaultReturn=*/ false); + return !error; +} + +static bool CompareClassLoaderTypes(ScopedObjectAccessUnchecked& soa, + ObjPtr<mirror::ClassLoader> image_class_loader, + ObjPtr<mirror::ClassLoader> class_loader, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (ClassLinker::IsBootClassLoader(soa, class_loader)) { + if (!ClassLinker::IsBootClassLoader(soa, image_class_loader)) { + *error_msg = "Hierarchies don't match"; return false; } - // Get element names. Sets error to true on failure. - auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (element == nullptr) { - *error_msg = "Null dex element"; - *error = true; // Null element is a critical error. - return false; // Had an error, stop the visit. - } - ObjPtr<mirror::String> name; - if (!GetDexPathListElementName(element, &name)) { - *error_msg = "Invalid dex path list element"; - *error = false; // Invalid element is not a critical error. - return false; // Stop the visit. - } - if (name != nullptr) { - out_dex_file_names->push_front(name); - } - return true; // Continue with the next Element. - }; - bool error = VisitClassLoaderDexElements(soa, - handle, - add_element_names, - /* defaultReturn= */ false); - if (error) { - // An error occurred during DexPathList Element visiting. + } else if (ClassLinker::IsBootClassLoader(soa, image_class_loader)) { + *error_msg = "Hierarchies don't match"; + return false; + } else if (class_loader->GetClass() != image_class_loader->GetClass()) { + *error_msg = StringPrintf("Class loader types don't match %s and %s", + image_class_loader->PrettyTypeOf().c_str(), + class_loader->PrettyTypeOf().c_str()); + return false; + } else if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) != + class_loader->GetClass()) { + *error_msg = StringPrintf("Unknown class loader type %s", + class_loader->PrettyTypeOf().c_str()); + // Unsupported class loader. + return false; + } + return true; +} + +static bool CompareDexFiles(const std::list<ObjPtr<mirror::String>>& image_dex_files, + const std::list<ObjPtr<mirror::String>>& loader_dex_files, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + bool equal = (image_dex_files.size() == loader_dex_files.size()) && + std::equal(image_dex_files.begin(), + image_dex_files.end(), + loader_dex_files.begin(), + [](ObjPtr<mirror::String> lhs, ObjPtr<mirror::String> rhs) + REQUIRES_SHARED(Locks::mutator_lock_) { + return lhs->Equals(rhs); + }); + if (!equal) { + VLOG(image) << "Image dex files " << image_dex_files.size(); + for (ObjPtr<mirror::String> name : image_dex_files) { + VLOG(image) << name->ToModifiedUtf8(); + } + VLOG(image) << "Loader dex files " << loader_dex_files.size(); + for (ObjPtr<mirror::String> name : loader_dex_files) { + VLOG(image) << name->ToModifiedUtf8(); + } + *error_msg = "Mismatch in dex files"; + } + return equal; +} + +static bool CompareClassLoaders(ScopedObjectAccessUnchecked& soa, + ObjPtr<mirror::ClassLoader> image_class_loader, + ObjPtr<mirror::ClassLoader> class_loader, + bool check_dex_file_names, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!CompareClassLoaderTypes(soa, image_class_loader, class_loader, error_msg)) { + return false; + } + + if (ClassLinker::IsBootClassLoader(soa, class_loader)) { + // No need to check further. + return true; + } + + if (check_dex_file_names) { + std::list<ObjPtr<mirror::String>> image_dex_files; + if (!GetDexFileNames(soa, image_class_loader, &image_dex_files, error_msg)) { + return false; + } + + std::list<ObjPtr<mirror::String>> loader_dex_files; + if (!GetDexFileNames(soa, class_loader, &loader_dex_files, error_msg)) { + return false; + } + + if (!CompareDexFiles(image_dex_files, loader_dex_files, error_msg)) { return false; } - class_loader = class_loader->GetParent(); + } + + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> shared_libraries_image_loader = field->GetObject(image_class_loader.Ptr()); + ObjPtr<mirror::Object> shared_libraries_loader = field->GetObject(class_loader.Ptr()); + if (shared_libraries_image_loader == nullptr) { + if (shared_libraries_loader != nullptr) { + *error_msg = "Mismatch in shared libraries"; + return false; + } + } else if (shared_libraries_loader == nullptr) { + *error_msg = "Mismatch in shared libraries"; + return false; + } else { + ObjPtr<mirror::ObjectArray<mirror::ClassLoader>> array1 = + shared_libraries_image_loader->AsObjectArray<mirror::ClassLoader>(); + ObjPtr<mirror::ObjectArray<mirror::ClassLoader>> array2 = + shared_libraries_loader->AsObjectArray<mirror::ClassLoader>(); + if (array1->GetLength() != array2->GetLength()) { + *error_msg = "Mismatch in number of shared libraries"; + return false; + } + + for (int32_t i = 0; i < array1->GetLength(); ++i) { + // Do a full comparison of the class loaders, including comparing their dex files. + if (!CompareClassLoaders(soa, + array1->Get(i), + array2->Get(i), + /*check_dex_file_names=*/ true, + error_msg)) { + return false; + } + } + } + + // Do a full comparison of the class loaders, including comparing their dex files. + if (!CompareClassLoaders(soa, + image_class_loader->GetParent(), + class_loader->GetParent(), + /*check_dex_file_names=*/ true, + error_msg)) { + return false; } return true; } @@ -1907,6 +2021,7 @@ bool ClassLinker::AddImageSpace( if (app_image) { ScopedObjectAccessUnchecked soa(Thread::Current()); + ScopedAssertNoThreadSuspension sants("Checking app image", soa.Self()); // 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] @@ -1919,21 +2034,12 @@ bool ClassLinker::AddImageSpace( *error_msg = "Unexpected BootClassLoader in app image"; return false; } - std::list<ObjPtr<mirror::String>> image_dex_file_names; - std::string temp_error_msg; - if (!FlattenPathClassLoader(image_class_loader.Get(), &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<ObjPtr<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. + // The dex files of `class_loader` are not setup yet, so we cannot do a full comparison + // of `class_loader` and `image_class_loader` in `CompareClassLoaders`. Therefore, we + // special case the comparison of dex files of the two class loaders, but then do full + // comparisons for their shared libraries and parent. auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements); + std::list<ObjPtr<mirror::String>> loader_dex_file_names; for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) { ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i); if (element != nullptr) { @@ -1944,31 +2050,29 @@ bool ClassLinker::AddImageSpace( } } } - // 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) { - VLOG(image) << "Image dex files " << image_dex_file_names.size(); - for (ObjPtr<mirror::String> name : image_dex_file_names) { - VLOG(image) << name->ToModifiedUtf8(); - } - VLOG(image) << "Loader dex files " << loader_dex_file_names.size(); - for (ObjPtr<mirror::String> name : loader_dex_file_names) { - VLOG(image) << name->ToModifiedUtf8(); - } - *error_msg = "Rejecting application image due to class loader mismatch"; - // Ignore class loader mismatch for now since these would just use possibly incorrect - // oat code anyways. The structural class check should be done in the parent. + std::string temp_error_msg; + std::list<ObjPtr<mirror::String>> image_dex_file_names; + bool success = GetDexFileNames( + soa, image_class_loader.Get(), &image_dex_file_names, &temp_error_msg); + if (success) { + // 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(); + image_dex_file_names.resize(image_count); + success = success && CompareDexFiles(image_dex_file_names, + loader_dex_file_names, + &temp_error_msg); + success = success && CompareClassLoaders(soa, + image_class_loader.Get(), + class_loader.Get(), + /*check_dex_file_names=*/ false, + &temp_error_msg); + } + if (!success) { + *error_msg = StringPrintf("Rejecting application image due to class loader mismatch: '%s'", + temp_error_msg.c_str()); + return false; } } |