Preverify InMemoryDexClassLoader-loaded classes from vdex

This patch creates a new subclass of OatFile - OatFileBackedByVdex -
which initializes OatDexClass instances using the verification info in
a vdex file created by a previous instance of the class loader.
The OatFile is not backed by an actual .oat file.

Bug: 72131483
Test: art/tools/run-libcore-tests.sh
Test: art/test.py -b -r -t 692 -t 693
Change-Id: I3fd055abe17ee9739c07f2e2f4fc2543e4ec8c9e
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index ae1eea5..eee8cfc 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -165,6 +165,37 @@
   void operator=(const NullableScopedUtfChars&);
 };
 
+static jobject CreateCookieFromOatFileManagerResult(
+    JNIEnv* env,
+    std::vector<std::unique_ptr<const DexFile>>& dex_files,
+    const OatFile* oat_file,
+    const std::vector<std::string>& error_msgs) {
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  if (dex_files.empty()) {
+    ScopedObjectAccess soa(env);
+    CHECK(!error_msgs.empty());
+    // The most important message is at the end. So set up nesting by going forward, which will
+    // wrap the existing exception as a cause for the following one.
+    auto it = error_msgs.begin();
+    auto itEnd = error_msgs.end();
+    for ( ; it != itEnd; ++it) {
+      ThrowWrappedIOException("%s", it->c_str());
+    }
+    return nullptr;
+  }
+
+  jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
+  if (array == nullptr) {
+    ScopedObjectAccess soa(env);
+    for (auto& dex_file : dex_files) {
+      if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
+        dex_file.release();  // NOLINT
+      }
+    }
+  }
+  return array;
+}
+
 static MemMap AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
   if (end <= start) {
     ScopedObjectAccess soa(env);
@@ -187,33 +218,6 @@
   return dex_mem_map;
 }
 
-static const DexFile* CreateDexFile(JNIEnv* env, MemMap&& dex_mem_map) {
-  std::string location = StringPrintf("Anonymous-DexFile@%p-%p",
-                                      dex_mem_map.Begin(),
-                                      dex_mem_map.End());
-  std::string error_message;
-  const ArtDexFileLoader dex_file_loader;
-  std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location,
-                                                               0,
-                                                               std::move(dex_mem_map),
-                                                               /* verify= */ true,
-                                                               /* verify_checksum= */ true,
-                                                               &error_message));
-  if (dex_file == nullptr) {
-    ScopedObjectAccess soa(env);
-    ThrowWrappedIOException("%s", error_message.c_str());
-    return nullptr;
-  }
-
-  if (!dex_file->DisableWrite()) {
-    ScopedObjectAccess soa(env);
-    ThrowWrappedIOException("Failed to make dex file read-only");
-    return nullptr;
-  }
-
-  return dex_file.release();
-}
-
 struct ScopedIntArrayAccessor {
  public:
   ScopedIntArrayAccessor(JNIEnv* env, jintArray arr) : env_(env), array_(arr) {
@@ -238,7 +242,9 @@
                                                   jobjectArray buffers,
                                                   jobjectArray arrays,
                                                   jintArray jstarts,
-                                                  jintArray jends) {
+                                                  jintArray jends,
+                                                  jobject class_loader,
+                                                  jobjectArray dex_elements) {
   jsize buffers_length = env->GetArrayLength(buffers);
   CHECK_EQ(buffers_length, env->GetArrayLength(arrays));
   CHECK_EQ(buffers_length, env->GetArrayLength(jstarts));
@@ -248,8 +254,8 @@
   ScopedIntArrayAccessor ends(env, jends);
 
   // Allocate memory for dex files and copy data from ByteBuffers.
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  dex_files.reserve(buffers_length);
+  std::vector<MemMap> dex_mem_maps;
+  dex_mem_maps.reserve(buffers_length);
   for (jsize i = 0; i < buffers_length; ++i) {
     jobject buffer = env->GetObjectArrayElement(buffers, i);
     jbyteArray array = reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(arrays, i));
@@ -278,15 +284,20 @@
       env->GetByteArrayRegion(array, start, end - start, destination);
     }
 
-    std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(dex_data)));
-    if (dex_file == nullptr) {
-      DCHECK(env->ExceptionCheck());
-      return nullptr;
-    }
-    dex_files.push_back(std::move(dex_file));
+    dex_mem_maps.push_back(std::move(dex_data));
   }
 
