diff options
author | 2016-03-28 20:39:50 -0700 | |
---|---|---|
committer | 2016-05-17 11:08:41 -0700 | |
commit | f0192c86a58b2f43378c9a2113007538dd38ddbf (patch) | |
tree | df3fef8e9ccd30152819e893e6212ce45b5f51a5 | |
parent | 783d02e527107e91880f12bddf41afd313919132 (diff) |
Support to pass <uses-library> option through to dex2oat.
This change takes an app's shared libraries specified by <uses-library>
and passes it through to dex2oat to be used during compilation.
Part of a multi-project change.
Bug: 26880306
(cherry-picked from commit 26e8a2f150cd7f7195a10650ab8a5b6fa5014bc8)
Change-Id: I72a352abdfc37eacd8bedfa6c218e3809ca8e39c
-rw-r--r-- | dex2oat/dex2oat.cc | 38 | ||||
-rw-r--r-- | runtime/class_linker.cc | 10 | ||||
-rw-r--r-- | runtime/class_linker.h | 6 | ||||
-rw-r--r-- | runtime/gc/heap.cc | 2 | ||||
-rw-r--r-- | runtime/oat.h | 2 | ||||
-rw-r--r-- | runtime/oat_file.h | 3 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 6 | ||||
-rw-r--r-- | runtime/oat_file_assistant_test.cc | 3 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 314 | ||||
-rw-r--r-- | runtime/oat_file_manager.h | 11 | ||||
-rw-r--r-- | runtime/runtime.cc | 2 | ||||
-rw-r--r-- | test/138-duplicate-classes-check/src/FancyLoader.java | 229 | ||||
-rw-r--r-- | test/138-duplicate-classes-check/src/Main.java | 7 |
13 files changed, 350 insertions, 283 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index be38336f03..58651060ce 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1312,7 +1312,7 @@ class Dex2Oat FINAL { if (IsBootImage() && image_filenames_.size() > 1) { // If we're compiling the boot image, store the boot classpath into the Key-Value store. // We need this for the multi-image case. - key_value_store_->Put(OatHeader::kBootClassPath, GetMultiImageBootClassPath()); + key_value_store_->Put(OatHeader::kBootClassPathKey, GetMultiImageBootClassPath()); } if (!IsBootImage()) { @@ -1348,12 +1348,19 @@ class Dex2Oat FINAL { // Open dex files for class path. const std::vector<std::string> class_path_locations = GetClassPathLocations(runtime_->GetClassPathString()); - OpenClassPathFiles(class_path_locations, &class_path_files_); + OpenClassPathFiles(class_path_locations, &class_path_files_, runtime_->GetInstructionSet()); // Store the classpath we have right now. std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_); - key_value_store_->Put(OatHeader::kClassPathKey, - OatFile::EncodeDexFileDependencies(class_path_files)); + std::string encoded_class_path; + if (class_path_locations.size() == 1 && + class_path_locations[0] == OatFile::kSpecialSharedLibrary) { + // When passing the special shared library as the classpath, it is the only path. + encoded_class_path = OatFile::kSpecialSharedLibrary; + } else { + encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files); + } + key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path); } // Now that we have finalized key_value_store_, start writing the oat file. @@ -1966,12 +1973,31 @@ class Dex2Oat FINAL { // Opens requested class path files and appends them to opened_dex_files. static void OpenClassPathFiles(const std::vector<std::string>& class_path_locations, - std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { + std::vector<std::unique_ptr<const DexFile>>* opened_dex_files, + InstructionSet isa) { DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is nullptr"; for (const std::string& location : class_path_locations) { + // Stop early if we detect the special shared library, which may be passed as the classpath + // for dex2oat when we want to skip the shared libraries check. + if (location == OatFile::kSpecialSharedLibrary) { + break; + } std::string error_msg; if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) { - LOG(WARNING) << "Failed to open dex file '" << location << "': " << error_msg; + // If we fail to open the dex file because it's been stripped, try to open the dex file + // from its corresponding oat file. + OatFileAssistant oat_file_assistant(location.c_str(), isa, false, false); + std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile()); + if (oat_file == nullptr) { + LOG(WARNING) << "Failed to open dex file and associated oat file for '" << location + << "': " << error_msg; + } else { + std::vector<std::unique_ptr<const DexFile>> oat_dex_files = + oat_file_assistant.LoadDexFiles(*oat_file, location.c_str()); + opened_dex_files->insert(opened_dex_files->end(), + std::make_move_iterator(oat_dex_files.begin()), + std::make_move_iterator(oat_dex_files.end())); + } } } } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e9b8643223..1835c728b3 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1063,9 +1063,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { return true; } -static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - mirror::ClassLoader* class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) { +bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) { return class_loader == nullptr || class_loader->GetClass() == soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); @@ -1106,7 +1105,7 @@ static bool FlattenPathClassLoader(mirror::ClassLoader* class_loader, soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements); CHECK(dex_path_list_field != nullptr); CHECK(dex_elements_field != nullptr); - while (!IsBootClassLoader(soa, class_loader)) { + while (!ClassLinker::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()); @@ -7815,7 +7814,8 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { return descriptor; } -jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) { +jobject ClassLinker::CreatePathClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files) { // SOAAlreadyRunnable is protected, and we need something to add a global reference. // We could move the jobject to the callers, but all call-sites do this... ScopedObjectAccessUnchecked soa(self); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 45c07673ee..f6ce545a19 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -560,7 +560,7 @@ class ClassLinker { // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files. // Note: the objects are not completely set up. Do not use this outside of tests and the compiler. - jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) + jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); @@ -611,6 +611,10 @@ class ClassLinker { const std::set<DexCacheResolvedClasses>& classes) REQUIRES(!dex_lock_); + static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* AddMethodToConflictTable(mirror::Class* klass, ArtMethod* conflict_method, ArtMethod* interface_method, diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index fa540c0f9b..cdd5f2e120 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -311,7 +311,7 @@ Heap::Heap(size_t initial_size, const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader(); const char* boot_classpath = - boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey); if (boot_classpath == nullptr) { continue; } diff --git a/runtime/oat.h b/runtime/oat.h index 543d99f2ad..57675dc738 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -43,7 +43,7 @@ class PACKED(4) OatHeader { static constexpr const char* kNativeDebuggableKey = "native-debuggable"; static constexpr const char* kCompilerFilter = "compiler-filter"; static constexpr const char* kClassPathKey = "classpath"; - static constexpr const char* kBootClassPath = "bootclasspath"; + static constexpr const char* kBootClassPathKey = "bootclasspath"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 9470624df8..aa727ff45b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -48,6 +48,9 @@ class DummyOatFile; class OatFile { public: + // Special classpath that skips shared library check. + static constexpr const char* kSpecialSharedLibrary = "&"; + typedef art::OatDexFile OatDexFile; // Opens an oat file contained within the given elf file. This is always opened as diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 713e2f3fa9..fba10ca014 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -771,7 +771,11 @@ bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, argv.push_back("--runtime-arg"); argv.push_back("-classpath"); argv.push_back("--runtime-arg"); - argv.push_back(runtime->GetClassPathString()); + std::string class_path = runtime->GetClassPathString(); + if (class_path == "") { + class_path = OatFile::kSpecialSharedLibrary; + } + argv.push_back(class_path); if (runtime->IsDebuggable()) { argv.push_back("--debuggable"); } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 764b969eaa..15a1aa4d10 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1264,8 +1264,7 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { class RaceGenerateTask : public Task { public: explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location) - : dex_location_(dex_location), oat_location_(oat_location), - loaded_oat_file_(nullptr) + : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr) {} void Run(Thread* self ATTRIBUTE_UNUSED) { diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index bc01da4fc4..0af6716af7 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -36,11 +36,6 @@ namespace art { -// For b/21333911. -// Only enabled for debug builds to prevent bit rot. There are too many performance regressions for -// normal builds. -static constexpr bool kDuplicateClassesCheck = kIsDebugBuild; - // If true, then we attempt to load the application image if it exists. static constexpr bool kEnableAppImage = true; @@ -173,7 +168,7 @@ class DexFileAndClassPair : ValueObject { void Next() { ++current_class_index_; - cached_descriptor_ = GetClassDescriptor(dex_file_.get(), current_class_index_); + cached_descriptor_ = GetClassDescriptor(dex_file_, current_class_index_); } size_t GetCurrentClassIndex() const { @@ -185,7 +180,12 @@ class DexFileAndClassPair : ValueObject { } const DexFile* GetDexFile() const { - return dex_file_.get(); + return dex_file_; + } + + void DeleteDexFile() { + delete dex_file_; + dex_file_ = nullptr; } private: @@ -196,7 +196,7 @@ class DexFileAndClassPair : ValueObject { } const char* cached_descriptor_; - std::shared_ptr<const DexFile> dex_file_; + const DexFile* dex_file_; size_t current_class_index_; bool from_loaded_oat_; // We only need to compare mismatches between what we load now // and what was loaded before. Any old duplicates must have been @@ -219,53 +219,299 @@ static void AddDexFilesFromOat(const OatFile* oat_file, } static void AddNext(/*inout*/DexFileAndClassPair* original, - /*inout*/std::priority_queue<DexFileAndClassPair>* heap) { + /*inout*/std::priority_queue<DexFileAndClassPair>* heap, + bool owning_dex_files) { if (original->DexFileHasMoreClasses()) { original->Next(); heap->push(std::move(*original)); + } else if (owning_dex_files) { + original->DeleteDexFile(); + } +} + +static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap, + bool owning_dex_files) { + if (owning_dex_files) { + while (!heap->empty()) { + delete heap->top().GetDexFile(); + heap->pop(); + } + } +} + +static void IterateOverJavaDexFile(mirror::Object* dex_file, + ArtField* const cookie_field, + std::function<bool(const DexFile*)> fn) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (dex_file != nullptr) { + mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + if (long_array == nullptr) { + // This should never happen so log a warning. + LOG(WARNING) << "Null DexFile::mCookie"; + return; + } + int32_t long_array_size = long_array->GetLength(); + // Start from 1 to skip the oat file. + for (int32_t j = 1; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( + long_array->GetWithoutChecks(j))); + if (!fn(cp_dex_file)) { + return; + } + } + } +} + +static void IterateOverPathClassLoader( + ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader, + MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements, + std::function<bool(const DexFile*)> fn) SHARED_REQUIRES(Locks::mutator_lock_) { + // Handle this step. + // Handle as if this is the child PathClassLoader. + // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. + // We need to get the DexPathList and loop through it. + ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + 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 != nullptr && cookie_field != 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) { + dex_elements.Assign(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); + IterateOverJavaDexFile(dex_file, cookie_field, fn); + } + } + } +} + +static bool GetDexFilesFromClassLoader( + ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader, + std::priority_queue<DexFileAndClassPair>* queue) SHARED_REQUIRES(Locks::mutator_lock_) { + if (ClassLinker::IsBootClassLoader(soa, class_loader)) { + // The boot class loader. We don't load any of these files, as we know we compiled against + // them correctly. + return true; + } + + // Unsupported class-loader? + if (class_loader->GetClass() != + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { + VLOG(class_linker) << "Unsupported class-loader " << PrettyClass(class_loader->GetClass()); + return false; + } + + bool recursive_result = GetDexFilesFromClassLoader(soa, class_loader->GetParent(), queue); + if (!recursive_result) { + // Something wrong up the chain. + return false; } + + // Collect all the dex files. + auto GetDexFilesFn = [&] (const DexFile* cp_dex_file) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (cp_dex_file->NumClassDefs() > 0) { + queue->emplace(cp_dex_file, 0U, true); + } + return true; // Continue looking. + }; + + // Handle for dex-cache-element. + StackHandleScope<3> hs(soa.Self()); + MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements( + hs.NewHandle<mirror::ObjectArray<mirror::Object>>(nullptr)); + Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); + + IterateOverPathClassLoader(soa, h_class_loader, dex_elements, GetDexFilesFn); + + return true; +} + +static void GetDexFilesFromDexElementsArray( + ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ObjectArray<mirror::Object>> dex_elements, + std::priority_queue<DexFileAndClassPair>* queue) SHARED_REQUIRES(Locks::mutator_lock_) { + if (dex_elements.Get() == nullptr) { + // Nothing to do. + return; + } + + ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + const mirror::Class* const element_class = soa.Decode<mirror::Class*>( + WellKnownClasses::dalvik_system_DexPathList__Element); + const mirror::Class* const dexfile_class = soa.Decode<mirror::Class*>( + WellKnownClasses::dalvik_system_DexFile); + + // Collect all the dex files. + auto GetDexFilesFn = [&] (const DexFile* cp_dex_file) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) { + queue->emplace(cp_dex_file, 0U, true); + } + return true; // Continue looking. + }; + + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + mirror::Object* element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + continue; + } + + // We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile. + + mirror::Object* dex_file; + if (element->GetClass() == element_class) { + dex_file = dex_file_field->GetObject(element); + } else if (element->GetClass() == dexfile_class) { + dex_file = element; + } else { + LOG(WARNING) << "Unsupported element in dex_elements: " << PrettyClass(element->GetClass()); + continue; + } + + IterateOverJavaDexFile(dex_file, cookie_field, GetDexFilesFn); + } +} + +static bool AreSharedLibrariesOk(const std::string shared_libraries, + std::priority_queue<DexFileAndClassPair>& queue) { + if (shared_libraries.empty()) { + if (queue.empty()) { + // No shared libraries or oat files, as expected. + return true; + } + } else { + if (shared_libraries.compare(OatFile::kSpecialSharedLibrary) == 0) { + // If we find the special shared library, skip the shared libraries check. + return true; + } + // Shared libraries is a series of dex file paths and their checksums, each separated by '*'. + std::vector<std::string> shared_libraries_split; + Split(shared_libraries, '*', &shared_libraries_split); + + size_t index = 0; + std::priority_queue<DexFileAndClassPair> temp = queue; + while (!temp.empty() && index < shared_libraries_split.size() - 1) { + DexFileAndClassPair pair(temp.top()); + const DexFile* dex_file = pair.GetDexFile(); + std::string dex_filename(dex_file->GetLocation()); + uint32_t dex_checksum = dex_file->GetLocationChecksum(); + if (dex_filename != shared_libraries_split[index] || + dex_checksum != std::stoul(shared_libraries_split[index + 1])) { + break; + } + temp.pop(); + index += 2; + } + + // Check is successful if it made it through the queue and all the shared libraries. + return temp.empty() && index == shared_libraries_split.size(); + } + return false; } // Check for class-def collisions in dex files. // -// This works by maintaining a heap with one class from each dex file, sorted by the class -// descriptor. Then a dex-file/class pair is continually removed from the heap and compared +// This first walks the class loader chain, getting all the dex files from the class loader. If +// the class loader is null or one of the class loaders in the chain is unsupported, we collect +// dex files from all open non-boot oat files to be safe. +// +// This first checks whether the shared libraries are in the expected order and the oat files +// have the expected checksums. If so, we exit early. Otherwise, we do the collision check. +// +// The collision check works by maintaining a heap with one class from each dex file, sorted by the +// class descriptor. Then a dex-file/class pair is continually removed from the heap and compared // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. bool OatFileManager::HasCollisions(const OatFile* oat_file, + jobject class_loader, + jobjectArray dex_elements, std::string* error_msg /*out*/) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); - if (!kDuplicateClassesCheck) { - return false; + + std::priority_queue<DexFileAndClassPair> queue; + bool owning_dex_files = false; + + // Try to get dex files from the given class loader. If the class loader is null, or we do + // not support one of the class loaders in the chain, conservatively compare against all + // (non-boot) oat files. + bool class_loader_ok = false; + { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::ClassLoader> h_class_loader = + hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)); + Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements = + hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements)); + if (h_class_loader.Get() != nullptr && + GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) { + class_loader_ok = true; + + // In this case, also take into account the dex_elements array, if given. We don't need to + // read it otherwise, as we'll compare against all open oat files anyways. + GetDexFilesFromDexElementsArray(soa, h_dex_elements, &queue); + } else if (h_class_loader.Get() != nullptr) { + VLOG(class_linker) << "Something unsupported with " + << PrettyClass(h_class_loader->GetClass()); + } } // Dex files are registered late - once a class is actually being loaded. We have to compare // against the open oat files. Take the oat_file_manager_lock_ that protects oat_files_ accesses. ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); - std::priority_queue<DexFileAndClassPair> queue; + if (!class_loader_ok) { + // Add dex files from already loaded oat files, but skip boot. - // Add dex files from already loaded oat files, but skip boot. - std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); - // The same OatFile can be loaded multiple times at different addresses. In this case, we don't - // need to check both against each other since they would have resolved the same way at compile - // time. - std::unordered_set<std::string> unique_locations; - for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) { - DCHECK_NE(loaded_oat_file.get(), oat_file); - const std::string& location = loaded_oat_file->GetLocation(); - if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) == - boot_oat_files.end() && location != oat_file->GetLocation() && - unique_locations.find(location) == unique_locations.end()) { - unique_locations.insert(location); - AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue); + // Clean up the queue. + while (!queue.empty()) { + queue.pop(); + } + + // Anything we load now is something we own and must be released later. + owning_dex_files = true; + + std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); + // The same OatFile can be loaded multiple times at different addresses. In this case, we don't + // need to check both against each other since they would have resolved the same way at compile + // time. + std::unordered_set<std::string> unique_locations; + for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) { + DCHECK_NE(loaded_oat_file.get(), oat_file); + const std::string& location = loaded_oat_file->GetLocation(); + if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) == + boot_oat_files.end() && location != oat_file->GetLocation() && + unique_locations.find(location) == unique_locations.end()) { + unique_locations.insert(location); + AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue); + } } } - if (queue.empty()) { - // No other oat files, return early. + // Exit if shared libraries are ok. Do a full duplicate classes check otherwise. + const std::string + shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); + if (AreSharedLibrariesOk(shared_libraries, queue)) { + FreeDexFilesInHeap(&queue, owning_dex_files); return false; } @@ -290,16 +536,17 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, compare_pop.GetCachedDescriptor(), compare_pop.GetDexFile()->GetLocation().c_str(), top.GetDexFile()->GetLocation().c_str()); + FreeDexFilesInHeap(&queue, owning_dex_files); return true; } queue.pop(); - AddNext(&top, &queue); + AddNext(&top, &queue, owning_dex_files); } else { // Something else. Done here. break; } } - AddNext(&compare_pop, &queue); + AddNext(&compare_pop, &queue, owning_dex_files); } return false; @@ -363,7 +610,8 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if (oat_file != nullptr) { // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = !HasCollisions(oat_file.get(), /*out*/ &error_msg); + bool accept_oat_file = + !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg); if (!accept_oat_file) { // Failed the collision check. Print warning. if (Runtime::Current()->IsDexFileFallbackEnabled()) { diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 7017dfc6ec..a1d1275e63 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -116,9 +116,16 @@ class OatFileManager { void DumpForSigQuit(std::ostream& os); private: - // Check for duplicate class definitions of the given oat file against all open oat files. + // Check that the shared libraries in the given oat file match those in the given class loader and + // dex elements. If the class loader is null or we do not support one of the class loaders in the + // chain, compare against all non-boot oat files instead. If the shared libraries are not ok, + // check for duplicate class definitions of the given oat file against the oat files (either from + // the class loader and dex elements if possible or all non-boot oat files otherwise). // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, /*out*/std::string* error_msg) const + bool HasCollisions(const OatFile* oat_file, + jobject class_loader, + jobjectArray dex_elements, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ca8f8bb510..63976d0b14 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -856,7 +856,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location, if (index == 0) { // First file. See if this is a multi-image environment, and if so, enqueue the other images. const OatHeader& boot_oat_header = oat_file->GetOatHeader(); - const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey); if (boot_cp != nullptr) { gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0], boot_cp, diff --git a/test/138-duplicate-classes-check/src/FancyLoader.java b/test/138-duplicate-classes-check/src/FancyLoader.java deleted file mode 100644 index 03ec948767..0000000000 --- a/test/138-duplicate-classes-check/src/FancyLoader.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; - -/** - * A class loader with atypical behavior: we try to load a private - * class implementation before asking the system or boot loader. This - * is used to create multiple classes with identical names in a single VM. - * - * If DexFile is available, we use that; if not, we assume we're not in - * Dalvik and instantiate the class with defineClass(). - * - * The location of the DEX files and class data is dependent upon the - * test framework. - */ -public class FancyLoader extends ClassLoader { - /* this is where the "alternate" .class files live */ - static final String CLASS_PATH = "classes-ex/"; - - /* this is the "alternate" DEX/Jar file */ - static final String DEX_FILE = System.getenv("DEX_LOCATION") + - "/138-duplicate-classes-check-ex.jar"; - - /* on Dalvik, this is a DexFile; otherwise, it's null */ - private Class mDexClass; - - private Object mDexFile; - - /** - * Construct FancyLoader, grabbing a reference to the DexFile class - * if we're running under Dalvik. - */ - public FancyLoader(ClassLoader parent) { - super(parent); - - try { - mDexClass = parent.loadClass("dalvik.system.DexFile"); - } catch (ClassNotFoundException cnfe) { - // ignore -- not running Dalvik - } - } - - /** - * Finds the class with the specified binary name. - * - * We search for a file in CLASS_PATH or pull an entry from DEX_FILE. - * If we don't find a match, we throw an exception. - */ - protected Class<?> findClass(String name) throws ClassNotFoundException - { - if (mDexClass != null) { - return findClassDalvik(name); - } else { - return findClassNonDalvik(name); - } - } - - /** - * Finds the class with the specified binary name, from a DEX file. - */ - private Class<?> findClassDalvik(String name) - throws ClassNotFoundException { - - if (mDexFile == null) { - synchronized (FancyLoader.class) { - Constructor ctor; - /* - * Construct a DexFile object through reflection. - */ - try { - ctor = mDexClass.getConstructor(new Class[] {String.class}); - } catch (NoSuchMethodException nsme) { - throw new ClassNotFoundException("getConstructor failed", - nsme); - } - - try { - mDexFile = ctor.newInstance(DEX_FILE); - } catch (InstantiationException ie) { - throw new ClassNotFoundException("newInstance failed", ie); - } catch (IllegalAccessException iae) { - throw new ClassNotFoundException("newInstance failed", iae); - } catch (InvocationTargetException ite) { - throw new ClassNotFoundException("newInstance failed", ite); - } - } - } - - /* - * Call DexFile.loadClass(String, ClassLoader). - */ - Method meth; - - try { - meth = mDexClass.getMethod("loadClass", - new Class[] { String.class, ClassLoader.class }); - } catch (NoSuchMethodException nsme) { - throw new ClassNotFoundException("getMethod failed", nsme); - } - - try { - meth.invoke(mDexFile, name, this); - } catch (IllegalAccessException iae) { - throw new ClassNotFoundException("loadClass failed", iae); - } catch (InvocationTargetException ite) { - throw new ClassNotFoundException("loadClass failed", - ite.getCause()); - } - - return null; - } - - /** - * Finds the class with the specified binary name, from .class files. - */ - private Class<?> findClassNonDalvik(String name) - throws ClassNotFoundException { - - String pathName = CLASS_PATH + name + ".class"; - //System.out.println("--- Fancy: looking for " + pathName); - - File path = new File(pathName); - RandomAccessFile raf; - - try { - raf = new RandomAccessFile(path, "r"); - } catch (FileNotFoundException fnfe) { - throw new ClassNotFoundException("Not found: " + pathName); - } - - /* read the entire file in */ - byte[] fileData; - try { - fileData = new byte[(int) raf.length()]; - raf.readFully(fileData); - } catch (IOException ioe) { - throw new ClassNotFoundException("Read error: " + pathName); - } finally { - try { - raf.close(); - } catch (IOException ioe) { - // drop - } - } - - /* create the class */ - //System.out.println("--- Fancy: defining " + name); - try { - return defineClass(name, fileData, 0, fileData.length); - } catch (Throwable th) { - throw new ClassNotFoundException("defineClass failed", th); - } - } - - /** - * Load a class. - * - * Normally a class loader wouldn't override this, but we want our - * version of the class to take precedence over an already-loaded - * version. - * - * We still want the system classes (e.g. java.lang.Object) from the - * bootstrap class loader. - */ - protected Class<?> loadClass(String name, boolean resolve) - throws ClassNotFoundException - { - Class res; - - /* - * 1. Invoke findLoadedClass(String) to check if the class has - * already been loaded. - * - * This doesn't change. - */ - res = findLoadedClass(name); - if (res != null) { - System.out.println("FancyLoader.loadClass: " - + name + " already loaded"); - if (resolve) - resolveClass(res); - return res; - } - - /* - * 3. Invoke the findClass(String) method to find the class. - */ - try { - res = findClass(name); - if (resolve) - resolveClass(res); - } - catch (ClassNotFoundException e) { - // we couldn't find it, so eat the exception and keep going - } - - /* - * 2. Invoke the loadClass method on the parent class loader. If - * the parent loader is null the class loader built-in to the - * virtual machine is used, instead. - * - * (Since we're not in java.lang, we can't actually invoke the - * parent's loadClass() method, but we passed our parent to the - * super-class which can take care of it for us.) - */ - res = super.loadClass(name, resolve); // returns class or throws - return res; - } -} diff --git a/test/138-duplicate-classes-check/src/Main.java b/test/138-duplicate-classes-check/src/Main.java index a9b5bb04ea..a2ef281939 100644 --- a/test/138-duplicate-classes-check/src/Main.java +++ b/test/138-duplicate-classes-check/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import dalvik.system.DexClassLoader; import java.io.File; import java.lang.reflect.Method; @@ -30,7 +31,11 @@ public class Main { // Now run the class from the -ex file. - FancyLoader loader = new FancyLoader(getClass().getClassLoader()); + String dexPath = System.getenv("DEX_LOCATION") + "/138-duplicate-classes-check-ex.jar"; + String optimizedDirectory = System.getenv("DEX_LOCATION"); + String librarySearchPath = null; + DexClassLoader loader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath, + getClass().getClassLoader()); try { Class testEx = loader.loadClass("TestEx"); |