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/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 @@
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 @@
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 @@
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 @@
!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 @@
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 @@
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 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 @@
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) {