-  return ConvertDexFilesToJavaArray(env, /* oat_file= */ nullptr, dex_files);
+  // Hand MemMaps over to OatFileManager to open the dex files and potentially
+  // create a backing OatFile instance from an anonymous vdex.
+  std::vector<std::string> error_msgs;
+  const OatFile* oat_file = nullptr;
+  std::vector<std::unique_ptr<const DexFile>> dex_files =
+      Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps),
+                                                                  class_loader,
+                                                                  dex_elements,
+                                                                  /*out*/ &oat_file,
+                                                                  /*out*/ &error_msgs);
+  return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
 }
 
 // TODO(calin): clean up the unused parameters (here and in libcore).
@@ -302,42 +313,15 @@
     return nullptr;
   }
 
-  Runtime* const runtime = Runtime::Current();
-  ClassLinker* linker = runtime->GetClassLinker();
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
   std::vector<std::string> error_msgs;
   const OatFile* oat_file = nullptr;
-
-  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
-                                                               class_loader,
-                                                               dex_elements,
-                                                               /*out*/ &oat_file,
-                                                               /*out*/ &error_msgs);
-
-  if (!dex_files.empty()) {
-    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
-    if (array == nullptr) {
-      ScopedObjectAccess soa(env);
-      for (auto& dex_file : dex_files) {
-        if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
-          dex_file.release();  // NOLINT
-        }
-      }
-    }
-    return array;
-  } else {
-    ScopedObjectAccess soa(env);
-    CHECK(!error_msgs.empty());
-    // The most important message is at the end. So set up nesting by going forward, which will
-    // wrap the existing exception as a cause for the following one.
-    auto it = error_msgs.begin();
-    auto itEnd = error_msgs.end();
-    for ( ; it != itEnd; ++it) {
-      ThrowWrappedIOException("%s", it->c_str());
-    }
-
-    return nullptr;
-  }
+  std::vector<std::unique_ptr<const DexFile>> dex_files =
+      Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
+                                                                  class_loader,
+                                                                  dex_elements,
+                                                                  /*out*/ &oat_file,
+                                                                  /*out*/ &error_msgs);
+  return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
 }
 
 static jstring DexFile_getClassLoaderContext(JNIEnv* env,
@@ -938,6 +922,8 @@
                 "[[B"
                 "[I"
                 "[I"
+                "Ljava/lang/ClassLoader;"
+                "[Ldalvik/system/DexPathList$Element;"
                 ")Ljava/lang/Object;"),
   NATIVE_METHOD(DexFile, getClassLoaderContext,
                 "(Ljava/lang/ClassLoader;"
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 98a09e1..624a1de 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -36,6 +36,7 @@
 #include <android-base/logging.h>
 #include "android-base/stringprintf.h"
 
+#include "arch/instruction_set_features.h"
 #include "art_method.h"
 #include "base/bit_vector.h"
 #include "base/enums.h"
@@ -67,6 +68,7 @@
 #include "oat_file_manager.h"
 #include "runtime-inl.h"
 #include "vdex_file.h"
+#include "verifier/verifier_deps.h"
 
 namespace art {
 
@@ -163,6 +165,7 @@
   virtual void PreSetup(const std::string& elf_filename) = 0;
 
   bool Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg);
+  bool Setup(const std::vector<const DexFile*>& dex_files);
 
   // Setters exposed for ElfOatFile.
 
@@ -448,6 +451,27 @@
   return true;
 }
 
+bool 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);
+    oat_dex_files_storage_.push_back(oat_dex_file);
+
+    // Add the location and canonical location (if different) to the oat_dex_files_ table.
+    std::string_view key(oat_dex_file->GetDexFileLocation());
+    oat_dex_files_.Put(key, oat_dex_file);
+    if (canonical_location != dex_location) {
+      std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
+      oat_dex_files_.Put(canonical_key, oat_dex_file);
+    }
+  }
+
+  return true;
+}
+
 bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -1375,6 +1399,94 @@
   return loaded;
 }
 
