Support loading vdex files without odex.
GetDexoptNeeded with 'verify' as filter will return false when a vdex is
available.
Test: 628-vdex, 820-vdex-multidex, test.py
Bug: 176960283
Change-Id: I3ac2f747d1e7f5331a49a22c94983959e3b60122
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 499d90b..887c992 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -165,9 +165,9 @@
virtual void PreSetup(const std::string& elf_filename) = 0;
- // Setup functions return true on success, false on failure.
bool Setup(int zip_fd, ArrayRef<const std::string> dex_filenames, std::string* error_msg);
- bool Setup(const std::vector<const DexFile*>& dex_files);
+
+ void Setup(const std::vector<const DexFile*>& dex_files);
// Setters exposed for ElfOatFile.
@@ -465,13 +465,15 @@
return true;
}
-bool OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
+void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
for (const DexFile* dex_file : dex_files) {
std::string dex_location = dex_file->GetLocation();
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location.c_str());
// Create an OatDexFile and add it to the owning container.
- OatDexFile* oat_dex_file = new OatDexFile(this, dex_file, dex_location, canonical_location);
+ OatDexFile* oat_dex_file =
+ new OatDexFile(this, dex_file->Begin(), dex_file->GetLocationChecksum(), dex_location, canonical_location);
+ dex_file->SetOatDexFile(oat_dex_file);
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table.
@@ -482,8 +484,6 @@
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
-
- return true;
}
bool OatFileBase::Setup(int zip_fd,
@@ -1524,17 +1524,76 @@
std::unique_ptr<VdexFile>&& vdex_file,
const std::string& location) {
std::unique_ptr<OatFileBackedByVdex> oat_file(new OatFileBackedByVdex(location));
- if (!oat_file->Setup(dex_files, std::move(vdex_file))) {
- return nullptr;
- }
+ // SetVdex will take ownership of the VdexFile.
+ oat_file->SetVdex(vdex_file.release());
+ oat_file->SetupHeader(dex_files.size());
+ // Initialize OatDexFiles.
+ oat_file->Setup(dex_files);
return oat_file.release();
}
- bool Setup(const std::vector<const DexFile*>& dex_files, std::unique_ptr<VdexFile>&& vdex_file) {
- DCHECK(!IsExecutable());
+ static OatFileBackedByVdex* Open(int zip_fd,
+ std::unique_ptr<VdexFile>&& vdex_file,
+ const std::string& dex_location,
+ std::string* error_msg) {
+ std::unique_ptr<OatFileBackedByVdex> oat_file(new OatFileBackedByVdex(vdex_file->GetName()));
+ if (vdex_file->HasDexSection()) {
+ uint32_t i = 0;
+ for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr);
+ dex_file_start != nullptr;
+ dex_file_start = vdex_file->GetNextDexFileData(dex_file_start), ++i) {
+ // Create the OatDexFile and add it to the owning container.
+ std::string location = DexFileLoader::GetMultiDexLocation(i, dex_location.c_str());
+ std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(location.c_str());
+ OatDexFile* oat_dex_file = new OatDexFile(oat_file.get(),
+ dex_file_start,
+ vdex_file->GetLocationChecksum(i),
+ location,
+ canonical_location);
+ oat_file->oat_dex_files_storage_.push_back(oat_dex_file);
+
+ std::string_view key(oat_dex_file->GetDexFileLocation());
+ oat_file->oat_dex_files_.Put(key, oat_dex_file);
+ if (canonical_location != location) {
+ std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
+ oat_file->oat_dex_files_.Put(canonical_key, oat_dex_file);
+ }
+ }
+ oat_file->SetupHeader(oat_file->oat_dex_files_storage_.size());
+ } else {
+ // No need for any verification when loading dex files as we already have
+ // a vdex file.
+ const ArtDexFileLoader dex_file_loader;
+ bool loaded = false;
+ if (zip_fd != -1) {
+ loaded = dex_file_loader.OpenZip(zip_fd,
+ dex_location,
+ /*verify=*/ false,
+ /*verify_checksum=*/ false,
+ error_msg,
+ &oat_file->external_dex_files_);
+ } else {
+ loaded = dex_file_loader.Open(dex_location.c_str(),
+ dex_location,
+ /*verify=*/ false,
+ /*verify_checksum=*/ false,
+ error_msg,
+ &oat_file->external_dex_files_);
+ }
+ if (!loaded) {
+ return nullptr;
+ }
+ oat_file->SetupHeader(oat_file->external_dex_files_.size());
+ oat_file->Setup(MakeNonOwningPointerVector(oat_file->external_dex_files_));
+ }
// SetVdex will take ownership of the VdexFile.
- SetVdex(vdex_file.release());
+ oat_file->SetVdex(vdex_file.release());
+ return oat_file.release();
+ }
+
+ void SetupHeader(size_t number_of_dex_files) {
+ DCHECK(!IsExecutable());
// Create a fake OatHeader with a key store containing only the compiler
// filter (it helps debugging and is required by
@@ -1543,39 +1602,15 @@
InstructionSetFeatures::FromCppDefines();
SafeMap<std::string, std::string> store;
store.Put(OatHeader::kCompilerFilter, CompilerFilter::NameOfFilter(CompilerFilter::kVerify));
+ store.Put(OatHeader::kConcurrentCopying,
+ kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
oat_header_.reset(OatHeader::Create(kRuntimeISA,
isa_features.get(),
- dex_files.size(),
+ number_of_dex_files,
&store));
const uint8_t* begin = reinterpret_cast<const uint8_t*>(oat_header_.get());
SetBegin(begin);
SetEnd(begin + oat_header_->GetHeaderSize());
-
- // Load VerifierDeps from VDEX and copy bit vectors of verified classes.
- ArrayRef<const uint8_t> deps_data = GetVdexFile()->GetVerifierDepsData();
- if (!verifier::VerifierDeps::ParseVerifiedClasses(dex_files,
- deps_data,
- &verified_classes_per_dex_)) {
- return false;
- }
-
- // Initialize OatDexFiles.
- if (!OatFileBase::Setup(dex_files)) {
- return false;
- }
-
- return true;
- }
-
- bool IsClassVerifiedInVdex(const OatDexFile& oat_dex_file, uint16_t class_def_index) const {
- // Determine the index of the DexFile, assuming the order of OatDexFiles
- // in `oat_dex_files_storage_` is the same.
- const std::vector<const OatDexFile*>& oat_dex_files = GetOatDexFiles();
- auto oat_dex_file_it = std::find(oat_dex_files.begin(), oat_dex_files.end(), &oat_dex_file);
- DCHECK(oat_dex_file_it != oat_dex_files.end());
- size_t dex_index = oat_dex_file_it - oat_dex_files.begin();
- // Check the bitvector of verified classes from the vdex.
- return verified_classes_per_dex_[dex_index][class_def_index];
}
protected:
@@ -1611,7 +1646,6 @@
private:
std::unique_ptr<OatHeader> oat_header_;
- std::vector<std::vector<bool>> verified_classes_per_dex_;
DISALLOW_COPY_AND_ASSIGN(OatFileBackedByVdex);
};
@@ -1638,13 +1672,12 @@
std::string vdex_filename = GetVdexFilename(oat_filename);
- // Check that the files even exist, fast-fail.
+ // Check that the vdex file even exists, fast-fail. We don't check the odex
+ // file as we use the absence of an odex file for test the functionality of
+ // vdex-only.
if (!OS::FileExists(vdex_filename.c_str())) {
*error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str());
return nullptr;
- } else if (!OS::FileExists(oat_filename.c_str())) {
- *error_msg = StringPrintf("File %s does not exist.", oat_filename.c_str());
- return nullptr;
}
// Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is
@@ -1725,6 +1758,14 @@
return OatFileBackedByVdex::Open(dex_files, std::move(vdex_file), location);
}
+OatFile* OatFile::OpenFromVdex(int zip_fd,
+ std::unique_ptr<VdexFile>&& vdex_file,
+ const std::string& location,
+ std::string* error_msg) {
+ CheckLocation(location);
+ return OatFileBackedByVdex::Open(zip_fd, std::move(vdex_file), location, error_msg);
+}
+
OatFile::OatFile(const std::string& location, bool is_executable)
: location_(location),
vdex_(nullptr),
@@ -1916,15 +1957,15 @@
}
OatDexFile::OatDexFile(const OatFile* oat_file,
- const DexFile* dex_file,
+ const uint8_t* dex_file_pointer,
+ uint32_t dex_file_location_checksum,
const std::string& dex_file_location,
const std::string& canonical_dex_file_location)
: oat_file_(oat_file),
dex_file_location_(dex_file_location),
canonical_dex_file_location_(canonical_dex_file_location),
- dex_file_location_checksum_(dex_file->GetLocationChecksum()),
- dex_file_pointer_(reinterpret_cast<const uint8_t*>(dex_file)) {
- dex_file->SetOatDexFile(this);
+ dex_file_location_checksum_(dex_file_location_checksum),
+ dex_file_pointer_(dex_file_pointer) {
DCHECK(IsBackedByVdexOnly());
}
@@ -1968,13 +2009,12 @@
}
OatFile::OatClass OatDexFile::GetOatClass(uint16_t class_def_index) const {
- // If this is an OatFileBackedByVdex, initialize the OatClass using the vdex's VerifierDeps.
if (IsBackedByVdexOnly()) {
- bool is_vdex_verified = down_cast<const OatFileBackedByVdex*>(oat_file_)->IsClassVerifiedInVdex(
- *this,
- class_def_index);
+ // If there is only a vdex file, return that the class is not ready. The
+ // caller will have to call `VdexFile::ComputeClassStatus` to compute the
+ // actual class status, because we need to do the assignability type checks.
return OatFile::OatClass(oat_file_,
- is_vdex_verified ? ClassStatus::kVerified : ClassStatus::kNotReady,
+ ClassStatus::kNotReady,
/* type= */ kOatClassNoneCompiled,
/* bitmap_size= */ 0u,
/* bitmap_pointer= */ nullptr,
@@ -2283,4 +2323,8 @@
CHECK(Runtime::Current()->IsAotCompiler());
}
+bool OatFile::IsBackedByVdexOnly() const {
+ return oat_dex_files_storage_.size() >= 1 && oat_dex_files_storage_[0]->IsBackedByVdexOnly();
+}
+
} // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index d4f4d95..a22e043 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -163,6 +163,16 @@
std::unique_ptr<VdexFile>&& vdex_file,
const std::string& location);
+ // Initialize OatFile instance from an already loaded VdexFile. The dex files
+ // will be opened through `zip_fd` or `dex_location` if `zip_fd` is -1.
+ static OatFile* OpenFromVdex(int zip_fd,
+ std::unique_ptr<VdexFile>&& vdex_file,
+ const std::string& location,
+ std::string* error_msg);
+
+ // Return whether the `OatFile` uses a vdex-only file.
+ bool IsBackedByVdexOnly() const;
+
virtual ~OatFile();
bool IsExecutable() const {
@@ -467,6 +477,7 @@
friend class OatClass;
friend class art::OatDexFile;
friend class OatDumper; // For GetBase and GetLimit
+ friend class OatFileBackedByVdex;
friend class OatFileBase;
DISALLOW_COPY_AND_ASSIGN(OatFile);
};
@@ -582,7 +593,8 @@
// Create an OatDexFile wrapping an existing DexFile. Will set the OatDexFile
// pointer in the DexFile.
OatDexFile(const OatFile* oat_file,
- const DexFile* dex_file,
+ const uint8_t* dex_file_pointer,
+ uint32_t dex_file_checksum,
const std::string& dex_file_location,
const std::string& canonical_dex_file_location);
@@ -607,6 +619,7 @@
friend class OatFile;
friend class OatFileBase;
+ friend class OatFileBackedByVdex;
DISALLOW_COPY_AND_ASSIGN(OatDexFile);
};
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8cfb8d5..4b0783f 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -99,6 +99,8 @@
only_load_system_executable_(only_load_system_executable),
odex_(this, /*is_oat_location=*/ false),
oat_(this, /*is_oat_location=*/ true),
+ vdex_for_odex_(this, /*is_oat_location=*/ false),
+ vdex_for_oat_(this, /*is_oat_location=*/ true),
zip_fd_(zip_fd) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
@@ -122,6 +124,8 @@
std::string odex_file_name;
if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
odex_.Reset(odex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
+ std::string vdex_file_name = GetVdexFilename(odex_file_name);
+ vdex_for_odex_.Reset(vdex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
} else {
LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
}
@@ -131,6 +135,8 @@
std::string oat_file_name;
if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
oat_.Reset(oat_file_name, /*use_fd=*/ false);
+ std::string vdex_file_name = GetVdexFilename(oat_file_name);
+ vdex_for_oat_.Reset(vdex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
} else {
LOG(WARNING) << "Failed to determine oat file name for dex location "
<< dex_location_ << ": " << error_msg;
@@ -406,7 +412,9 @@
CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter();
// Verify the image checksum
- if (CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) {
+ if (file.IsBackedByVdexOnly()) {
+ VLOG(oat) << "Image checksum test skipped for vdex file " << file.GetLocation();
+ } else if (CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) {
if (!ValidateBootClassPathChecksums(file)) {
VLOG(oat) << "Oat image checksum does not match image checksum.";
return kOatBootImageOutOfDate;
@@ -633,26 +641,41 @@
// apps gets installed or when they load private, secondary dex file.
// For apps on the system partition the odex location will not be
// writable and thus the oat location might be more up to date.
+
+ // If the odex is not useable, and we have a useable vdex, return the vdex
+ // instead.
+ if (!odex_.IsUseable() && vdex_for_odex_.IsUseable()) {
+ return vdex_for_odex_;
+ }
return odex_;
}
// We cannot write to the odex location. This must be a system app.
- // If the oat location is usable take it.
+ // If the oat location is useable take it.
if (oat_.IsUseable()) {
return oat_;
}
- // The oat file is not usable but the odex file might be up to date.
+ // The oat file is not useable but the odex file might be up to date.
// This is an indication that we are dealing with an up to date prebuilt
// (that doesn't need relocation).
- if (odex_.Status() == kOatUpToDate) {
+ if (odex_.IsUseable()) {
return odex_;
}
+ // Look for a useable vdex file.
+ if (vdex_for_oat_.IsUseable()) {
+ return vdex_for_oat_;
+ }
+ if (vdex_for_odex_.IsUseable()) {
+ return vdex_for_odex_;
+ }
+
// We got into the worst situation here:
- // - the oat location is not usable
+ // - the oat location is not useable
// - the prebuild odex location is not up to date
+ // - the vdex-only file is not useable
// - and we don't have the original dex file anymore (stripped).
// Pick the odex if it exists, or the oat if not.
return (odex_.Status() == kOatCannotOpen) ? oat_ : odex_;
@@ -705,47 +728,7 @@
status_attempted_ = true;
const OatFile* file = GetFile();
if (file == nullptr) {
- // Check to see if there is a vdex file we can make use of.
- std::string error_msg;
- std::string vdex_filename = GetVdexFilename(filename_);
- std::unique_ptr<VdexFile> vdex;
- if (use_fd_) {
- if (vdex_fd_ >= 0) {
- struct stat s;
- int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s));
- if (rc == -1) {
- error_msg = StringPrintf("Failed getting length of the vdex file %s.", strerror(errno));
- } else {
- vdex = VdexFile::Open(vdex_fd_,
- s.st_size,
- vdex_filename,
- /*writable=*/ false,
- /*low_4gb=*/ false,
- /*unquicken=*/ false,
- &error_msg);
- }
- }
- } else {
- vdex = VdexFile::Open(vdex_filename,
- /*writable=*/ false,
- /*low_4gb=*/ false,
- /*unquicken=*/ false,
- &error_msg);
- }
- if (vdex == nullptr) {
- status_ = kOatCannotOpen;
- VLOG(oat) << "unable to open vdex file " << vdex_filename << ": " << error_msg;
- } else {
- if (oat_file_assistant_->DexChecksumUpToDate(*vdex, &error_msg)) {
- // The vdex file does not contain enough information to determine
- // whether it is up to date with respect to the boot image, so we
- // assume it is out of date.
- VLOG(oat) << error_msg;
- status_ = kOatBootImageOutOfDate;
- } else {
- status_ = kOatDexOutOfDate;
- }
- }
+ status_ = kOatCannotOpen;
} else {
status_ = oat_file_assistant_->GivenOatFileStatus(*file);
VLOG(oat) << file->GetLocation() << " is " << status_
@@ -792,46 +775,87 @@
const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
CHECK(!file_released_) << "GetFile called after oat file released.";
- if (!load_attempted_) {
- load_attempted_ = true;
- if (filename_provided_) {
- bool executable = oat_file_assistant_->load_executable_;
- if (executable && oat_file_assistant_->only_load_system_executable_) {
- executable = LocationIsOnSystem(filename_.c_str());
- }
- VLOG(oat) << "Loading " << filename_ << " with executable: " << executable;
- std::string error_msg;
- if (use_fd_) {
- if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
- ArrayRef<const std::string> dex_locations(&oat_file_assistant_->dex_location_,
- /*size=*/ 1u);
- file_.reset(OatFile::Open(zip_fd_,
- vdex_fd_,
- oat_fd_,
- filename_.c_str(),
- executable,
- /*low_4gb=*/ false,
- dex_locations,
- /*reservation=*/ nullptr,
- &error_msg));
+ if (load_attempted_) {
+ return file_.get();
+ }
+ load_attempted_ = true;
+ if (!filename_provided_) {
+ return nullptr;
+ }
+
+ std::string error_msg;
+ bool executable = oat_file_assistant_->load_executable_;
+ if (android::base::EndsWith(filename_, kVdexExtension)) {
+ executable = false;
+ // Check to see if there is a vdex file we can make use of.
+ std::unique_ptr<VdexFile> vdex;
+ if (use_fd_) {
+ if (vdex_fd_ >= 0) {
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s));
+ if (rc == -1) {
+ error_msg = StringPrintf("Failed getting length of the vdex file %s.", strerror(errno));
+ } else {
+ vdex = VdexFile::Open(vdex_fd_,
+ s.st_size,
+ filename_,
+ /*writable=*/ false,
+ /*low_4gb=*/ false,
+ /*unquicken=*/ false,
+ &error_msg);
}
- } else {
- file_.reset(OatFile::Open(/*zip_fd=*/ -1,
- filename_.c_str(),
+ }
+ } else {
+ vdex = VdexFile::Open(filename_,
+ /*writable=*/ false,
+ /*low_4gb=*/ false,
+ /*unquicken=*/ false,
+ &error_msg);
+ }
+ if (vdex == nullptr) {
+ VLOG(oat) << "unable to open vdex file " << filename_ << ": " << error_msg;
+ } else {
+ file_.reset(OatFile::OpenFromVdex(zip_fd_,
+ std::move(vdex),
+ oat_file_assistant_->dex_location_,
+ &error_msg));
+ }
+ } else {
+ if (executable && oat_file_assistant_->only_load_system_executable_) {
+ executable = LocationIsOnSystem(filename_.c_str());
+ }
+ VLOG(oat) << "Loading " << filename_ << " with executable: " << executable;
+ if (use_fd_) {
+ if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
+ ArrayRef<const std::string> dex_locations(&oat_file_assistant_->dex_location_,
+ /*size=*/ 1u);
+ file_.reset(OatFile::Open(zip_fd_,
+ vdex_fd_,
+ oat_fd_,
filename_.c_str(),
executable,
/*low_4gb=*/ false,
- oat_file_assistant_->dex_location_,
+ dex_locations,
+ /*reservation=*/ nullptr,
&error_msg));
}
- if (file_.get() == nullptr) {
- VLOG(oat) << "OatFileAssistant test for existing oat file "
- << filename_ << ": " << error_msg;
- } else {
- VLOG(oat) << "Successfully loaded " << filename_ << " with executable: " << executable;
- }
+ } else {
+ file_.reset(OatFile::Open(/*zip_fd=*/ -1,
+ filename_.c_str(),
+ filename_.c_str(),
+ executable,
+ /*low_4gb=*/ false,
+ oat_file_assistant_->dex_location_,
+ &error_msg));
}
}
+ if (file_.get() == nullptr) {
+ VLOG(oat) << "OatFileAssistant test for existing oat file "
+ << filename_
+ << ": " << error_msg;
+ } else {
+ VLOG(oat) << "Successfully loaded " << filename_ << " with executable: " << executable;
+ }
return file_.get();
}
@@ -859,6 +883,11 @@
return true;
}
+ if (file->IsBackedByVdexOnly()) {
+ // Only a vdex file, we don't depend on the class loader context.
+ return true;
+ }
+
if (!CompilerFilter::IsVerificationEnabled(file->GetCompilerFilter())) {
// If verification is not enabled we don't need to verify the class loader context and we
// assume it's ok.
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 8784875..e771dcc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -430,9 +430,19 @@
bool required_dex_checksums_found_;
bool has_original_dex_files_;
+ // The AOT-compiled file of an app when the APK of the app is in /data.
OatFileInfo odex_;
+ // The AOT-compiled file of an app when the APK of the app is on a read-only partition
+ // (for example /system).
OatFileInfo oat_;
+ // The vdex-only file next to `odex_` when `odex_' cannot be used (for example
+ // it is out of date).
+ OatFileInfo vdex_for_odex_;
+ // The vdex-only file next to 'oat_` when `oat_' cannot be used (for example
+ // it is out of date).
+ OatFileInfo vdex_for_oat_;
+
// File descriptor corresponding to apk, dex file, or zip.
int zip_fd_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 902e5af..16d3ce0 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -501,13 +501,15 @@
vdex_fd.get(),
/* oat_fd= */ -1,
zip_fd.get());
- EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasDexFiles());
}
@@ -564,7 +566,7 @@
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
-// Case: We have a DEX file and up-to-date (ODEX) VDEX file for it, but no
+// Case: We have a DEX file and up-to-date VDEX file for it, but no
// ODEX file.
TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) {
std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar";
@@ -579,12 +581,9 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- // Even though the vdex file is up to date, because we don't have the oat
- // file, we can't know that the vdex depends on the boot image and is up to
- // date with respect to the boot image. Instead we must assume the vdex file
- // depends on the boot image and is out of date with respect to the boot
- // image.
- EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
// Make sure we don't crash in this case when we dump the status. We don't
@@ -594,9 +593,9 @@
VerifyOptimizationStatus(
&oat_file_assistant,
dex_location,
- "run-from-apk",
+ "verify",
"unknown",
- "io-error-no-oat");
+ "up-to-date");
}
// Case: We have a DEX file and empty VDEX and ODEX files.
@@ -637,12 +636,7 @@
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- // Even though the vdex file is up to date, because we don't have the oat
- // file, we can't know that the vdex depends on the boot image and is up to
- // date with respect to the boot image. Instead we must assume the vdex file
- // depends on the boot image and is out of date with respect to the boot
- // image.
- EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
}
@@ -831,11 +825,11 @@
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -846,9 +840,9 @@
VerifyOptimizationStatus(
&oat_file_assistant,
dex_location,
- "run-from-apk-fallback",
+ "verify",
"unknown",
- "boot-image-more-recent");
+ "up-to-date");
}
// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
@@ -1454,9 +1448,12 @@
odex_dir = odex_dir + std::string(GetInstructionSetString(kRuntimeISA));
mkdir(odex_dir.c_str(), 0700);
std::string oat_location = odex_dir + "/" + filebase + ".odex";
+ std::string vdex_location = odex_dir + "/" + filebase + ".vdex";
std::string art_location = odex_dir + "/" + filebase + ".art";
// Clean up in case previous run crashed.
remove(oat_location.c_str());
+ remove(vdex_location.c_str());
+ remove(art_location.c_str());
// Start the runtime to initialize the system's class loader.
Thread::Current()->TransitionFromSuspendedToRunnable();
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index f03e77f..8a0f550 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -185,6 +185,11 @@
DCHECK(error_msg != nullptr);
DCHECK(context != nullptr);
+ if (oat_file->IsBackedByVdexOnly()) {
+ // Only a vdex file, we don't depend on the class loader context.
+ return true;
+ }
+
if (!CompilerFilter::IsVerificationEnabled(oat_file->GetCompilerFilter())) {
// If verification is not enabled we don't need to check if class loader context matches
// as the oat file is either extracted or assumed verified.
@@ -272,7 +277,7 @@
// Proceed with oat file loading.
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
- << reinterpret_cast<uintptr_t>(oat_file.get())
+ << (oat_file != nullptr ? oat_file->GetLocation() : "")
<< " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
CHECK(oat_file == nullptr || odex_location == oat_file->GetLocation())
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 97655f6..6e11728 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -384,6 +384,12 @@
ClassStatus ComputeClassStatus(Thread* self, Handle<mirror::Class> cls) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Return the name of the underlying `MemMap` of the vdex file, typically the
+ // location on disk of the vdex file.
+ const std::string& GetName() const {
+ return mmap_.GetName();
+ }
+
private:
uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const;
diff --git a/test/677-fsi/expected-stderr.txt b/test/677-fsi/expected-stderr.txt
index f74e4de..35c3918 100644
--- a/test/677-fsi/expected-stderr.txt
+++ b/test/677-fsi/expected-stderr.txt
@@ -1 +1,2 @@
oat file has dex code, but APK has uncompressed dex code
+oat file has dex code, but APK has uncompressed dex code
diff --git a/test/820-vdex-multidex/expected-stderr.txt b/test/820-vdex-multidex/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/820-vdex-multidex/expected-stderr.txt
diff --git a/test/820-vdex-multidex/expected-stdout.txt b/test/820-vdex-multidex/expected-stdout.txt
new file mode 100644
index 0000000..e965047
--- /dev/null
+++ b/test/820-vdex-multidex/expected-stdout.txt
@@ -0,0 +1 @@
+Hello
diff --git a/test/820-vdex-multidex/info.txt b/test/820-vdex-multidex/info.txt
new file mode 100644
index 0000000..f6b87ea
--- /dev/null
+++ b/test/820-vdex-multidex/info.txt
@@ -0,0 +1 @@
+Test that vdex logic works with multidex.
diff --git a/test/820-vdex-multidex/run b/test/820-vdex-multidex/run
new file mode 100644
index 0000000..3f6dc3c
--- /dev/null
+++ b/test/820-vdex-multidex/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/820-vdex-multidex/src-multidex/Foo.java b/test/820-vdex-multidex/src-multidex/Foo.java
new file mode 100644
index 0000000..6d18efb
--- /dev/null
+++ b/test/820-vdex-multidex/src-multidex/Foo.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2021 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 Foo {
+}
diff --git a/test/820-vdex-multidex/src/Main.java b/test/820-vdex-multidex/src/Main.java
new file mode 100644
index 0000000..164e784
--- /dev/null
+++ b/test/820-vdex-multidex/src/Main.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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 {
+ public static void main(String[] args) {
+ System.out.println("Hello");
+ }
+}
+
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 78310b6..9e85a73 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -993,7 +993,8 @@
if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
elif [ "$TEST_VDEX" = "y" ]; then
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
+ # We delete the odex file so that the runtime only picks up the vdex file.
+ vdex_cmdline="rm $DEX_LOCATION/oat/$ISA/$name.odex"
elif [ "$TEST_DM" = "y" ]; then
dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 33d7c70..13ed23f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1139,6 +1139,7 @@
"810-checker-invoke-super-default",
"811-checker-invoke-super-secondary",
"817-hiddenapi",
+ "820-vdex-multidex",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",