From: Andreas Gampe Date: Thu, 22 May 2014 01:46:59 +0000 (-0700) Subject: ART: Native support for multidex X-Git-Tag: android-x86-6.0-r1~3200^2~109^2^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=833a48501d560c9fa7fc78ef619888138c2d374f;p=android-x86%2Fart.git ART: Native support for multidex Native support for zip files with multiple classesX.dex. Works by explicitly looking for those files in ascending order. As these files have no file system representation for themselves, introduce synthetic dex locations: the name of the originating file plus a colon plus the name of the dex file, e.g., test.jar:classes2.dex. Opening a zip dex file will return all dex files in this way. This keeps the changes to dex2oat minimal. To hide multidex/synthetic names from the Java layer, let the handle of dalvik.system.DexFile refer to a vector of DexFile objects. When opening a location, test possible synthetic names and add them to the vector. Thus, the original multidex jar in the classpath will be associated with all embedded dex files. Change-Id: I0de107e1369cbc94416c544aca3b17525c9eac8b --- diff --git a/compiler/image_test.cc b/compiler/image_test.cc index e8bbaef1e..d52ec0ad5 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -129,11 +129,7 @@ TEST_F(ImageTest, WriteRead) { runtime_.reset(); java_lang_dex_file_ = NULL; - std::string error_msg; - std::unique_ptr dex(DexFile::Open(GetLibCoreDexFileName().c_str(), - GetLibCoreDexFileName().c_str(), - &error_msg)); - ASSERT_TRUE(dex.get() != nullptr) << error_msg; + std::unique_ptr dex(LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str())); // Remove the reservation of the memory for use to load the image. UnreserveImageSpace(); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 2d5514063..de2960c56 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -478,11 +478,8 @@ class Dex2Oat { continue; } std::string error_msg; - const DexFile* dex_file = DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg); - if (dex_file == nullptr) { + if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) { LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg; - } else { - dex_files.push_back(dex_file); } } } @@ -536,12 +533,9 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } - const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg); - if (dex_file == nullptr) { + if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; - } else { - dex_files.push_back(dex_file); } ATRACE_END(); } @@ -1319,13 +1313,11 @@ static int dex2oat(int argc, char** argv) { << error_msg; return EXIT_FAILURE; } - const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location, &error_msg); - if (dex_file == nullptr) { + if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) { LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location << "': " << error_msg; return EXIT_FAILURE; } - dex_files.push_back(dex_file); ATRACE_END(); } else { size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files); diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index c0bce840e..351de3d48 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -63,6 +63,10 @@ File* ScopedFlock::GetFile() { return file_.get(); } +bool ScopedFlock::HasFile() { + return file_.get() != nullptr; +} + ScopedFlock::ScopedFlock() { } ScopedFlock::~ScopedFlock() { diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h index 26b4eb0c2..f8ed805be 100644 --- a/runtime/base/scoped_flock.h +++ b/runtime/base/scoped_flock.h @@ -40,6 +40,10 @@ class ScopedFlock { // Returns the (locked) file associated with this instance. File* GetFile(); + + // Returns whether a file is held. + bool HasFile(); + ~ScopedFlock(); private: std::unique_ptr file_; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 61f94d4cb..60453c3cc 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* error_msgs, + std::vector* 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* error_msgs, + std::vector* 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 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 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* 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 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* 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 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 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* 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//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) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 7d7bf15ee..60dad7b93 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -273,23 +273,12 @@ class ClassLinker { std::string* error_msg) LOCKS_EXCLUDED(dex_lock_); - // Finds the oat file for a dex location, generating the oat file if - // it is missing or out of date. Returns the DexFile from within the - // created oat file. - const DexFile* FindOrCreateOatFileForDexLocation(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::vector* error_msgs) + // Find or create the oat file holding dex_location. Then load all corresponding dex files + // (if multidex) into the given vector. + bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location, + std::vector* error_msgs, + std::vector* dex_files) LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); - // Find a DexFile within an OatFile given a DexFile location. Note - // that this returns null if the location checksum of the DexFile - // does not match the OatFile. - const DexFile* FindDexFileInOatFileFromDexLocation(const char* location, - const uint32_t* const location_checksum, - InstructionSet isa, - std::vector* error_msgs) - LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); - // Returns true if oat file contains the dex file with the given location and checksum. static bool VerifyOatFileChecksums(const OatFile* oat_file, @@ -545,21 +534,47 @@ class ClassLinker { const OatFile* FindOpenedOatFileForDexFile(const DexFile& dex_file) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const OatFile* FindOpenedOatFileFromDexLocation(const char* dex_location, - const uint32_t* const dex_location_checksum) + + // Find an opened oat file that contains dex_location. If oat_location is not nullptr, the file + // must have that location, else any oat location is accepted. + const OatFile* FindOpenedOatFile(const char* oat_location, const char* dex_location, + const uint32_t* const dex_location_checksum) LOCKS_EXCLUDED(dex_lock_); const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) LOCKS_EXCLUDED(dex_lock_); - const DexFile* FindDexFileInOatLocation(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::string* error_msg) + + // Note: will not register the oat file. + const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location, + uint32_t dex_location_checksum, + const char* oat_location, + std::string* error_msg) LOCKS_EXCLUDED(dex_lock_); - const DexFile* VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location, - const char* dex_location, - std::string* error_msg, - bool* open_failed) + // Creates the oat file from the dex_location to the oat_location. Needs a file descriptor for + // the file to be written, which is assumed to be under a lock. + const OatFile* CreateOatFileForDexLocation(const char* dex_location, + int fd, const char* oat_location, + std::vector* error_msgs) + LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); + + // Finds an OatFile that contains a DexFile for the given a DexFile location. + // + // Note 1: this will not check open oat files, which are assumed to be stale when this is run. + // Note 2: Does not register the oat file. It is the caller's job to register if the file is to + // be kept. + const OatFile* FindOatFileContainingDexFileFromDexLocation(const char* location, + const uint32_t* const location_checksum, + InstructionSet isa, + std::vector* error_msgs) + LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); + + // Find a verify an oat file with the given dex file. Will return nullptr when the oat file + // was not found or the dex file could not be verified. + // Note: Does not register the oat file. + const OatFile* LoadOatFileAndVerifyDexFile(const std::string& oat_file_location, + const char* dex_location, + std::string* error_msg, + bool* open_failed) LOCKS_EXCLUDED(dex_lock_); mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle klass, diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 044d08b54..1df4c05e3 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -156,6 +156,18 @@ class CommonRuntimeTest : public testing::Test { return !kIsTargetBuild; } + const DexFile* LoadExpectSingleDexFile(const char* location) { + std::vector dex_files; + std::string error_msg; + if (!DexFile::Open(location, location, &error_msg, &dex_files)) { + LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; + return nullptr; + } else { + CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; + return dex_files[0]; + } + } + virtual void SetUp() { SetEnvironmentVariables(android_data_); dalvik_cache_.append(android_data_.c_str()); @@ -164,12 +176,7 @@ class CommonRuntimeTest : public testing::Test { ASSERT_EQ(mkdir_result, 0); std::string error_msg; - java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName().c_str(), - GetLibCoreDexFileName().c_str(), &error_msg); - if (java_lang_dex_file_ == nullptr) { - LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "': " - << error_msg << "\n"; - } + java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str()); boot_class_path_.push_back(java_lang_dex_file_); std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); @@ -233,7 +240,7 @@ class CommonRuntimeTest : public testing::Test { // There's a function to clear the array, but it's not public... typedef void (*IcuCleanupFn)(); void* sym = dlsym(RTLD_DEFAULT, "u_cleanup_" U_ICU_VERSION_SHORT); - CHECK(sym != nullptr); + CHECK(sym != nullptr) << dlerror(); IcuCleanupFn icu_cleanup_fn = reinterpret_cast(sym); (*icu_cleanup_fn)(); @@ -264,7 +271,8 @@ class CommonRuntimeTest : public testing::Test { return GetAndroidRoot(); } - const DexFile* OpenTestDexFile(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::vector OpenTestDexFiles(const char* name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(name != nullptr); std::string filename; if (IsHost()) { @@ -277,26 +285,36 @@ class CommonRuntimeTest : public testing::Test { filename += name; filename += ".jar"; std::string error_msg; - const DexFile* dex_file = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg); - CHECK(dex_file != nullptr) << "Failed to open '" << filename << "': " << error_msg; - CHECK_EQ(PROT_READ, dex_file->GetPermissions()); - CHECK(dex_file->IsReadOnly()); - opened_dex_files_.push_back(dex_file); - return dex_file; + std::vector dex_files; + bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files); + CHECK(success) << "Failed to open '" << filename << "': " << error_msg; + for (const DexFile* dex_file : dex_files) { + CHECK_EQ(PROT_READ, dex_file->GetPermissions()); + CHECK(dex_file->IsReadOnly()); + } + opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end()); + return dex_files; + } + + const DexFile* OpenTestDexFile(const char* name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::vector vector = OpenTestDexFiles(name); + EXPECT_EQ(1U, vector.size()); + return vector[0]; } jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile* dex_file = OpenTestDexFile(dex_name); - CHECK(dex_file != nullptr); - class_linker_->RegisterDexFile(*dex_file); - std::vector class_path; - class_path.push_back(dex_file); + std::vector dex_files = OpenTestDexFiles(dex_name); + CHECK_NE(0U, dex_files.size()); + for (const DexFile* dex_file : dex_files) { + class_linker_->RegisterDexFile(*dex_file); + } ScopedObjectAccessUnchecked soa(Thread::Current()); ScopedLocalRef class_loader_local(soa.Env(), soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get()); soa.Self()->SetClassLoaderOverride(soa.Decode(class_loader_local.get())); - Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path); + Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files); return class_loader; } diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 10f34d973..d368e416f 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -87,7 +87,21 @@ static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { CHECK(checksum != NULL); uint32_t magic; - ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); + + // Strip ":...", which is the location + const char* zip_entry_name = kClassesDex; + const char* file_part = filename; + std::unique_ptr file_part_ptr; + + + if (IsMultiDexLocation(filename)) { + std::pair pair = SplitMultiDexLocation(filename); + file_part_ptr.reset(pair.first); + file_part = pair.first; + zip_entry_name = pair.second; + } + + ScopedFd fd(OpenAndReadMagic(file_part, &magic, error_msg)); if (fd.get() == -1) { DCHECK(!error_msg->empty()); return false; @@ -95,13 +109,13 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* if (IsZipMagic(magic)) { std::unique_ptr zip_archive(ZipArchive::OpenFromFd(fd.release(), filename, error_msg)); if (zip_archive.get() == NULL) { - *error_msg = StringPrintf("Failed to open zip archive '%s'", filename); + *error_msg = StringPrintf("Failed to open zip archive '%s'", file_part); return false; } - std::unique_ptr zip_entry(zip_archive->Find(kClassesDex, error_msg)); + std::unique_ptr zip_entry(zip_archive->Find(zip_entry_name, error_msg)); if (zip_entry.get() == NULL) { - *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, - kClassesDex, error_msg->c_str()); + *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part, + zip_entry_name, error_msg->c_str()); return false; } *checksum = zip_entry->GetCrc32(); @@ -119,20 +133,26 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* return false; } -const DexFile* DexFile::Open(const char* filename, - const char* location, - std::string* error_msg) { +bool DexFile::Open(const char* filename, const char* location, std::string* error_msg, + std::vector* dex_files) { uint32_t magic; ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); if (fd.get() == -1) { DCHECK(!error_msg->empty()); - return NULL; + return false; } if (IsZipMagic(magic)) { - return DexFile::OpenZip(fd.release(), location, error_msg); + return DexFile::OpenZip(fd.release(), location, error_msg, dex_files); } if (IsDexMagic(magic)) { - return DexFile::OpenFile(fd.release(), location, true, error_msg); + std::unique_ptr dex_file(DexFile::OpenFile(fd.release(), location, true, + error_msg)); + if (dex_file.get() != nullptr) { + dex_files->push_back(dex_file.release()); + return true; + } else { + return false; + } } *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); return nullptr; @@ -217,13 +237,14 @@ const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify, const char* DexFile::kClassesDex = "classes.dex"; -const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg) { +bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg, + std::vector* dex_files) { std::unique_ptr zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg)); if (zip_archive.get() == nullptr) { DCHECK(!error_msg->empty()); - return nullptr; + return false; } - return DexFile::Open(*zip_archive, location, error_msg); + return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files); } const DexFile* DexFile::OpenMemory(const std::string& location, @@ -238,17 +259,20 @@ const DexFile* DexFile::OpenMemory(const std::string& location, error_msg); } -const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location, - std::string* error_msg) { +const DexFile* DexFile::Open(const ZipArchive& zip_archive, const char* entry_name, + const std::string& location, std::string* error_msg, + ZipOpenErrorCode* error_code) { CHECK(!location.empty()); - std::unique_ptr zip_entry(zip_archive.Find(kClassesDex, error_msg)); + std::unique_ptr zip_entry(zip_archive.Find(entry_name, error_msg)); if (zip_entry.get() == NULL) { + *error_code = ZipOpenErrorCode::kEntryNotFound; return nullptr; } - std::unique_ptr map(zip_entry->ExtractToMemMap(location.c_str(), kClassesDex, error_msg)); + std::unique_ptr map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg)); if (map.get() == NULL) { - *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", kClassesDex, location.c_str(), + *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), error_msg->c_str()); + *error_code = ZipOpenErrorCode::kExtractToMemoryError; return nullptr; } std::unique_ptr dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(), @@ -256,20 +280,63 @@ const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& l if (dex_file.get() == nullptr) { *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(), error_msg->c_str()); + *error_code = ZipOpenErrorCode::kDexFileError; return nullptr; } if (!dex_file->DisableWrite()) { *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str()); + *error_code = ZipOpenErrorCode::kMakeReadOnlyError; return nullptr; } CHECK(dex_file->IsReadOnly()) << location; if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), location.c_str(), error_msg)) { + *error_code = ZipOpenErrorCode::kVerifyError; return nullptr; } + *error_code = ZipOpenErrorCode::kNoError; return dex_file.release(); } +bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location, + std::string* error_msg, std::vector* dex_files) { + ZipOpenErrorCode error_code; + std::unique_ptr dex_file(Open(zip_archive, kClassesDex, location, error_msg, + &error_code)); + if (dex_file.get() == nullptr) { + return false; + } else { + // Had at least classes.dex. + dex_files->push_back(dex_file.release()); + + // Now try some more. + size_t i = 2; + + // We could try to avoid std::string allocations by working on a char array directly. As we + // do not expect a lot of iterations, this seems too involved and brittle. + + while (i < 100) { + std::string name = StringPrintf("classes%zu.dex", i); + std::string fake_location = location + ":" + name; + std::unique_ptr next_dex_file(Open(zip_archive, name.c_str(), fake_location, + error_msg, &error_code)); + if (next_dex_file.get() == nullptr) { + if (error_code != ZipOpenErrorCode::kEntryNotFound) { + LOG(WARNING) << error_msg; + } + break; + } else { + dex_files->push_back(next_dex_file.release()); + } + + i++; + } + + return true; + } +} + + const DexFile* DexFile::OpenMemory(const byte* base, size_t size, const std::string& location, @@ -865,6 +932,25 @@ bool DexFile::LineNumForPcCb(void* raw_context, uint32_t address, uint32_t line_ } } +bool DexFile::IsMultiDexLocation(const char* location) { + return strrchr(location, kMultiDexSeparator) != nullptr; +} + +std::pair DexFile::SplitMultiDexLocation( + const char* location) { + const char* colon_ptr = strrchr(location, kMultiDexSeparator); + + // Check it's synthetic. + CHECK_NE(colon_ptr, static_cast(nullptr)); + + size_t colon_index = colon_ptr - location; + char* tmp = new char[colon_index + 1]; + strncpy(tmp, location, colon_index); + tmp[colon_index] = 0; + + return std::make_pair(tmp, colon_ptr + 1); +} + std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]", dex_file.GetLocation().c_str(), diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 8270a2b00..04f1cc141 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -63,6 +63,13 @@ class DexFile { // The value of an invalid index. static const uint16_t kDexNoIndex16 = 0xFFFF; + // The separator charactor in MultiDex locations. + static constexpr char kMultiDexSeparator = ':'; + + // A string version of the previous. This is a define so that we can merge string literals in the + // preprocessor. + #define kMultiDexSeparatorString ":" + // Raw header_item. struct Header { uint8_t magic_[8]; @@ -352,8 +359,9 @@ class DexFile { // Return true if the checksum could be found, false otherwise. static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); - // Opens .dex file, guessing the container format based on file extension - static const DexFile* Open(const char* filename, const char* location, std::string* error_msg); + // Opens .dex files found in the container, guessing the container format based on file extension. + static bool Open(const char* filename, const char* location, std::string* error_msg, + std::vector* dex_files); // Opens .dex file, backed by existing memory static const DexFile* Open(const uint8_t* base, size_t size, @@ -363,9 +371,9 @@ class DexFile { return OpenMemory(base, size, location, location_checksum, NULL, error_msg); } - // Opens .dex file from the classes.dex in a zip archive - static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location, - std::string* error_msg); + // Open all classesXXX.dex files from a zip archive. + static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location, + std::string* error_msg, std::vector* dex_files); // Closes a .dex file. virtual ~DexFile(); @@ -823,8 +831,24 @@ class DexFile { // Opens a .dex file static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg); - // Opens a dex file from within a .jar, .zip, or .apk file - static const DexFile* OpenZip(int fd, const std::string& location, std::string* error_msg); + // Opens dex files from within a .jar, .zip, or .apk file + static bool OpenZip(int fd, const std::string& location, std::string* error_msg, + std::vector* dex_files); + + enum class ZipOpenErrorCode { // private + kNoError, + kEntryNotFound, + kExtractToMemoryError, + kDexFileError, + kMakeReadOnlyError, + kVerifyError + }; + + // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-nullptr + // return. + static const DexFile* Open(const ZipArchive& zip_archive, const char* entry_name, + const std::string& location, std::string* error_msg, + ZipOpenErrorCode* error_code); // Opens a .dex file at the given address backed by a MemMap static const DexFile* OpenMemory(const std::string& location, @@ -855,6 +879,18 @@ class DexFile { DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb, void* context, const byte* stream, LocalInfo* local_in_reg) const; + // Check whether a location denotes a multidex dex file. This is a very simple check: returns + // whether the string contains the separator character. + static bool IsMultiDexLocation(const char* location); + + // Splits a multidex location at the last separator character. The second component is a pointer + // to the character after the separator. The first is a copy of the substring up to the separator. + // + // Note: It's the caller's job to free the first component of the returned pair. + // Bug 15313523: gcc/libc++ don't allow a unique_ptr for the first component + static std::pair SplitMultiDexLocation(const char* location); + + // The base address of the memory mapping. const byte* const begin_; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index a814c343c..c1e00fcb0 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -146,8 +146,11 @@ static const DexFile* OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); std::string error_msg; - const DexFile* dex_file = DexFile::Open(location, location, &error_msg); - CHECK(dex_file != nullptr) << error_msg; + std::vector tmp; + bool success = DexFile::Open(location, location, &error_msg, &tmp); + CHECK(success) << error_msg; + EXPECT_EQ(1U, tmp.size()); + const DexFile* dex_file = tmp[0]; EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); EXPECT_TRUE(dex_file->IsReadOnly()); return dex_file; diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index d0ce00fc6..93faeaee7 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -115,7 +115,14 @@ static const DexFile* OpenDexFileBase64(const char* base64, const char* location // read dex file ScopedObjectAccess soa(Thread::Current()); - return DexFile::Open(location, location, error_msg); + std::vector tmp; + bool success = DexFile::Open(location, location, error_msg, &tmp); + CHECK(success) << error_msg; + EXPECT_EQ(1U, tmp.size()); + const DexFile* dex_file = tmp[0]; + EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); + EXPECT_TRUE(dex_file->IsReadOnly()); + return dex_file; } @@ -170,7 +177,15 @@ static const DexFile* FixChecksumAndOpen(byte* bytes, size_t length, const char* // read dex file ScopedObjectAccess soa(Thread::Current()); - return DexFile::Open(location, location, error_msg); + std::vector tmp; + if (!DexFile::Open(location, location, error_msg, &tmp)) { + return nullptr; + } + EXPECT_EQ(1U, tmp.size()); + const DexFile* dex_file = tmp[0]; + EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); + EXPECT_TRUE(dex_file->IsReadOnly()); + return dex_file; } static bool ModifyAndLoad(const char* location, size_t offset, uint8_t new_val, diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc index 5e2d89ebe..0d00cc313 100644 --- a/runtime/dex_method_iterator_test.cc +++ b/runtime/dex_method_iterator_test.cc @@ -21,26 +21,15 @@ namespace art { class DexMethodIteratorTest : public CommonRuntimeTest { - public: - const DexFile* OpenDexFile(const std::string& partial_filename) { - std::string dfn = GetDexFileName(partial_filename); - std::string error_msg; - const DexFile* dexfile = DexFile::Open(dfn.c_str(), dfn.c_str(), &error_msg); - if (dexfile == nullptr) { - LG << "Failed to open '" << dfn << "': " << error_msg; - } - return dexfile; - } }; TEST_F(DexMethodIteratorTest, Basic) { ScopedObjectAccess soa(Thread::Current()); std::vector dex_files; - dex_files.push_back(OpenDexFile("core-libart")); - dex_files.push_back(OpenDexFile("conscrypt")); - dex_files.push_back(OpenDexFile("okhttp")); - dex_files.push_back(OpenDexFile("core-junit")); - dex_files.push_back(OpenDexFile("bouncycastle")); + const char* jars[] = { "core-libart", "conscrypt", "okhttp", "core-junit", "bouncycastle" }; + for (size_t i = 0; i < 5; ++i) { + dex_files.push_back(LoadExpectSingleDexFile(GetDexFileName(jars[i]).c_str())); + } DexMethodIterator it(dex_files); while (it.HasNext()) { const DexFile& dex_file = it.GetDexFile(); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 9512a5a7f..440d3d0b5 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -26,6 +26,7 @@ #include #include "base/logging.h" +#include "base/stl_util.h" #include "class_linker.h" #include "common_throws.h" #include "dex_file-inl.h" @@ -106,34 +107,19 @@ static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNa return 0; } - uint32_t dex_location_checksum; - uint32_t* dex_location_checksum_pointer = &dex_location_checksum; + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + std::unique_ptr> dex_files(new std::vector()); std::vector error_msgs; - std::string error_msg; - if (!DexFile::GetChecksum(sourceName.c_str(), dex_location_checksum_pointer, &error_msg)) { - dex_location_checksum_pointer = NULL; - } - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - const DexFile* dex_file; - if (outputName.c_str() == nullptr) { - // FindOrCreateOatFileForDexLocation can tolerate a missing dex_location_checksum - dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(), - dex_location_checksum_pointer, - kRuntimeISA, - &error_msgs); + bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs, + dex_files.get()); + + if (success) { + return static_cast(reinterpret_cast(dex_files.release())); } else { - // FindOrCreateOatFileForDexLocation requires the dex_location_checksum - if (dex_location_checksum_pointer == NULL) { - ScopedObjectAccess soa(env); - DCHECK(!error_msg.empty()); - ThrowIOException("%s", error_msg.c_str()); - return 0; - } - dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum, - outputName.c_str(), &error_msgs); - } - if (dex_file == nullptr) { + // The vector should be empty after a failed loading attempt. + DCHECK_EQ(0U, dex_files->size()); + ScopedObjectAccess soa(env); CHECK(!error_msgs.empty()); // The most important message is at the end. So set up nesting by going forward, which will @@ -146,35 +132,41 @@ static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNa return 0; } - return static_cast(reinterpret_cast(dex_file)); } -static const DexFile* toDexFile(jlong dex_file_address, JNIEnv* env) { - const DexFile* dex_file = reinterpret_cast(static_cast(dex_file_address)); - if (UNLIKELY(dex_file == nullptr)) { +static std::vector* toDexFiles(jlong dex_file_address, JNIEnv* env) { + std::vector* dex_files = reinterpret_cast*>( + static_cast(dex_file_address)); + if (UNLIKELY(dex_files == nullptr)) { ScopedObjectAccess soa(env); ThrowNullPointerException(NULL, "dex_file == null"); } - return dex_file; + return dex_files; } static void DexFile_closeDexFile(JNIEnv* env, jclass, jlong cookie) { - const DexFile* dex_file; - dex_file = toDexFile(cookie, env); - if (dex_file == nullptr) { + std::unique_ptr> dex_files(toDexFiles(cookie, env)); + if (dex_files.get() == nullptr) { return; } ScopedObjectAccess soa(env); - if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { - return; + + size_t index = 0; + for (const DexFile* dex_file : *dex_files) { + if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { + (*dex_files)[index] = nullptr; + } + index++; } - delete dex_file; + + STLDeleteElements(dex_files.get()); + // Unique_ptr will delete the vector itself. } static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jlong cookie) { - const DexFile* dex_file = toDexFile(cookie, env); - if (dex_file == NULL) { + std::vector* dex_files = toDexFiles(cookie, env); + if (dex_files == NULL) { VLOG(class_linker) << "Failed to find dex_file"; return NULL; } @@ -184,33 +176,60 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j return NULL; } const std::string descriptor(DotToDescriptor(class_name.c_str())); - const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str()); - if (dex_class_def == NULL) { - VLOG(class_linker) << "Failed to find dex_class_def"; - return NULL; + + for (const DexFile* dex_file : *dex_files) { + const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str()); + if (dex_class_def != nullptr) { + ScopedObjectAccess soa(env); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + class_linker->RegisterDexFile(*dex_file); + StackHandleScope<1> hs(soa.Self()); + Handle class_loader( + hs.NewHandle(soa.Decode(javaLoader))); + mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, + *dex_class_def); + if (result != nullptr) { + VLOG(class_linker) << "DexFile_defineClassNative returning " << result; + return soa.AddLocalReference(result); + } + } } - ScopedObjectAccess soa(env); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - class_linker->RegisterDexFile(*dex_file); - StackHandleScope<1> hs(soa.Self()); - Handle class_loader( - hs.NewHandle(soa.Decode(javaLoader))); - mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, - *dex_class_def); - VLOG(class_linker) << "DexFile_defineClassNative returning " << result; - return soa.AddLocalReference(result); + VLOG(class_linker) << "Failed to find dex_class_def"; + return nullptr; } +// Needed as a compare functor for sets of const char +struct CharPointerComparator { + bool operator()(const char *str1, const char *str2) const { + return strcmp(str1, str2) < 0; + } +}; + +// Note: this can be an expensive call, as we sort out duplicates in MultiDex files. static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie) { jobjectArray result = nullptr; - const DexFile* dex_file = toDexFile(cookie, env); - if (dex_file != nullptr) { - result = env->NewObjectArray(dex_file->NumClassDefs(), WellKnownClasses::java_lang_String, - nullptr); - if (result != nullptr) { + std::vector* dex_files = toDexFiles(cookie, env); + + if (dex_files != nullptr) { + // Push all class descriptors into a set. Use set instead of unordered_set as we want to + // retrieve all in the end. + std::set descriptors; + for (const DexFile* dex_file : *dex_files) { for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - std::string descriptor(DescriptorToDot(dex_file->GetClassDescriptor(class_def))); + const char* descriptor = dex_file->GetClassDescriptor(class_def); + descriptors.insert(descriptor); + } + } + + // Now create output array and copy the set into it. + result = env->NewObjectArray(descriptors.size(), WellKnownClasses::java_lang_String, nullptr); + if (result != nullptr) { + auto it = descriptors.begin(); + auto it_end = descriptors.end(); + jsize i = 0; + for (; it != it_end; it++, ++i) { + std::string descriptor(DescriptorToDot(*it)); ScopedLocalRef jdescriptor(env, env->NewStringUTF(descriptor.c_str())); if (jdescriptor.get() == nullptr) { return nullptr; diff --git a/runtime/utils.cc b/runtime/utils.cc index e5b8b224d..c52549e74 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1232,6 +1232,7 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is std::string DexFilenameToOdexFilename(const std::string& location, const InstructionSet isa) { // location = /foo/bar/baz.jar // odex_location = /foo/bar//baz.odex + CHECK_GE(location.size(), 4U) << location; // must be at least .123 std::string odex_location(location); InsertIsaDirectory(isa, &odex_location); diff --git a/runtime/utils.h b/runtime/utils.h index a61d30fb4..68ea47541 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -401,6 +401,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Returns an .odex file name next adjacent to the dex location. // For example, for "/foo/bar/baz.jar", return "/foo/bar//baz.odex". +// Note: does not support multidex location strings. std::string DexFilenameToOdexFilename(const std::string& location, InstructionSet isa); // Check whether the given magic matches a known file type. diff --git a/test/113-multidex/build b/test/113-multidex/build new file mode 100644 index 000000000..ec8706ea0 --- /dev/null +++ b/test/113-multidex/build @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright (C) 2014 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. + +# Stop if something fails. +set -e + +mkdir classes + +# All except Main +${JAVAC} -d classes `find src -name '*.java'` +rm classes/Main.class +${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes + +# Only Main +${JAVAC} -d classes `find src -name '*.java'` +rm classes/Second.class classes/FillerA.class classes/FillerB.class classes/Inf*.class +${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes + +zip $TEST_NAME.jar classes.dex classes2.dex diff --git a/test/113-multidex/expected.txt b/test/113-multidex/expected.txt new file mode 100644 index 000000000..603e91180 --- /dev/null +++ b/test/113-multidex/expected.txt @@ -0,0 +1,12 @@ +FillerA +Second +Second::zcall +Second::zcall1 +Second::zcall2 +Second::zcall3 +Second::zcall4 +Second::zcall5 +Second::zcall6 +Second::zcall7 +Second::zcall8 +Second::zcall9 diff --git a/test/113-multidex/info.txt b/test/113-multidex/info.txt new file mode 100644 index 000000000..d0a4ac1ba --- /dev/null +++ b/test/113-multidex/info.txt @@ -0,0 +1,2 @@ +Test whether we can run code from an application split into multiple dex files (similar to +MultiDex). diff --git a/test/113-multidex/src/FillerA.java b/test/113-multidex/src/FillerA.java new file mode 100644 index 000000000..d169018d7 --- /dev/null +++ b/test/113-multidex/src/FillerA.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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. + */ + +public class FillerA { + public void methodA() { + } + + public void methodB() { + } +} diff --git a/test/113-multidex/src/FillerB.java b/test/113-multidex/src/FillerB.java new file mode 100644 index 000000000..ec3ac9d7d --- /dev/null +++ b/test/113-multidex/src/FillerB.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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. + */ + +public class FillerB { + public void methodC() { + } + + public void methodD() { + } +} diff --git a/test/113-multidex/src/Inf1.java b/test/113-multidex/src/Inf1.java new file mode 100644 index 000000000..3deb6b443 --- /dev/null +++ b/test/113-multidex/src/Inf1.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf1 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf2.java b/test/113-multidex/src/Inf2.java new file mode 100644 index 000000000..ac0950948 --- /dev/null +++ b/test/113-multidex/src/Inf2.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf2 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf3.java b/test/113-multidex/src/Inf3.java new file mode 100644 index 000000000..d6c377b0a --- /dev/null +++ b/test/113-multidex/src/Inf3.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf3 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf4.java b/test/113-multidex/src/Inf4.java new file mode 100644 index 000000000..a1801b904 --- /dev/null +++ b/test/113-multidex/src/Inf4.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf4 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf5.java b/test/113-multidex/src/Inf5.java new file mode 100644 index 000000000..e8115ce63 --- /dev/null +++ b/test/113-multidex/src/Inf5.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf5 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf6.java b/test/113-multidex/src/Inf6.java new file mode 100644 index 000000000..554bdb856 --- /dev/null +++ b/test/113-multidex/src/Inf6.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf6 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf7.java b/test/113-multidex/src/Inf7.java new file mode 100644 index 000000000..19827757a --- /dev/null +++ b/test/113-multidex/src/Inf7.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf7 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Inf8.java b/test/113-multidex/src/Inf8.java new file mode 100644 index 000000000..87296db51 --- /dev/null +++ b/test/113-multidex/src/Inf8.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +public interface Inf8 { + public void zcall(); + public void zcall1(); + public void zcall2(); + public void zcall3(); + public void zcall4(); + public void zcall5(); + public void zcall6(); + public void zcall7(); + public void zcall8(); + public void zcall9(); +} \ No newline at end of file diff --git a/test/113-multidex/src/Main.java b/test/113-multidex/src/Main.java new file mode 100644 index 000000000..1c7422052 --- /dev/null +++ b/test/113-multidex/src/Main.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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. + */ + +public class Main { + static public void main(String[] args) throws Exception { + System.out.println(new FillerA().getClass().getName()); + + Inf1 second = new Second(); + System.out.println(second.getClass().getName()); + second.zcall(); + second.zcall1(); + second.zcall2(); + second.zcall3(); + second.zcall4(); + second.zcall5(); + second.zcall6(); + second.zcall7(); + second.zcall8(); + second.zcall9(); + } + +} diff --git a/test/113-multidex/src/Second.java b/test/113-multidex/src/Second.java new file mode 100644 index 000000000..d0c253593 --- /dev/null +++ b/test/113-multidex/src/Second.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 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. + */ + +public class Second implements Inf1, Inf2, Inf3, Inf4, Inf5, Inf6, Inf7, Inf8 { + public void zcall() { + System.out.println("Second::zcall"); + } + + public void zcall1() { + System.out.println("Second::zcall1"); + } + + public void zcall2() { + System.out.println("Second::zcall2"); + } + + public void zcall3() { + System.out.println("Second::zcall3"); + } + + public void zcall4() { + System.out.println("Second::zcall4"); + } + + public void zcall5() { + System.out.println("Second::zcall5"); + } + + public void zcall6() { + System.out.println("Second::zcall6"); + } + + public void zcall7() { + System.out.println("Second::zcall7"); + } + + public void zcall8() { + System.out.println("Second::zcall8"); + } + + public void zcall9() { + System.out.println("Second::zcall9"); + } +} diff --git a/tools/generate-operator-out.py b/tools/generate-operator-out.py index 56b867494..6baa6e304 100755 --- a/tools/generate-operator-out.py +++ b/tools/generate-operator-out.py @@ -23,11 +23,12 @@ import string import sys -_ENUM_START_RE = re.compile(r'\benum\b\s+(\S+)\s+:?.*\{') +_ENUM_START_RE = re.compile(r'\benum\b\s+(class\s+)?(\S+)\s+:?.*\{(\s+// private)?') _ENUM_VALUE_RE = re.compile(r'([A-Za-z0-9_]+)(.*)') _ENUM_END_RE = re.compile(r'^\s*\};$') _ENUMS = {} _NAMESPACES = {} +_ENUM_CLASSES = {} def Confused(filename, line_number, line): sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line)) @@ -38,7 +39,9 @@ def Confused(filename, line_number, line): def ProcessFile(filename): lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') in_enum = False + is_enum_class = False line_number = 0 + namespaces = [] enclosing_classes = [] @@ -51,11 +54,18 @@ def ProcessFile(filename): m = _ENUM_START_RE.search(raw_line) if m: # Yes, so add an empty entry to _ENUMS for this enum. - enum_name = m.group(1) + + # Except when it's private + if m.group(3) is not None: + continue + + is_enum_class = m.group(1) is not None + enum_name = m.group(2) if len(enclosing_classes) > 0: enum_name = '::'.join(enclosing_classes) + '::' + enum_name _ENUMS[enum_name] = [] _NAMESPACES[enum_name] = '::'.join(namespaces) + _ENUM_CLASSES[enum_name] = is_enum_class in_enum = True continue @@ -139,7 +149,10 @@ def ProcessFile(filename): Confused(filename, line_number, raw_line) if len(enclosing_classes) > 0: - enum_value = '::'.join(enclosing_classes) + '::' + enum_value + if is_enum_class: + enum_value = enum_name + '::' + enum_value + else: + enum_value = '::'.join(enclosing_classes) + '::' + enum_value _ENUMS[enum_name].append((enum_value, enum_text)) @@ -170,7 +183,8 @@ def main(): print ' switch (rhs) {' for (enum_value, enum_text) in _ENUMS[enum_name]: print ' case %s: os << "%s"; break;' % (enum_value, enum_text) - print ' default: os << "%s[" << static_cast(rhs) << "]"; break;' % enum_name + if not _ENUM_CLASSES[enum_name]: + print ' default: os << "%s[" << static_cast(rhs) << "]"; break;' % enum_name print ' }' print ' return os;' print '}'