Merge "Revert "Refactor image loading.""
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 222be14..58becb1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -318,14 +318,12 @@
   }
 
   // Load image space(s).
-  std::vector<std::unique_ptr<space::ImageSpace>> boot_image_spaces;
   if (space::ImageSpace::LoadBootImage(image_file_name,
                                        image_instruction_set,
-                                       &boot_image_spaces,
+                                       &boot_image_spaces_,
                                        &requested_alloc_space_begin)) {
-    for (std::unique_ptr<space::ImageSpace>& space : boot_image_spaces) {
-      boot_image_spaces_.push_back(space.get());
-      AddSpace(space.release());
+    for (auto space : boot_image_spaces_) {
+      AddSpace(space);
     }
   }
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 985eff3..826f382f 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -181,19 +181,18 @@
   bool have_android_data = false;
   *dalvik_cache_exists = false;
   GetDalvikCache(GetInstructionSetString(image_isa),
-                 /* create_if_absent */ true,
+                 true,
                  dalvik_cache,
                  &have_android_data,
                  dalvik_cache_exists,
                  is_global_cache);
 
-  if (*dalvik_cache_exists) {
-    DCHECK(have_android_data);
+  if (have_android_data && *dalvik_cache_exists) {
     // Always set output location even if it does not exist,
     // so that the caller knows where to create the image.
     //
     // image_location = /system/framework/boot.art
-    // *image_filename = /data/dalvik-cache/<image_isa>/system@framework@boot.art
+    // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
     std::string error_msg;
     if (!GetDalvikCacheFilename(image_location,
                                 dalvik_cache->c_str(),
@@ -382,6 +381,33 @@
   return nullptr;
 }
 
+static bool ChecksumsMatch(const char* image_a, const char* image_b, std::string* error_msg) {
+  DCHECK(error_msg != nullptr);
+
+  ImageHeader hdr_a;
+  ImageHeader hdr_b;
+
+  if (!ReadSpecificImageHeader(image_a, &hdr_a)) {
+    *error_msg = StringPrintf("Cannot read header of %s", image_a);
+    return false;
+  }
+  if (!ReadSpecificImageHeader(image_b, &hdr_b)) {
+    *error_msg = StringPrintf("Cannot read header of %s", image_b);
+    return false;
+  }
+
+  if (hdr_a.GetOatChecksum() != hdr_b.GetOatChecksum()) {
+    *error_msg = StringPrintf("Checksum mismatch: %u(%s) vs %u(%s)",
+                              hdr_a.GetOatChecksum(),
+                              image_a,
+                              hdr_b.GetOatChecksum(),
+                              image_b);
+    return false;
+  }
+
+  return true;
+}
+
 static bool CanWriteToDalvikCache(const InstructionSet isa) {
   const std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(isa));
   if (access(dalvik_cache.c_str(), O_RDWR) == 0) {
@@ -481,9 +507,9 @@
 
 // Helper class encapsulating loading, so we can access private ImageSpace members (this is a
 // friend class), but not declare functions in the header.
-class ImageSpace::Loader {
+class ImageSpaceLoader {
  public:
-  static std::unique_ptr<ImageSpace> Load(const std::string& image_location,
+  static std::unique_ptr<ImageSpace> Load(const char* image_location,
                                           const std::string& image_filename,
                                           bool is_zygote,
                                           bool is_global_cache,
@@ -515,7 +541,7 @@
     // Since we are the boot image, pass null since we load the oat file from the boot image oat
     // file name.
     return Init(image_filename.c_str(),
-                image_location.c_str(),
+                image_location,
                 validate_oat_file,
                 /* oat_file */nullptr,
                 error_msg);
@@ -1445,187 +1471,6 @@
   }
 };
 
-class ImageSpace::BootImageLoader {
- public:
-  BootImageLoader(const std::string& image_location, InstructionSet image_isa)
-      : image_location_(image_location),
-        image_isa_(image_isa),
-        is_zygote_(Runtime::Current()->IsZygote()),
-        has_system_(false),
-        has_cache_(false),
-        is_global_cache_(true),
-        dalvik_cache_(),
-        cache_filename_() {
-  }
-
-  bool IsZygote() const { return is_zygote_; }
-
-  void FindImageFiles() {
-    std::string system_filename;
-    bool dalvik_cache_exists = false;
-    bool found_image = FindImageFilenameImpl(image_location_.c_str(),
-                                             image_isa_,
-                                             &has_system_,
-                                             &system_filename,
-                                             &dalvik_cache_exists,
-                                             &dalvik_cache_,
-                                             &is_global_cache_,
-                                             &has_cache_,
-                                             &cache_filename_);
-    DCHECK_EQ(dalvik_cache_exists, !dalvik_cache_.empty());
-    DCHECK_EQ(found_image, has_system_ || has_cache_);
-  }
-
-  bool HasSystem() const { return has_system_; }
-  bool HasCache() const { return has_cache_; }
-
-  bool DalvikCacheExists() const { return !dalvik_cache_.empty(); }
-  bool IsGlobalCache() const { return is_global_cache_; }
-
-  const std::string& GetDalvikCache() const {
-    DCHECK(DalvikCacheExists());
-    return dalvik_cache_;
-  }
-
-  const std::string& GetCacheFilename() const {
-    DCHECK(DalvikCacheExists());
-    return cache_filename_;
-  }
-
-  bool LoadFromSystem(/*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
-                      /*out*/ uint8_t** oat_file_end,
-                      /*out*/ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
-    std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
-    std::vector<std::string> locations;
-    if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) {
-      return false;
-    }
-    std::vector<std::unique_ptr<ImageSpace>> spaces;
-    spaces.reserve(locations.size());
-    for (const std::string& location : locations) {
-      filename = GetSystemImageFilename(location.c_str(), image_isa_);
-      spaces.push_back(Loader::Load(location,
-                                    filename,
-                                    is_zygote_,
-                                    is_global_cache_,
-                                    /* validate_oat_file */ false,
-                                    error_msg));
-      if (spaces.back() == nullptr) {
-        return false;
-      }
-    }
-    *oat_file_end = GetOatFileEnd(spaces);
-    boot_image_spaces->swap(spaces);
-    return true;
-  }
-
-  bool LoadFromDalvikCache(
-      bool validate_system_checksums,
-      bool validate_oat_file,
-      /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
-      /*out*/ uint8_t** oat_file_end,
-      /*out*/ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(DalvikCacheExists());
-    std::vector<std::string> locations;
-    if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) {
-      return false;
-    }
-    std::vector<std::unique_ptr<ImageSpace>> spaces;
-    spaces.reserve(locations.size());
-    for (const std::string& location : locations) {
-      std::string filename;
-      if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) {
-        return false;
-      }
-      spaces.push_back(Loader::Load(location,
-                                    filename,
-                                    is_zygote_,
-                                    is_global_cache_,
-                                    validate_oat_file,
-                                    error_msg));
-      if (spaces.back() == nullptr) {
-        return false;
-      }
-      if (validate_system_checksums) {
-        ImageHeader system_hdr;
-        std::string system_filename = GetSystemImageFilename(location.c_str(), image_isa_);
-        if (!ReadSpecificImageHeader(system_filename.c_str(), &system_hdr)) {
-          *error_msg = StringPrintf("Cannot read header of %s", system_filename.c_str());
-          return false;
-        }
-        if (spaces.back()->GetImageHeader().GetOatChecksum() != system_hdr.GetOatChecksum()) {
-          *error_msg = StringPrintf("Checksum mismatch: %u(%s) vs %u(%s)",
-                                    spaces.back()->GetImageHeader().GetOatChecksum(),
-                                    filename.c_str(),
-                                    system_hdr.GetOatChecksum(),
-                                    system_filename.c_str());
-          return false;
-        }
-      }
-    }
-    *oat_file_end = GetOatFileEnd(spaces);
-    boot_image_spaces->swap(spaces);
-    return true;
-  }
-
- private:
-  // Extract boot class path from oat file associated with `image_filename`
-  // and list all associated image locations.
-  static bool GetBootClassPathImageLocations(const std::string& image_location,
-                                             const std::string& image_filename,
-                                             /*out*/ std::vector<std::string>* all_locations,
-                                             /*out*/ std::string* error_msg) {
-    std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_filename);
-    std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
-                                                    oat_filename,
-                                                    oat_filename,
-                                                    /* requested_base */ nullptr,
-                                                    /* oat_file_begin */ nullptr,
-                                                    /* executable */ false,
-                                                    /* low_4gb */ false,
-                                                    /* abs_dex_location */ nullptr,
-                                                    error_msg));
-    if (oat_file == nullptr) {
-      *error_msg = StringPrintf("Failed to open oat file '%s' for image file %s: %s",
-                                oat_filename.c_str(),
-                                image_filename.c_str(),
-                                error_msg->c_str());
-      return false;
-    }
-    const OatHeader& oat_header = oat_file->GetOatHeader();
-    const char* boot_classpath = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
-    if (boot_classpath == nullptr || boot_classpath[0] == 0) {
-      *error_msg = StringPrintf("No boot class path in oat file '%s' for image file %s",
-                                oat_filename.c_str(),
-                                image_filename.c_str());
-      return false;
-    }
-
-    all_locations->push_back(image_location);
-    ExtractMultiImageLocations(image_location, boot_classpath, all_locations);
-    return true;
-  }
-
-  uint8_t* GetOatFileEnd(const std::vector<std::unique_ptr<ImageSpace>>& spaces) {
-    DCHECK(std::is_sorted(
-        spaces.begin(),
-        spaces.end(),
-        [](const std::unique_ptr<ImageSpace>& lhs, const std::unique_ptr<ImageSpace>& rhs) {
-          return lhs->GetOatFileEnd() < rhs->GetOatFileEnd();
-        }));
-    return AlignUp(spaces.back()->GetOatFileEnd(), kPageSize);
-  }
-
-  const std::string& image_location_;
-  InstructionSet image_isa_;
-  bool is_zygote_;
-  bool has_system_;
-  bool has_cache_;
-  bool is_global_cache_;
-  std::string dalvik_cache_;
-  std::string cache_filename_;
-};
-
 static constexpr uint64_t kLowSpaceValue = 50 * MB;
 static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
 
@@ -1661,56 +1506,70 @@
   return true;
 }
 
