Support pass BCP art/vdex/oat files by FD

This is currently used by odrefresh.

Main runtime changes are:

1. When reading the image header, also tries to read from the FD if
   provided.

2. Initialize the art/vdex/odex FDs in the boot image chunk if provided.
   This allows them to be opened (in case if the file is not accessible
   by path) in LoadComponents.

3. OpenBootDexFiles: accept BCP JARs passed as FD, in case if the file
   is not accessible by path.

4. The new FDs are passed from new runtime args,
   -Xbootclasspath{art,vdex,oat}fds.

It is used in odrefresh to pass the previous output of BCP extensions to
be used in the next phase, system server compilation.

Bug: 193720426
Test: odrefresh --use-compilation-os=10 --force-compile # exit 80
      odrefresh --verify # exit 0 (was 79 previosly)
Test: Verify the checksum of output files are the same, with
      --use-compilation-os or not, i.e. regardless where the compilation
      happens
Test: mma in art/
Change-Id: I209085f047c42823ff20415804f65a9b32378b40
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index e377308..09cdd50 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -423,6 +423,9 @@
     return gc::space::ImageSpace::LoadBootImage(/*boot_class_path=*/ boot_class_path,
                                                 /*boot_class_path_locations=*/ libcore_dex_files,
                                                 /*boot_class_path_fds=*/ std::vector<int>(),
+                                                /*boot_class_path_image_fds=*/ std::vector<int>(),
+                                                /*boot_class_path_vdex_fds=*/ std::vector<int>(),
+                                                /*boot_class_path_oat_fds=*/ std::vector<int>(),
                                                 android::base::Split(image_location, ":"),
                                                 kRuntimeISA,
                                                 relocate,
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index fbf30c4..ff23f55 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -735,21 +735,88 @@
     }
   }
 
+  static bool AddCompiledBootClasspathFds(
+      /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
+      /*inout*/ std::vector<std::string>& bcp_image_fds,
+      /*inout*/ std::vector<std::string>& bcp_vdex_fds,
+      /*inout*/ std::vector<std::string>& bcp_oat_fds,
+      const std::string& image_path) {
+    bool added_any = false;
+    std::unique_ptr<File> image_file(OS::OpenFileForReading(image_path.c_str()));
+    if (image_file && image_file->IsValid()) {
+      bcp_image_fds.push_back(std::to_string(image_file->Fd()));
+      output_files.push_back(std::move(image_file));
+      added_any = true;
+    } else {
+      bcp_image_fds.push_back("-1");
+    }
+
+    std::string oat_path = ReplaceFileExtension(image_path, "oat");
+    std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_path.c_str()));
+    if (oat_file && oat_file->IsValid()) {
+      bcp_oat_fds.push_back(std::to_string(oat_file->Fd()));
+      output_files.push_back(std::move(oat_file));
+      added_any = true;
+    } else {
+      bcp_oat_fds.push_back("-1");
+    }
+
+    std::string vdex_path = ReplaceFileExtension(image_path, "vdex");
+    std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex_path.c_str()));
+    if (vdex_file && vdex_file->IsValid()) {
+      bcp_vdex_fds.push_back(std::to_string(vdex_file->Fd()));
+      output_files.push_back(std::move(vdex_file));
+      added_any = true;
+    } else {
+      bcp_vdex_fds.push_back("-1");
+    }
+    return added_any;
+  }
+
   static bool AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
                                   /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