+class OatFileBackedByVdex final : public OatFileBase {
+ public:
+  explicit OatFileBackedByVdex(const std::string& filename)
+      : OatFileBase(filename, /*executable=*/ false) {}
+
+  static OatFileBackedByVdex* Open(const std::vector<const DexFile*>& dex_files,
+                                   std::unique_ptr<VdexFile>&& vdex_file,
+                                   const std::string& location) {
+    std::unique_ptr<OatFileBackedByVdex> oat_file(new OatFileBackedByVdex(location));
+    oat_file->Initialize(dex_files, std::move(vdex_file));
+    return oat_file.release();
+  }
+
+  void Initialize(const std::vector<const DexFile*>& dex_files,
+                  std::unique_ptr<VdexFile>&& vdex_file) {
+    DCHECK(!IsExecutable());
+
+    // SetVdex will take ownership of the VdexFile.
+    SetVdex(vdex_file.release());
+
+    // Create a dummy OatHeader.
+    std::unique_ptr<const InstructionSetFeatures> isa_features =
+        InstructionSetFeatures::FromCppDefines();
+    oat_header_.reset(OatHeader::Create(kRuntimeISA,
+                                        isa_features.get(),
+                                        dex_files.size(),
+                                        nullptr));
+    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();
+    verified_classes_per_dex_ = verifier::VerifierDeps::ParseVerifiedClasses(dex_files, deps_data);
+
+    // Initialize OatDexFiles.
+    Setup(dex_files);
+  }
+
+  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:
+  void PreLoad() override {}
+
+  bool Load(const std::string& elf_filename ATTRIBUTE_UNUSED,
+            bool writable ATTRIBUTE_UNUSED,
+            bool executable ATTRIBUTE_UNUSED,
+            bool low_4gb ATTRIBUTE_UNUSED,
+            MemMap* reservation ATTRIBUTE_UNUSED,
+            std::string* error_msg ATTRIBUTE_UNUSED) override {
+    LOG(FATAL) << "Unsupported";
+    UNREACHABLE();
+  }
+
+  bool Load(int oat_fd ATTRIBUTE_UNUSED,
+            bool writable ATTRIBUTE_UNUSED,
+            bool executable ATTRIBUTE_UNUSED,
+            bool low_4gb ATTRIBUTE_UNUSED,
+            MemMap* reservation ATTRIBUTE_UNUSED,
+            std::string* error_msg ATTRIBUTE_UNUSED) override {
+    LOG(FATAL) << "Unsupported";
+    UNREACHABLE();
+  }
+
+  void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) override {}
+
+  const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name ATTRIBUTE_UNUSED,
+                                          std::string* error_msg) const override {
+    *error_msg = "Unsupported";
+    return nullptr;
+  }
+
+ private:
+  std::unique_ptr<OatHeader> oat_header_;
+  std::vector<std::vector<bool>> verified_classes_per_dex_;
+
+  DISALLOW_COPY_AND_ASSIGN(OatFileBackedByVdex);
+};
+
 //////////////////////////
 // General OatFile code //
 //////////////////////////
@@ -1548,6 +1660,13 @@
                                  error_msg);
 }
 
+OatFile* OatFile::OpenFromVdex(const std::vector<const DexFile*>& dex_files,
+                               std::unique_ptr<VdexFile>&& vdex_file,
+                               const std::string& location) {
+  CheckLocation(location);
+  return OatFileBackedByVdex::Open(dex_files, std::move(vdex_file), location);
+}
+
 OatFile::OatFile(const std::string& location, bool is_executable)
     : location_(location),
       vdex_(nullptr),
@@ -1731,6 +1850,20 @@
       lookup_table_ = TypeLookupTable::Open(dex_data, lookup_table_data_, num_class_defs);
     }
   }
+  DCHECK(!IsBackedByVdexOnly());
+}
+
+OatDexFile::OatDexFile(const OatFile* oat_file,
+                       const DexFile* dex_file,
+                       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);
+  DCHECK(IsBackedByVdexOnly());
 }
 
 OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {
@@ -1765,7 +1898,24 @@
   return oat_class_offsets_pointer_[class_def_index];
 }
 
+bool OatDexFile::IsBackedByVdexOnly() const {
+  return oat_class_offsets_pointer_ == nullptr;
+}
+
 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);
+    return OatFile::OatClass(oat_file_,
+                             is_vdex_verified ? ClassStatus::kVerified : ClassStatus::kNotReady,
+                             /* type= */ kOatClassNoneCompiled,
+                             /* bitmap_size= */ 0u,
+                             /* bitmap_pointer= */ nullptr,
+                             /* methods_pointer= */ nullptr);
+  }
+
   uint32_t oat_class_offset = GetOatClassOffset(class_def_index);
 
   const uint8_t* oat_class_pointer = oat_file_->Begin() + oat_class_offset;
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 78ec969..fbe596e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -124,6 +124,12 @@
                                const char* abs_dex_location,
                                std::string* error_msg);
 
