summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jiakai Zhang <jiakaiz@google.com> 2025-03-21 09:40:52 -0700
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2025-03-21 09:40:52 -0700
commit69982a895b80ce048a17a45216c3298ef091d354 (patch)
treea3b767f5cfde69b9c9823f7e6c61d43c3d82f792
parentecc715d90910f08d6558918a616968f13756313e (diff)
parentf1cf622ecc1a27d95236dd0ec1da0936182ecafd (diff)
Support loading SDM files at runtime. am: f1cf622ecc
Original change: https://android-review.googlesource.com/c/platform/art/+/3548339 Change-Id: Ib51474ec7f960f08d3f58b04466e27605d20264c Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--artd/artd.cc4
-rw-r--r--libartbase/base/file_utils.cc9
-rw-r--r--libartbase/base/file_utils.h7
-rw-r--r--runtime/dex2oat_environment_test.h52
-rw-r--r--runtime/dexopt_test.cc32
-rw-r--r--runtime/dexopt_test.h10
-rw-r--r--runtime/oat/oat_file.cc123
-rw-r--r--runtime/oat/oat_file.h7
-rw-r--r--runtime/oat/oat_file_assistant.cc42
-rw-r--r--runtime/oat/oat_file_assistant.h36
-rw-r--r--runtime/oat/oat_file_assistant_test.cc305
11 files changed, 613 insertions, 14 deletions
diff --git a/artd/artd.cc b/artd/artd.cc
index 59f6b88d9f..4ef1036fe1 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -238,6 +238,10 @@ ArtifactsLocation ArtifactsLocationToAidl(OatFileAssistant::Location location) {
return ArtifactsLocation::NEXT_TO_DEX;
case OatFileAssistant::Location::kLocationDm:
return ArtifactsLocation::DM;
+ case OatFileAssistant::Location::kLocationSdmOat:
+ return ArtifactsLocation::SDM_DALVIK_CACHE;
+ case OatFileAssistant::Location::kLocationSdmOdex:
+ return ArtifactsLocation::SDM_NEXT_TO_DEX;
// No default. All cases should be explicitly handled, or the compilation will fail.
}
// This should never happen. Just in case we get a non-enumerator value.
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 2acebb9b4f..4253fa1ce7 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -715,6 +715,15 @@ std::string GetDmFilename(const std::string& dex_location) {
return ReplaceFileExtension(dex_location, kDmExtension);
}
+std::string GetSdmFilename(const std::string& dex_location, InstructionSet isa) {
+ return ReplaceFileExtension(dex_location,
+ StringPrintf("%s%s", GetInstructionSetString(isa), kSdmExtension));
+}
+
+std::string GetSdcFilename(const std::string& oat_location) {
+ return ReplaceFileExtension(oat_location, kSdcExtension);
+}
+
// check for the file in /system, followed by /system_ext
std::string GetSystemOdexFilenameForApex(std::string_view location, InstructionSet isa) {
DCHECK(LocationIsOnApex(location));
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index e8aa5f663a..89f2420aa4 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -39,6 +39,7 @@ static constexpr const char* kVdexExtension = ".vdex";
static constexpr const char* kArtExtension = ".art";
static constexpr const char* kDmExtension = ".dm";
static constexpr const char* kSdmExtension = ".sdm";
+static constexpr const char* kSdcExtension = ".sdc";
// These methods return the Android Root, which is the historical location of
// the Android "system" directory, containing the built Android artifacts. On
@@ -174,6 +175,12 @@ std::string GetVdexFilename(const std::string& oat_filename);
// Returns the dm filename for the given dex location.
std::string GetDmFilename(const std::string& dex_location);
+// Returns the sdm filename for the given dex location.
+std::string GetSdmFilename(const std::string& dex_location, InstructionSet isa);
+
+// Returns the sdc filename for the given oat filename.
+std::string GetSdcFilename(const std::string& oat_filename);
+
// Returns the odex location on /system for a DEX file on /apex. The caller must make sure that
// `location` is on /apex.
std::string GetSystemOdexFilenameForApex(std::string_view location, InstructionSet isa);
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 7e8378aa67..53598bcfa0 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -20,13 +20,16 @@
#include <sys/wait.h>
#include <fstream>
+#include <memory>
#include <optional>
#include <string>
#include <vector>
+#include "android-base/file.h"
#include "android-base/result.h"
#include "android-base/strings.h"
#include "base/file_utils.h"
+#include "base/globals.h"
#include "base/macros.h"
#include "base/os.h"
#include "base/stl_util.h"
@@ -40,6 +43,7 @@
#include "gc/space/image_space.h"
#include "gtest/gtest.h"
#include "oat/oat_file_assistant.h"
+#include "oat/sdc_file.h"
#include "runtime.h"
#include "ziparchive/zip_writer.h"
@@ -256,7 +260,9 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe
return WEXITSTATUS(res.status_code);
}
- void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) {
+ void CreateDexMetadata(const std::string& vdex,
+ const std::string& out_dm,
+ bool page_aligned = false) {
// Read the vdex bytes.
std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str()));
std::vector<uint8_t> data(vdex_file->GetLength());
@@ -265,13 +271,55 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe
// Zip the content.
FILE* file = fopen(out_dm.c_str(), "wbe");
ZipWriter writer(file);
- writer.StartEntry("primary.vdex", ZipWriter::kAlign32);
+ writer.StartAlignedEntry(
+ "primary.vdex", /*flags=*/0, /*alignment=*/page_aligned ? kMaxPageSize : 4);
writer.WriteBytes(data.data(), data.size());
writer.FinishEntry();
writer.Finish();
fflush(file);
fclose(file);
}
+
+ void CreateSecureDexMetadata(const std::string& odex,
+ const std::string& art,
+ const std::string& out_sdm) {
+ // Zip the content.
+ std::unique_ptr<File> sdm_file(OS::CreateEmptyFileWriteOnly(out_sdm.c_str()));
+ ASSERT_NE(sdm_file, nullptr);
+ ZipWriter writer(fdopen(sdm_file->Fd(), "wb"));
+
+ std::string odex_data;
+ ASSERT_TRUE(android::base::ReadFileToString(odex, &odex_data));
+ writer.StartAlignedEntry("primary.odex", /*flags=*/0, /*alignment=*/kMaxPageSize);
+ writer.WriteBytes(odex_data.data(), odex_data.size());
+ writer.FinishEntry();
+
+ if (!art.empty()) {
+ std::string art_data;
+ ASSERT_TRUE(android::base::ReadFileToString(art, &art_data));
+ writer.StartAlignedEntry("primary.art", /*flags=*/0, /*alignment=*/kMaxPageSize);
+ writer.WriteBytes(art_data.data(), art_data.size());
+ writer.FinishEntry();
+ }
+
+ writer.Finish();
+ ASSERT_EQ(sdm_file->FlushClose(), 0);
+ }
+
+ void CreateSecureDexMetadataCompanion(const std::string& sdm,
+ const std::string& apex_versions,
+ const std::string& out_sdc) {
+ struct stat sdm_st;
+ ASSERT_EQ(stat(sdm.c_str(), &sdm_st), 0);
+
+ std::unique_ptr<File> sdc_file(OS::CreateEmptyFileWriteOnly(out_sdc.c_str()));
+ ASSERT_NE(sdc_file, nullptr);
+ SdcWriter sdc_writer(std::move(*sdc_file));
+ sdc_writer.SetSdmTimestampNs(TimeSpecToNs(sdm_st.st_mtim));
+ sdc_writer.SetApexVersions(apex_versions);
+ std::string error_msg;
+ ASSERT_TRUE(sdc_writer.Save(&error_msg)) << error_msg;
+ }
};
} // namespace art
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 40e10142d7..6239f7de87 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -202,6 +202,38 @@ void DexoptTest::GenerateOatForTest(const char* dex_location, CompilerFilter::Fi
GenerateOatForTest(dex_location, filter, /*with_alternate_image=*/false);
}
+void DexoptTest::GenerateSdmDmForTest(const std::string& dex_location,
+ const std::string& sdm_location,
+ const std::string& dm_location,
+ CompilerFilter::Filter filter,
+ bool include_app_image,
+ const char* compilation_reason,
+ const std::vector<std::string>& extra_args) {
+ std::string tmp_dir = GetScratchDir() + "/sdm_tmp";
+ ASSERT_EQ(0, mkdir(tmp_dir.c_str(), 0700));
+
+ std::string odex_location = tmp_dir + "/TestDex.odex";
+ std::string vdex_location = tmp_dir + "/TestDex.vdex";
+ std::string art_location;
+
+ std::vector<std::string> extra_args_with_app_image = extra_args;
+ if (include_app_image) {
+ art_location = tmp_dir + "/TestDex.art";
+ extra_args_with_app_image.push_back("--app-image-file=" + art_location);
+ }
+
+ // Generate temporary ODEX, VDEX, and ART files in order to create the SDM and DM files from.
+ ASSERT_NO_FATAL_FAILURE(GenerateOdexForTest(
+ dex_location, odex_location, filter, compilation_reason, extra_args_with_app_image));
+
+ // Create the SDM and DM files.
+ ASSERT_NO_FATAL_FAILURE(CreateSecureDexMetadata(odex_location, art_location, sdm_location));
+ ASSERT_NO_FATAL_FAILURE(CreateDexMetadata(vdex_location, dm_location, /*page_aligned=*/true));
+
+ // Cleanup the temporary files.
+ ASSERT_NO_FATAL_FAILURE(ClearDirectory(tmp_dir.c_str()));
+}
+
void DexoptTest::ReserveImageSpace() {
MemMap::Init();
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
index cf32785c0b..fa18fdd94b 100644
--- a/runtime/dexopt_test.h
+++ b/runtime/dexopt_test.h
@@ -64,6 +64,16 @@ class DexoptTest : public Dex2oatEnvironmentTest {
// Generate a standard oat file in the oat location.
void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter);
+ // Generate sdm and dm files for the purposes of test.
+ // If `include_app_image` is true, generates an app image and includes it in the sdm file.
+ void GenerateSdmDmForTest(const std::string& dex_location,
+ const std::string& sdm_location,
+ const std::string& dm_location,
+ CompilerFilter::Filter filter,
+ bool include_app_image,
+ const char* compilation_reason = nullptr,
+ const std::vector<std::string>& extra_args = {});
+
bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
private:
diff --git a/runtime/oat/oat_file.cc b/runtime/oat/oat_file.cc
index 3f72401f43..ec845cd0c1 100644
--- a/runtime/oat/oat_file.cc
+++ b/runtime/oat/oat_file.cc
@@ -20,25 +20,34 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <cstddef>
#include <cstdint>
+#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <memory>
#include <optional>
#include <sstream>
+#include <string>
#include <type_traits>
+#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "arch/instruction_set_features.h"
#include "art_method.h"
+#include "base/array_ref.h"
#include "base/bit_vector.h"
#include "base/file_utils.h"
+#include "base/globals.h"
#include "base/logging.h" // For VLOG_IS_ON.
+#include "base/macros.h"
#include "base/mem_map.h"
#include "base/os.h"
#include "base/pointer_size.h"
#include "base/stl_util.h"
#include "base/systrace.h"
+#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "base/zip_archive.h"
@@ -59,6 +68,7 @@
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "oat.h"
+#include "oat/sdc_file.h"
#include "oat_file-inl.h"
#include "oat_file_manager.h"
#include "runtime-inl.h"
@@ -174,6 +184,14 @@ class OatFileBase : public OatFile {
/*inout*/ MemMap* reservation, // Where to load if not null.
/*out*/ std::string* error_msg);
+ template <typename kOatFileBaseSubType>
+ static OatFileBase* OpenOatFileFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg);
+
protected:
OatFileBase(const std::string& filename, bool executable) : OatFile(filename, executable) {}
@@ -316,6 +334,61 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd,
return ret.release();
}
+template <typename kOatFileBaseSubType>
+OatFileBase* OatFileBase::OpenOatFileFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg) {
+ std::string elf_filename = sdm_filename + kZipSeparator + "primary.odex";
+ std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(elf_filename, executable));
+
+ struct stat sdm_st;
+ if (stat(sdm_filename.c_str(), &sdm_st) != 0) {
+ *error_msg = ART_FORMAT("Failed to stat sdm file '{}': {}", sdm_filename, strerror(errno));
+ return nullptr;
+ }
+
+ std::unique_ptr<SdcReader> sdc_reader = SdcReader::Load(sdc_filename, error_msg);
+ if (sdc_reader == nullptr) {
+ return nullptr;
+ }
+ if (sdc_reader->GetSdmTimestampNs() != TimeSpecToNs(sdm_st.st_mtim)) {
+ // The sdm file had been replaced after the sdc file was created.
+ *error_msg = ART_FORMAT("Obsolete sdc file '{}'", sdc_filename);
+ return nullptr;
+ }
+ // The apex-versions value in the sdc file, written by ART Service, is the value of
+ // `Runtime::GetApexVersions` at the time where the sdm file was first seen on device. We use it
+ // to override the APEX versions in the oat header. This is for detecting samegrade placebos.
+ ret->override_apex_versions_ = sdc_reader->GetApexVersions();
+
+ if (!ret->Load(elf_filename, executable, /*low_4gb=*/false, /*reservation=*/nullptr, error_msg)) {
+ return nullptr;
+ }
+
+ if (!ret->ComputeFields(elf_filename, error_msg)) {
+ return nullptr;
+ }
+
+ ret->PreSetup(elf_filename);
+
+ ret->vdex_ = VdexFile::OpenFromDm(dm_filename, ret->vdex_begin_, ret->vdex_end_, error_msg);
+ if (ret->vdex_ == nullptr) {
+ return nullptr;
+ }
+
+ if (!ret->Setup(/*zip_fd=*/-1,
+ ArrayRef<const std::string>(&dex_filename, /*size=*/1u),
+ /*dex_files=*/{},
+ error_msg)) {
+ return nullptr;
+ }
+
+ return ret.release();
+}
+
bool OatFileBase::LoadVdex(const std::string& vdex_filename, bool low_4gb, std::string* error_msg) {
vdex_ = VdexFile::OpenAtAddress(vdex_begin_,
vdex_end_ - vdex_begin_,
@@ -1330,11 +1403,11 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
return false;
#else
{
- UniqueCPtr<char> absolute_path(realpath(elf_filename.c_str(), nullptr));
- if (absolute_path == nullptr) {
- *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
- return false;
- }
+ // `elf_filename` is in the format of `/path/to/oat` or `/path/to/zip!/primary.odex`. We can
+ // reuse `GetDexCanonicalLocation` to resolve the real path of the part before "!" even though
+ // `elf_filename` does not refer to a dex file.
+ static_assert(std::string_view(kZipSeparator).starts_with(DexFileLoader::kMultiDexSeparator));
+ std::string absolute_path = DexFileLoader::GetDexCanonicalLocation(elf_filename.c_str());
#ifdef ART_TARGET_ANDROID
android_dlextinfo extinfo = {};
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD; // Force-load, don't reuse handle
@@ -1350,9 +1423,9 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
}
if (strncmp(kAndroidArtApexDefaultPath,
- absolute_path.get(),
+ absolute_path.c_str(),
sizeof(kAndroidArtApexDefaultPath) - 1) != 0 ||
- absolute_path.get()[sizeof(kAndroidArtApexDefaultPath) - 1] != '/') {
+ absolute_path.c_str()[sizeof(kAndroidArtApexDefaultPath) - 1] != '/') {
// Use the system namespace for OAT files outside the ART APEX. Search
// paths and links don't matter here, but permitted paths do, and the
// system namespace is configured to allow loading from all appropriate
@@ -1361,7 +1434,7 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
extinfo.library_namespace = GetSystemLinkerNamespace();
}
- dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
+ dlopen_handle_ = android_dlopen_ext(absolute_path.c_str(), RTLD_NOW, &extinfo);
if (reservation != nullptr && dlopen_handle_ != nullptr) {
// Find used pages from the reservation.
struct dl_iterate_context {
@@ -1435,7 +1508,7 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
return false;
}
MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
- dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
+ dlopen_handle_ = dlopen(absolute_path.c_str(), RTLD_NOW);
if (dlopen_handle_ != nullptr) {
if (!host_dlopen_handles_.insert(dlopen_handle_).second) {
dlclose(dlopen_handle_);
@@ -2034,6 +2107,38 @@ OatFile* OatFile::OpenFromVdex(int zip_fd,
return OatFileBackedByVdex::Open(zip_fd, std::move(vdex_file), location, context, error_msg);
}
+OatFile* OatFile::OpenFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg) {
+ ScopedTrace trace("Open sdm file " + sdm_filename);
+ CHECK(!sdm_filename.empty());
+ CHECK(!sdc_filename.empty());
+ CHECK(!dm_filename.empty());
+ CHECK(!dex_filename.empty());
+
+ // Check if the dm file exists, to fail fast. The dm file contains the vdex that is essential for
+ // using the odex in the sdm file.
+ if (!OS::FileExists(dm_filename.c_str())) {
+ *error_msg =
+ ART_FORMAT("Not loading sdm file because dm file '{}' does not exist", dm_filename);
+ return nullptr;
+ }
+
+ // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is
+ // disabled.
+ OatFile* with_dlopen = OatFileBase::OpenOatFileFromSdm<DlOpenOatFile>(
+ sdm_filename, sdc_filename, dm_filename, dex_filename, executable, error_msg);
+ if (with_dlopen != nullptr) {
+ return with_dlopen;
+ }
+
+ return OatFileBase::OpenOatFileFromSdm<ElfOatFile>(
+ sdm_filename, sdc_filename, dm_filename, dex_filename, executable, error_msg);
+}
+
OatFile::OatFile(const std::string& location, bool is_executable)
: location_(location),
vdex_(nullptr),
diff --git a/runtime/oat/oat_file.h b/runtime/oat/oat_file.h
index 53f1d173d1..33645fa4ba 100644
--- a/runtime/oat/oat_file.h
+++ b/runtime/oat/oat_file.h
@@ -187,6 +187,13 @@ class OatFile {
ClassLoaderContext* context,
std::string* error_msg);
+ static OatFile* OpenFromSdm(const std::string& sdm_filename,
+ const std::string& sdc_filename,
+ const std::string& dm_filename,
+ const std::string& dex_filename,
+ bool executable,
+ /*out*/ std::string* error_msg);
+
// Set the start of the app image.
// Needed for initializing app image relocations in the .data.img.rel.ro section.
void SetAppImageBegin(uint8_t* app_image_begin) const {
diff --git a/runtime/oat/oat_file_assistant.cc b/runtime/oat/oat_file_assistant.cc
index 8df8459589..53f412908d 100644
--- a/runtime/oat/oat_file_assistant.cc
+++ b/runtime/oat/oat_file_assistant.cc
@@ -191,6 +191,12 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
oat_file_name,
/*is_oat_location=*/true,
/*use_fd=*/false));
+ info_list_.push_back(
+ std::make_unique<OatFileInfoBackedBySdm>(this,
+ GetSdmFilename(dex_location_, isa),
+ /*is_oat_location=*/true,
+ GetDmFilename(dex_location_),
+ GetSdcFilename(oat_file_name)));
}
if (!odex_file_name.empty()) {
@@ -202,6 +208,12 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
zip_fd,
vdex_fd,
oat_fd));
+ info_list_.push_back(
+ std::make_unique<OatFileInfoBackedBySdm>(this,
+ GetSdmFilename(dex_location_, isa),
+ /*is_oat_location=*/false,
+ GetDmFilename(dex_location_),
+ GetSdcFilename(odex_file_name)));
}
// When there is no odex/oat available (e.g., they are both out of date), we look for a useable
@@ -324,7 +336,8 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_fil
OatFileInfo& info = GetBestInfo();
DexOptNeeded dexopt_needed = info.GetDexOptNeeded(
target_compiler_filter, GetDexOptTrigger(target_compiler_filter, profile_changed, downgrade));
- if (dexopt_needed != kNoDexOptNeeded && info.GetType() == OatFileType::kDm) {
+ if (dexopt_needed != kNoDexOptNeeded &&
+ (info.GetType() == OatFileType::kDm || info.GetType() == OatFileType::kSdm)) {
// The usable vdex file is in the DM file. This information cannot be encoded in the integer.
// Return kDex2OatFromScratch so that neither the vdex in the "oat" location nor the vdex in the
// "odex" location will be picked by installd.
@@ -956,6 +969,10 @@ bool OatFileAssistant::OatFileInfoBackedByOat::FileExists() const {
return use_fd_ || OatFileInfo::FileExists();
}
+bool OatFileAssistant::OatFileInfoBackedBySdm::FileExists() const {
+ return OatFileInfo::FileExists() && OS::FileExists(sdc_filename_.c_str());
+}
+
bool OatFileAssistant::OatFileInfoBackedByVdex::FileExists() const {
return use_fd_ || OatFileInfo::FileExists();
}
@@ -1015,6 +1032,21 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedByOat::LoadFile(
}
}
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedBySdm::LoadFile(
+ std::string* error_msg) const {
+ bool executable = oat_file_assistant_->load_executable_;
+ if (executable && oat_file_assistant_->only_load_trusted_executable_) {
+ executable = LocationIsTrusted(filename_, /*trust_art_apex_data_files=*/true);
+ }
+
+ return std::unique_ptr<OatFile>(OatFile::OpenFromSdm(filename_,
+ sdc_filename_,
+ dm_filename_,
+ oat_file_assistant_->dex_location_,
+ executable,
+ error_msg));
+}
+
std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedByVdex::LoadFile(
std::string* error_msg) const {
// Check to see if there is a vdex file we can make use of.
@@ -1289,7 +1321,13 @@ bool OatFileAssistant::ZipFileOnlyContainsUncompressedDex() {
OatFileAssistant::Location OatFileAssistant::GetLocation(OatFileInfo& info) {
if (info.IsUseable()) {
- if (info.GetType() == OatFileType::kDm) {
+ if (info.GetType() == OatFileType::kSdm) {
+ if (info.IsOatLocation()) {
+ return kLocationSdmOat;
+ } else {
+ return kLocationSdmOdex;
+ }
+ } else if (info.GetType() == OatFileType::kDm) {
return kLocationDm;
} else if (info.IsOatLocation()) {
return kLocationOat;
diff --git a/runtime/oat/oat_file_assistant.h b/runtime/oat/oat_file_assistant.h
index bcb0c8e9db..80eabba34b 100644
--- a/runtime/oat/oat_file_assistant.h
+++ b/runtime/oat/oat_file_assistant.h
@@ -123,8 +123,14 @@ class OatFileAssistant {
kLocationOat = 1,
// In the "oat" folder next to the dex file.
kLocationOdex = 2,
- // In the DM file. This means the only usable file is the vdex file.
+ // In the dm file. This means the only usable file is the vdex file.
kLocationDm = 3,
+ // The oat and art files are in the sdm file next to the dex file. The vdex file is in the dm
+ // file next to the dex file. The sdc file is in the global "dalvik-cache" folder.
+ kLocationSdmOat = 4,
+ // The oat and art files are in the sdm file next to the dex file. The vdex file is in the dm
+ // file next to the dex file. The sdc file is next to the dex file.
+ kLocationSdmOdex = 5,
};
// Represents the status of the current oat file and/or vdex file.
@@ -381,6 +387,7 @@ class OatFileAssistant {
enum class OatFileType {
kNone,
kOat,
+ kSdm,
kVdex,
kDm,
};
@@ -529,6 +536,33 @@ class OatFileAssistant {
const int oat_fd_;
};
+ class OatFileInfoBackedBySdm : public OatFileInfo {
+ public:
+ OatFileInfoBackedBySdm(OatFileAssistant* oat_file_assistant,
+ const std::string& sdm_filename,
+ bool is_oat_location,
+ const std::string& dm_filename,
+ const std::string& sdc_filename)
+ : OatFileInfo(oat_file_assistant, sdm_filename, is_oat_location),
+ dm_filename_(dm_filename),
+ sdc_filename_(sdc_filename) {}
+
+ OatFileType GetType() override { return OatFileType::kSdm; }
+
+ const char* GetLocationDebugString() override {
+ return IsOatLocation() ? "sdm with sdc in dalvik-cache" : "sdm with sdc next to the dex file";
+ }
+
+ bool FileExists() const override;
+
+ protected:
+ std::unique_ptr<OatFile> LoadFile(std::string* error_msg) const override;
+
+ private:
+ const std::string dm_filename_;
+ const std::string sdc_filename_;
+ };
+
class OatFileInfoBackedByVdex : public OatFileInfo {
public:
OatFileInfoBackedByVdex(OatFileAssistant* oat_file_assistant,
diff --git a/runtime/oat/oat_file_assistant_test.cc b/runtime/oat/oat_file_assistant_test.cc
index 5bd9d1ed32..c3e2f63e24 100644
--- a/runtime/oat/oat_file_assistant_test.cc
+++ b/runtime/oat/oat_file_assistant_test.cc
@@ -33,6 +33,7 @@
#include "android-base/strings.h"
#include "arch/instruction_set.h"
#include "art_field-inl.h"
+#include "base/file_utils.h"
#include "base/os.h"
#include "base/utils.h"
#include "class_linker.h"
@@ -2183,6 +2184,310 @@ TEST_P(OatFileAssistantTest, ShouldRecompileForImageFromSpeedProfile) {
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
}
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDC file is in odex
+// location.
+// Expect: The best artifact location should be kLocationSdmOdex. Dexopt should be performed only if
+// the compiler filter is better than "speed-profile".
+//
+// The legacy version should return kDex2OatFromScratch if the target compiler filter is better than
+// "verify".
+TEST_P(OatFileAssistantTest, SdmUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDC file is in oat
+// location.
+// Expect: The best artifact location should be kLocationSdmOat.
+TEST_P(OatFileAssistantTest, SdmUpToDateSdcInOatLocation) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg))
+ << error_msg;
+ std::string sdc_location = GetSdcFilename(oat_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOat);
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDM file contains no
+// ART file.
+// Expect: The best artifact location should be kLocationSdmOdex.
+TEST_P(OatFileAssistantTest, SdmUpToDateNoArt) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/false,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file. Meanwhile, we have an ODEX
+// file that is also up to date.
+// Expect: The ODEX file is preferred over the SDM file. The best artifact location should be
+// kLocationOdex.
+TEST_P(OatFileAssistantTest, SdmAndOdexUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ ASSERT_NO_FATAL_FAILURE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeedProfile,
+ /*compilation_reason=*/"bg-dexopt"));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "bg-dexopt",
+ "up-to-date",
+ OatFileAssistant::kLocationOdex);
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file. Meanwhile, we have a VDEX
+// file that is also up to date.
+// Expect: The SDM file is preferred over the VDEX file. The best artifact location should be
+// kLocationSdmOdex.
+TEST_P(OatFileAssistantTest, SdmAndVdexUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ ASSERT_NO_FATAL_FAILURE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeedProfile,
+ /*compilation_reason=*/"bg-dexopt"));
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+}
+
+// Case: We have SDM, DM, and SDC files for a compressed DEX file.
+// Expect: The SDM file is still picked. Dexopt should be performed if the compiler filter is
+// "speed-profile" or above.
+TEST_P(OatFileAssistantTest, SdmUpToDateCompressedDex) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud",
+ /*extra_args=*/{"--copy-dex-files=false"}));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, runtime_->GetApexVersions(), sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(&oat_file_assistant,
+ "speed-profile",
+ "cloud",
+ "up-to-date",
+ OatFileAssistant::kLocationSdmOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationSdmOdex);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, but the SDC file contains the
+// wrong APEX versions.
+// Expect: The SDM file is rejected, while the DM file is still picked. Dexopt should be performed
+// if the compiler filter is better than "verify".
+TEST_P(OatFileAssistantTest, SdmApexVersionMismatch) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string sdm_location =
+ GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ ASSERT_NO_FATAL_FAILURE(GenerateSdmDmForTest(dex_location,
+ sdm_location,
+ dm_location,
+ CompilerFilter::kSpeedProfile,
+ /*include_app_image=*/true,
+ /*compilation_reason=*/"cloud"));
+ ASSERT_NO_FATAL_FAILURE(
+ CreateSecureDexMetadataCompanion(sdm_location, "wrong-apex-version", sdc_location));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyOptimizationStatusWithInstance(
+ &oat_file_assistant, "verify", "vdex", "up-to-date", OatFileAssistant::kLocationDm);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpaceProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpaceProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
class CollectDexCacheVisitor : public DexCacheVisitor {
public:
explicit CollectDexCacheVisitor(