diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/base/scoped_flock.cc | 4 | ||||
-rw-r--r-- | runtime/base/scoped_flock.h | 4 | ||||
-rw-r--r-- | runtime/class_linker.cc | 364 | ||||
-rw-r--r-- | runtime/class_linker.h | 67 | ||||
-rw-r--r-- | runtime/common_runtime_test.h | 58 | ||||
-rw-r--r-- | runtime/dex_file.cc | 124 | ||||
-rw-r--r-- | runtime/dex_file.h | 50 | ||||
-rw-r--r-- | runtime/dex_file_test.cc | 7 | ||||
-rw-r--r-- | runtime/dex_file_verifier_test.cc | 19 | ||||
-rw-r--r-- | runtime/dex_method_iterator_test.cc | 19 | ||||
-rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 135 | ||||
-rw-r--r-- | runtime/utils.cc | 1 | ||||
-rw-r--r-- | runtime/utils.h | 1 |
13 files changed, 610 insertions, 243 deletions
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index c0bce84..351de3d 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 26b4eb0..f8ed805 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> file_; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 61f94d4..60453c3 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) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 7d7bf15..60dad7b 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<std::string>* 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<std::string>* error_msgs, + std::vector<const DexFile*>* 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<std::string>* 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<std::string>* 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<std::string>* 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<mirror::Class> klass, diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 044d08b..1df4c05 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<const DexFile*> 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<IcuCleanupFn>(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<const DexFile*> 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<const DexFile*> 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<const DexFile*> 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<const DexFile*> class_path; - class_path.push_back(dex_file); + std::vector<const DexFile*> 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<jobject> 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<mirror::ClassLoader*>(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 10f34d9..d368e41 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<const char> file_part_ptr; + + + if (IsMultiDexLocation(filename)) { + std::pair<const char*, const char*> 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<ZipArchive> 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<ZipEntry> zip_entry(zip_archive->Find(kClassesDex, error_msg)); + std::unique_ptr<ZipEntry> 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<const DexFile*>* 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<const DexFile> 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<const DexFile*>* dex_files) { std::unique_ptr<ZipArchive> 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<ZipEntry> zip_entry(zip_archive.Find(kClassesDex, error_msg)); + std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg)); if (zip_entry.get() == NULL) { + *error_code = ZipOpenErrorCode::kEntryNotFound; return nullptr; } - std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), kClassesDex, error_msg)); + std::unique_ptr<MemMap> 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<const DexFile> 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<const DexFile*>* dex_files) { + ZipOpenErrorCode error_code; + std::unique_ptr<const DexFile> 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<const DexFile> 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<const char*, const char*> DexFile::SplitMultiDexLocation( + const char* location) { + const char* colon_ptr = strrchr(location, kMultiDexSeparator); + + // Check it's synthetic. + CHECK_NE(colon_ptr, static_cast<const char*>(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 8270a2b..04f1cc1 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<const DexFile*>* 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<const DexFile*>* 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<const DexFile*>* 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<const char*, const char*> 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 a814c34..c1e00fc 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<const DexFile*> 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 d0ce00f..93faeae 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<const DexFile*> 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<const DexFile*> 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 5e2d89e..0d00cc3 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<const DexFile*> 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 9512a5a..440d3d0 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -26,6 +26,7 @@ #include <unistd.h> #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<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>()); std::vector<std::string> 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<jlong>(reinterpret_cast<uintptr_t>(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<jlong>(reinterpret_cast<uintptr_t>(dex_file)); } -static const DexFile* toDexFile(jlong dex_file_address, JNIEnv* env) { - const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address)); - if (UNLIKELY(dex_file == nullptr)) { +static std::vector<const DexFile*>* toDexFiles(jlong dex_file_address, JNIEnv* env) { + std::vector<const DexFile*>* dex_files = reinterpret_cast<std::vector<const DexFile*>*>( + static_cast<uintptr_t>(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<std::vector<const DexFile*>> 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<const DexFile*>* 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<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader*>(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<jclass>(result); + } + } } - ScopedObjectAccess soa(env); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - class_linker->RegisterDexFile(*dex_file); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(soa.Decode<mirror::ClassLoader*>(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<jclass>(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<const DexFile*>* 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<const char*, CharPointerComparator> 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<jstring> jdescriptor(env, env->NewStringUTF(descriptor.c_str())); if (jdescriptor.get() == nullptr) { return nullptr; diff --git a/runtime/utils.cc b/runtime/utils.cc index e5b8b22..c52549e 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/<isa>/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 a61d30f..68ea475 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/<isa>/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. |