+  // Initialize OatFile instance from an already loaded VdexFile. This assumes
+  // the vdex does not have a dex section and accepts a vector of DexFiles separately.
+  static OatFile* OpenFromVdex(const std::vector<const DexFile*>& dex_files,
+                               std::unique_ptr<VdexFile>&& vdex_file,
+                               const std::string& location);
+
   virtual ~OatFile();
 
   bool IsExecutable() const {
@@ -545,6 +551,15 @@
              const uint32_t* oat_class_offsets_pointer,
              const DexLayoutSections* dex_layout_sections);
 
+  // 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 std::string& dex_file_location,
+             const std::string& canonical_dex_file_location);
+
+  bool IsBackedByVdexOnly() const;
+
   static void AssertAotCompiler();
 
   const OatFile* const oat_file_ = nullptr;
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index f9823a1..92acdd0 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -647,6 +647,16 @@
   return dex_files;
 }
 
+static std::vector<const DexFile::Header*> GetDexFileHeaders(const std::vector<MemMap>& maps) {
+  std::vector<const DexFile::Header*> headers;
+  headers.reserve(maps.size());
+  for (const MemMap& map : maps) {
+    DCHECK(map.IsValid());
+    headers.push_back(reinterpret_cast<const DexFile::Header*>(map.Begin()));
+  }
+  return headers;
+}
+
 static std::vector<const DexFile::Header*> GetDexFileHeaders(
     const std::vector<const DexFile*>& dex_files) {
   std::vector<const DexFile::Header*> headers;
@@ -657,6 +667,134 @@
   return headers;
 }
 
+std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
+    std::vector<MemMap>&& dex_mem_maps,
+    jobject class_loader,
+    jobjectArray dex_elements,
+    const OatFile** out_oat_file,
+    std::vector<std::string>* error_msgs) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenDexFilesFromOat_Impl(
+      std::move(dex_mem_maps),
+      class_loader,
+      dex_elements,
+      out_oat_file,
+      error_msgs);
+
+  if (error_msgs->empty()) {
+    // Remove write permission from DexFile pages. We do this at the end because
+    // OatFile assigns OatDexFile pointer in the DexFile objects.
+    for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+      if (!dex_file->DisableWrite()) {
+        error_msgs->push_back("Failed to make dex file " + dex_file->GetLocation() + " read-only");
+      }
+    }
+  }
+
+  if (!error_msgs->empty()) {
+    return std::vector<std::unique_ptr<const DexFile>>();
+  }
+
+  return dex_files;
+}
+
+std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat_Impl(
+    std::vector<MemMap>&& dex_mem_maps,
+    jobject class_loader,
+    jobjectArray dex_elements,
+    const OatFile** out_oat_file,
+    std::vector<std::string>* error_msgs) {
+  ScopedTrace trace(__FUNCTION__);
+  std::string error_msg;
+  DCHECK(error_msgs != nullptr);
+
+  // Extract dex file headers from `dex_mem_maps`.
+  const std::vector<const DexFile::Header*> dex_headers = GetDexFileHeaders(dex_mem_maps);
+
+  // Determine dex/vdex locations and the combined location checksum.
+  uint32_t location_checksum;
+  std::string dex_location;
+  std::string vdex_path;
+  bool has_vdex = OatFileAssistant::AnonymousDexVdexLocation(dex_headers,
+                                                             kRuntimeISA,
+                                                             &location_checksum,
+                                                             &dex_location,
+                                                             &vdex_path);
+
+  // Attempt to open an existing vdex and check dex file checksums match.
+  std::unique_ptr<VdexFile> vdex_file = nullptr;
+  if (has_vdex && OS::FileExists(vdex_path.c_str())) {
+    vdex_file = VdexFile::Open(vdex_path,
+                               /* writable= */ false,
+                               /* low_4gb= */ false,
+                               /* unquicken= */ false,
+                               &error_msg);
+    if (vdex_file == nullptr) {
+      LOG(WARNING) << "Failed to open vdex " << vdex_path << ": " << error_msg;
+    } else if (!vdex_file->MatchesDexFileChecksums(dex_headers)) {
+      LOG(WARNING) << "Failed to open vdex " << vdex_path << ": dex file checksum mismatch";
+      vdex_file.reset(nullptr);
+    }
+  }
+
+  // Load dex files. Skip structural dex file verification if vdex was found
+  // and dex checksums matched.
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  for (size_t i = 0; i < dex_mem_maps.size(); ++i) {
+    static constexpr bool kVerifyChecksum = true;
+    const ArtDexFileLoader dex_file_loader;
+    std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
+        DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()),
+        location_checksum,
+        std::move(dex_mem_maps[i]),
+        /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
+        kVerifyChecksum,
+        &error_msg));
+    if (dex_file != nullptr) {
+      dex::tracking::RegisterDexFile(dex_file.get());  // Register for tracking.
+      dex_files.push_back(std::move(dex_file));
+    } else {
+      error_msgs->push_back("Failed to open dex files from memory: " + error_msg);
+    }
+  }
+
+  // Check if we should proceed to creating an OatFile instance backed by the vdex.
+  // We need: (a) an existing vdex, (b) class loader (can be null if invoked via reflection),
+  // and (c) no errors during dex file loading.
+  if (vdex_file == nullptr || class_loader == nullptr || !error_msgs->empty()) {
+    return dex_files;
+  }
+
+  // Attempt to create a class loader context, check OpenDexFiles succeeds (prerequisite
+  // for using the context later).
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::CreateContextForClassLoader(
+      class_loader,
+      dex_elements);
+  if (context == nullptr) {
+    LOG(ERROR) << "Could not create class loader context for " << vdex_path;
+    return dex_files;
+  }
+  DCHECK(context->OpenDexFiles(kRuntimeISA, ""))
+      << "Context created from already opened dex files should not attempt to open again";
+
+  // Check that we can use the vdex against this boot class path and in this class loader context.
+  // Note 1: We do not need a class loader collision check because there is no compiled code.
+  // Note 2: If these checks fail, we cannot fast-verify because the vdex does not contain
+  //         full VerifierDeps.
+  if (!vdex_file->MatchesBootClassPathChecksums() ||
+      !vdex_file->MatchesClassLoaderContext(*context.get())) {
+    return dex_files;
+  }
+
+  // Initialize an OatFile instance backed by the loaded vdex.
+  std::unique_ptr<OatFile> oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files),
+                                                          std::move(vdex_file),
+                                                          dex_location));
+  DCHECK(oat_file != nullptr);
+  VLOG(class_linker) << "Registering " << oat_file->GetLocation();
+  *out_oat_file = RegisterOatFile(std::move(oat_file));
+  return dex_files;
+}
+
 // 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).
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index e5eae9f..d09b6d6 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -37,6 +37,7 @@
 
 class ClassLoaderContext;
 class DexFile;
