|  | /* | 
|  | * Copyright (C) 2015 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "oat_file_manager.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <queue> | 
|  | #include <vector> | 
|  | #include <sys/stat.h> | 
|  |  | 
|  | #include "android-base/file.h" | 
|  | #include "android-base/stringprintf.h" | 
|  | #include "android-base/strings.h" | 
|  |  | 
|  | #include "art_field-inl.h" | 
|  | #include "base/bit_vector-inl.h" | 
|  | #include "base/file_utils.h" | 
|  | #include "base/logging.h"  // For VLOG. | 
|  | #include "base/mutex-inl.h" | 
|  | #include "base/sdk_version.h" | 
|  | #include "base/stl_util.h" | 
|  | #include "base/systrace.h" | 
|  | #include "class_linker.h" | 
|  | #include "class_loader_context.h" | 
|  | #include "dex/art_dex_file_loader.h" | 
|  | #include "dex/dex_file-inl.h" | 
|  | #include "dex/dex_file_loader.h" | 
|  | #include "dex/dex_file_tracking_registrar.h" | 
|  | #include "gc/scoped_gc_critical_section.h" | 
|  | #include "gc/space/image_space.h" | 
|  | #include "handle_scope-inl.h" | 
|  | #include "jit/jit.h" | 
|  | #include "jni/java_vm_ext.h" | 
|  | #include "jni/jni_internal.h" | 
|  | #include "mirror/class_loader.h" | 
|  | #include "mirror/object-inl.h" | 
|  | #include "oat_file.h" | 
|  | #include "oat_file_assistant.h" | 
|  | #include "obj_ptr-inl.h" | 
|  | #include "scoped_thread_state_change-inl.h" | 
|  | #include "thread-current-inl.h" | 
|  | #include "thread_list.h" | 
|  | #include "thread_pool.h" | 
|  | #include "vdex_file.h" | 
|  | #include "verifier/verifier_deps.h" | 
|  | #include "well_known_classes.h" | 
|  |  | 
|  | namespace art { | 
|  |  | 
|  | using android::base::StringPrintf; | 
|  |  | 
|  | // If true, we attempt to load the application image if it exists. | 
|  | static constexpr bool kEnableAppImage = true; | 
|  |  | 
|  | const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file, | 
|  | bool in_memory) { | 
|  | // Use class_linker vlog to match the log for dex file registration. | 
|  | VLOG(class_linker) << "Registered oat file " << oat_file->GetLocation(); | 
|  | PaletteNotifyOatFileLoaded(oat_file->GetLocation().c_str()); | 
|  |  | 
|  | WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | CHECK(in_memory || | 
|  | !only_use_system_oat_files_ || | 
|  | LocationIsTrusted(oat_file->GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) || | 
|  | !oat_file->IsExecutable()) | 
|  | << "Registering a non /system oat file: " << oat_file->GetLocation() << " android-root=" | 
|  | << GetAndroidRoot(); | 
|  | DCHECK(oat_file != nullptr); | 
|  | if (kIsDebugBuild) { | 
|  | CHECK(oat_files_.find(oat_file) == oat_files_.end()); | 
|  | for (const std::unique_ptr<const OatFile>& existing : oat_files_) { | 
|  | CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation(); | 
|  | // Check that we don't have an oat file with the same address. Copies of the same oat file | 
|  | // should be loaded at different addresses. | 
|  | CHECK_NE(oat_file->Begin(), existing->Begin()) << "Oat file already mapped at that location"; | 
|  | } | 
|  | } | 
|  | const OatFile* ret = oat_file.get(); | 
|  | oat_files_.insert(std::move(oat_file)); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void OatFileManager::UnRegisterAndDeleteOatFile(const OatFile* oat_file) { | 
|  | WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | DCHECK(oat_file != nullptr); | 
|  | std::unique_ptr<const OatFile> compare(oat_file); | 
|  | auto it = oat_files_.find(compare); | 
|  | CHECK(it != oat_files_.end()); | 
|  | oat_files_.erase(it); | 
|  | compare.release();  // NOLINT b/117926937 | 
|  | } | 
|  |  | 
|  | const OatFile* OatFileManager::FindOpenedOatFileFromDexLocation( | 
|  | const std::string& dex_base_location) const { | 
|  | ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { | 
|  | const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles(); | 
|  | for (const OatDexFile* oat_dex_file : oat_dex_files) { | 
|  | if (DexFileLoader::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) { | 
|  | return oat_file.get(); | 
|  | } | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location) | 
|  | const { | 
|  | ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | return FindOpenedOatFileFromOatLocationLocked(oat_location); | 
|  | } | 
|  |  | 
|  | const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked( | 
|  | const std::string& oat_location) const { | 
|  | for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { | 
|  | if (oat_file->GetLocation() == oat_location) { | 
|  | return oat_file.get(); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const { | 
|  | std::vector<gc::space::ImageSpace*> image_spaces = | 
|  | Runtime::Current()->GetHeap()->GetBootImageSpaces(); | 
|  | std::vector<const OatFile*> oat_files; | 
|  | oat_files.reserve(image_spaces.size()); | 
|  | for (gc::space::ImageSpace* image_space : image_spaces) { | 
|  | oat_files.push_back(image_space->GetOatFile()); | 
|  | } | 
|  | return oat_files; | 
|  | } | 
|  |  | 
|  | OatFileManager::OatFileManager() | 
|  | : only_use_system_oat_files_(false) {} | 
|  |  | 
|  | OatFileManager::~OatFileManager() { | 
|  | // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for | 
|  | // UnRegisterOatFileLocation. | 
|  | oat_files_.clear(); | 
|  | } | 
|  |  | 
|  | std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles( | 
|  | const std::vector<gc::space::ImageSpace*>& spaces) { | 
|  | std::vector<const OatFile*> oat_files; | 
|  | oat_files.reserve(spaces.size()); | 
|  | for (gc::space::ImageSpace* space : spaces) { | 
|  | // The oat file was generated in memory if the image space has a profile. | 
|  | bool in_memory = !space->GetProfileFiles().empty(); | 
|  | oat_files.push_back(RegisterOatFile(space->ReleaseOatFile(), in_memory)); | 
|  | } | 
|  | return oat_files; | 
|  | } | 
|  |  | 
|  | bool OatFileManager::ShouldLoadAppImage(const OatFile* source_oat_file) const { | 
|  | Runtime* const runtime = Runtime::Current(); | 
|  | return kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable()); | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( | 
|  | const char* dex_location, | 
|  | jobject class_loader, | 
|  | jobjectArray dex_elements, | 
|  | const OatFile** out_oat_file, | 
|  | std::vector<std::string>* error_msgs) { | 
|  | ScopedTrace trace(StringPrintf("%s(%s)", __FUNCTION__, dex_location)); | 
|  | CHECK(dex_location != nullptr); | 
|  | CHECK(error_msgs != nullptr); | 
|  |  | 
|  | // Verify we aren't holding the mutator lock, which could starve GC when | 
|  | // hitting the disk. | 
|  | Thread* const self = Thread::Current(); | 
|  | Locks::mutator_lock_->AssertNotHeld(self); | 
|  | Runtime* const runtime = Runtime::Current(); | 
|  |  | 
|  | std::vector<std::unique_ptr<const DexFile>> dex_files; | 
|  | std::unique_ptr<ClassLoaderContext> context( | 
|  | ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements)); | 
|  |  | 
|  | // If the class_loader is null there's not much we can do. This happens if a dex files is loaded | 
|  | // directly with DexFile APIs instead of using class loaders. | 
|  | if (class_loader == nullptr) { | 
|  | LOG(WARNING) << "Opening an oat file without a class loader. " | 
|  | << "Are you using the deprecated DexFile APIs?"; | 
|  | } else if (context != nullptr) { | 
|  | OatFileAssistant oat_file_assistant(dex_location, | 
|  | kRuntimeISA, | 
|  | context.get(), | 
|  | runtime->GetOatFilesExecutable(), | 
|  | only_use_system_oat_files_); | 
|  |  | 
|  | // Get the current optimization status for trace debugging. | 
|  | // Implementation detail note: GetOptimizationStatus will select the same | 
|  | // oat file as GetBestOatFile used below, and in doing so it already pre-populates | 
|  | // some OatFileAssistant internal fields. | 
|  | std::string odex_location; | 
|  | std::string compilation_filter; | 
|  | std::string compilation_reason; | 
|  | std::string odex_status; | 
|  | oat_file_assistant.GetOptimizationStatus( | 
|  | &odex_location, | 
|  | &compilation_filter, | 
|  | &compilation_reason, | 
|  | &odex_status); | 
|  |  | 
|  | Runtime::Current()->GetAppInfo()->RegisterOdexStatus( | 
|  | dex_location, | 
|  | compilation_filter, | 
|  | compilation_reason, | 
|  | odex_status); | 
|  |  | 
|  | ScopedTrace odex_loading(StringPrintf( | 
|  | "location=%s status=%s filter=%s reason=%s", | 
|  | odex_location.c_str(), | 
|  | odex_status.c_str(), | 
|  | compilation_filter.c_str(), | 
|  | compilation_reason.c_str())); | 
|  |  | 
|  | const bool has_registered_app_info = Runtime::Current()->GetAppInfo()->HasRegisteredAppInfo(); | 
|  | const AppInfo::CodeType code_type = | 
|  | Runtime::Current()->GetAppInfo()->GetRegisteredCodeType(dex_location); | 
|  | // We only want to madvise primary/split dex artifacts as a startup optimization. However, | 
|  | // as the code_type for those artifacts may not be set until the initial app info registration, | 
|  | // we conservatively madvise everything until the app info registration is complete. | 
|  | const bool should_madvise_vdex_and_odex = !has_registered_app_info || | 
|  | code_type == AppInfo::CodeType::kPrimaryApk || | 
|  | code_type == AppInfo::CodeType::kSplitApk; | 
|  |  | 
|  | // Proceed with oat file loading. | 
|  | std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); | 
|  | VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()=" | 
|  | << (oat_file != nullptr ? oat_file->GetLocation() : "") | 
|  | << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; | 
|  |  | 
|  | CHECK(oat_file == nullptr || odex_location == oat_file->GetLocation()) | 
|  | << "OatFileAssistant non-determinism in choosing best oat files. " | 
|  | << "optimization-status-location=" << odex_location | 
|  | << " best_oat_file-location=" << oat_file->GetLocation(); | 
|  |  | 
|  | if (oat_file != nullptr) { | 
|  | // Load the dex files from the oat file. | 
|  | bool added_image_space = false; | 
|  | if (oat_file->IsExecutable()) { | 
|  | if (should_madvise_vdex_and_odex) { | 
|  | VLOG(oat) << "Madvising oat file: " << oat_file->GetLocation(); | 
|  | size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex(); | 
|  | Runtime::MadviseFileForRange(madvise_size_limit, | 
|  | oat_file->Size(), | 
|  | oat_file->Begin(), | 
|  | oat_file->End(), | 
|  | oat_file->GetLocation()); | 
|  | } | 
|  |  | 
|  | ScopedTrace app_image_timing("AppImage:Loading"); | 
|  |  | 
|  | // We need to throw away the image space if we are debuggable but the oat-file source of the | 
|  | // image is not otherwise we might get classes with inlined methods or other such things. | 
|  | std::unique_ptr<gc::space::ImageSpace> image_space; | 
|  | if (ShouldLoadAppImage(oat_file.get())) { | 
|  | image_space = oat_file_assistant.OpenImageSpace(oat_file.get()); | 
|  | } | 
|  | if (image_space != nullptr) { | 
|  | ScopedObjectAccess soa(self); | 
|  | StackHandleScope<1> hs(self); | 
|  | Handle<mirror::ClassLoader> h_loader( | 
|  | hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); | 
|  | // Can not load app image without class loader. | 
|  | if (h_loader != nullptr) { | 
|  | std::string temp_error_msg; | 
|  | // Add image space has a race condition since other threads could be reading from the | 
|  | // spaces array. | 
|  | { | 
|  | ScopedThreadSuspension sts(self, ThreadState::kSuspended); | 
|  | gc::ScopedGCCriticalSection gcs(self, | 
|  | gc::kGcCauseAddRemoveAppImageSpace, | 
|  | gc::kCollectorTypeAddRemoveAppImageSpace); | 
|  | ScopedSuspendAll ssa("Add image space"); | 
|  | runtime->GetHeap()->AddSpace(image_space.get()); | 
|  | } | 
|  | { | 
|  | ScopedTrace image_space_timing("Adding image space"); | 
|  | added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), | 
|  | h_loader, | 
|  | /*out*/&dex_files, | 
|  | /*out*/&temp_error_msg); | 
|  | } | 
|  | if (added_image_space) { | 
|  | // Successfully added image space to heap, release the map so that it does not get | 
|  | // freed. | 
|  | image_space.release();  // NOLINT b/117926937 | 
|  |  | 
|  | // Register for tracking. | 
|  | for (const auto& dex_file : dex_files) { | 
|  | dex::tracking::RegisterDexFile(dex_file.get()); | 
|  | } | 
|  | } else { | 
|  | LOG(INFO) << "Failed to add image file " << temp_error_msg; | 
|  | dex_files.clear(); | 
|  | { | 
|  | ScopedThreadSuspension sts(self, ThreadState::kSuspended); | 
|  | gc::ScopedGCCriticalSection gcs(self, | 
|  | gc::kGcCauseAddRemoveAppImageSpace, | 
|  | gc::kCollectorTypeAddRemoveAppImageSpace); | 
|  | ScopedSuspendAll ssa("Remove image space"); | 
|  | runtime->GetHeap()->RemoveSpace(image_space.get()); | 
|  | } | 
|  | // Non-fatal, don't update error_msg. | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (!added_image_space) { | 
|  | DCHECK(dex_files.empty()); | 
|  |  | 
|  | if (oat_file->RequiresImage()) { | 
|  | LOG(WARNING) << "Loading " | 
|  | << oat_file->GetLocation() | 
|  | << " non-executable as it requires an image which we failed to load"; | 
|  | // file as non-executable. | 
|  | OatFileAssistant nonexecutable_oat_file_assistant(dex_location, | 
|  | kRuntimeISA, | 
|  | context.get(), | 
|  | /*load_executable=*/false, | 
|  | only_use_system_oat_files_); | 
|  | oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release()); | 
|  |  | 
|  | // The file could be deleted concurrently (for example background | 
|  | // dexopt, or secondary oat file being deleted by the app). | 
|  | if (oat_file == nullptr) { | 
|  | LOG(WARNING) << "Failed to reload oat file non-executable " << dex_location; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (oat_file != nullptr) { | 
|  | dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location); | 
|  |  | 
|  | // Register for tracking. | 
|  | for (const auto& dex_file : dex_files) { | 
|  | dex::tracking::RegisterDexFile(dex_file.get()); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (dex_files.empty()) { | 
|  | ScopedTrace failed_to_open_dex_files("FailedToOpenDexFilesFromOat"); | 
|  | error_msgs->push_back("Failed to open dex files from " + odex_location); | 
|  | } else { | 
|  | // Opened dex files from an oat file, madvise them to their loaded state. | 
|  | for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { | 
|  | OatDexFile::MadviseDexFileAtLoad(*dex_file); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (oat_file != nullptr) { | 
|  | VdexFile* vdex_file = oat_file->GetVdexFile(); | 
|  | if (should_madvise_vdex_and_odex && vdex_file != nullptr) { | 
|  | VLOG(oat) << "Madvising vdex file: " << vdex_file->GetName(); | 
|  | // Opened vdex file from an oat file, madvise it to its loaded state. | 
|  | // TODO(b/196052575): Unify dex and vdex madvise knobs and behavior. | 
|  | const size_t madvise_size_limit = Runtime::Current()->GetMadviseWillNeedSizeVdex(); | 
|  | Runtime::MadviseFileForRange(madvise_size_limit, | 
|  | vdex_file->Size(), | 
|  | vdex_file->Begin(), | 
|  | vdex_file->End(), | 
|  | vdex_file->GetName()); | 
|  | } | 
|  |  | 
|  | VLOG(class_linker) << "Registering " << oat_file->GetLocation(); | 
|  | *out_oat_file = RegisterOatFile(std::move(oat_file)); | 
|  | } | 
|  | } else { | 
|  | // oat_file == nullptr | 
|  | // Verify if any of the dex files being loaded is already in the class path. | 
|  | // If so, report an error with the current stack trace. | 
|  | // Most likely the developer didn't intend to do this because it will waste | 
|  | // performance and memory. | 
|  | if (oat_file_assistant.GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) { | 
|  | std::set<const DexFile*> already_exists_in_classpath = | 
|  | context->CheckForDuplicateDexFiles(MakeNonOwningPointerVector(dex_files)); | 
|  | if (!already_exists_in_classpath.empty()) { | 
|  | ScopedTrace duplicate_dex_files("DuplicateDexFilesInContext"); | 
|  | auto duplicate_it = already_exists_in_classpath.begin(); | 
|  | std::string duplicates = (*duplicate_it)->GetLocation(); | 
|  | for (duplicate_it++ ; duplicate_it != already_exists_in_classpath.end(); duplicate_it++) { | 
|  | duplicates += "," + (*duplicate_it)->GetLocation(); | 
|  | } | 
|  |  | 
|  | std::ostringstream out; | 
|  | out << "Trying to load dex files which is already loaded in the same ClassLoader " | 
|  | << "hierarchy.\n" | 
|  | << "This is a strong indication of bad ClassLoader construct which leads to poor " | 
|  | << "performance and wastes memory.\n" | 
|  | << "The list of duplicate dex files is: " << duplicates << "\n" | 
|  | << "The current class loader context is: " | 
|  | << context->EncodeContextForOatFile("") << "\n" | 
|  | << "Java stack trace:\n"; | 
|  |  | 
|  | { | 
|  | ScopedObjectAccess soa(self); | 
|  | self->DumpJavaStack(out); | 
|  | } | 
|  |  | 
|  | // We log this as an ERROR to stress the fact that this is most likely unintended. | 
|  | // Note that ART cannot do anything about it. It is up to the app to fix their logic. | 
|  | // Here we are trying to give a heads up on why the app might have performance issues. | 
|  | LOG(ERROR) << out.str(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we arrive here with an empty dex files list, it means we fail to load | 
|  | // it/them through an .oat file. | 
|  | if (dex_files.empty()) { | 
|  | std::string error_msg; | 
|  | static constexpr bool kVerifyChecksum = true; | 
|  | const ArtDexFileLoader dex_file_loader; | 
|  | if (!dex_file_loader.Open(dex_location, | 
|  | dex_location, | 
|  | Runtime::Current()->IsVerificationEnabled(), | 
|  | kVerifyChecksum, | 
|  | /*out*/ &error_msg, | 
|  | &dex_files)) { | 
|  | ScopedTrace fail_to_open_dex_from_apk("FailedToOpenDexFilesFromApk"); | 
|  | LOG(WARNING) << error_msg; | 
|  | error_msgs->push_back("Failed to open dex files from " + std::string(dex_location) | 
|  | + " because: " + error_msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (Runtime::Current()->GetJit() != nullptr) { | 
|  | Runtime::Current()->GetJit()->RegisterDexFiles(dex_files, class_loader); | 
|  | } | 
|  |  | 
|  | // Now that we loaded the dex/odex files, notify the runtime. | 
|  | // Note that we do this everytime we load dex files. | 
|  | Runtime::Current()->NotifyDexFileLoaded(); | 
|  |  | 
|  | return dex_files; | 
|  | } | 
|  |  | 
|  | static std::vector<const DexFile::Header*> GetDexFileHeaders(const std::vector<MemMap>& maps) { | 
|  | std::vector<const DexFile::Header*> headers; | 
|  | headers.reserve(maps.size()); | 
|  | for (const MemMap& map : maps) { | 
|  | DCHECK(map.IsValid()); | 
|  | headers.push_back(reinterpret_cast<const DexFile::Header*>(map.Begin())); | 
|  | } | 
|  | return headers; | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( | 
|  | std::vector<MemMap>&& dex_mem_maps, | 
|  | jobject class_loader, | 
|  | jobjectArray dex_elements, | 
|  | const OatFile** out_oat_file, | 
|  | std::vector<std::string>* error_msgs) { | 
|  | std::vector<std::unique_ptr<const DexFile>> dex_files = OpenDexFilesFromOat_Impl( | 
|  | std::move(dex_mem_maps), | 
|  | class_loader, | 
|  | dex_elements, | 
|  | out_oat_file, | 
|  | error_msgs); | 
|  |  | 
|  | if (error_msgs->empty()) { | 
|  | // Remove write permission from DexFile pages. We do this at the end because | 
|  | // OatFile assigns OatDexFile pointer in the DexFile objects. | 
|  | for (std::unique_ptr<const DexFile>& dex_file : dex_files) { | 
|  | if (!dex_file->DisableWrite()) { | 
|  | error_msgs->push_back("Failed to make dex file " + dex_file->GetLocation() + " read-only"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!error_msgs->empty()) { | 
|  | return std::vector<std::unique_ptr<const DexFile>>(); | 
|  | } | 
|  |  | 
|  | return dex_files; | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat_Impl( | 
|  | std::vector<MemMap>&& dex_mem_maps, | 
|  | jobject class_loader, | 
|  | jobjectArray dex_elements, | 
|  | const OatFile** out_oat_file, | 
|  | std::vector<std::string>* error_msgs) { | 
|  | ScopedTrace trace(__FUNCTION__); | 
|  | std::string error_msg; | 
|  | DCHECK(error_msgs != nullptr); | 
|  |  | 
|  | // Extract dex file headers from `dex_mem_maps`. | 
|  | const std::vector<const DexFile::Header*> dex_headers = GetDexFileHeaders(dex_mem_maps); | 
|  |  | 
|  | // Determine dex/vdex locations and the combined location checksum. | 
|  | std::string dex_location; | 
|  | std::string vdex_path; | 
|  | bool has_vdex = OatFileAssistant::AnonymousDexVdexLocation(dex_headers, | 
|  | kRuntimeISA, | 
|  | &dex_location, | 
|  | &vdex_path); | 
|  |  | 
|  | // Attempt to open an existing vdex and check dex file checksums match. | 
|  | std::unique_ptr<VdexFile> vdex_file = nullptr; | 
|  | if (has_vdex && OS::FileExists(vdex_path.c_str())) { | 
|  | vdex_file = VdexFile::Open(vdex_path, | 
|  | /* writable= */ false, | 
|  | /* low_4gb= */ false, | 
|  | &error_msg); | 
|  | if (vdex_file == nullptr) { | 
|  | LOG(WARNING) << "Failed to open vdex " << vdex_path << ": " << error_msg; | 
|  | } else if (!vdex_file->MatchesDexFileChecksums(dex_headers)) { | 
|  | LOG(WARNING) << "Failed to open vdex " << vdex_path << ": dex file checksum mismatch"; | 
|  | vdex_file.reset(nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Load dex files. Skip structural dex file verification if vdex was found | 
|  | // and dex checksums matched. | 
|  | std::vector<std::unique_ptr<const DexFile>> dex_files; | 
|  | for (size_t i = 0; i < dex_mem_maps.size(); ++i) { | 
|  | static constexpr bool kVerifyChecksum = true; | 
|  | const ArtDexFileLoader dex_file_loader; | 
|  | std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open( | 
|  | DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()), | 
|  | dex_headers[i]->checksum_, | 
|  | std::move(dex_mem_maps[i]), | 
|  | /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(), | 
|  | kVerifyChecksum, | 
|  | &error_msg)); | 
|  | if (dex_file != nullptr) { | 
|  | dex::tracking::RegisterDexFile(dex_file.get());  // Register for tracking. | 
|  | dex_files.push_back(std::move(dex_file)); | 
|  | } else { | 
|  | error_msgs->push_back("Failed to open dex files from memory: " + error_msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check if we should proceed to creating an OatFile instance backed by the vdex. | 
|  | // We need: (a) an existing vdex, (b) class loader (can be null if invoked via reflection), | 
|  | // and (c) no errors during dex file loading. | 
|  | if (vdex_file == nullptr || class_loader == nullptr || !error_msgs->empty()) { | 
|  | return dex_files; | 
|  | } | 
|  |  | 
|  | // Attempt to create a class loader context, check OpenDexFiles succeeds (prerequisite | 
|  | // for using the context later). | 
|  | std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::CreateContextForClassLoader( | 
|  | class_loader, | 
|  | dex_elements); | 
|  | if (context == nullptr) { | 
|  | LOG(ERROR) << "Could not create class loader context for " << vdex_path; | 
|  | return dex_files; | 
|  | } | 
|  | DCHECK(context->OpenDexFiles()) | 
|  | << "Context created from already opened dex files should not attempt to open again"; | 
|  |  | 
|  | // Initialize an OatFile instance backed by the loaded vdex. | 
|  | std::unique_ptr<OatFile> oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files), | 
|  | std::move(vdex_file), | 
|  | dex_location)); | 
|  | if (oat_file != nullptr) { | 
|  | VLOG(class_linker) << "Registering " << oat_file->GetLocation(); | 
|  | *out_oat_file = RegisterOatFile(std::move(oat_file)); | 
|  | } | 
|  | return dex_files; | 
|  | } | 
|  |  | 
|  | // Check how many vdex files exist in the same directory as the vdex file we are about | 
|  | // to write. If more than or equal to kAnonymousVdexCacheSize, unlink the least | 
|  | // recently used one(s) (according to stat-reported atime). | 
|  | static bool UnlinkLeastRecentlyUsedVdexIfNeeded(const std::string& vdex_path_to_add, | 
|  | std::string* error_msg) { | 
|  | std::string basename = android::base::Basename(vdex_path_to_add); | 
|  | if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) { | 
|  | // File is not for in memory dex files. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (OS::FileExists(vdex_path_to_add.c_str())) { | 
|  | // File already exists and will be overwritten. | 
|  | // This will not change the number of entries in the cache. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | auto last_slash = vdex_path_to_add.rfind('/'); | 
|  | CHECK(last_slash != std::string::npos); | 
|  | std::string vdex_dir = vdex_path_to_add.substr(0, last_slash + 1); | 
|  |  | 
|  | if (!OS::DirectoryExists(vdex_dir.c_str())) { | 
|  | // Folder does not exist yet. Cache has zero entries. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::vector<std::pair<time_t, std::string>> cache; | 
|  |  | 
|  | DIR* c_dir = opendir(vdex_dir.c_str()); | 
|  | if (c_dir == nullptr) { | 
|  | *error_msg = "Unable to open " + vdex_dir + " to delete unused vdex files"; | 
|  | return false; | 
|  | } | 
|  | for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) { | 
|  | if (de->d_type != DT_REG) { | 
|  | continue; | 
|  | } | 
|  | basename = de->d_name; | 
|  | if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) { | 
|  | continue; | 
|  | } | 
|  | std::string fullname = vdex_dir + basename; | 
|  |  | 
|  | struct stat s; | 
|  | int rc = TEMP_FAILURE_RETRY(stat(fullname.c_str(), &s)); | 
|  | if (rc == -1) { | 
|  | *error_msg = "Failed to stat() anonymous vdex file " + fullname; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | cache.push_back(std::make_pair(s.st_atime, fullname)); | 
|  | } | 
|  | CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; | 
|  |  | 
|  | if (cache.size() < OatFileManager::kAnonymousVdexCacheSize) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::sort(cache.begin(), | 
|  | cache.end(), | 
|  | [](const auto& a, const auto& b) { return a.first < b.first; }); | 
|  | for (size_t i = OatFileManager::kAnonymousVdexCacheSize - 1; i < cache.size(); ++i) { | 
|  | if (unlink(cache[i].second.c_str()) != 0) { | 
|  | *error_msg = "Could not unlink anonymous vdex file " + cache[i].second; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | class BackgroundVerificationTask final : public Task { | 
|  | public: | 
|  | BackgroundVerificationTask(const std::vector<const DexFile*>& dex_files, | 
|  | jobject class_loader, | 
|  | const std::string& vdex_path) | 
|  | : dex_files_(dex_files), | 
|  | vdex_path_(vdex_path) { | 
|  | Thread* const self = Thread::Current(); | 
|  | ScopedObjectAccess soa(self); | 
|  | // Create a global ref for `class_loader` because it will be accessed from a different thread. | 
|  | class_loader_ = soa.Vm()->AddGlobalRef(self, soa.Decode<mirror::ClassLoader>(class_loader)); | 
|  | CHECK(class_loader_ != nullptr); | 
|  | } | 
|  |  | 
|  | ~BackgroundVerificationTask() { | 
|  | Thread* const self = Thread::Current(); | 
|  | ScopedObjectAccess soa(self); | 
|  | soa.Vm()->DeleteGlobalRef(self, class_loader_); | 
|  | } | 
|  |  | 
|  | void Run(Thread* self) override { | 
|  | std::string error_msg; | 
|  | ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); | 
|  | verifier::VerifierDeps verifier_deps(dex_files_); | 
|  |  | 
|  | // Iterate over all classes and verify them. | 
|  | for (const DexFile* dex_file : dex_files_) { | 
|  | for (uint32_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); cdef_idx++) { | 
|  | const dex::ClassDef& class_def = dex_file->GetClassDef(cdef_idx); | 
|  |  | 
|  | // Take handles inside the loop. The background verification is low priority | 
|  | // and we want to minimize the risk of blocking anyone else. | 
|  | ScopedObjectAccess soa(self); | 
|  | StackHandleScope<2> hs(self); | 
|  | Handle<mirror::ClassLoader> h_loader(hs.NewHandle( | 
|  | soa.Decode<mirror::ClassLoader>(class_loader_))); | 
|  | Handle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(class_linker->FindClass( | 
|  | self, | 
|  | dex_file->GetClassDescriptor(class_def), | 
|  | h_loader))); | 
|  |  | 
|  | if (h_class == nullptr) { | 
|  | CHECK(self->IsExceptionPending()); | 
|  | self->ClearException(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (&h_class->GetDexFile() != dex_file) { | 
|  | // There is a different class in the class path or a parent class loader | 
|  | // with the same descriptor. This `h_class` is not resolvable, skip it. | 
|  | continue; | 
|  | } | 
|  |  | 
|  | CHECK(h_class->IsResolved()) << h_class->PrettyDescriptor(); | 
|  | class_linker->VerifyClass(self, &verifier_deps, h_class); | 
|  | if (h_class->IsErroneous()) { | 
|  | // ClassLinker::VerifyClass throws, which isn't useful here. | 
|  | CHECK(soa.Self()->IsExceptionPending()); | 
|  | soa.Self()->ClearException(); | 
|  | } | 
|  |  | 
|  | CHECK(h_class->IsVerified() || h_class->IsErroneous()) | 
|  | << h_class->PrettyDescriptor() << ": state=" << h_class->GetStatus(); | 
|  |  | 
|  | if (h_class->IsVerified()) { | 
|  | verifier_deps.RecordClassVerified(*dex_file, class_def); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Delete old vdex files if there are too many in the folder. | 
|  | if (!UnlinkLeastRecentlyUsedVdexIfNeeded(vdex_path_, &error_msg)) { | 
|  | LOG(ERROR) << "Could not unlink old vdex files " << vdex_path_ << ": " << error_msg; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Construct a vdex file and write `verifier_deps` into it. | 
|  | if (!VdexFile::WriteToDisk(vdex_path_, | 
|  | dex_files_, | 
|  | verifier_deps, | 
|  | &error_msg)) { | 
|  | LOG(ERROR) << "Could not write anonymous vdex " << vdex_path_ << ": " << error_msg; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Finalize() override { | 
|  | delete this; | 
|  | } | 
|  |  | 
|  | private: | 
|  | const std::vector<const DexFile*> dex_files_; | 
|  | jobject class_loader_; | 
|  | const std::string vdex_path_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BackgroundVerificationTask); | 
|  | }; | 
|  |  | 
|  | void OatFileManager::RunBackgroundVerification(const std::vector<const DexFile*>& dex_files, | 
|  | jobject class_loader) { | 
|  | Runtime* const runtime = Runtime::Current(); | 
|  | Thread* const self = Thread::Current(); | 
|  |  | 
|  | if (runtime->IsJavaDebuggable()) { | 
|  | // Threads created by ThreadPool ("runtime threads") are not allowed to load | 
|  | // classes when debuggable to match class-initialization semantics | 
|  | // expectations. Do not verify in the background. | 
|  | return; | 
|  | } | 
|  |  | 
|  | { | 
|  | // Temporarily create a class loader context to see if we recognize the | 
|  | // chain. | 
|  | std::unique_ptr<ClassLoaderContext> context( | 
|  | ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr)); | 
|  | if (context == nullptr) { | 
|  | // We only run background verification for class loaders we know the lookup | 
|  | // chain. Because the background verification runs on runtime threads, | 
|  | // which do not call Java, we won't be able to load classes when | 
|  | // verifying, which is something the current verifier relies on. | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!IsSdkVersionSetAndAtLeast(runtime->GetTargetSdkVersion(), SdkVersion::kQ)) { | 
|  | // Do not run for legacy apps as they may depend on the previous class loader behaviour. | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (runtime->IsShuttingDown(self)) { | 
|  | // Not allowed to create new threads during runtime shutdown. | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (dex_files.size() < 1) { | 
|  | // Nothing to verify. | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::string dex_location = dex_files[0]->GetLocation(); | 
|  | const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory(); | 
|  | if (!android::base::StartsWith(dex_location, data_dir)) { | 
|  | // For now, we only run background verification for secondary dex files. | 
|  | // Running it for primary or split APKs could have some undesirable | 
|  | // side-effects, like overloading the device on app startup. | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::string error_msg; | 
|  | std::string odex_filename; | 
|  | if (!OatFileAssistant::DexLocationToOdexFilename(dex_location, | 
|  | kRuntimeISA, | 
|  | &odex_filename, | 
|  | &error_msg)) { | 
|  | LOG(WARNING) << "Could not get odex filename for " << dex_location << ": " << error_msg; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (LocationIsOnArtApexData(odex_filename) && Runtime::Current()->DenyArtApexDataFiles()) { | 
|  | // Ignore vdex file associated with this odex file as the odex file is not trustworthy. | 
|  | return; | 
|  | } | 
|  |  | 
|  | { | 
|  | WriterMutexLock mu(self, *Locks::oat_file_manager_lock_); | 
|  | if (verification_thread_pool_ == nullptr) { | 
|  | verification_thread_pool_.reset( | 
|  | new ThreadPool("Verification thread pool", /* num_threads= */ 1)); | 
|  | verification_thread_pool_->StartWorkers(self); | 
|  | } | 
|  | } | 
|  | verification_thread_pool_->AddTask(self, new BackgroundVerificationTask( | 
|  | dex_files, | 
|  | class_loader, | 
|  | GetVdexFilename(odex_filename))); | 
|  | } | 
|  |  | 
|  | void OatFileManager::WaitForWorkersToBeCreated() { | 
|  | DCHECK(!Runtime::Current()->IsShuttingDown(Thread::Current())) | 
|  | << "Cannot create new threads during runtime shutdown"; | 
|  | if (verification_thread_pool_ != nullptr) { | 
|  | verification_thread_pool_->WaitForWorkersToBeCreated(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void OatFileManager::DeleteThreadPool() { | 
|  | verification_thread_pool_.reset(nullptr); | 
|  | } | 
|  |  | 
|  | void OatFileManager::WaitForBackgroundVerificationTasks() { | 
|  | if (verification_thread_pool_ != nullptr) { | 
|  | Thread* const self = Thread::Current(); | 
|  | verification_thread_pool_->WaitForWorkersToBeCreated(); | 
|  | verification_thread_pool_->Wait(self, /* do_work= */ true, /* may_hold_locks= */ false); | 
|  | } | 
|  | } | 
|  |  | 
|  | void OatFileManager::ClearOnlyUseTrustedOatFiles() { | 
|  | only_use_system_oat_files_ = false; | 
|  | } | 
|  |  | 
|  | void OatFileManager::SetOnlyUseTrustedOatFiles() { | 
|  | ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | if (!oat_files_.empty()) { | 
|  | LOG(FATAL) << "Unexpected non-empty loaded oat files "; | 
|  | } | 
|  | only_use_system_oat_files_ = true; | 
|  | } | 
|  |  | 
|  | void OatFileManager::DumpForSigQuit(std::ostream& os) { | 
|  | ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); | 
|  | for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { | 
|  | if (ContainsElement(boot_oat_files, oat_file.get())) { | 
|  | continue; | 
|  | } | 
|  | os << oat_file->GetLocation() << ": " << oat_file->GetCompilerFilter() << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool OatFileManager::ContainsPc(const void* code) { | 
|  | ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); | 
|  | std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); | 
|  | for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { | 
|  | if (oat_file->Contains(code)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace art |