-                                  const std::vector<std::string>& bcp_jars) {
+                                  const std::vector<std::string>& bcp_jars,
+                                  const InstructionSet isa,
+                                  bool use_compiled_bcp_ext_as_fds) {
     auto bcp_fds = std::vector<std::string>();
+    auto bcp_image_fds = std::vector<std::string>();
+    auto bcp_oat_fds = std::vector<std::string>();
+    auto bcp_vdex_fds = std::vector<std::string>();
+    bool added_any = false;
     for (const std::string& jar : bcp_jars) {
       std::unique_ptr<File> jar_file(OS::OpenFileForReading(jar.c_str()));
-      if (!jar_file->IsValid()) {
+      if (jar_file && !jar_file->IsValid()) {
         LOG(ERROR) << "Failed to open a BCP jar " << jar;
         return false;
       }
       bcp_fds.push_back(std::to_string(jar_file->Fd()));
       output_files.push_back(std::move(jar_file));
+
+      // When enabled, for each JAR, also pass the corresponding .art/.vdex/.odex as FDs.
+      if (use_compiled_bcp_ext_as_fds) {
+        std::string image_path = GetApexDataBootImage(jar);
+        if (!image_path.empty()) {
+          added_any |= AddCompiledBootClasspathFds(output_files,
+                                                   bcp_image_fds,
+                                                   bcp_oat_fds,
+                                                   bcp_vdex_fds,
+                                                   GetSystemImageFilename(image_path.c_str(), isa));
+        }
+      }
     }
     args.emplace_back("--runtime-arg");
     args.emplace_back(Concatenate({"-Xbootclasspathfds:", android::base::Join(bcp_fds, ':')}));
+    if (use_compiled_bcp_ext_as_fds && added_any) {
+      args.emplace_back("--runtime-arg");
+      args.emplace_back(Concatenate({"-Xbootclasspathimagefds:",
+        android::base::Join(bcp_image_fds, ':')}));
+      args.emplace_back("--runtime-arg");
+      args.emplace_back(Concatenate({"-Xbootclasspathoatfds:",
+        android::base::Join(bcp_oat_fds, ':')}));
+      args.emplace_back("--runtime-arg");
+      args.emplace_back(Concatenate({"-Xbootclasspathvdexfds:",
+        android::base::Join(bcp_vdex_fds, ':')}));
+    }
     return true;
   }
 
@@ -1119,7 +1186,8 @@
     args.emplace_back("--runtime-arg");
     args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
     auto bcp_jars = android::base::Split(config_.GetDex2oatBootClasspath(), ":");
-    if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars)) {
+    if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars, isa,
+                             /*use_compiled_bcp_ext_as_fds=*/ false)) {
       return false;
     }
 
@@ -1285,7 +1353,8 @@
       args.emplace_back("--runtime-arg");
       args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetBootClasspath()}));
       auto bcp_jars = android::base::Split(config_.GetBootClasspath(), ":");
-      if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars)) {
+      if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars, isa,
+                               /*use_compiled_bcp_ext_as_fds=*/ true)) {
         return false;
       }
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 7706a5c..55fdb72 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -263,6 +263,9 @@
            const std::vector<std::string>& boot_class_path,
            const std::vector<std::string>& boot_class_path_locations,
            const std::vector<int>& boot_class_path_fds,
+           const std::vector<int>& boot_class_path_image_fds,
+           const std::vector<int>& boot_class_path_vdex_fds,
+           const std::vector<int>& boot_class_path_oat_fds,
            const std::vector<std::string>& image_file_names,
            const InstructionSet image_instruction_set,
            CollectorType foreground_collector_type,
