Create vdex file for dex loaded with InMemoryDexClassLoader
Previous CL introduced a background verification thread for dex bytecode
loaded with InMemoryDexClassLoader. Extend the logic to collect the
results of class verification into an instance of VerifierDeps and dump
it into a vdex file in the app's data folder.
The background thread does not collect full VerifierDeps (e.g.
assignability dependencies, etc), just a bit vector of whether a class
was successfully verified or not.
The vdex format is extended to include boot classpath checksums and the
class loader context it was created for. These are optional and
currently left empty for regular vdex files.
The generated vdex files are treated as a cache with a limited capacity,
currently capped at 8 files. The least recently used file (in terms of
atime reported by stat()) is unlinked if the cache is full and a new
vdex is about to be generated.
Bug: 72131483
Test: art/tools/run-libcore-tests.sh
Test: art/test.py -b -r -t 692 -t 693
Change-Id: I26080d894d34d8f35f00c7925db569f22f008d2c
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 0cc7d29..43262f2 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -430,6 +430,7 @@
"liblog",
"libnativebridge",
"libnativeloader",
+ "libsigchain_dummy",
"libunwindstack",
"libz",
],
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index d1ea655..ae1eea5 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -340,10 +340,27 @@
}
}
+static jstring DexFile_getClassLoaderContext(JNIEnv* env,
+ jclass,
+ jobject class_loader,
+ jobjectArray dex_elements) {
+ CHECK(class_loader != nullptr);
+ constexpr const char* kBaseDir = "";
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
+ if (context == nullptr || !context->OpenDexFiles(kRuntimeISA, kBaseDir)) {
+ LOG(WARNING) << "Could not establish class loader context";
+ return nullptr;
+ }
+ std::string str_context = context->EncodeContextForOatFile(kBaseDir);
+ return env->NewStringUTF(str_context.c_str());
+}
+
static void DexFile_verifyInBackgroundNative(JNIEnv* env,
jclass,
jobject cookie,
- jobject class_loader) {
+ jobject class_loader,
+ jstring class_loader_context) {
CHECK(cookie != nullptr);
CHECK(class_loader != nullptr);
@@ -356,8 +373,17 @@
}
CHECK(oat_file == nullptr) << "Called verifyInBackground on a dex file backed by oat";
+ ScopedUtfChars class_loader_context_utf(env, class_loader_context);
+ if (env->ExceptionCheck()) {
+ LOG(ERROR) << "Failed to unwrap class loader context string";
+ return;
+ }
+
// Hand over to OatFileManager to spawn a verification thread.
- Runtime::Current()->GetOatFileManager().RunBackgroundVerification(dex_files, class_loader);
+ Runtime::Current()->GetOatFileManager().RunBackgroundVerification(
+ dex_files,
+ class_loader,
+ class_loader_context_utf.c_str());
}
static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
@@ -913,7 +939,15 @@
"[I"
"[I"
")Ljava/lang/Object;"),
- NATIVE_METHOD(DexFile, verifyInBackgroundNative, "(Ljava/lang/Object;Ljava/lang/ClassLoader;)V"),
+ NATIVE_METHOD(DexFile, getClassLoaderContext,
+ "(Ljava/lang/ClassLoader;"
+ "[Ldalvik/system/DexPathList$Element;"
+ ")Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, verifyInBackgroundNative,
+ "(Ljava/lang/Object;"
+ "Ljava/lang/ClassLoader;"
+ "Ljava/lang/String;"
+ ")V"),
NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f122e57..b5e7ce8 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -19,6 +19,7 @@
#include <sstream>
#include <sys/stat.h>
+#include "zlib.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -31,6 +32,7 @@
#include "base/string_view_cpp20.h"
#include "base/utils.h"
#include "class_linker.h"
+#include "class_loader_context.h"
#include "compiler_filter.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file_loader.h"
@@ -42,12 +44,14 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "vdex_file.h"
-#include "class_loader_context.h"
namespace art {
using android::base::StringPrintf;
+static constexpr const char* kAnonymousDexPrefix = "Anonymous-DexFile@";
+static constexpr const char* kVdexExtension = ".vdex";
+
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
switch (status) {
case OatFileAssistant::kOatCannotOpen:
@@ -429,6 +433,54 @@
return kOatUpToDate;
}
+bool OatFileAssistant::AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& headers,
+ InstructionSet isa,
+ /* out */ uint32_t* location_checksum,
+ /* out */ std::string* dex_location,
+ /* out */ std::string* vdex_filename) {
+ uint32_t checksum = adler32(0L, Z_NULL, 0);
+ for (const DexFile::Header* header : headers) {
+ checksum = adler32_combine(checksum,
+ header->checksum_,
+ header->file_size_ - DexFile::kNumNonChecksumBytes);
+ }
+ *location_checksum = checksum;
+
+ const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory();
+ if (data_dir.empty() || Runtime::Current()->IsZygote()) {
+ *dex_location = StringPrintf("%s%u", kAnonymousDexPrefix, checksum);
+ return false;
+ }
+ *dex_location = StringPrintf("%s/%s%u.jar", data_dir.c_str(), kAnonymousDexPrefix, checksum);
+
+ std::string odex_filename;
+ std::string error_msg;
+ if (!DexLocationToOdexFilename(*dex_location, isa, &odex_filename, &error_msg)) {
+ LOG(WARNING) << "Could not get odex filename for " << *dex_location << ": " << error_msg;
+ return false;
+ }
+
+ *vdex_filename = GetVdexFilename(odex_filename);
+ return true;
+}
+
+bool OatFileAssistant::IsAnonymousVdexBasename(const std::string& basename) {
+ DCHECK(basename.find('/') == std::string::npos);
+ // `basename` must have format: <kAnonymousDexPrefix><checksum><kVdexExtension>
+ if (basename.size() < strlen(kAnonymousDexPrefix) + strlen(kVdexExtension) + 1 ||
+ !android::base::StartsWith(basename.c_str(), kAnonymousDexPrefix) ||
+ !android::base::EndsWith(basename, kVdexExtension)) {
+ return false;
+ }
+ // Check that all characters between the prefix and extension are decimal digits.
+ for (size_t i = strlen(kAnonymousDexPrefix); i < basename.size() - strlen(kVdexExtension); ++i) {
+ if (!std::isdigit(basename[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
static bool DexLocationToOdexNames(const std::string& location,
InstructionSet isa,
std::string* odex_filename,
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 83ae3cb..1f3f74f 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -245,6 +245,21 @@
std::string* oat_filename,
std::string* error_msg);
+ // Computes the location checksum, dex location and vdex filename by combining
+ // the checksums of the individual dex files. If the data directory of the process
+ // is known, creates an absolute path in that directory and tries to infer path
+ // of a corresponding vdex file. Otherwise only creates a basename dex_location
+ // from the combined checksums. Returns true if all out-arguments have been set.
+ static bool AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& dex_headers,
+ InstructionSet isa,
+ /* out */ uint32_t* location_checksum,
+ /* out */ std::string* dex_location,
+ /* out */ std::string* vdex_filename);
+
+ // Returns true if a filename (given as basename) is a name of a vdex for
+ // anonymous dex file(s) created by AnonymousDexVdexLocation.
+ static bool IsAnonymousVdexBasename(const std::string& basename);
+
private:
class OatFileInfo {
public:
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 7e5785e..f9823a1 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -19,6 +19,7 @@
#include <memory>
#include <queue>
#include <vector>
+#include <sys/stat.h>
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -50,6 +51,8 @@
#include "thread-current-inl.h"
#include "thread_list.h"
#include "thread_pool.h"
+#include "vdex_file.h"
+#include "verifier/verifier_deps.h"
#include "well_known_classes.h"
namespace art {
@@ -644,10 +647,90 @@
return dex_files;
}
+static std::vector<const DexFile::Header*> GetDexFileHeaders(
+ const std::vector<const DexFile*>& dex_files) {
+ std::vector<const DexFile::Header*> headers;
+ headers.reserve(dex_files.size());
+ for (const DexFile* dex_file : dex_files) {
+ headers.push_back(&dex_file->GetHeader());
+ }
+ return headers;
+}
+
+// Check how many vdex files exist in the same directory as the vdex file we are about
+// to write. If more than or equal to kAnonymousVdexCacheSize, unlink the least
+// recently used one(s) (according to stat-reported atime).
+static bool UnlinkLeastRecentlyUsedVdexIfNeeded(const std::string& vdex_path_to_add,
+ std::string* error_msg) {
+ if (OS::FileExists(vdex_path_to_add.c_str())) {
+ // File already exists and will be overwritten.
+ // This will not change the number of entries in the cache.
+ return true;
+ }
+
+ auto last_slash = vdex_path_to_add.rfind('/');
+ CHECK(last_slash != std::string::npos);
+ std::string vdex_dir = vdex_path_to_add.substr(0, last_slash + 1);
+
+ if (!OS::DirectoryExists(vdex_dir.c_str())) {
+ // Folder does not exist yet. Cache has zero entries.
+ return true;
+ }
+
+ std::vector<std::pair<time_t, std::string>> cache;
+
+ DIR* c_dir = opendir(vdex_dir.c_str());
+ if (c_dir == nullptr) {
+ *error_msg = "Unable to open " + vdex_dir + " to delete unused vdex files";
+ return false;
+ }
+ for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string basename = de->d_name;
+ if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) {
+ continue;
+ }
+ std::string fullname = vdex_dir + basename;
+
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(stat(fullname.c_str(), &s));
+ if (rc == -1) {
+ *error_msg = "Failed to stat() anonymous vdex file " + fullname;
+ return false;
+ }
+
+ cache.push_back(std::make_pair(s.st_atime, fullname));
+ }
+ CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
+
+ if (cache.size() < OatFileManager::kAnonymousVdexCacheSize) {
+ return true;
+ }
+
+ std::sort(cache.begin(),
+ cache.end(),
+ [](const auto& a, const auto& b) { return a.first < b.first; });
+ for (size_t i = OatFileManager::kAnonymousVdexCacheSize - 1; i < cache.size(); ++i) {
+ if (unlink(cache[i].second.c_str()) != 0) {
+ *error_msg = "Could not unlink anonymous vdex file " + cache[i].second;
+ return false;
+ }
+ }
+
+ return true;
+}
+
class BackgroundVerificationTask final : public Task {
public:
- BackgroundVerificationTask(const std::vector<const DexFile*>& dex_files, jobject class_loader)
- : dex_files_(dex_files) {
+ BackgroundVerificationTask(const std::vector<const DexFile*>& dex_files,
+ jobject class_loader,
+ const char* class_loader_context,
+ const std::string& vdex_path)
+ : dex_files_(dex_files),
+ class_loader_context_(class_loader_context),
+ vdex_path_(vdex_path) {
Thread* const self = Thread::Current();
ScopedObjectAccess soa(self);
// Create a global ref for `class_loader` because it will be accessed from a different thread.
@@ -662,11 +745,15 @@
}
void Run(Thread* self) override {
+ std::string error_msg;
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ verifier::VerifierDeps verifier_deps(dex_files_);
// Iterate over all classes and verify them.
for (const DexFile* dex_file : dex_files_) {
for (uint32_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); cdef_idx++) {
+ const dex::ClassDef& class_def = dex_file->GetClassDef(cdef_idx);
+
// Take handles inside the loop. The background verification is low priority
// and we want to minimize the risk of blocking anyone else.
ScopedObjectAccess soa(self);
@@ -675,7 +762,7 @@
soa.Decode<mirror::ClassLoader>(class_loader_)));
Handle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(class_linker->FindClass(
self,
- dex_file->GetClassDescriptor(dex_file->GetClassDef(cdef_idx)),
+ dex_file->GetClassDescriptor(class_def),
h_loader)));
if (h_class == nullptr) {
@@ -700,8 +787,28 @@
CHECK(h_class->IsVerified() || h_class->IsErroneous())
<< h_class->PrettyDescriptor() << ": state=" << h_class->GetStatus();
+
+ if (h_class->IsVerified()) {
+ verifier_deps.RecordClassVerified(*dex_file, class_def);
+ }
}
}
+
+ // Delete old vdex files if there are too many in the folder.
+ if (!UnlinkLeastRecentlyUsedVdexIfNeeded(vdex_path_, &error_msg)) {
+ LOG(ERROR) << "Could not unlink old vdex files " << vdex_path_ << ": " << error_msg;
+ return;
+ }
+
+ // Construct a vdex file and write `verifier_deps` into it.
+ if (!VdexFile::WriteToDisk(vdex_path_,
+ dex_files_,
+ verifier_deps,
+ class_loader_context_,
+ &error_msg)) {
+ LOG(ERROR) << "Could not write anonymous vdex " << vdex_path_ << ": " << error_msg;
+ return;
+ }
}
void Finalize() override {
@@ -711,12 +818,15 @@
private:
const std::vector<const DexFile*> dex_files_;
jobject class_loader_;
+ const std::string class_loader_context_;
+ const std::string vdex_path_;
DISALLOW_COPY_AND_ASSIGN(BackgroundVerificationTask);
};
void OatFileManager::RunBackgroundVerification(const std::vector<const DexFile*>& dex_files,
- jobject class_loader) {
+ jobject class_loader,
+ const char* class_loader_context) {
if (Runtime::Current()->IsJavaDebuggable()) {
// Threads created by ThreadPool ("runtime threads") are not allowed to load
// classes when debuggable to match class-initialization semantics
@@ -730,13 +840,25 @@
return;
}
- if (verification_thread_pool_ == nullptr) {
- verification_thread_pool_.reset(new ThreadPool("Verification thread pool",
- /* num_threads= */ 1));
- verification_thread_pool_->StartWorkers(self);
+ uint32_t location_checksum;
+ std::string dex_location;
+ std::string vdex_path;
+ if (OatFileAssistant::AnonymousDexVdexLocation(GetDexFileHeaders(dex_files),
+ kRuntimeISA,
+ &location_checksum,
+ &dex_location,
+ &vdex_path)) {
+ if (verification_thread_pool_ == nullptr) {
+ verification_thread_pool_.reset(
+ new ThreadPool("Verification thread pool", /* num_threads= */ 1));
+ verification_thread_pool_->StartWorkers(self);
+ }
+ verification_thread_pool_->AddTask(self, new BackgroundVerificationTask(
+ dex_files,
+ class_loader,
+ class_loader_context,
+ vdex_path));
}
-
- verification_thread_pool_->AddTask(self, new BackgroundVerificationTask(dex_files, class_loader));
}
void OatFileManager::WaitForWorkersToBeCreated() {
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 7535696..e5eae9f 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -106,7 +106,8 @@
// Spawn a background thread which verifies all classes in the given dex files.
void RunBackgroundVerification(const std::vector<const DexFile*>& dex_files,
- jobject class_loader);
+ jobject class_loader,
+ const char* class_loader_context);
// Wait for thread pool workers to be created. This is used during shutdown as
// threads are not allowed to attach while runtime is in shutdown lock.
@@ -118,6 +119,9 @@
// Wait for all background verification tasks to finish. This is only used by tests.
void WaitForBackgroundVerificationTasks();
+ // Maximum number of anonymous vdex files kept in the process' data folder.
+ static constexpr size_t kAnonymousVdexCacheSize = 8u;
+
private:
enum class CheckCollisionResult {
kSkippedUnsupportedClassLoader,
diff --git a/runtime/runtime.h b/runtime/runtime.h
index e811230..ff4755e 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -593,6 +593,18 @@
}
}
+ const std::string& GetProcessDataDirectory() const {
+ return process_data_directory_;
+ }
+
+ void SetProcessDataDirectory(const char* data_dir) {
+ if (data_dir == nullptr) {
+ process_data_directory_.clear();
+ } else {
+ process_data_directory_ = data_dir;
+ }
+ }
+
bool IsDexFileFallbackEnabled() const {
return allow_dex_file_fallback_;
}
@@ -1138,6 +1150,9 @@
// The package of the app running in this process.
std::string process_package_name_;
+ // The data directory of the app running in this process.
+ std::string process_data_directory_;
+
// Whether threads should dump their native stack on SIGQUIT.
bool dump_native_stack_on_sig_quit_;
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 61cd982..4b24769 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -17,6 +17,7 @@
#include "vdex_file.h"
#include <sys/mman.h> // For the PROT_* and MAP_* constants.
+#include <sys/stat.h> // for mkdir()
#include <memory>
#include <unordered_set>
@@ -27,12 +28,17 @@
#include "base/leb128.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
+#include "class_linker.h"
+#include "class_loader_context.h"
#include "dex/art_dex_file_loader.h"
#include "dex/class_accessor-inl.h"
-#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
#include "dex_to_dex_decompiler.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
#include "quicken_info.h"
+#include "runtime.h"
+#include "verifier/verifier_deps.h"
namespace art {
@@ -61,9 +67,13 @@
VdexFile::VerifierDepsHeader::VerifierDepsHeader(uint32_t number_of_dex_files,
uint32_t verifier_deps_size,
- bool has_dex_section)
+ bool has_dex_section,
+ uint32_t bootclasspath_checksums_size,
+ uint32_t class_loader_context_size)
: number_of_dex_files_(number_of_dex_files),
- verifier_deps_size_(verifier_deps_size) {
+ verifier_deps_size_(verifier_deps_size),
+ bootclasspath_checksums_size_(bootclasspath_checksums_size),
+ class_loader_context_size_(class_loader_context_size) {
memcpy(magic_, kVdexMagic, sizeof(kVdexMagic));
memcpy(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion));
if (has_dex_section) {
@@ -317,4 +327,100 @@
return GetQuickeningInfoAt(quickening_info, quickening_offset);
}
+static std::string ComputeBootClassPathChecksumString() {
+ Runtime* const runtime = Runtime::Current();
+ return gc::space::ImageSpace::GetBootClassPathChecksums(
+ runtime->GetHeap()->GetBootImageSpaces(),
+ runtime->GetClassLinker()->GetBootClassPath());
+}
+
+static bool CreateDirectories(const std::string& child_path, /* out */ std::string* error_msg) {
+ size_t last_slash_pos = child_path.find_last_of('/');
+ CHECK_NE(last_slash_pos, std::string::npos) << "Invalid path: " << child_path;
+ std::string parent_path = child_path.substr(0, last_slash_pos);
+ if (OS::DirectoryExists(parent_path.c_str())) {
+ return true;
+ } else if (CreateDirectories(parent_path, error_msg)) {
+ if (mkdir(parent_path.c_str(), 0700) == 0) {
+ return true;
+ }
+ *error_msg = "Could not create directory " + parent_path;
+ return false;
+ } else {
+ return false;
+ }
+}
+
+bool VdexFile::WriteToDisk(const std::string& path,
+ const std::vector<const DexFile*>& dex_files,
+ const verifier::VerifierDeps& verifier_deps,
+ const std::string& class_loader_context,
+ std::string* error_msg) {
+ std::vector<uint8_t> verifier_deps_data;
+ verifier_deps.Encode(dex_files, &verifier_deps_data);
+
+ std::string boot_checksum = ComputeBootClassPathChecksumString();
+ DCHECK_NE(boot_checksum, "");
+
+ VdexFile::VerifierDepsHeader deps_header(dex_files.size(),
+ verifier_deps_data.size(),
+ /* has_dex_section= */ false,
+ boot_checksum.size(),
+ class_loader_context.size());
+
+ if (!CreateDirectories(path, error_msg)) {
+ return false;
+ }
+
+ std::unique_ptr<File> out(OS::CreateEmptyFileWriteOnly(path.c_str()));
+ if (out == nullptr) {
+ *error_msg = "Could not open " + path + " for writing";
+ return false;
+ }
+
+ if (!out->WriteFully(reinterpret_cast<const char*>(&deps_header), sizeof(deps_header))) {
+ *error_msg = "Could not write vdex header to " + path;
+ out->Unlink();
+ return false;
+ }
+
+ for (const DexFile* dex_file : dex_files) {
+ const uint32_t* checksum_ptr = &dex_file->GetHeader().checksum_;
+ static_assert(sizeof(*checksum_ptr) == sizeof(VdexFile::VdexChecksum));
+ if (!out->WriteFully(reinterpret_cast<const char*>(checksum_ptr),
+ sizeof(VdexFile::VdexChecksum))) {
+ *error_msg = "Could not write dex checksums to " + path;
+ out->Unlink();
+ return false;
+ }
+ }
+
+ if (!out->WriteFully(reinterpret_cast<const char*>(verifier_deps_data.data()),
+ verifier_deps_data.size())) {
+ *error_msg = "Could not write verifier deps to " + path;
+ out->Unlink();
+ return false;
+ }
+
+ if (!out->WriteFully(boot_checksum.c_str(), boot_checksum.size())) {
+ *error_msg = "Could not write boot classpath checksum to " + path;
+ out->Unlink();
+ return false;
+ }
+
+ if (!out->WriteFully(class_loader_context.c_str(), class_loader_context.size())) {
+ *error_msg = "Could not write class loader context to " + path;
+ out->Unlink();
+ return false;
+ }
+
+ if (out->FlushClose() != 0) {
+ *error_msg = "Could not flush and close " + path;
+ out->Unlink();
+ return false;
+ }
+
+ return true;
+}
+
} // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index b357d95..88114fb 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -25,11 +25,16 @@
#include "base/mem_map.h"
#include "base/os.h"
#include "dex/compact_offset_table.h"
+#include "dex/dex_file.h"
#include "quicken_info.h"
namespace art {
-class DexFile;
+class ClassLoaderContext;
+
+namespace verifier {
+class VerifierDeps;
+} // namespace verifier
// VDEX files contain extracted DEX files. The VdexFile class maps the file to
// memory and provides tools for accessing its individual sections.
@@ -58,11 +63,16 @@
class VdexFile {
public:
+ using VdexChecksum = uint32_t;
+ using QuickeningTableOffsetType = uint32_t;
+
struct VerifierDepsHeader {
public:
VerifierDepsHeader(uint32_t number_of_dex_files_,
uint32_t verifier_deps_size,
- bool has_dex_section);
+ bool has_dex_section,
+ uint32_t bootclasspath_checksums_size = 0,
+ uint32_t class_loader_context_size = 0);
const char* GetMagic() const { return reinterpret_cast<const char*>(magic_); }
const char* GetVerifierDepsVersion() const {
@@ -81,19 +91,31 @@
uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; }
uint32_t GetNumberOfDexFiles() const { return number_of_dex_files_; }
+ uint32_t GetBootClassPathChecksumStringSize() const { return bootclasspath_checksums_size_; }
+ uint32_t GetClassLoaderContextStringSize() const { return class_loader_context_size_; }
size_t GetSizeOfChecksumsSection() const {
return sizeof(VdexChecksum) * GetNumberOfDexFiles();
}
+ const VdexChecksum* GetDexChecksumsArray() const {
+ return reinterpret_cast<const VdexChecksum*>(
+ reinterpret_cast<const uint8_t*>(this) + sizeof(VerifierDepsHeader));
+ }
+
+ VdexChecksum GetDexChecksumAtOffset(size_t idx) const {
+ DCHECK_LT(idx, GetNumberOfDexFiles());
+ return GetDexChecksumsArray()[idx];
+ }
+
static constexpr uint8_t kVdexInvalidMagic[] = { 'w', 'd', 'e', 'x' };
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
// The format version of the verifier deps header and the verifier deps.
- // Last update: Add `redefined_classes_`.
- static constexpr uint8_t kVerifierDepsVersion[] = { '0', '2', '0', '\0' };
+ // Last update: Add boot checksum, class loader context.
+ static constexpr uint8_t kVerifierDepsVersion[] = { '0', '2', '1', '\0' };
// The format version of the dex section header and the dex section, containing
// both the dex code and the quickening data.
@@ -109,6 +131,8 @@
uint8_t dex_section_version_[4];
uint32_t number_of_dex_files_;
uint32_t verifier_deps_size_;
+ uint32_t bootclasspath_checksums_size_;
+ uint32_t class_loader_context_size_;
};
struct DexSectionHeader {
@@ -132,7 +156,7 @@
uint32_t dex_shared_data_size_;
uint32_t quickening_info_size_;
- friend class VdexFile; // For updatig quickening_info_size_.
+ friend class VdexFile; // For updating quickening_info_size_.
};
size_t GetComputedFileSize() const {
@@ -144,15 +168,14 @@
size += GetDexSectionHeader().GetDexSectionSize();
size += GetDexSectionHeader().GetQuickeningInfoSize();
}
+ size += header.GetBootClassPathChecksumStringSize();
+ size += header.GetClassLoaderContextStringSize();
return size;
}
// Note: The file is called "primary" to match the naming with profiles.
static const constexpr char* kVdexNameInDmFile = "primary.vdex";
- typedef uint32_t VdexChecksum;
- using QuickeningTableOffsetType = uint32_t;
-
explicit VdexFile(MemMap&& mmap) : mmap_(std::move(mmap)) {}
// Returns nullptr if the vdex file cannot be opened or is not valid.
@@ -250,13 +273,22 @@
}
ArrayRef<const uint8_t> GetQuickeningInfo() const {
- if (GetVerifierDepsHeader().HasDexSection()) {
- return ArrayRef<const uint8_t>(
- GetVerifierDepsData().data() + GetVerifierDepsHeader().GetVerifierDepsSize(),
- GetDexSectionHeader().GetQuickeningInfoSize());
- } else {
- return ArrayRef<const uint8_t>();
- }
+ return ArrayRef<const uint8_t>(
+ GetVerifierDepsData().end(),
+ GetVerifierDepsHeader().HasDexSection()
+ ? GetDexSectionHeader().GetQuickeningInfoSize() : 0);
+ }
+
+ ArrayRef<const uint8_t> GetBootClassPathChecksumData() const {
+ return ArrayRef<const uint8_t>(
+ GetQuickeningInfo().end(),
+ GetVerifierDepsHeader().GetBootClassPathChecksumStringSize());
+ }
+
+ ArrayRef<const uint8_t> GetClassLoaderContextData() const {
+ return ArrayRef<const uint8_t>(
+ GetBootClassPathChecksumData().end(),
+ GetVerifierDepsHeader().GetClassLoaderContextStringSize());
}
bool IsValid() const {
@@ -300,6 +332,16 @@
return GetVerifierDepsHeader().HasDexSection();
}
+ // Writes a vdex into `path` and returns true on success.
+ // The vdex will not contain a dex section but will store checksums of `dex_files`,
+ // encoded `verifier_deps`, as well as the current boot class path cheksum and
+ // encoded `class_loader_context`.
+ static bool WriteToDisk(const std::string& path,
+ const std::vector<const DexFile*>& dex_files,
+ const verifier::VerifierDeps& verifier_deps,
+ const std::string& class_loader_context,
+ std::string* error_msg);
+
private:
uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const;
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index bdffda6..b45f143 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -525,18 +525,20 @@
void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file,
const dex::ClassDef& class_def,
FailureKind failure_kind) {
- if (failure_kind != FailureKind::kNoFailure) {
- // The `verified_classes_` bit vector is initialized to `false`.
- // Only continue if we are about to write `true`.
- return;
+ // The `verified_classes_` bit vector is initialized to `false`.
+ // Only continue if we are about to write `true`.
+ if (failure_kind == FailureKind::kNoFailure) {
+ VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
+ if (thread_deps != nullptr) {
+ thread_deps->RecordClassVerified(dex_file, class_def);
+ }
}
+}
- VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
- if (thread_deps != nullptr) {
- DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file);
- DCHECK_EQ(dex_deps->verified_classes_.size(), dex_file.NumClassDefs());
- dex_deps->verified_classes_[dex_file.GetIndexForClassDef(class_def)] = true;
- }
+void VerifierDeps::RecordClassVerified(const DexFile& dex_file, const dex::ClassDef& class_def) {
+ DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
+ DCHECK_EQ(dex_deps->verified_classes_.size(), dex_file.NumClassDefs());
+ dex_deps->verified_classes_[dex_file.GetIndexForClassDef(class_def)] = true;
}
void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file,
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index bf9cdc7..5002db0 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -66,6 +66,12 @@
// same set of dex files.
void MergeWith(std::unique_ptr<VerifierDeps> other, const std::vector<const DexFile*>& dex_files);
+ // Record information that a class was verified.
+ // Note that this function is different from MaybeRecordVerificationStatus() which
+ // looks up thread-local VerifierDeps first.
+ void RecordClassVerified(const DexFile& dex_file, const dex::ClassDef& class_def)
+ REQUIRES(!Locks::verifier_deps_lock_);
+
// Record the verification status of the class defined in `class_def`.
static void MaybeRecordVerificationStatus(const DexFile& dex_file,
const dex::ClassDef& class_def,