-bool ImageSpace::LoadBootImage(
-    const std::string& image_location,
-    const InstructionSet image_isa,
-    /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
-    /*out*/ uint8_t** oat_file_end) {
+std::unique_ptr<ImageSpace> ImageSpace::CreateBootImage(const char* image_location,
+                                                        const InstructionSet image_isa,
+                                                        bool secondary_image,
+                                                        std::string* error_msg) {
   ScopedTrace trace(__FUNCTION__);
 
-  DCHECK(boot_image_spaces != nullptr);
-  DCHECK(boot_image_spaces->empty());
-  DCHECK(oat_file_end != nullptr);
-  DCHECK_NE(image_isa, InstructionSet::kNone);
-
-  if (image_location.empty()) {
-    return false;
-  }
-
-  BootImageLoader loader(image_location, image_isa);
-
   // Step 0: Extra zygote work.
 
   // Step 0.a: If we're the zygote, mark boot.
-  if (loader.IsZygote() && CanWriteToDalvikCache(image_isa)) {
+  const bool is_zygote = Runtime::Current()->IsZygote();
+  if (is_zygote && !secondary_image && CanWriteToDalvikCache(image_isa)) {
     MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
   }
 
-  loader.FindImageFiles();
-
   // Step 0.b: If we're the zygote, check for free space, and prune the cache preemptively,
   //           if necessary. While the runtime may be fine (it is pretty tolerant to
   //           out-of-disk-space situations), other parts of the platform are not.
   //
   //           The advantage of doing this proactively is that the later steps are simplified,
   //           i.e., we do not need to code retries.
+  std::string system_filename;
+  bool has_system = false;
+  std::string cache_filename;
+  bool has_cache = false;
+  bool dalvik_cache_exists = false;
+  bool is_global_cache = true;
+  std::string dalvik_cache;
+  bool found_image = FindImageFilenameImpl(image_location,
+                                           image_isa,
+                                           &has_system,
+                                           &system_filename,
+                                           &dalvik_cache_exists,
+                                           &dalvik_cache,
+                                           &is_global_cache,
+                                           &has_cache,
+                                           &cache_filename);
+
   bool dex2oat_enabled = Runtime::Current()->IsImageDex2OatEnabled();
 
-  if (loader.IsZygote() && loader.DalvikCacheExists()) {
+  if (is_zygote && dalvik_cache_exists && !secondary_image) {
     // Extra checks for the zygote. These only apply when loading the first image, explained below.
-    const std::string& dalvik_cache = loader.GetDalvikCache();
     DCHECK(!dalvik_cache.empty());
     std::string local_error_msg;
     // All secondary images are verified when the primary image is verified.
-    bool verified =
-        VerifyImage(image_location.c_str(), dalvik_cache.c_str(), image_isa, &local_error_msg);
+    bool verified = VerifyImage(image_location, dalvik_cache.c_str(), image_isa, &local_error_msg);
+    // If we prune for space at a secondary image, we may end up in a crash loop with the _exit
+    // path.
     bool check_space = CheckSpace(dalvik_cache, &local_error_msg);
     if (!verified || !check_space) {
+      // Note: it is important to only prune for space on the primary image, or we will hit the
+      //       restart path.
       LOG(WARNING) << local_error_msg << " Preemptively pruning the dalvik cache.";
       PruneDalvikCache(image_isa);
 
       // Re-evaluate the image.
-      loader.FindImageFiles();
+      found_image = FindImageFilenameImpl(image_location,
+                                          image_isa,
+                                          &has_system,
+                                          &system_filename,
+                                          &dalvik_cache_exists,
+                                          &dalvik_cache,
+                                          &is_global_cache,
+                                          &has_cache,
+                                          &cache_filename);
     }
     if (!check_space) {
       // Disable compilation/patching - we do not want to fill up the space again.
@@ -1721,16 +1580,39 @@
   // Collect all the errors.
   std::vector<std::string> error_msgs;
 
-  // Step 1: Check if we have an existing image in the dalvik cache.
-  if (loader.HasCache()) {
+  // Step 1: Check if we have an existing and relocated image.
+
+  // Step 1.a: Have files in system and cache. Then they need to match.
+  if (found_image && has_system && has_cache) {
     std::string local_error_msg;
-    // If we have system image, validate system image checksums, otherwise validate the oat file.
-    if (loader.LoadFromDalvikCache(/* validate_system_checksums */ loader.HasSystem(),
-                                   /* validate_oat_file */ !loader.HasSystem(),
-                                   boot_image_spaces,
-                                   oat_file_end,
-                                   &local_error_msg)) {
-      return true;
+    // Check that the files are matching.
+    if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str(), &local_error_msg)) {
+      std::unique_ptr<ImageSpace> relocated_space =
+          ImageSpaceLoader::Load(image_location,
+                                 cache_filename,
+                                 is_zygote,
+                                 is_global_cache,
+                                 /* validate_oat_file */ false,
+                                 &local_error_msg);
+      if (relocated_space != nullptr) {
+        return relocated_space;
+      }
+    }
+    error_msgs.push_back(local_error_msg);
+  }
+
+  // Step 1.b: Only have a cache file.
+  if (found_image && !has_system && has_cache) {
+    std::string local_error_msg;
+    std::unique_ptr<ImageSpace> cache_space =
+        ImageSpaceLoader::Load(image_location,
+                               cache_filename,
+                               is_zygote,
+                               is_global_cache,
+                               /* validate_oat_file */ true,
+                               &local_error_msg);
+    if (cache_space != nullptr) {
+      return cache_space;
     }
     error_msgs.push_back(local_error_msg);
   }
@@ -1740,64 +1622,83 @@
   // Step 2.a: We are not required to relocate it. Then we can use it directly.
   bool relocate = Runtime::Current()->ShouldRelocate();
 
-  if (loader.HasSystem() && !relocate) {
+  if (found_image && has_system && !relocate) {
     std::string local_error_msg;
-    if (loader.LoadFromSystem(boot_image_spaces, oat_file_end, &local_error_msg)) {
-      return true;
+    std::unique_ptr<ImageSpace> system_space =
+        ImageSpaceLoader::Load(image_location,
+                               system_filename,
+                               is_zygote,
+                               is_global_cache,
+                               /* validate_oat_file */ false,
+                               &local_error_msg);
+    if (system_space != nullptr) {
+      return system_space;
     }
     error_msgs.push_back(local_error_msg);
   }
 
-  // Step 2.b: We require a relocated image. Then we must patch it.
-  if (loader.HasSystem() && relocate) {
+  // Step 2.b: We require a relocated image. Then we must patch it. This step fails if this is a
+  //           secondary image.
+  if (found_image && has_system && relocate) {
     std::string local_error_msg;
     if (!dex2oat_enabled) {
       local_error_msg = "Patching disabled.";
-    } else if (ImageCreationAllowed(loader.IsGlobalCache(), image_isa, &local_error_msg)) {
-      bool patch_success = RelocateImage(
-          image_location.c_str(), loader.GetDalvikCache().c_str(), image_isa, &local_error_msg);
+    } else if (secondary_image) {
+      // We really want a working image. Prune and restart.
+      PruneDalvikCache(image_isa);
+      _exit(1);
+    } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
+      bool patch_success =
+          RelocateImage(image_location, dalvik_cache.c_str(), image_isa, &local_error_msg);
       if (patch_success) {
-        if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false,
-                                       /* validate_oat_file */ false,
-                                       boot_image_spaces,
-                                       oat_file_end,
-                                       &local_error_msg)) {
-          return true;
+        std::unique_ptr<ImageSpace> patched_space =
+            ImageSpaceLoader::Load(image_location,
+                                   cache_filename,
+                                   is_zygote,
+                                   is_global_cache,
+                                   /* validate_oat_file */ false,
+                                   &local_error_msg);
+        if (patched_space != nullptr) {
+          return patched_space;
         }
       }
     }
     error_msgs.push_back(StringPrintf("Cannot relocate image %s to %s: %s",
-                                      image_location.c_str(),
-                                      loader.GetCacheFilename().c_str(),
+                                      image_location,
+                                      cache_filename.c_str(),
                                       local_error_msg.c_str()));
   }
 
-  // Step 3: We do not have an existing image in /system,
-  //         so generate an image into the dalvik cache.
-  if (!loader.HasSystem()) {
+  // Step 3: We do not have an existing image in /system, so generate an image into the dalvik
+  //         cache. This step fails if this is a secondary image.
+  if (!has_system) {
     std::string local_error_msg;
     if (!dex2oat_enabled) {
       local_error_msg = "Image compilation disabled.";
-    } else if (ImageCreationAllowed(loader.IsGlobalCache(), image_isa, &local_error_msg)) {
-      bool compilation_success =
-          GenerateImage(loader.GetCacheFilename(), image_isa, &local_error_msg);
+    } else if (secondary_image) {
+      local_error_msg = "Cannot compile a secondary image.";
+    } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
+      bool compilation_success = GenerateImage(cache_filename, image_isa, &local_error_msg);
       if (compilation_success) {
-        if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false,
-                                       /* validate_oat_file */ false,
-                                       boot_image_spaces,
-                                       oat_file_end,
-                                       &local_error_msg)) {
-          return true;
+        std::unique_ptr<ImageSpace> compiled_space =
+            ImageSpaceLoader::Load(image_location,
+                                   cache_filename,
+                                   is_zygote,
+                                   is_global_cache,
+                                   /* validate_oat_file */ false,
+                                   &local_error_msg);
+        if (compiled_space != nullptr) {
+          return compiled_space;
         }
       }
     }
     error_msgs.push_back(StringPrintf("Cannot compile image to %s: %s",
-                                      loader.GetCacheFilename().c_str(),
+                                      cache_filename.c_str(),
                                       local_error_msg.c_str()));
   }
 
-  // We failed. Prune the cache the free up space, create a compound error message
-  // and return false.
+  // We failed. Prune the cache the free up space, create a compound error message and return no
+  // image.
   PruneDalvikCache(image_isa);
 
   std::ostringstream oss;
@@ -1808,11 +1709,84 @@
     }
     oss << msg;
   }
+  *error_msg = oss.str();
 
-  LOG(ERROR) << "Could not create image space with image file '" << image_location << "'. "
-      << "Attempting to fall back to imageless running. Error was: " << oss.str();
+  return nullptr;
+}
 
-  return false;
+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;
 }
 
 ImageSpace::~ImageSpace() {
@@ -1841,7 +1815,11 @@
 std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
                                                            const OatFile* oat_file,
                                                            std::string* error_msg) {
-  return Loader::Init(image, image, /*validate_oat_file*/false, oat_file, /*out*/error_msg);
+  return ImageSpaceLoader::Init(image,
+                                image,
+                                /*validate_oat_file*/false,
+                                oat_file,
+                                /*out*/error_msg);
 }
 
 const OatFile* ImageSpace::GetOatFile() const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 771ba2a..3383d6b3 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -41,11 +41,11 @@
   // 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_location,
-      const InstructionSet image_isa,
-      /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
-      /*out*/ uint8_t** oat_file_end) REQUIRES_SHARED(Locks::mutator_lock_);
+  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.
   static std::unique_ptr<ImageSpace> CreateFromAppImage(const char* image,
@@ -197,11 +197,23 @@
 
   const std::string image_location_;
 
+  friend class ImageSpaceLoader;
   friend class Space;
 
  private:
-  class Loader;
-  class BootImageLoader;
+  // 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);
 };