@@ -464,6 +467,9 @@
   if (space::ImageSpace::LoadBootImage(boot_class_path,
                                        boot_class_path_locations,
                                        boot_class_path_fds,
+                                       boot_class_path_image_fds,
+                                       boot_class_path_vdex_fds,
+                                       boot_class_path_oat_fds,
                                        image_file_names,
                                        image_instruction_set,
                                        runtime->ShouldRelocate(),
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 345109d..27616fb 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -202,6 +202,9 @@
        const std::vector<std::string>& boot_class_path,
        const std::vector<std::string>& boot_class_path_locations,
        const std::vector<int>& boot_class_path_fds,
+       const std::vector<int>& boot_class_path_image_fds,
+       const std::vector<int>& boot_class_path_vdex_fds,
+       const std::vector<int>& boot_class_path_oat_fds,
        const std::vector<std::string>& image_file_names,
        InstructionSet image_instruction_set,
        CollectorType foreground_collector_type,
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index c1410ce..3930ad5 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1418,11 +1418,17 @@
   BootImageLayout(ArrayRef<const std::string> image_locations,
                   ArrayRef<const std::string> boot_class_path,
                   ArrayRef<const std::string> boot_class_path_locations,
-                  ArrayRef<const int> boot_class_path_fds)
+                  ArrayRef<const int> boot_class_path_fds,
+                  ArrayRef<const int> boot_class_path_image_fds,
+                  ArrayRef<const int> boot_class_path_vdex_fds,
+                  ArrayRef<const int> boot_class_path_oat_fds)
      : image_locations_(image_locations),
        boot_class_path_(boot_class_path),
        boot_class_path_locations_(boot_class_path_locations),
-       boot_class_path_fds_(boot_class_path_fds) {}
+       boot_class_path_fds_(boot_class_path_fds),
+       boot_class_path_image_fds_(boot_class_path_image_fds),
+       boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
+       boot_class_path_oat_fds_(boot_class_path_oat_fds) {}
 
   std::string GetPrimaryImageLocation();
 
@@ -1537,6 +1543,9 @@
   ArrayRef<const std::string> boot_class_path_;
   ArrayRef<const std::string> boot_class_path_locations_;
   ArrayRef<const int> boot_class_path_fds_;
+  ArrayRef<const int> boot_class_path_image_fds_;
+  ArrayRef<const int> boot_class_path_vdex_fds_;
+  ArrayRef<const int> boot_class_path_oat_fds_;
 
   std::vector<ImageChunk> chunks_;
   uint32_t base_address_ = 0u;
@@ -1819,8 +1828,19 @@
   DCHECK_LT(bcp_index, boot_class_path_.size());
 
   std::string actual_filename = ExpandLocation(base_filename, bcp_index);
+  int bcp_image_fd = bcp_index < boot_class_path_image_fds_.size()
+      ? boot_class_path_image_fds_[bcp_index]
+      : -1;
   ImageHeader header;
-  if (!ReadSpecificImageHeader(actual_filename.c_str(), &header, error_msg)) {
+  auto image_file = bcp_image_fd >= 0
+      ? std::make_unique<File>(bcp_image_fd, actual_filename, /*check_usage=*/ false)
+      : std::unique_ptr<File>(OS::OpenFileForReading(actual_filename.c_str()));
+  if (!image_file || !image_file->IsOpened()) {
+    *error_msg = StringPrintf("Unable to open file \"%s\" for reading image header",
+                              actual_filename.c_str());
+    return false;
+  }
+  if (!ReadSpecificImageHeader(image_file.get(), actual_filename.c_str(), &header, error_msg)) {
     return false;
   }
   const char* file_description = actual_filename.c_str();
@@ -1842,6 +1862,20 @@
   chunk.boot_image_component_count = header.GetBootImageComponentCount();
   chunk.boot_image_checksum = header.GetBootImageChecksum();
   chunk.boot_image_size = header.GetBootImageSize();
+  // When BCP art/vdex/oat FDs are also passed, initialize the chunk accordingly.
+  if (bcp_index < boot_class_path_image_fds_.size()) {
+    // The FD of .art needs to be duplicated because it'll be owned/used later.
+    int fd = boot_class_path_image_fds_[bcp_index];
+    if (fd >= 0) {
+      chunk.art_fd.reset(dup(fd));
+    }
+  }
+  if (bcp_index < boot_class_path_vdex_fds_.size()) {
+    chunk.vdex_fd.reset(boot_class_path_vdex_fds_[bcp_index]);
+  }
+  if (bcp_index < boot_class_path_oat_fds_.size()) {
+    chunk.oat_fd.reset(boot_class_path_oat_fds_[bcp_index]);
+  }
   chunks_.push_back(std::move(chunk));
   next_bcp_index_ = bcp_index + header.GetComponentCount();
   total_component_count_ += header.GetComponentCount();
@@ -2227,6 +2261,9 @@
   BootImageLoader(const std::vector<std::string>& boot_class_path,
                   const std::vector<std::string>& boot_class_path_locations,
                   const std::vector<int>& boot_class_path_fds,
+                  const std::vector<int>& boot_class_path_image_fds,
+                  const std::vector<int>& boot_class_path_vdex_fds,
+                  const std::vector<int>& boot_class_path_oat_fds,
                   const std::vector<std::string>& image_locations,
                   InstructionSet image_isa,
                   bool relocate,
@@ -2234,6 +2271,9 @@
       : boot_class_path_(boot_class_path),
         boot_class_path_locations_(boot_class_path_locations),
         boot_class_path_fds_(boot_class_path_fds),
+        boot_class_path_image_fds_(boot_class_path_image_fds),
+        boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
+        boot_class_path_oat_fds_(boot_class_path_oat_fds),
         image_locations_(image_locations),
         image_isa_(image_isa),
         relocate_(relocate),
@@ -2245,7 +2285,10 @@
     BootImageLayout layout(image_locations_,
                            boot_class_path_,
                            boot_class_path_locations_,
-                           boot_class_path_fds_);
+                           boot_class_path_fds_,
+                           boot_class_path_image_fds_,
+                           boot_class_path_vdex_fds_,
+                           boot_class_path_oat_fds_);
     std::string image_location = layout.GetPrimaryImageLocation();
     std::string system_filename;
     bool found_image = FindImageFilenameImpl(image_location.c_str(),
@@ -2869,6 +2912,7 @@
                                      executable_,
                                      /*low_4gb=*/ false,
                                      dex_filenames,
+                                     dex_fds,
                                      image_reservation,
                                      error_msg));
         // We no longer need the file descriptors and they will be closed by
@@ -3127,6 +3171,9 @@
   const ArrayRef<const std::string> boot_class_path_;
   const ArrayRef<const std::string> boot_class_path_locations_;
   const ArrayRef<const int> boot_class_path_fds_;
+  const ArrayRef<const int> boot_class_path_image_fds_;
+  const ArrayRef<const int> boot_class_path_vdex_fds_;
+  const ArrayRef<const int> boot_class_path_oat_fds_;
   const ArrayRef<const std::string> image_locations_;
   const InstructionSet image_isa_;
   const bool relocate_;
@@ -3144,7 +3191,10 @@
   BootImageLayout layout(image_locations_,
                          boot_class_path_,
                          boot_class_path_locations_,
-                         boot_class_path_fds_);
+                         boot_class_path_fds_,
+                         boot_class_path_image_fds_,
+                         boot_class_path_vdex_fds_,
+                         boot_class_path_oat_fds_);
   if (!layout.LoadFromSystem(image_isa_, error_msg)) {
     return false;
   }
