diff options
Diffstat (limited to 'runtime/class_linker.cc')
| -rw-r--r-- | runtime/class_linker.cc | 364 |
1 files changed, 270 insertions, 94 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 61f94d4cbf..60453c3cc1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -634,15 +634,22 @@ OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) { const char* dex_location = dex_file.GetLocation().c_str(); uint32_t dex_location_checksum = dex_file.GetLocationChecksum(); - return FindOpenedOatFileFromDexLocation(dex_location, &dex_location_checksum); + return FindOpenedOatFile(nullptr, dex_location, &dex_location_checksum); } -const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation( - const char* dex_location, const uint32_t* const dex_location_checksum) { +const OatFile* ClassLinker::FindOpenedOatFile(const char* oat_location, const char* dex_location, + const uint32_t* const dex_location_checksum) { ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i < oat_files_.size(); i++) { const OatFile* oat_file = oat_files_[i]; DCHECK(oat_file != NULL); + + if (oat_location != nullptr) { + if (oat_file->GetLocation() != oat_location) { + continue; + } + } + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, dex_location_checksum, false); @@ -653,10 +660,229 @@ const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation( return NULL; } -const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::string* error_msg) { +static std::string GetMultiDexClassesDexName(size_t number, const char* dex_location) { + if (number == 0) { + return dex_location; + } else { + return StringPrintf("%s" kMultiDexSeparatorString "classes%zu.dex", dex_location, number + 1); + } +} + +static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, const char* dex_location, + bool generated, + std::vector<std::string>* error_msgs, + std::vector<const DexFile*>* dex_files) { + if (oat_file == nullptr) { + return false; + } + + size_t old_size = dex_files->size(); // To rollback on error. + + bool success = true; + for (size_t i = 0; success; ++i) { + std::string next_name_str = GetMultiDexClassesDexName(i, dex_location); + const char* next_name = next_name_str.c_str(); + + uint32_t dex_location_checksum; + uint32_t* dex_location_checksum_pointer = &dex_location_checksum; + std::string error_msg; + if (!DexFile::GetChecksum(next_name, dex_location_checksum_pointer, &error_msg)) { + DCHECK_EQ(false, i == 0 && generated); + dex_location_checksum_pointer = nullptr; + } + + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false); + + if (oat_dex_file == nullptr) { + if (i == 0 && generated) { + std::string error_msg; + error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out " + " file'%s'", dex_location, dex_location_checksum, + oat_file->GetLocation().c_str()); + error_msgs->push_back(error_msg); + } + break; // Not found, done. + } + + // Checksum test. Test must succeed when generated. + success = !generated; + if (dex_location_checksum_pointer != nullptr) { + success = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum(); + } + + if (success) { + const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); + if (dex_file == nullptr) { + success = false; + error_msgs->push_back(error_msg); + } else { + dex_files->push_back(dex_file); + } + } + + // When we generated the file, we expect success, or something is terribly wrong. + CHECK_EQ(false, generated && !success) + << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str() + << std::hex << " dex_location_checksum=" << dex_location_checksum + << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum(); + } + + if (dex_files->size() == old_size) { + success = false; // We did not even find classes.dex + } + + if (success) { + return true; + } else { + // Free all the dex files we have loaded. + auto it = dex_files->begin() + old_size; + auto it_end = dex_files->end(); + for (; it != it_end; it++) { + delete *it; + } + dex_files->erase(dex_files->begin() + old_size, it_end); + + return false; + } +} + +// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This +// complicates the loading process, as we should not use an iterative loading process, because that +// would register the oat file and dex files that come before the broken one. Instead, check all +// multidex ahead of time. +bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location, + std::vector<std::string>* error_msgs, + std::vector<const DexFile*>* dex_files) { + // 1) Check whether we have an open oat file. + // This requires a dex checksum, use the "primary" one. + uint32_t dex_location_checksum; + uint32_t* dex_location_checksum_pointer = &dex_location_checksum; + bool have_checksum = true; + std::string checksum_error_msg; + if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) { + dex_location_checksum_pointer = nullptr; + have_checksum = false; + } + + bool needs_registering = false; + + std::unique_ptr<const OatFile> open_oat_file(FindOpenedOatFile(oat_location, dex_location, + dex_location_checksum_pointer)); + + // 2) If we do not have an open one, maybe there's one on disk already. + + // In case the oat file is not open, we play a locking game here so + // that if two different processes race to load and register or generate + // (or worse, one tries to open a partial generated file) we will be okay. + // This is actually common with apps that use DexClassLoader to work + // around the dex method reference limit and that have a background + // service running in a separate process. + ScopedFlock scoped_flock; + + if (open_oat_file.get() == nullptr) { + if (oat_location != nullptr) { + // Can only do this if we have a checksum, else error. + if (!have_checksum) { + error_msgs->push_back(checksum_error_msg); + return false; + } + + std::string error_msg; + + // We are loading or creating one in the future. Time to set up the file lock. + if (!scoped_flock.Init(oat_location, &error_msg)) { + error_msgs->push_back(error_msg); + return false; + } + + open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum, + oat_location, &error_msg)); + + if (open_oat_file.get() == nullptr) { + std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s", + dex_location, oat_location, error_msg.c_str()); + VLOG(class_linker) << compound_msg; + error_msgs->push_back(compound_msg); + } + } else { + // TODO: What to lock here? + open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location, + dex_location_checksum_pointer, + kRuntimeISA, error_msgs)); + } + needs_registering = true; + } + + // 3) If we have an oat file, check all contained multidex files for our dex_location. + // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument. + bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, false, error_msgs, + dex_files); + if (success) { + const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it. + if (needs_registering) { + // We opened the oat file, so we must register it. + RegisterOatFile(oat_file); + } + return true; + } else { + if (needs_registering) { + // We opened it, delete it. + open_oat_file.reset(); + } else { + open_oat_file.release(); // Do not delete open oat files. + } + } + + // 4) If it's not the case (either no oat file or mismatches), regenerate and load. + + // Need a checksum, fail else. + if (!have_checksum) { + error_msgs->push_back(checksum_error_msg); + return false; + } + + // Look in cache location if no oat_location is given. + std::string cache_location; + if (oat_location == nullptr) { + // Use the dalvik cache. + const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA))); + cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str()); + oat_location = cache_location.c_str(); + } + + // Definitely need to lock now. + if (!scoped_flock.HasFile()) { + std::string error_msg; + if (!scoped_flock.Init(oat_location, &error_msg)) { + error_msgs->push_back(error_msg); + return false; + } + } + + // Create the oat file. + open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(), + oat_location, error_msgs)); + + // Failed, bail. + if (open_oat_file.get() == nullptr) { + return false; + } + + // Try to load again, but stronger checks. + success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, true, error_msgs, + dex_files); + if (success) { + RegisterOatFile(open_oat_file.release()); + return true; + } else { + return false; + } +} + +const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location, + uint32_t dex_location_checksum, + const char* oat_location, + std::string* error_msg) { std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler(), error_msg)); @@ -699,44 +925,21 @@ const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location, actual_dex_checksum); return nullptr; } - const DexFile* dex_file = oat_dex_file->OpenDexFile(error_msg); - if (dex_file != nullptr) { - RegisterOatFile(oat_file.release()); - } - return dex_file; -} - -const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation( - const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::vector<std::string>* error_msgs) { - // We play a locking game here so that if two different processes - // race to generate (or worse, one tries to open a partial generated - // file) we will be okay. This is actually common with apps that use - // DexClassLoader to work around the dex method reference limit and - // that have a background service running in a separate process. - ScopedFlock scoped_flock; - std::string error_msg; - if (!scoped_flock.Init(oat_location, &error_msg)) { - error_msgs->push_back(error_msg); + std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg)); + if (dex_file.get() != nullptr) { + return oat_file.release(); + } else { return nullptr; } +} - // Check if we already have an up-to-date output file - const DexFile* dex_file = FindDexFileInOatLocation(dex_location, dex_location_checksum, - oat_location, &error_msg); - if (dex_file != nullptr) { - return dex_file; - } - std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s", - dex_location, oat_location, error_msg.c_str()); - VLOG(class_linker) << compound_msg; - error_msgs->push_back(compound_msg); - +const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location, + int fd, const char* oat_location, + std::vector<std::string>* error_msgs) { // Generate the output oat file for the dex file VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location; - if (!GenerateOatFile(dex_location, scoped_flock.GetFile()->Fd(), oat_location, &error_msg)) { + std::string error_msg; + if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) { CHECK(!error_msg.empty()); error_msgs->push_back(error_msg); return nullptr; @@ -745,27 +948,13 @@ const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation( !Runtime::Current()->IsCompiler(), &error_msg)); if (oat_file.get() == nullptr) { - compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s", - oat_location, error_msg.c_str()); + std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s", + oat_location, error_msg.c_str()); error_msgs->push_back(compound_msg); return nullptr; } - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum); - if (oat_dex_file == nullptr) { - error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out file " - "'%s'", dex_location, dex_location_checksum, oat_location); - error_msgs->push_back(error_msg); - return nullptr; - } - const DexFile* result = oat_dex_file->OpenDexFile(&error_msg); - CHECK(result != nullptr) << error_msgs << ", " << error_msg; - CHECK_EQ(dex_location_checksum, result->GetLocationChecksum()) - << "dex_location=" << dex_location << " oat_location=" << oat_location << std::hex - << " dex_location_checksum=" << dex_location_checksum - << " DexFile::GetLocationChecksum()=" << result->GetLocationChecksum(); - RegisterOatFile(oat_file.release()); - return result; + + return oat_file.release(); } bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, @@ -832,17 +1021,17 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, return false; } -const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location, - const char* dex_location, - std::string* error_msg, - bool* open_failed) { +const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_file_location, + const char* dex_location, + std::string* error_msg, + bool* open_failed) { std::unique_ptr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg)); if (oat_file.get() == nullptr) { *open_failed = true; return nullptr; } *open_failed = false; - const DexFile* dex_file = nullptr; + std::unique_ptr<const DexFile> dex_file; uint32_t dex_location_checksum; if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) { // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is @@ -855,49 +1044,38 @@ const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& o error_msg->c_str()); return nullptr; } - dex_file = oat_dex_file->OpenDexFile(error_msg); + dex_file.reset(oat_dex_file->OpenDexFile(error_msg)); } else { bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum, kRuntimeISA, error_msg); if (!verified) { return nullptr; } - dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum)->OpenDexFile(error_msg); + dex_file.reset(oat_file->GetOatDexFile(dex_location, + &dex_location_checksum)->OpenDexFile(error_msg)); } - if (dex_file != nullptr) { - RegisterOatFile(oat_file.release()); + + if (dex_file.get() != nullptr) { + return oat_file.release(); + } else { + return nullptr; } - return dex_file; } -const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation( +const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation( const char* dex_location, const uint32_t* const dex_location_checksum, InstructionSet isa, std::vector<std::string>* error_msgs) { - const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location, - dex_location_checksum); - if (open_oat_file != nullptr) { - const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location, - dex_location_checksum); - std::string error_msg; - const DexFile* ret = oat_dex_file->OpenDexFile(&error_msg); - if (ret == nullptr) { - error_msgs->push_back(error_msg); - } - return ret; - } - // Look for an existing file next to dex. for example, for // /foo/bar/baz.jar, look for /foo/bar/<isa>/baz.odex. std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa)); bool open_failed; std::string error_msg; - const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(odex_filename, dex_location, - &error_msg, &open_failed); - if (dex_file != nullptr) { - return dex_file; + const OatFile* oat_file = LoadOatFileAndVerifyDexFile(odex_filename, dex_location, &error_msg, + &open_failed); + if (oat_file != nullptr) { + return oat_file; } if (dex_location_checksum == nullptr) { error_msgs->push_back(StringPrintf("Failed to open oat file from %s and no classes.dex found in" @@ -910,10 +1088,10 @@ const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation( const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA))); std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str())); - dex_file = VerifyAndOpenDexFileFromOatFile(cache_location, dex_location, &cache_error_msg, - &open_failed); - if (dex_file != nullptr) { - return dex_file; + oat_file = LoadOatFileAndVerifyDexFile(cache_location, dex_location, &cache_error_msg, + &open_failed); + if (oat_file != nullptr) { + return oat_file; } if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) { PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location; @@ -924,9 +1102,7 @@ const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation( VLOG(class_linker) << compound_msg; error_msgs->push_back(compound_msg); - // Try to generate oat file if it wasn't found or was obsolete. - return FindOrCreateOatFileForDexLocation(dex_location, *dex_location_checksum, - cache_location.c_str(), error_msgs); + return nullptr; } const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) { |