+class MemMap;
 class OatFile;
 class ThreadPool;
 
@@ -100,6 +101,23 @@
       /*out*/ std::vector<std::string>* error_msgs)
       REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_);
 
+  // Opens dex files provided in `dex_mem_maps` and attempts to find an anonymous
+  // vdex file created during a previous load attempt. If found, will initialize
+  // an instance of OatFile to back the DexFiles and preverify them using the
+  // vdex's VerifierDeps.
+  //
+  // Returns an empty vector if the dex files could not be loaded. In this
+  // case, there will be at least one error message returned describing why no
+  // dex files could not be loaded. The 'error_msgs' argument must not be
+  // null, regardless of whether there is an error or not.
+  std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
+      std::vector<MemMap>&& dex_mem_maps,
+      jobject class_loader,
+      jobjectArray dex_elements,
+      /*out*/ const OatFile** out_oat_file,
+      /*out*/ std::vector<std::string>* error_msgs)
+      REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_);
+
   void DumpForSigQuit(std::ostream& os);
 
   void SetOnlyUseSystemOatFiles(bool enforce, bool assert_no_files_loaded);
@@ -130,6 +148,14 @@
     kPerformedHasCollisions,
   };
 
+  std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat_Impl(
+      std::vector<MemMap>&& dex_mem_maps,
+      jobject class_loader,
+      jobjectArray dex_elements,
+      /*out*/ const OatFile** out_oat_file,
+      /*out*/ std::vector<std::string>* error_msgs)
+      REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_);
+
   // Check that the class loader context of the given oat file matches the given context.
   // This will perform a check that all class loaders in the chain have the same type and
   // classpath.
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 4b24769..cd60fab 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -423,4 +423,48 @@
   return true;
 }
 