@@ -3172,7 +3222,10 @@
   BootImageLayout layout(ArrayRef<const std::string>(runtime->GetImageLocations()),
                          ArrayRef<const std::string>(runtime->GetBootClassPath()),
                          ArrayRef<const std::string>(runtime->GetBootClassPathLocations()),
-                         ArrayRef<const int>(runtime->GetBootClassPathFds()));
+                         ArrayRef<const int>(runtime->GetBootClassPathFds()),
+                         ArrayRef<const int>(runtime->GetBootClassPathImageFds()),
+                         ArrayRef<const int>(runtime->GetBootClassPathVdexFds()),
+                         ArrayRef<const int>(runtime->GetBootClassPathOatFds()));
   const std::string image_location = layout.GetPrimaryImageLocation();
   std::unique_ptr<ImageHeader> image_header;
   std::string error_msg;
@@ -3195,6 +3248,9 @@
     const std::vector<std::string>& boot_class_path,
     const std::vector<std::string>& boot_class_path_locations,
     const std::vector<int>& boot_class_path_fds,
+    const std::vector<int>& boot_class_path_image_fds,
+    const std::vector<int>& boot_class_path_vdex_fds,
+    const std::vector<int>& boot_class_path_odex_fds,
     const std::vector<std::string>& image_locations,
     const InstructionSet image_isa,
     bool relocate,
@@ -3217,6 +3273,9 @@
   BootImageLoader loader(boot_class_path,
                          boot_class_path_locations,
                          boot_class_path_fds,
+                         boot_class_path_image_fds,
+                         boot_class_path_vdex_fds,
+                         boot_class_path_odex_fds,
                          image_locations,
                          image_isa,
                          relocate,
@@ -3488,7 +3547,10 @@
     BootImageLayout layout(image_locations,
                            boot_class_path.SubArray(/*pos=*/ 0u, bcp_size),
                            boot_class_path_locations.SubArray(/*pos=*/ 0u, bcp_size),
-                           bcp_fds);
+                           bcp_fds,
+                           /*boot_class_path_image_fds=*/ ArrayRef<const int>(),
+                           /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(),
+                           /*boot_class_path_oat_fds=*/ ArrayRef<const int>());
     std::string primary_image_location = layout.GetPrimaryImageLocation();
     std::string system_filename;
     bool has_system = false;
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index c8879cb..28cc795 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -125,6 +125,9 @@
       const std::vector<std::string>& boot_class_path,
       const std::vector<std::string>& boot_class_path_locations,
       const std::vector<int>& boot_class_path_fds,
+      const std::vector<int>& boot_class_path_image_fds,
+      const std::vector<int>& boot_class_path_vdex_fds,
+      const std::vector<int>& boot_class_path_oat_fds,
       const std::vector<std::string>& image_locations,
       const InstructionSet image_isa,
       bool relocate,
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index 5e1bc11..973bf5e 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -137,6 +137,9 @@
     return ImageSpace::LoadBootImage(bcp,
                                      bcp_locations,
                                      /*boot_class_path_fds=*/ std::vector<int>(),
+                                     /*boot_class_path_image_fds=*/ std::vector<int>(),
+                                     /*boot_class_path_vdex_fds=*/ std::vector<int>(),
+                                     /*boot_class_path_oat_fds=*/ std::vector<int>(),
                                      full_image_locations,
                                      kRuntimeISA,
                                      /*relocate=*/ false,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 179cfc9..347b58f 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -127,6 +127,7 @@
                                   bool executable,
                                   bool low_4gb,
                                   ArrayRef<const std::string> dex_filenames,
+                                  ArrayRef<const int> dex_fds,
                                   /*inout*/MemMap* reservation,  // Where to load if not null.
                                   /*out*/std::string* error_msg);
 
