ART: Move boot-image loading to ImageSpace

Move the complete loading sequence for multi-image into ImageSpace.
This simplifies Heap initialization.

Test: m test-art-host
Change-Id: I5a9ee818ce26f205c641e96288f5b4c42257e202
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 6019540..c726944 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1617,6 +1617,81 @@
   return nullptr;
 }
 
+bool ImageSpace::LoadBootImage(const std::string& image_file_name,
+                               const InstructionSet image_instruction_set,
+                               std::vector<space::ImageSpace*>* boot_image_spaces,
+                               uint8_t** oat_file_end) {
+  DCHECK(boot_image_spaces != nullptr);
+  DCHECK(boot_image_spaces->empty());
+  DCHECK(oat_file_end != nullptr);
+  DCHECK_NE(image_instruction_set, InstructionSet::kNone);
+
+  if (image_file_name.empty()) {
+    return false;
+  }
+
+  // For code reuse, handle this like a work queue.
+  std::vector<std::string> image_file_names;
+  image_file_names.push_back(image_file_name);
+
+  bool error = false;
+  uint8_t* oat_file_end_tmp = *oat_file_end;
+
+  for (size_t index = 0; index < image_file_names.size(); ++index) {
+    std::string& image_name = image_file_names[index];
+    std::string error_msg;
+    std::unique_ptr<space::ImageSpace> boot_image_space_uptr = CreateBootImage(
+        image_name.c_str(),
+        image_instruction_set,
+        index > 0,
+        &error_msg);
+    if (boot_image_space_uptr != nullptr) {
+      space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
+      boot_image_spaces->push_back(boot_image_space);
+      // Oat files referenced by image files immediately follow them in memory, ensure alloc space
+      // isn't going to get in the middle
+      uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
+      CHECK_GT(oat_file_end_addr, boot_image_space->End());
+      oat_file_end_tmp = AlignUp(oat_file_end_addr, kPageSize);
+
+      if (index == 0) {
+        // If this was the first space, check whether there are more images to load.
+        const OatFile* boot_oat_file = boot_image_space->GetOatFile();
+        if (boot_oat_file == nullptr) {
+          continue;
+        }
+
+        const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
+        const char* boot_classpath =
+            boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+        if (boot_classpath == nullptr) {
+          continue;
+        }
+
+        ExtractMultiImageLocations(image_file_name, boot_classpath, &image_file_names);
+      }
+    } else {
+      error = true;
+      LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
+          << "Attempting to fall back to imageless running. Error was: " << error_msg
+          << "\nAttempted image: " << image_name;
+      break;
+    }
+  }
+
+  if (error) {
+    // Remove already loaded spaces.
+    for (space::Space* loaded_space : *boot_image_spaces) {
+      delete loaded_space;
+    }
+    boot_image_spaces->clear();
+    return false;
+  }
+
+  *oat_file_end = oat_file_end_tmp;
+  return true;
+}
+
 std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
                                                            const OatFile* oat_file,
                                                            std::string* error_msg) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 0ba131b..489a289 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
 #define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
 
+#include "arch/instruction_set.h"
 #include "gc/accounting/space_bitmap.h"
 #include "runtime.h"
 #include "space.h"
@@ -35,18 +36,15 @@
     return kSpaceTypeImageSpace;
   }
 
-  // Create a boot image space from an image file for a specified instruction
-  // set. Cannot be used for future allocation or collected.
+  // Load boot image spaces from a primary image file for a specified instruction set.
   //
-  // Create also opens the OatFile associated with the image file so
-  // that it be contiguously allocated with the image before the
-  // creation of the alloc space. The ReleaseOatFile will later be
-  // used to transfer ownership of the OatFile to the ClassLinker when
-  // it is initialized.
-  static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
-                                     InstructionSet image_isa,
-                                     bool secondary_image,
-                                     std::string* error_msg)
+  // On successful return, the loaded spaces are added to boot_image_spaces (which must be
+  // empty on entry) and oat_file_end is updated with the (page-aligned) end of the last
+  // oat file.
+  static bool LoadBootImage(const std::string& image_file_name,
+                            const InstructionSet image_instruction_set,
+                            std::vector<space::ImageSpace*>* boot_image_spaces,
+                            uint8_t** oat_file_end)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Try to open an existing app image space.
@@ -189,6 +187,20 @@
   friend class Space;
 
  private:
+  // Create a boot image space from an image file for a specified instruction
+  // set. Cannot be used for future allocation or collected.
+  //
+  // Create also opens the OatFile associated with the image file so
+  // that it be contiguously allocated with the image before the
+  // creation of the alloc space. The ReleaseOatFile will later be
+  // used to transfer ownership of the OatFile to the ClassLinker when
+  // it is initialized.
+  static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
+                                     InstructionSet image_isa,
+                                     bool secondary_image,
+                                     std::string* error_msg)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   DISALLOW_COPY_AND_ASSIGN(ImageSpace);
 };