+bool VdexFile::MatchesDexFileChecksums(const std::vector<const DexFile::Header*>& dex_headers)
+    const {
+  const VerifierDepsHeader& header = GetVerifierDepsHeader();
+  if (dex_headers.size() != header.GetNumberOfDexFiles()) {
+    LOG(WARNING) << "Mismatch of number of dex files in vdex (expected="
+        << header.GetNumberOfDexFiles() << ", actual=" << dex_headers.size() << ")";
+    return false;
+  }
+  const VdexChecksum* checksums = header.GetDexChecksumsArray();
+  for (size_t i = 0; i < dex_headers.size(); ++i) {
+    if (checksums[i] != dex_headers[i]->checksum_) {
+      LOG(WARNING) << "Mismatch of dex file checksum in vdex (index=" << i << ")";
+      return false;
+    }
+  }
+  return true;
+}
+
+bool VdexFile::MatchesBootClassPathChecksums() const {
+  ArrayRef<const uint8_t> data = GetBootClassPathChecksumData();
+  std::string vdex(reinterpret_cast<const char*>(data.data()), data.size());
+  std::string runtime = ComputeBootClassPathChecksumString();
+  if (vdex == runtime) {
+    return true;
+  } else {
+    LOG(WARNING) << "Mismatch of boot class path checksum in vdex (expected="
+        << vdex << ", actual=" << runtime << ")";
+    return false;
+  }
+}
+
+bool VdexFile::MatchesClassLoaderContext(const ClassLoaderContext& context) const {
+  ArrayRef<const uint8_t> data = GetClassLoaderContextData();
+  std::string spec(reinterpret_cast<const char*>(data.data()), data.size());
+  ClassLoaderContext::VerificationResult result = context.VerifyClassLoaderContextMatch(spec);
+  if (result != ClassLoaderContext::VerificationResult::kMismatch) {
+    return true;
+  } else {
+    LOG(WARNING) << "Mismatch of class loader context in vdex (expected="
+        << spec << ", actual=" << context.EncodeContextForOatFile("") << ")";
+    return false;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 88114fb..102b73f 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -342,6 +342,18 @@
                           const std::string& class_loader_context,
                           std::string* error_msg);
 
+  // Returns true if the dex file checksums stored in the vdex header match
+  // the checksums in `dex_headers`. Both the number of dex files and their
+  // order must match too.
+  bool MatchesDexFileChecksums(const std::vector<const DexFile::Header*>& dex_headers) const;
+
+  // Returns true if the boot class path checksum stored in the vdex matches
+  // the checksum of boot class path in the current runtime.
+  bool MatchesBootClassPathChecksums() const;
+
+  // Returns true if the class loader context stored in the vdex matches `context`.
+  bool MatchesClassLoaderContext(const ClassLoaderContext& context) const;
+
  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 b45f143..ed5488c 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -737,6 +737,25 @@
   }
 }
 
+void VerifierDeps::DecodeDexFileDeps(DexFileDeps& deps,
+                                     const uint8_t** data_start,
+                                     const uint8_t* data_end) {
+  DecodeStringVector(data_start, data_end, &deps.strings_);
+  DecodeSet(data_start, data_end, &deps.assignable_types_);
+  DecodeSet(data_start, data_end, &deps.unassignable_types_);
+  DecodeSet(data_start, data_end, &deps.classes_);
+  DecodeSet(data_start, data_end, &deps.fields_);
+  DecodeSet(data_start, data_end, &deps.methods_);
+  DecodeUint16SparseBitVector(data_start,
+                              data_end,
+                              &deps.verified_classes_,
+                              /* sparse_value= */ false);
+  DecodeUint16SparseBitVector(data_start,
+                              data_end,
+                              &deps.redefined_classes_,
+                              /* sparse_value= */ true);
+}
+
 VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
                            ArrayRef<const uint8_t> data)
     : VerifierDeps(dex_files, /*output_only=*/ false) {
@@ -750,24 +769,30 @@
   const uint8_t* data_end = data_start + data.size();
   for (const DexFile* dex_file : dex_files) {
     DexFileDeps* deps = GetDexFileDeps(*dex_file);
-    DecodeStringVector(&data_start, data_end, &deps->strings_);
-    DecodeSet(&data_start, data_end, &deps->assignable_types_);
-    DecodeSet(&data_start, data_end, &deps->unassignable_types_);
-    DecodeSet(&data_start, data_end, &deps->classes_);
-    DecodeSet(&data_start, data_end, &deps->fields_);
-    DecodeSet(&data_start, data_end, &deps->methods_);
-    DecodeUint16SparseBitVector(&data_start,
-                                data_end,
-                                &deps->verified_classes_,
-                                /* sparse_value= */ false);
-    DecodeUint16SparseBitVector(&data_start,
-                                data_end,
-                                &deps->redefined_classes_,
-                                /* sparse_value= */ true);
+    DecodeDexFileDeps(*deps, &data_start, data_end);
   }
   CHECK_LE(data_start, data_end);
 }
 