@@ -248,6 +249,7 @@
                                       bool executable,
                                       bool low_4gb,
                                       ArrayRef<const std::string> dex_filenames,
+                                      ArrayRef<const int> dex_fds,
                                       /*inout*/MemMap* reservation,
                                       /*out*/std::string* error_msg) {
   std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable));
@@ -271,7 +273,7 @@
     return nullptr;
   }
 
-  if (!ret->Setup(zip_fd, dex_filenames, /*dex_fds=*/ArrayRef<const int>(), error_msg)) {
+  if (!ret->Setup(zip_fd, dex_filenames, dex_fds, error_msg)) {
     return nullptr;
   }
 
@@ -1831,6 +1833,7 @@
                        bool executable,
                        bool low_4gb,
                        ArrayRef<const std::string> dex_filenames,
+                       ArrayRef<const int> dex_fds,
                        /*inout*/MemMap* reservation,
                        /*out*/std::string* error_msg) {
   CHECK(!oat_location.empty()) << oat_location;
@@ -1846,6 +1849,7 @@
                                                                 executable,
                                                                 low_4gb,
                                                                 dex_filenames,
+                                                                dex_fds,
                                                                 reservation,
                                                                 error_msg);
   return with_internal;
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index beb231c..5c98176 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -154,6 +154,7 @@
                        bool executable,
                        bool low_4gb,
                        ArrayRef<const std::string> dex_filenames,
+                       ArrayRef<const int> dex_fds,
                        /*inout*/MemMap* reservation,  // Where to load if not null.
                        /*out*/std::string* error_msg);
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 67ee940..109769c 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -877,6 +877,7 @@
                                   executable,
                                   /*low_4gb=*/ false,
                                   dex_locations,
+                                  /*dex_fds=*/ ArrayRef<const int>(),
                                   /*reservation=*/ nullptr,
                                   &error_msg));
       }
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index af45213..f27947e 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -115,6 +115,15 @@
       .Define("-Xbootclasspathfds:_")
           .WithType<ParseIntList<':'>>()
           .IntoKey(M::BootClassPathFds)
+      .Define("-Xbootclasspathimagefds:_")
+          .WithType<ParseIntList<':'>>()
+          .IntoKey(M::BootClassPathImageFds)
+      .Define("-Xbootclasspathvdexfds:_")
+          .WithType<ParseIntList<':'>>()
+          .IntoKey(M::BootClassPathVdexFds)
+      .Define("-Xbootclasspathoatfds:_")
+          .WithType<ParseIntList<':'>>()
+          .IntoKey(M::BootClassPathOatFds)
       .Define("-Xcheck:jni")
           .IntoKey(M::CheckJni)
       .Define("-Xms_")
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 91cbd67..b64c5e3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1206,6 +1206,7 @@
 
 static size_t OpenBootDexFiles(ArrayRef<const std::string> dex_filenames,
                                ArrayRef<const std::string> dex_locations,
+                               ArrayRef<const int> dex_fds,
                                std::vector<std::unique_ptr<const DexFile>>* dex_files) {
   DCHECK(dex_files != nullptr) << "OpenDexFiles: out-param is nullptr";
   size_t failure_count = 0;
@@ -1213,20 +1214,22 @@
   for (size_t i = 0; i < dex_filenames.size(); i++) {
     const char* dex_filename = dex_filenames[i].c_str();
     const char* dex_location = dex_locations[i].c_str();
+    const int dex_fd = i < dex_fds.size() ? dex_fds[i] : -1;
     static constexpr bool kVerifyChecksum = true;
     std::string error_msg;
-    if (!OS::FileExists(dex_filename)) {
+    if (!OS::FileExists(dex_filename) && dex_fd < 0) {
       LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
       continue;
     }
     bool verify = Runtime::Current()->IsVerificationEnabled();
     if (!dex_file_loader.Open(dex_filename,
+                              dex_fd,
                               dex_location,
                               verify,
                               kVerifyChecksum,
                               &error_msg,
                               dex_files)) {
-      LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
+      LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "' / fd " << dex_fd << ": " << error_msg;
       ++failure_count;
     }
   }
@@ -1413,6 +1416,16 @@
     return false;
   }
 
+  boot_class_path_image_fds_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathImageFds);
+  boot_class_path_vdex_fds_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathVdexFds);
+  boot_class_path_oat_fds_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathOatFds);
+  CHECK(boot_class_path_image_fds_.empty() ||
+        boot_class_path_image_fds_.size() == boot_class_path_fds_.size());
+  CHECK(boot_class_path_vdex_fds_.empty() ||
+        boot_class_path_vdex_fds_.size() == boot_class_path_fds_.size());
+  CHECK(boot_class_path_oat_fds_.empty() ||
+        boot_class_path_oat_fds_.size() == boot_class_path_fds_.size());
+
   class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
   properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);
 
