Fix app image generation and checking with shared libraries.
We can now have multiple class loaders in an app image.
bug: 111174995
Test: dex2oat_test, test.py
Change-Id: Ie45c030b483efa78df2605fbcafb4e641b80c59a
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 12a8354..7df7078 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1929,7 +1929,7 @@
// 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.
@@ -1964,6 +1964,7 @@
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.
@@ -2846,11 +2847,12 @@
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;
}
@@ -2890,11 +2892,12 @@
}
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 1fa21d5..97a5f24 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1069,7 +1069,8 @@
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 @@
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 @@
return GetOdexDir() + "/Context.odex";
}
+ std::string GetUsedImageLocation() {
+ return GetOdexDir() + "/Context.art";
+ }
+
const char* kEmptyClassPathKey = "PCL[]";
};
@@ -1213,6 +1221,55 @@
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 9ef2875..1186669 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -219,6 +219,7 @@
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 a3fc1cd..b466282 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -145,9 +145,11 @@
// 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.
@@ -622,7 +624,7 @@
{
// 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())) {
@@ -1400,27 +1402,15 @@
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) {
@@ -1631,13 +1621,10 @@
});
// 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.
@@ -1645,7 +1632,7 @@
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.
@@ -1964,18 +1951,17 @@
}
} 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);
@@ -2253,10 +2239,8 @@
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.
@@ -3401,6 +3385,7 @@
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)),
@@ -3409,6 +3394,7 @@
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 3c377a3..6a12c2f 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -81,6 +81,7 @@
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 @@
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 @@
// 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 7f2877f..0d7634f 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -1482,7 +1482,7 @@
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()) {}
@@ -1623,7 +1623,7 @@
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()),
@@ -2264,6 +2264,7 @@
}
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 639fa7e..873d80f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1094,51 +1094,165 @@
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;
}
- class_loader = class_loader->GetParent();
+
+ 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;
+ }
+ }
+
+ 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 @@
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 @@
*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 @@
}
}
}
- // 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);
- }
+ 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 (!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.
+ if (!success) {
+ *error_msg = StringPrintf("Rejecting application image due to class loader mismatch: '%s'",
+ temp_error_msg.c_str());
+ return false;
}
}