+std::vector<std::vector<bool>> VerifierDeps::ParseVerifiedClasses(
+    const std::vector<const DexFile*>& dex_files,
+    ArrayRef<const uint8_t> data) {
+  DCHECK(!data.empty());
+  DCHECK(!dex_files.empty());
+
+  std::vector<std::vector<bool>> verified_classes_per_dex;
+  verified_classes_per_dex.reserve(dex_files.size());
+
+  const uint8_t* data_start = data.data();
+  const uint8_t* data_end = data_start + data.size();
+  for (const DexFile* dex_file : dex_files) {
+    DexFileDeps deps(dex_file->NumClassDefs());
+    DecodeDexFileDeps(deps, &data_start, data_end);
+    verified_classes_per_dex.push_back(std::move(deps.verified_classes_));
+  }
+  return verified_classes_per_dex;
+}
+
 bool VerifierDeps::Equals(const VerifierDeps& rhs) const {
   if (dex_deps_.size() != rhs.dex_deps_.size()) {
     return false;
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 5002db0..3980ca2 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -144,6 +144,13 @@
     return output_only_;
   }
 
+  // Parses raw VerifierDeps data to extract bitvectors of which class def indices
+  // were verified or not. The given `dex_files` must match the order and count of
+  // dex files used to create the VerifierDeps.
+  static std::vector<std::vector<bool>> ParseVerifiedClasses(
+      const std::vector<const DexFile*>& dex_files,
+      ArrayRef<const uint8_t> data);
+
  private:
   static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
 
@@ -234,6 +241,11 @@
 
   VerifierDeps(const std::vector<const DexFile*>& dex_files, bool output_only);
 
+  // Helper function to share DexFileDeps decoding code.
+  static void DecodeDexFileDeps(DexFileDeps& deps,
+                                const uint8_t** data_start,
+                                const uint8_t* data_end);
+
   // Finds the DexFileDep instance associated with `dex_file`, or nullptr if
   // `dex_file` is not reported as being compiled.
   DexFileDeps* GetDexFileDeps(const DexFile& dex_file);
diff --git a/test/692-vdex-inmem-loader/expected.txt b/test/692-vdex-inmem-loader/expected.txt
index a127604..889d7d1 100644
--- a/test/692-vdex-inmem-loader/expected.txt
+++ b/test/692-vdex-inmem-loader/expected.txt
@@ -2,3 +2,7 @@
 Hello
 Hello
 Hello
+Hello
+Hello
+Hello
+Hello
diff --git a/test/692-vdex-inmem-loader/src/Main.java b/test/692-vdex-inmem-loader/src/Main.java
index 75aef8b..4d21f5b 100644
--- a/test/692-vdex-inmem-loader/src/Main.java
+++ b/test/692-vdex-inmem-loader/src/Main.java
@@ -42,13 +42,18 @@
 
   private static void test(ClassLoader loader,
                            boolean expectedHasVdexFile,
+                           boolean expectedBackedByOat,
                            boolean invokeMethod) throws Exception {
     // If ART created a vdex file, it must have verified all the classes.
-    boolean expectedClassesVerified = expectedHasVdexFile;
+    // That happens if and only if we expect a vdex at the end of the test but
+    // do not expect it to have been loaded.
+    boolean expectedClassesVerified = expectedHasVdexFile && !expectedBackedByOat;
 
     waitForVerifier();
     check(expectedClassesVerified, areClassesVerified(loader), "areClassesVerified");
     check(expectedHasVdexFile, hasVdexFile(loader), "areClassesVerified");
+    check(expectedBackedByOat, isBackedByOatFile(loader), "isBackedByOatFile");
+    check(expectedBackedByOat, areClassesPreverified(loader), "areClassesPreverified");
 
     if (invokeMethod) {
       loader.loadClass("art.ClassB").getDeclaredMethod("printHello").invoke(null);
@@ -58,24 +63,47 @@
   public static void main(String[] args) throws Exception {
     System.loadLibrary(args[0]);
     ClassLoader[] loaders = null;
+
+    // Feature is disabled in debuggable mode because runtime threads are not
+    // allowed to load classes.
     boolean featureEnabled = !isDebuggable();
 
     // Data directory not set. Background verification job should not have run
     // and vdex should not have been created.
-    test(singleLoader(), /*hasVdex*/ false, /*invokeMethod*/ true);
+    test(singleLoader(), /*hasVdex*/ false, /*backedByOat*/ false, /*invokeMethod*/ true);
 
     // Set data directory for this process.
     setProcessDataDir(DEX_LOCATION);
 
     // Data directory is now set. Background verification job should have run,
     // should have verified classes and written results to a vdex.
-    test(singleLoader(), /*hasVdex*/ featureEnabled, /*invokeMethod*/ true);
+    test(singleLoader(), /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true);
+    test(singleLoader(), /*hasVdex*/ featureEnabled, /*backedByOat*/ true, /*invokeMethod*/ true);
 
     // Test loading the two dex files with separate class loaders.
     // Background verification task should still verify all classes.
     loaders = multiLoader();
-    test(loaders[0], /*hasVdex*/ featureEnabled, /*invokeMethod*/ false);
-    test(loaders[1], /*hasVdex*/ featureEnabled, /*invokeMethod*/ true);
+    test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ false);
+    test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true);
+
+    loaders = multiLoader();
+    test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled,
+        /*invokeMethod*/ false);
+    test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled,
+        /*invokeMethod*/ true);
+
+    // Change boot classpath checksum.
+    appendToBootClassLoader(DEX_EXTRA, /*isCorePlatform*/ false);
+
+    loaders = multiLoader();
+    test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ false);
+    test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true);
+
+    loaders = multiLoader();
+    test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled,
+        /*invokeMethod*/ false);
+    test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled,
+        /*invokeMethod*/ true);
   }
 
   private static native boolean isDebuggable();