@@ -1537,6 +1550,9 @@
                        GetBootClassPath(),
                        GetBootClassPathLocations(),
                        GetBootClassPathFds(),
+                       GetBootClassPathImageFds(),
+                       GetBootClassPathVdexFds(),
+                       GetBootClassPathOatFds(),
                        image_locations_,
                        instruction_set_,
                        // Override the collector type to CC if the read barrier config.
@@ -1744,8 +1760,12 @@
       if (runtime_options.Exists(Opt::BootClassPathDexList)) {
         extra_boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList));
       } else {
+        ArrayRef<const int> bcp_fds = start < GetBootClassPathFds().size()
+            ? ArrayRef<const int>(GetBootClassPathFds()).SubArray(start)
+            : ArrayRef<const int>();
         OpenBootDexFiles(ArrayRef<const std::string>(GetBootClassPath()).SubArray(start),
                          ArrayRef<const std::string>(GetBootClassPathLocations()).SubArray(start),
+                         bcp_fds,
                          &extra_boot_class_path);
       }
       class_linker_->AddExtraBootDexFiles(self, std::move(extra_boot_class_path));
@@ -1764,6 +1784,7 @@
     } else {
       OpenBootDexFiles(ArrayRef<const std::string>(GetBootClassPath()),
                        ArrayRef<const std::string>(GetBootClassPathLocations()),
+                       ArrayRef<const int>(GetBootClassPathFds()),
                        &boot_class_path);
     }
     if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index eb313e0..221bcaf 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -292,6 +292,18 @@
     return boot_class_path_fds_;
   }
 
+  const std::vector<int>& GetBootClassPathImageFds() const {
+    return boot_class_path_image_fds_;
+  }
+
+  const std::vector<int>& GetBootClassPathVdexFds() const {
+    return boot_class_path_vdex_fds_;
+  }
+
+  const std::vector<int>& GetBootClassPathOatFds() const {
+    return boot_class_path_oat_fds_;
+  }
+
   // Returns the checksums for the boot image, extensions and extra boot class path dex files,
   // based on the image spaces and boot class path dex files loaded in memory.
   const std::string& GetBootClassPathChecksums() const {
@@ -1139,6 +1151,9 @@
   std::vector<std::string> boot_class_path_locations_;
   std::string boot_class_path_checksums_;
   std::vector<int> boot_class_path_fds_;
+  std::vector<int> boot_class_path_image_fds_;
+  std::vector<int> boot_class_path_vdex_fds_;
+  std::vector<int> boot_class_path_oat_fds_;
   std::string class_path_string_;
   std::vector<std::string> properties_;
 
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index f5085e0..209c3d8 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -41,6 +41,9 @@
 RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPath)           // std::vector<std::string>
 RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations)  // std::vector<std::string>
 RUNTIME_OPTIONS_KEY (ParseIntList<':'>,   BootClassPathFds)        // std::vector<int>
+RUNTIME_OPTIONS_KEY (ParseIntList<':'>,   BootClassPathImageFds)   // std::vector<int>
+RUNTIME_OPTIONS_KEY (ParseIntList<':'>,   BootClassPathVdexFds)    // std::vector<int>
+RUNTIME_OPTIONS_KEY (ParseIntList<':'>,   BootClassPathOatFds)     // std::vector<int>
 RUNTIME_OPTIONS_KEY (std::string,         ClassPath)
 RUNTIME_OPTIONS_KEY (ParseStringList<':'>,Image)
 RUNTIME_OPTIONS_KEY (Unit,                CheckJni)