@@ -83,6 +111,8 @@
   private static native void waitForVerifier();
   private static native boolean areClassesVerified(ClassLoader loader);
   private static native boolean hasVdexFile(ClassLoader loader);
+  private static native boolean isBackedByOatFile(ClassLoader loader);
+  private static native boolean areClassesPreverified(ClassLoader loader);
 
   // Defined in 674-hiddenapi.
   private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
index a5d09e9..a10e2e7 100644
--- a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
+++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
@@ -102,6 +102,77 @@
          OS::FileExists(vdex_filename.c_str());
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isBackedByOatFile(JNIEnv*,
+                                                                  jclass,
+                                                                  jobject loader) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> h_loader = hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader));
+
+  bool is_first = true;
+  bool all_backed_by_oat = false;
+
+  VisitClassLoaderDexFiles(
+      soa,
+      h_loader,
+      [&](const DexFile* dex_file) {
+        bool is_backed_by_oat = (dex_file->GetOatDexFile() != nullptr);
+        if (is_first) {
+          all_backed_by_oat = is_backed_by_oat;
+          is_first = false;
+        } else if (all_backed_by_oat != is_backed_by_oat) {
+          // DexFiles should either all or none be backed by oat.
+          LOG(ERROR) << "isBackedByOatFile is inconsistent";
+        }
+        return true;
+      });
+  return all_backed_by_oat ? JNI_TRUE : JNI_FALSE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_areClassesPreverified(JNIEnv*,
+                                                                      jclass,
+                                                                      jobject loader) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> h_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));
+
+  std::vector<const DexFile*> dex_files;
+  VisitClassLoaderDexFiles(
+      soa,
+      h_loader,
+      [&](const DexFile* dex_file) {
+        dex_files.push_back(dex_file);
+        return true;
+      });
+
+  MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr));
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
+  bool is_first = true;
+  bool all_preverified = false;
+  for (const DexFile* dex_file : dex_files) {
+    for (uint16_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); ++cdef_idx) {
+      const char* desc = dex_file->GetClassDescriptor(dex_file->GetClassDef(cdef_idx));
+      h_class.Assign(class_linker->FindClass(soa.Self(), desc, h_loader));
+      CHECK(h_class != nullptr) << "Could not find class " << desc;
+
+      ClassStatus oat_file_class_status(ClassStatus::kNotReady);
+      bool is_preverified = class_linker->VerifyClassUsingOatFile(
+          *dex_file, h_class.Get(), oat_file_class_status);
+
+      if (is_first) {
+        all_preverified = is_preverified;
+        is_first = false;
+      } else if (all_preverified != is_preverified) {
+        // Classes should either all or none be preverified.
+        LOG(ERROR) << "areClassesPreverified is inconsistent";
+      }
+    }
+  }
+
+  return all_preverified ? JNI_TRUE : JNI_FALSE;
+}
+
 extern "C" JNIEXPORT jint JNICALL Java_Main_getVdexCacheSize(JNIEnv*, jclass) {
   return static_cast<jint>(OatFileManager::kAnonymousVdexCacheSize);
 }