Revert^3 "Boot image extension."

This reverts commit 02820f424714e711bbd4cb4b04a109416eb0c8b8.
Also reverts commit beb66b38dcce937d7eee9ef2d07b6402c720f8ee
that contained a follow-up fix.

Reason for revert: b/144001974

Bug: 119800099
Bug: 143492855
Bug: 144001974
Change-Id: I4da5330c3efa9f0c3508e85344c031d3b360ca0a
diff --git a/CleanSpec.mk b/CleanSpec.mk
index a094a24..671bdb2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -99,6 +99,7 @@
 
 # Remove dex2oat artifacts for boot image extensions (workaround for broken dependencies).
 $(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
+$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 7c3e791..c29cd29 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -63,7 +63,6 @@
     $$(error found $(1) expected interpreter, interp-ac, or optimizing)
   endif
 
-  core_image_location := $(HOST_OUT_JAVA_LIBRARIES)/core$$(core_infix)$(CORE_IMG_SUFFIX)
   core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
   core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
 
@@ -77,46 +76,18 @@
   HOST_CORE_OAT_OUTS += $$(core_oat_name)
 
 $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
-$$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-# In addition to the primary core image containing HOST_CORE_IMG_DEX_FILES,
-# also build a boot image extension for the remaining HOST_CORE_DEX_FILES.
-$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
+$$(core_image_name): $$(HOST_CORE_IMG_DEX_LOCATIONS) $$(core_dex2oat_dependency)
 	@echo "host dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
-	$$(hide) ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) \
-	  --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	$$(hide) ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
 	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  $$(addprefix --dex-file=,$$(HOST_CORE_IMG_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(HOST_CORE_IMG_DEX_LOCATIONS)) \
 	  --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
-	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
-          --image=$$(PRIVATE_CORE_IMG_NAME) \
-	  --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) \
-	  --instruction-set=$$($(2)ART_HOST_ARCH) \
-	  $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
-	  --host --android-root=$$(HOST_OUT) \
-	  --generate-debug-info --generate-build-id \
-	  --runtime-arg -XX:SlowDebug=true \
-	  --no-inline-from=core-oj-hostdex.jar \
-	  $$(PRIVATE_CORE_COMPILE_OPTIONS) && \
-	ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) \
-	  --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
-	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
-	  --runtime-arg -Xbootclasspath:$$(subst $$(space),:,$$(strip \
-	        $$(HOST_CORE_DEX_FILES))) \
-	  --runtime-arg -Xbootclasspath-locations:$$(subst $$(space),:,$$(strip \
-	        $$(HOST_CORE_DEX_LOCATIONS))) \
-	  $$(addprefix --dex-file=, \
-	      $$(filter-out $$(HOST_CORE_IMG_DEX_FILES),$$(HOST_CORE_DEX_FILES))) \
-	  $$(addprefix --dex-location=, \
-	      $$(filter-out $$(HOST_CORE_IMG_DEX_LOCATIONS),$$(HOST_CORE_DEX_LOCATIONS))) \
-	  --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
-	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
-	  --boot-image=$$(PRIVATE_CORE_IMAGE_LOCATION) \
-	  --image=$$(PRIVATE_CORE_IMG_NAME) \
-	  --instruction-set=$$($(2)ART_HOST_ARCH) \
+	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
+	  --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(2)ART_HOST_ARCH) \
 	  $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
 	  --host --android-root=$$(HOST_OUT) \
 	  --generate-debug-info --generate-build-id \
@@ -178,7 +149,6 @@
     $$(error found $(1) expected interpreter, interp-ac, or optimizing)
   endif
 
-  core_image_location := $(ART_TARGET_TEST_OUT)/core$$(core_infix)$(CORE_IMG_SUFFIX)
   core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
   core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
 
@@ -196,53 +166,24 @@
   TARGET_CORE_OAT_OUTS += $$(core_oat_name)
 
 $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
-$$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-# In addition to the primary core image containing TARGET_CORE_IMG_DEX_FILES,
-# also build a boot image extension for the remaining TARGET_CORE_DEX_FILES.
-$$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
+$$(core_image_name): $$(TARGET_CORE_IMG_DEX_FILES) $$(core_dex2oat_dependency)
 	@echo "target dex2oat: $$@"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $$(DEX2OAT) \
-	  --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
 	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  $$(addprefix --dex-file=,$$(TARGET_CORE_IMG_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(TARGET_CORE_IMG_DEX_LOCATIONS)) \
 	  --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
-	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
-	  --image=$$(PRIVATE_CORE_IMG_NAME) \
-	  --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) \
-	  --instruction-set=$$($(2)TARGET_ARCH) \
+	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
+	  --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(2)TARGET_ARCH) \
 	  --instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
 	  --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
 	  --android-root=$$(PRODUCT_OUT)/system \
 	  --generate-debug-info --generate-build-id \
 	  --runtime-arg -XX:SlowDebug=true \
-	  $$(PRIVATE_CORE_COMPILE_OPTIONS) && \
-	$$(DEX2OAT) \
-	  --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
-	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
-	  --runtime-arg -Xbootclasspath:$$(subst $$(space),:,$$(strip \
-	        $$(TARGET_CORE_DEX_FILES))) \
-	  --runtime-arg -Xbootclasspath-locations:$$(subst $$(space),:,$$(strip \
-	        $$(TARGET_CORE_DEX_LOCATIONS))) \
-	  $$(addprefix --dex-file=, \
-	       $$(filter-out $$(TARGET_CORE_IMG_DEX_FILES),$$(TARGET_CORE_DEX_FILES))) \
-	  $$(addprefix --dex-location=, \
-	       $$(filter-out $$(TARGET_CORE_IMG_DEX_LOCATIONS),$$(TARGET_CORE_DEX_LOCATIONS))) \
-	  --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
-	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
-	  --boot-image=$$(PRIVATE_CORE_IMAGE_LOCATION) \
-	  --image=$$(PRIVATE_CORE_IMG_NAME) \
-	  --instruction-set=$$($(2)TARGET_ARCH) \
-	  --instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
-	  --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
-	  --android-root=$$(PRODUCT_OUT)/system \
-	  --generate-debug-info --generate-build-id \
-	  --runtime-arg -XX:SlowDebug=true \
-	  $$(PRIVATE_CORE_COMPILE_OPTIONS) || \
-	(rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
+	  $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
 
 $$(core_oat_name): $$(core_image_name)
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index feda8ba..8e642e5 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -63,7 +63,6 @@
 #include "debug/method_debug_info.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
-#include "dex/dex_file_loader.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
 #include "dex2oat_options.h"
@@ -281,14 +280,6 @@
   UsageError("      Do not include the arch as part of the name, it is added automatically.");
   UsageError("      Example: --boot-image=/system/framework/boot.art");
   UsageError("               (specifies /system/framework/<arch>/boot.art as the image file)");
-  UsageError("      Example: --boot-image=boot.art:boot-framework.art");
-  UsageError("               (specifies <bcp-path1>/<arch>/boot.art as the image file and");
-  UsageError("               <bcp-path2>/<arch>/boot-framework.art as the image extension file");
-  UsageError("               with paths taken from corresponding boot class path components)");
-  UsageError("      Example: --boot-image=/apex/com.android.art/boot.art:/system/framework/*:*");
-  UsageError("               (specifies /apex/com.android.art/<arch>/boot.art as the image");
-  UsageError("               file and search for extensions in /framework/system and boot");
-  UsageError("               class path components' paths)");
   UsageError("      Default: $ANDROID_ROOT/system/framework/boot.art");
   UsageError("");
   UsageError("  --android-root=<path>: used to locate libraries for portable linking.");
@@ -761,31 +752,16 @@
 
   void ProcessOptions(ParserOptions* parser_options) {
     compiler_options_->compile_pic_ = true;  // All AOT compilation is PIC.
-
-    if (android_root_.empty()) {
-      const char* android_root_env_var = getenv("ANDROID_ROOT");
-      if (android_root_env_var == nullptr) {
-        Usage("--android-root unspecified and ANDROID_ROOT not set");
-      }
-      android_root_ += android_root_env_var;
-    }
-
-    if (!parser_options->boot_image_filename.empty()) {
-      boot_image_filename_ = parser_options->boot_image_filename;
-    }
-
     DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
     if (!image_filenames_.empty()) {
-      if (!boot_image_filename_.empty()) {
-        compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImageExtension;
-      } else if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
+      if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
         compiler_options_->image_type_ = CompilerOptions::ImageType::kApexBootImage;
       } else {
         compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
       }
     }
     if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
-      if (compiler_options_->IsBootImage() || compiler_options_->IsBootImageExtension()) {
+      if (compiler_options_->IsBootImage()) {
         Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
       }
       compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
@@ -842,9 +818,19 @@
       Usage("--oat-file arguments do not match --image arguments");
     }
 
-    if (!IsBootImage() && boot_image_filename_.empty()) {
-      DCHECK(!IsBootImageExtension());
-      boot_image_filename_ = GetDefaultBootImageLocation(android_root_);
+    if (android_root_.empty()) {
+      const char* android_root_env_var = getenv("ANDROID_ROOT");
+      if (android_root_env_var == nullptr) {
+        Usage("--android-root unspecified and ANDROID_ROOT not set");
+      }
+      android_root_ += android_root_env_var;
+    }
+
+    if (!IsBootImage() && parser_options->boot_image_filename.empty()) {
+      parser_options->boot_image_filename = GetDefaultBootImageLocation(android_root_);
+    }
+    if (!parser_options->boot_image_filename.empty()) {
+      boot_image_filename_ = parser_options->boot_image_filename;
     }
 
     if (dex_filenames_.empty() && zip_fd_ == -1) {
@@ -951,8 +937,8 @@
     // Fill some values into the key-value store for the oat header.
     key_value_store_.reset(new SafeMap<std::string, std::string>());
 
-    // Automatically force determinism for the boot image and boot image extensions in a host build.
-    if (!kIsTargetBuild && (IsBootImage() || IsBootImageExtension())) {
+    // Automatically force determinism for the boot image in a host build.
+    if (!kIsTargetBuild && IsBootImage()) {
       force_determinism_ = true;
     }
     compiler_options_->force_determinism_ = force_determinism_;
@@ -975,21 +961,18 @@
     if (image_filenames_[0].rfind('/') == std::string::npos) {
       Usage("Unusable boot image filename %s", image_filenames_[0].c_str());
     }
-    image_filenames_ = ImageSpace::ExpandMultiImageLocations(
-        ArrayRef<const std::string>(dex_locations_), image_filenames_[0], IsBootImageExtension());
+    image_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, image_filenames_[0]);
 
     if (oat_filenames_[0].rfind('/') == std::string::npos) {
       Usage("Unusable boot image oat filename %s", oat_filenames_[0].c_str());
     }
-    oat_filenames_ = ImageSpace::ExpandMultiImageLocations(
-        ArrayRef<const std::string>(dex_locations_), oat_filenames_[0], IsBootImageExtension());
+    oat_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_filenames_[0]);
 
     if (!oat_unstripped_.empty()) {
       if (oat_unstripped_[0].rfind('/') == std::string::npos) {
         Usage("Unusable boot image symbol filename %s", oat_unstripped_[0].c_str());
       }
-      oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(
-           ArrayRef<const std::string>(dex_locations_), oat_unstripped_[0], IsBootImageExtension());
+      oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_unstripped_[0]);
     }
   }
 
@@ -1221,13 +1204,9 @@
   bool OpenFile() {
     // Prune non-existent dex files now so that we don't create empty oat files for multi-image.
     PruneNonExistentDexFiles();
-    if (dex_locations_.empty()) {
-      LOG(ERROR) << "Nothing to compile after pruning non-existent dex files.";
-      return false;
-    }
 
     // Expand oat and image filenames for multi image.
-    if ((IsBootImage() || IsBootImageExtension()) && image_filenames_.size() == 1) {
+    if (IsBootImage() && image_filenames_.size() == 1) {
       ExpandOatAndImageFilenames();
     }
 
@@ -1452,13 +1431,12 @@
       return dex2oat::ReturnCode::kOther;
     }
 
-    // Verification results are null since we don't know if we will need them yet as the compiler
+    // Verification results are null since we don't know if we will need them yet as the compler
     // filter may change.
     callbacks_.reset(new QuickCompilerCallbacks(
-        // For class verification purposes, boot image extension is the same as boot image.
-        (IsBootImage() || IsBootImageExtension())
-            ? CompilerCallbacks::CallbackMode::kCompileBootImage
-            : CompilerCallbacks::CallbackMode::kCompileApp));
+        IsBootImage() ?
+            CompilerCallbacks::CallbackMode::kCompileBootImage :
+            CompilerCallbacks::CallbackMode::kCompileApp));
 
     RuntimeArgumentMap runtime_options;
     if (!PrepareRuntimeOptions(&runtime_options, callbacks_.get())) {
@@ -1511,7 +1489,7 @@
     //       store which is used for determining whether the oat file is up to date,
     //       together with the boot class path locations and checksums stored below.
     CompilerFilter::Filter original_compiler_filter = compiler_options_->GetCompilerFilter();
-    if (!IsBootImage() && !IsBootImageExtension() && IsVeryLarge(dex_files)) {
+    if (!IsBootImage() && IsVeryLarge(dex_files)) {
       // Disable app image to make sure dex2oat unloading is enabled.
       compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
 
@@ -1534,64 +1512,14 @@
       callbacks_->SetVerificationResults(verification_results_.get());
     }
 
-    if (IsBootImage() || IsBootImageExtension()) {
-      // For boot image or boot image extension, pass opened dex files to the Runtime::Create().
+    if (IsBootImage()) {
+      // For boot image, pass opened dex files to the Runtime::Create().
       // Note: Runtime acquires ownership of these dex files.
       runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_);
     }
     if (!CreateRuntime(std::move(runtime_options))) {
       return dex2oat::ReturnCode::kCreateRuntime;
     }
-    ArrayRef<const DexFile* const> bcp_dex_files(runtime_->GetClassLinker()->GetBootClassPath());
-    if (IsBootImage() || IsBootImageExtension()) {
-      // Check boot class path dex files and, if compiling an extension, the images it depends on.
-      if ((IsBootImage() && bcp_dex_files.size() != dex_files.size()) ||
-          (IsBootImageExtension() && bcp_dex_files.size() <= dex_files.size())) {
-        LOG(ERROR) << "Unexpected number of boot class path dex files for boot image or extension, "
-            << bcp_dex_files.size() << (IsBootImage() ? " != " : " <= ") << dex_files.size();
-        return dex2oat::ReturnCode::kOther;
-      }
-      if (!std::equal(dex_files.begin(), dex_files.end(), bcp_dex_files.end() - dex_files.size())) {
-        LOG(ERROR) << "Boot class path dex files do not end with the compiled dex files.";
-        return dex2oat::ReturnCode::kOther;
-      }
-      size_t bcp_df_pos = 0u;
-      size_t bcp_df_end = bcp_dex_files.size();
-      for (const std::string& bcp_location : runtime_->GetBootClassPathLocations()) {
-        if (bcp_df_pos == bcp_df_end || bcp_dex_files[bcp_df_pos]->GetLocation() != bcp_location) {
-          LOG(ERROR) << "Missing dex file for boot class component " << bcp_location;
-          return dex2oat::ReturnCode::kOther;
-        }
-        CHECK(!DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str()));
-        ++bcp_df_pos;
-        while (bcp_df_pos != bcp_df_end &&
-            DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str())) {
-          ++bcp_df_pos;
-        }
-      }
-      if (bcp_df_pos != bcp_df_end) {
-        LOG(ERROR) << "Unexpected dex file in boot class path "
-            << bcp_dex_files[bcp_df_pos]->GetLocation();
-        return dex2oat::ReturnCode::kOther;
-      }
-      auto lacks_image = [](const DexFile* df) {
-        if (kIsDebugBuild && df->GetOatDexFile() != nullptr) {
-          const OatFile* oat_file = df->GetOatDexFile()->GetOatFile();
-          CHECK(oat_file != nullptr);
-          const auto& image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
-          CHECK(std::any_of(image_spaces.begin(),
-                            image_spaces.end(),
-                            [=](const ImageSpace* space) {
-                              return oat_file == space->GetOatFile();
-                            }));
-        }
-        return df->GetOatDexFile() == nullptr;
-      };
-      if (std::any_of(bcp_dex_files.begin(), bcp_dex_files.end() - dex_files.size(), lacks_image)) {
-        LOG(ERROR) << "Missing required boot image(s) for boot image extension.";
-        return dex2oat::ReturnCode::kOther;
-      }
-    }
 
     if (!compilation_reason_.empty()) {
       key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_);
@@ -1601,32 +1529,17 @@
       // If we're compiling the boot image, store the boot classpath into the Key-Value store.
       // We use this when loading the boot image.
       key_value_store_->Put(OatHeader::kBootClassPathKey, android::base::Join(dex_locations_, ':'));
-    } else if (IsBootImageExtension()) {
-      // Validate the boot class path and record the dependency on the loaded boot images.
-      TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
-      Runtime* runtime = Runtime::Current();
-      std::string full_bcp = android::base::Join(runtime->GetBootClassPathLocations(), ':');
-      std::string extension_part = ":" + android::base::Join(dex_locations_, ':');
-      if (!android::base::EndsWith(full_bcp, extension_part)) {
-        LOG(ERROR) << "Full boot class path does not end with extension parts, full: " << full_bcp
-            << ", extension: " << extension_part.substr(1u);
-        return dex2oat::ReturnCode::kOther;
-      }
-      std::string bcp_dependency = full_bcp.substr(0u, full_bcp.size() - extension_part.size());
-      key_value_store_->Put(OatHeader::kBootClassPathKey, bcp_dependency);
-      ArrayRef<const DexFile* const> bcp_dex_files_dependency =
-          bcp_dex_files.SubArray(/*pos=*/ 0u, bcp_dex_files.size() - dex_files.size());
-      ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
-      key_value_store_->Put(
-          OatHeader::kBootClassPathChecksumsKey,
-          gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files_dependency));
-    } else {
+    }
+
+    if (!IsBootImage()) {
       if (CompilerFilter::DependsOnImageChecksum(original_compiler_filter)) {
         TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
         Runtime* runtime = Runtime::Current();
         key_value_store_->Put(OatHeader::kBootClassPathKey,
                               android::base::Join(runtime->GetBootClassPathLocations(), ':'));
         ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
+        ArrayRef<const DexFile* const> bcp_dex_files(
+            runtime->GetClassLinker()->GetBootClassPath());
         key_value_store_->Put(
             OatHeader::kBootClassPathChecksumsKey,
             gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files));
@@ -1694,7 +1607,7 @@
     CHECK(driver_ == nullptr);
     // If we use a swap file, ensure we are above the threshold to make it necessary.
     if (swap_fd_ != -1) {
-      if (!UseSwap(IsBootImage() || IsBootImageExtension(), dex_files)) {
+      if (!UseSwap(IsBootImage(), dex_files)) {
         close(swap_fd_);
         swap_fd_ = -1;
         VLOG(compiler) << "Decided to run without swap.";
@@ -1711,7 +1624,7 @@
     Thread* self = Thread::Current();
     WellKnownClasses::Init(self->GetJniEnv());
 
-    if (!IsBootImage() && !IsBootImageExtension()) {
+    if (!IsBootImage()) {
       constexpr bool kSaveDexInput = false;
       if (kSaveDexInput) {
         SaveDexInput();
@@ -1800,7 +1713,7 @@
 
     if (!no_inline_filters.empty()) {
       std::vector<const DexFile*> class_path_files;
-      if (!IsBootImage() && !IsBootImageExtension()) {
+      if (!IsBootImage()) {
         // The class loader context is used only for apps.
         class_path_files = class_loader_context_->FlattenOpenedDexFiles();
       }
@@ -1845,7 +1758,7 @@
                                      compiler_kind_,
                                      thread_count_,
                                      swap_fd_));
-    if (!IsBootImage() && !IsBootImageExtension()) {
+    if (!IsBootImage()) {
       driver_->SetClasspathDexFiles(class_loader_context_->FlattenOpenedDexFiles());
     }
 
@@ -1893,7 +1806,7 @@
     ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
 
     jobject class_loader = nullptr;
-    if (!IsBootImage() && !IsBootImageExtension()) {
+    if (!IsBootImage()) {
       class_loader =
           class_loader_context_->CreateClassLoader(compiler_options_->dex_files_for_oat_file_);
       callbacks_->SetDexFiles(&dex_files);
@@ -2037,7 +1950,7 @@
 
     {
       TimingLogger::ScopedTiming t2("dex2oat Write VDEX", timings_);
-      DCHECK(IsBootImage() || IsBootImageExtension() || oat_files_.size() == 1u);
+      DCHECK(IsBootImage() || oat_files_.size() == 1u);
       verifier::VerifierDeps* verifier_deps = callbacks_->GetVerifierDeps();
       for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
         File* vdex_file = vdex_files_[i].get();
@@ -2261,7 +2174,7 @@
   }
 
   bool IsImage() const {
-    return IsAppImage() || IsBootImage() || IsBootImageExtension();
+    return IsAppImage() || IsBootImage();
   }
 
   bool IsAppImage() const {
@@ -2272,10 +2185,6 @@
     return compiler_options_->IsBootImage();
   }
 
-  bool IsBootImageExtension() const {
-    return compiler_options_->IsBootImageExtension();
-  }
-
   bool IsHost() const {
     return is_host_;
   }
@@ -2480,7 +2389,7 @@
   bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options,
                              QuickCompilerCallbacks* callbacks) {
     RuntimeOptions raw_options;
-    if (IsBootImage()) {
+    if (boot_image_filename_.empty()) {
       std::string boot_class_path = "-Xbootclasspath:";
       boot_class_path += android::base::Join(dex_filenames_, ':');
       raw_options.push_back(std::make_pair(boot_class_path, nullptr));
@@ -2570,7 +2479,7 @@
   bool CreateImageFile()
       REQUIRES(!Locks::mutator_lock_) {
     CHECK(image_writer_ != nullptr);
-    if (!IsBootImage() && !IsBootImageExtension()) {
+    if (!IsBootImage()) {
       CHECK(image_filenames_.empty());
       image_filenames_.push_back(app_image_file_name_);
     }
@@ -2953,10 +2862,7 @@
   //   3) Compiling with --host
   //   4) Compiling on the host (not a target build)
   // Otherwise, print a stripped command line.
-  if (kIsDebugBuild ||
-      dex2oat->IsBootImage() || dex2oat->IsBootImageExtension() ||
-      dex2oat->IsHost() ||
-      !kIsTargetBuild) {
+  if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) {
     LOG(INFO) << CommandLine();
   } else {
     LOG(INFO) << StrippedCommandLine();
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index fc824c1..d0d5081 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -14,44 +14,31 @@
  * limitations under the License.
  */
 
-#include <fstream>
 #include <regex>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include <sys/mman.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 
 #include "common_runtime_test.h"
 
-#include "base/array_ref.h"
 #include "base/file_utils.h"
 #include "base/macros.h"
-#include "base/mem_map.h"
-#include "base/string_view_cpp20.h"
 #include "base/unix_file/fd_file.h"
 #include "base/utils.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
 #include "dex/method_reference.h"
-#include "gc/space/image_space.h"
 #include "profile/profile_compilation_info.h"
 #include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
 
 namespace art {
 
-// A suitable address for loading the core images.
-constexpr uint32_t kBaseAddress = 0x60000000;
-
 struct ImageSizes {
   size_t art_size = 0;
   size_t oat_size = 0;
@@ -145,12 +132,8 @@
       scratch_dir.pop_back();
     }
     CHECK(!scratch_dir.empty()) << "No directory " << scratch.GetFilename();
-    std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
-    ArrayRef<const std::string> dex_files(libcore_dex_files);
-    std::vector<std::string> local_extra_args = extra_args;
-    local_extra_args.push_back(android::base::StringPrintf("--base=0x%08x", kBaseAddress));
     std::string error_msg;
-    if (!CompileBootImage(local_extra_args, scratch.GetFilename(), dex_files, &error_msg)) {
+    if (!CompileBootImage(extra_args, scratch.GetFilename(), &error_msg)) {
       LOG(ERROR) << "Failed to compile image " << scratch.GetFilename() << error_msg;
     }
     std::string art_file = scratch.GetFilename() + ".art";
@@ -168,19 +151,19 @@
     scratch.Close();
     // Clear image files since we compile the image multiple times and don't want to leave any
     // artifacts behind.
-    ClearDirectory(scratch_dir.c_str(), /*recursive=*/ false);
+    ClearDirectory(scratch_dir.c_str(), /*recursive*/ false);
     return ret;
   }
 
   bool CompileBootImage(const std::vector<std::string>& extra_args,
                         const std::string& image_file_name_prefix,
-                        ArrayRef<const std::string> dex_files,
                         std::string* error_msg) {
     Runtime* const runtime = Runtime::Current();
     std::vector<std::string> argv;
     argv.push_back(runtime->GetCompilerExecutable());
     AddRuntimeArg(argv, "-Xms64m");
     AddRuntimeArg(argv, "-Xmx64m");
+    std::vector<std::string> dex_files = GetLibCoreDexFileNames();
     for (const std::string& dex_file : dex_files) {
       argv.push_back("--dex-file=" + dex_file);
       argv.push_back("--dex-location=" + dex_file);
@@ -199,6 +182,7 @@
     argv.push_back("--image=" + image_file_name_prefix + ".art");
     argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
     argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
+    argv.push_back("--base=0x60000000");
 
     std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
     argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
@@ -281,266 +265,4 @@
   }
 }
 
-TEST_F(Dex2oatImageTest, TestExtension) {
-  constexpr size_t kReservationSize = 256 * MB;  // This should be enough for the compiled images.
-  // Extend to both directions for maximum relocation difference.
-  static_assert(ART_BASE_ADDRESS_MIN_DELTA < 0);
-  static_assert(ART_BASE_ADDRESS_MAX_DELTA > 0);
-  static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MIN_DELTA));
-  static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MAX_DELTA));
-  constexpr size_t kExtra = ART_BASE_ADDRESS_MAX_DELTA - ART_BASE_ADDRESS_MIN_DELTA;
-  uint32_t min_relocated_address = kBaseAddress + ART_BASE_ADDRESS_MIN_DELTA;
-  std::string error_msg;
-  MemMap reservation = MemMap::MapAnonymous("Reservation",
-                                            reinterpret_cast<uint8_t*>(min_relocated_address),
-                                            kReservationSize + kExtra,
-                                            PROT_NONE,
-                                            /*low_4gb=*/ true,
-                                            /*reuse=*/ false,
-                                            /*reservation=*/ nullptr,
-                                            &error_msg);
-  ASSERT_TRUE(reservation.IsValid());
-
-  ScratchFile scratch;
-  std::string scratch_dir = scratch.GetFilename() + "-d";
-  int mkdir_result = mkdir(scratch_dir.c_str(), 0700);
-  ASSERT_EQ(0, mkdir_result);
-  scratch_dir += '/';
-  std::string image_dir = scratch_dir + GetInstructionSetString(kRuntimeISA);
-  mkdir_result = mkdir(image_dir.c_str(), 0700);
-  ASSERT_EQ(0, mkdir_result);
-  std::string filename_prefix = image_dir + "/core";
-
-  // Copy the libcore dex files to a custom dir inside `scratch_dir` so that we do not
-  // accidentally load pre-compiled core images from their original directory based on BCP paths.
-  std::string jar_dir = scratch_dir + "jars";
-  mkdir_result = mkdir(jar_dir.c_str(), 0700);
-  ASSERT_EQ(0, mkdir_result);
-  jar_dir += '/';
-  std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
-  for (std::string& dex_file : libcore_dex_files) {
-    size_t slash_pos = dex_file.rfind('/');
-    ASSERT_NE(std::string::npos, slash_pos);
-    std::string new_location = jar_dir + dex_file.substr(slash_pos + 1u);
-    std::ifstream src_stream(dex_file, std::ios::binary);
-    std::ofstream dst_stream(new_location, std::ios::binary);
-    dst_stream << src_stream.rdbuf();
-    dex_file = new_location;
-  }
-
-  ArrayRef<const std::string> full_bcp(libcore_dex_files);
-  size_t total_dex_files = full_bcp.size();
-  ASSERT_GE(total_dex_files, 4u);  // 2 for "head", 1 for "tail", at least one for "mid", see below.
-
-  // The primary image must contain at least core-oj and core-libart to initialize the runtime.
-  ASSERT_NE(std::string::npos, full_bcp[0].find("core-oj"));
-  ASSERT_NE(std::string::npos, full_bcp[1].find("core-libart"));
-  ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 2u);
-  // Middle part is everything else except for conscrypt.
-  ASSERT_NE(std::string::npos, full_bcp[full_bcp.size() - 1u].find("conscrypt"));
-  ArrayRef<const std::string> mid_bcp =
-      full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ total_dex_files - 1u);
-  ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 2u);
-  // Tail is just the conscrypt.
-  ArrayRef<const std::string> tail_dex_files =
-      full_bcp.SubArray(/*pos=*/ total_dex_files - 1u, /*length=*/ 1u);
-
-  // Prepare the "head", "mid" and "tail" names and locations.
-  std::string base_name = "core.art";
-  std::string base_location = scratch_dir + base_name;
-  std::vector<std::string> expanded_mid = gc::space::ImageSpace::ExpandMultiImageLocations(
-      mid_dex_files.SubArray(/*pos=*/ 0u, /*length=*/ 1u),
-      base_location,
-      /*boot_image_extension=*/ true);
-  CHECK_EQ(1u, expanded_mid.size());
-  std::string mid_location = expanded_mid[0];
-  size_t mid_slash_pos = mid_location.rfind('/');
-  ASSERT_NE(std::string::npos, mid_slash_pos);
-  std::string mid_name = mid_location.substr(mid_slash_pos + 1u);
-  CHECK_EQ(1u, tail_dex_files.size());
-  std::vector<std::string> expanded_tail = gc::space::ImageSpace::ExpandMultiImageLocations(
-      tail_dex_files, base_location, /*boot_image_extension=*/ true);
-  CHECK_EQ(1u, expanded_tail.size());
-  std::string tail_location = expanded_tail[0];
-  size_t tail_slash_pos = tail_location.rfind('/');
-  ASSERT_NE(std::string::npos, tail_slash_pos);
-  std::string tail_name = tail_location.substr(tail_slash_pos + 1u);
-
-  // Compile the "head", i.e. the primary boot image.
-  std::string base = android::base::StringPrintf("--base=0x%08x", kBaseAddress);
-  bool head_ok = CompileBootImage({base}, filename_prefix, head_dex_files, &error_msg);
-  ASSERT_TRUE(head_ok) << error_msg;
-
-  // Compile the "mid", i.e. the first extension.
-  std::string mid_bcp_string = android::base::Join(mid_bcp, ':');
-  std::vector<std::string> extra_args;
-  AddRuntimeArg(extra_args, "-Xbootclasspath:" + mid_bcp_string);
-  AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + mid_bcp_string);
-  extra_args.push_back("--boot-image=" + base_location);
-  bool mid_ok = CompileBootImage(extra_args, filename_prefix, mid_dex_files, &error_msg);
-  ASSERT_TRUE(mid_ok) << error_msg;
-
-  // Try to compile the "tail" without specifying the "mid" extension. This shall fail.
-  std::string full_bcp_string = android::base::Join(full_bcp, ':');
-  extra_args.clear();
-  AddRuntimeArg(extra_args, "-Xbootclasspath:" + full_bcp_string);
-  AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + full_bcp_string);
-  extra_args.push_back("--boot-image=" + base_location);
-  bool tail_ok = CompileBootImage(extra_args, filename_prefix, tail_dex_files, &error_msg);
-  ASSERT_FALSE(tail_ok) << error_msg;
-
-  // Now compile the tail against both "head" and "mid".
-  CHECK(StartsWith(extra_args.back(), "--boot-image="));
-  extra_args.back() = "--boot-image=" + base_location + ':' + mid_location;
-  tail_ok = CompileBootImage(extra_args, filename_prefix, tail_dex_files, &error_msg);
-  ASSERT_TRUE(tail_ok) << error_msg;
-
-  reservation = MemMap::Invalid();  // Free the reserved memory for loading images.
-
-  // Try to load the boot image with different image locations.
-  std::vector<std::string> boot_class_path = libcore_dex_files;
-  std::vector<std::unique_ptr<gc::space::ImageSpace>> boot_image_spaces;
-  bool relocate = false;
-  MemMap extra_reservation;
-  auto load = [&](const std::string& image_location) {
-    boot_image_spaces.clear();
-    extra_reservation = MemMap::Invalid();
-    ScopedObjectAccess soa(Thread::Current());
-    return gc::space::ImageSpace::LoadBootImage(/*boot_class_path=*/ boot_class_path,
-                                                /*boot_class_path_locations=*/ libcore_dex_files,
-                                                image_location,
-                                                kRuntimeISA,
-                                                gc::space::ImageSpaceLoadingOrder::kSystemFirst,
-                                                relocate,
-                                                /*executable=*/ true,
-                                                /*is_zygote=*/ false,
-                                                /*extra_reservation_size=*/ 0u,
-                                                &boot_image_spaces,
-                                                &extra_reservation);
-  };
-
-  for (bool r : { false, true}) {
-    relocate = r;
-
-    // Load primary image with full path.
-    bool load_ok = load(base_location);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_FALSE(extra_reservation.IsValid());
-    ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
-
-    // Fail to load primary image with just the name.
-    load_ok = load(base_name);
-    ASSERT_FALSE(load_ok);
-
-    // Fail to load primary image with a search path.
-    load_ok = load("*");
-    ASSERT_FALSE(load_ok);
-    load_ok = load(scratch_dir + "*");
-    ASSERT_FALSE(load_ok);
-
-    // Load the primary and first extension with full path.
-    load_ok = load(base_location + ':' + mid_location);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary with full path and fail to load first extension without full path.
-    load_ok = load(base_location + ':' + mid_name);
-    ASSERT_TRUE(load_ok) << error_msg;  // Primary image loaded successfully.
-    ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());  // But only the primary image.
-
-    // Load all the libcore images with full paths.
-    load_ok = load(base_location + ':' + mid_location + ':' + tail_location);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary and first extension with full paths, fail to load second extension by name.
-    load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary with full path and fail to load first extension without full path,
-    // fail to load second extension because it depends on the first.
-    load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
-    ASSERT_TRUE(load_ok) << error_msg;  // Primary image loaded successfully.
-    ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());  // But only the primary image.
-
-    // Load the primary with full path and extensions with a specified search path.
-    load_ok = load(base_location + ':' + scratch_dir + '*');
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary with full path and fail to find extensions in BCP path.
-    load_ok = load(base_location + ":*");
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
-  }
-
-  // Now copy the libcore dex files to the `scratch_dir` and retry loading the boot image
-  // with BCP in the scratch_dir so that the images can be found based on BCP paths.
-  for (std::string& bcp_component : boot_class_path) {
-    size_t slash_pos = bcp_component.rfind('/');
-    ASSERT_NE(std::string::npos, slash_pos);
-    std::string new_location = scratch_dir + bcp_component.substr(slash_pos + 1u);
-    std::ifstream src_stream(bcp_component, std::ios::binary);
-    std::ofstream dst_stream(new_location, std::ios::binary);
-    dst_stream << src_stream.rdbuf();
-    bcp_component = new_location;
-  }
-
-  for (bool r : { false, true}) {
-    relocate = r;
-
-    // Loading the primary image with just the name now succeeds.
-    bool load_ok = load(base_name);
-    ASSERT_TRUE(load_ok) << error_msg;
-
-    // Loading the primary image with a search path still fails.
-    load_ok = load("*");
-    ASSERT_FALSE(load_ok);
-    load_ok = load(scratch_dir + "*");
-    ASSERT_FALSE(load_ok);
-
-    // Load the primary and first extension without paths.
-    load_ok = load(base_name + ':' + mid_name);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary with full path and the first extension without full path.
-    load_ok = load(base_location + ':' + mid_name);
-    ASSERT_TRUE(load_ok) << error_msg;  // Loaded successfully.
-    ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());  // Including the extension.
-
-    // Load all the libcore images without paths.
-    load_ok = load(base_name + ':' + mid_name + ':' + tail_name);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary and first extension with full paths and second extension by name.
-    load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
-
-    // Load the primary with full path, first extension without path,
-    // and second extension with full path.
-    load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
-    ASSERT_TRUE(load_ok) << error_msg;  // Loaded successfully.
-    ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());  // Including both extensions.
-
-    // Load the primary with full path and find both extensions in BCP path.
-    load_ok = load(base_location + ":*");
-    ASSERT_TRUE(load_ok) << error_msg;
-    ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
-
-    // Fail to load any images with invalid image locations (named component after search paths).
-    load_ok = load(base_location + ":*:" + tail_location);
-    ASSERT_FALSE(load_ok);
-    load_ok = load(base_location + ':' + scratch_dir + "*:" + tail_location);
-    ASSERT_FALSE(load_ok);
-  }
-
-  ClearDirectory(scratch_dir.c_str());
-  int rmdir_result = rmdir(scratch_dir.c_str());
-  ASSERT_EQ(0, rmdir_result);
-}
-
 }  // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index fa08627..9e5da27 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -171,9 +171,8 @@
     // Create a generic tmp file, to be the base of the .art and .oat temporary files.
     ScratchFile location;
     std::vector<std::string> image_locations =
-        gc::space::ImageSpace::ExpandMultiImageLocations(
-            ArrayRef<const std::string>(out_helper.dex_file_locations),
-            location.GetFilename() + ".art");
+        gc::space::ImageSpace::ExpandMultiImageLocations(out_helper.dex_file_locations,
+                                                         location.GetFilename() + ".art");
     for (size_t i = 0u; i != class_path.size(); ++i) {
       out_helper.image_locations.push_back(ScratchFile(image_locations[i]));
     }
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 51bc6a8..760acc7 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -65,10 +65,6 @@
 using android::base::StringAppendF;
 using android::base::StringPrintf;
 
-// We do not allow the boot image and extensions to take more than 1GiB. They are
-// supposed to be much smaller and allocating more that this would likely fail anyway.
-static constexpr size_t kMaxTotalImageReservationSize = 1 * GB;
-
 Atomic<uint32_t> ImageSpace::bitmap_index_(0);
 
 ImageSpace::ImageSpace(const std::string& image_filename,
@@ -1375,531 +1371,6 @@
   }
 };
 
-static void AppendImageChecksum(uint32_t component_count,
-                                uint32_t checksum,
-                                /*inout*/std::string* checksums) {
-  static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
-  StringAppendF(checksums, "i;%u/%08x", component_count, checksum);
-}
-
-static bool CheckAndRemoveImageChecksum(uint32_t component_count,
-                                        uint32_t checksum,
-                                        /*inout*/std::string_view* oat_checksums,
-                                        /*out*/std::string* error_msg) {
-  std::string image_checksum;
-  AppendImageChecksum(component_count, checksum, &image_checksum);
-  if (!StartsWith(*oat_checksums, image_checksum)) {
-    *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
-                              std::string(*oat_checksums).c_str(),
-                              image_checksum.c_str());
-    return false;
-  }
-  oat_checksums->remove_prefix(image_checksum.size());
-  return true;
-}
-
-// Helper class to find the primary boot image and boot image extensions
-// and determine the boot image layout.
-class ImageSpace::BootImageLayout {
- public:
-  // Description of a "chunk" of the boot image, i.e. either primary boot image
-  // or a boot image extension, used in conjunction with the boot class path to
-  // load boot image components.
-  struct ImageChunk {
-    std::string base_location;
-    std::string base_filename;
-    size_t start_index;
-    size_t component_count;
-    size_t reservation_size;
-    uint32_t checksum;
-  };
-
-  BootImageLayout(const std::string& image_location, ArrayRef<const std::string> boot_class_path)
-     : image_location_(image_location),
-       boot_class_path_(boot_class_path) {}
-
-  std::string GetPrimaryImageLocation();
-
-  bool LoadFromSystem(InstructionSet image_isa, /*out*/std::string* error_msg) {
-    return LoadOrValidateFromSystem(image_isa, /*oat_checksums=*/ nullptr, error_msg);
-  }
-
-  bool ValidateFromSystem(InstructionSet image_isa,
-                          /*inout*/std::string_view* oat_checksums,
-                          /*out*/std::string* error_msg) {
-    DCHECK(oat_checksums != nullptr);
-    return LoadOrValidateFromSystem(image_isa, oat_checksums, error_msg);
-  }
-
-  bool LoadFromDalvikCache(const std::string& dalvik_cache, /*out*/std::string* error_msg) {
-    return LoadOrValidateFromDalvikCache(dalvik_cache, /*oat_checksums=*/ nullptr, error_msg);
-  }
-
-  bool ValidateFromDalvikCache(const std::string& dalvik_cache,
-                               /*inout*/std::string_view* oat_checksums,
-                               /*out*/std::string* error_msg) {
-    DCHECK(oat_checksums != nullptr);
-    return LoadOrValidateFromDalvikCache(dalvik_cache, oat_checksums, error_msg);
-  }
-
-  ArrayRef<const ImageChunk> GetChunks() const {
-    return ArrayRef<const ImageChunk>(chunks_);
-  }
-
-  uint32_t GetBaseAddress() const {
-    return base_address_;
-  }
-
-  size_t GetNextBcpIndex() const {
-    return next_bcp_index_;
-  }
-
-  size_t GetTotalComponentCount() const {
-    return total_component_count_;
-  }
-
-  size_t GetTotalReservationSize() const {
-    return total_reservation_size_;
-  }
-
- private:
-  std::string ExpandLocationImpl(const std::string& location,
-                                 size_t bcp_index,
-                                 bool boot_image_extension) {
-    std::vector<std::string> expanded = ExpandMultiImageLocations(
-        ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
-        location,
-        boot_image_extension);
-    DCHECK_EQ(expanded.size(), 1u);
-    return expanded[0];
-  }
-
-  std::string ExpandLocation(const std::string& location, size_t bcp_index) {
-    if (bcp_index == 0u) {
-      DCHECK_EQ(location, ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ false));
-      return location;
-    } else {
-      return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ true);
-    }
-  }
-
-  bool VerifyImageLocation(const std::vector<std::string>& components,
-                           /*out*/size_t* named_components_count,
-                           /*out*/std::string* error_msg);
-
-  bool MatchNamedComponents(
-      ArrayRef<const std::string> named_components,
-      /*out*/std::vector<std::pair<std::string, size_t>>* base_locations_and_bcp_indexes,
-      /*out*/std::string* error_msg);
-
-  bool ReadHeader(const std::string& base_location,
-                  const std::string& base_filename,
-                  size_t bcp_index,
-                  size_t bcp_component_count,
-                  /*out*/std::string* error_msg);
-
-  bool CheckAndRemoveLastChunkChecksum(/*inout*/std::string_view* oat_checksums,
-                                       /*out*/std::string* error_msg);
-
-  template <typename FilenameFn>
-  bool LoadOrValidate(FilenameFn&& filename_fn,
-                      /*inout*/std::string_view* oat_checksums,
-                      /*out*/std::string* error_msg);
-
-  bool LoadOrValidateFromSystem(InstructionSet image_isa,
-                                /*inout*/std::string_view* oat_checksums,
-                                /*out*/std::string* error_msg);
-
-  bool LoadOrValidateFromDalvikCache(const std::string& dalvik_cache,
-                                     /*inout*/std::string_view* oat_checksums,
-                                     /*out*/std::string* error_msg);
-
-  const std::string& image_location_;
-  ArrayRef<const std::string> boot_class_path_;
-
-  std::vector<ImageChunk> chunks_;
-  uint32_t base_address_ = 0u;
-  size_t next_bcp_index_ = 0u;
-  size_t total_component_count_ = 0u;
-  size_t total_reservation_size_ = 0u;
-};
-
-std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() {
-  size_t location_start = 0u;
-  size_t location_end = image_location_.find(':');
-  while (location_end == location_start) {
-    ++location_start;
-    location_end = image_location_.find(location_start, ':');
-  }
-  std::string location = (location_end == std::string::npos)
-      ? image_location_.substr(location_start)
-      : image_location_.substr(location_start, location_end - location_start);
-  if (image_location_.find('/') == std::string::npos) {
-    // No path, so use the path from the first boot class path component.
-    size_t slash_pos = boot_class_path_.empty()
-        ? std::string::npos
-        : boot_class_path_[0].rfind('/');
-    if (slash_pos == std::string::npos) {
-      return std::string();
-    }
-    location.insert(0u, boot_class_path_[0].substr(0u, slash_pos + 1u));
-  }
-  return location;
-}
-
-bool ImageSpace::BootImageLayout::VerifyImageLocation(
-    const std::vector<std::string>& components,
-    /*out*/size_t* named_components_count,
-    /*out*/std::string* error_msg) {
-  DCHECK(named_components_count != nullptr);
-
-  // Validate boot class path. Require a path and non-empty name in each component.
-  for (const std::string& bcp_component : boot_class_path_) {
-    size_t bcp_slash_pos = bcp_component.rfind('/');
-    if (bcp_slash_pos == std::string::npos || bcp_slash_pos == bcp_component.size() - 1u) {
-      *error_msg = StringPrintf("Invalid boot class path component: %s", bcp_component.c_str());
-      return false;
-    }
-  }
-
-  // Validate the format of image location components.
-  size_t components_size = components.size();
-  if (components_size == 0u) {
-    *error_msg = "Empty image location.";
-    return false;
-  }
-  size_t wildcards_start = components_size;  // No wildcards.
-  for (size_t i = 0; i != components_size; ++i) {
-    const std::string& component = components[i];
-    DCHECK(!component.empty());  // Guaranteed by Split().
-    size_t wildcard_pos = component.find('*');
-    if (wildcard_pos == std::string::npos) {
-      if (wildcards_start != components.size()) {
-        *error_msg =
-            StringPrintf("Image component without wildcard after component with wildcard: %s",
-                         component.c_str());
-        return false;
-      }
-      if (component.back() == '/') {
-        *error_msg = StringPrintf("Image component ends with path separator: %s",
-                                  component.c_str());
-        return false;
-      }
-    } else {
-      if (wildcards_start == components_size) {
-        wildcards_start = i;
-      }
-      // Wildcard must be the last character.
-      if (wildcard_pos != component.size() - 1u) {
-        *error_msg = StringPrintf("Unsupported wildcard (*) position in %s", component.c_str());
-        return false;
-      }
-      // And it must be either plain wildcard or preceded by a path separator.
-      if (component.size() != 1u && component[wildcard_pos - 1u] != '/') {
-        *error_msg = StringPrintf("Non-plain wildcard (*) not preceded by path separator '/': %s",
-                                  component.c_str());
-        return false;
-      }
-      if (i == 0) {
-        *error_msg = StringPrintf("Primary component contains wildcard (*): %s", component.c_str());
-        return false;
-      }
-    }
-  }
-
-  *named_components_count = wildcards_start;
-  return true;
-}
-
-bool ImageSpace::BootImageLayout::MatchNamedComponents(
-    ArrayRef<const std::string> named_components,
-    /*out*/std::vector<std::pair<std::string, size_t>>* base_locations_and_bcp_indexes,
-    /*out*/std::string* error_msg) {
-  DCHECK(!named_components.empty());
-  DCHECK(base_locations_and_bcp_indexes->empty());
-  base_locations_and_bcp_indexes->reserve(named_components.size());
-  size_t bcp_component_count = boot_class_path_.size();
-  size_t bcp_pos = 0;
-  std::string base_name;
-  for (size_t i = 0, size = named_components.size(); i != size; ++i) {
-    const std::string& component = named_components[i];
-    size_t slash_pos = component.rfind('/');
-    std::string base_location;
-    if (i == 0u) {
-      // The primary boot image name is taken as provided. It forms the base
-      // for expanding the extension filenames.
-      if (slash_pos != std::string::npos) {
-        base_name = component.substr(slash_pos + 1u);
-        base_location = component;
-      } else {
-        base_name = component;
-        size_t bcp_slash_pos = boot_class_path_[0u].rfind('/');
-        DCHECK_NE(bcp_slash_pos, std::string::npos);
-        base_location = boot_class_path_[0u].substr(0u, bcp_slash_pos + 1u) + component;
-      }
-    } else {
-      std::string to_match;
-      if (slash_pos != std::string::npos) {
-        // If we have the full path, we just need to match the filename to the BCP component.
-        base_location = component.substr(0u, slash_pos + 1u) + base_name;
-        to_match = component;
-      }
-      while (true) {
-        if (slash_pos == std::string::npos) {
-          // If we do not have a full path, we need to update the path based on the BCP location.
-          size_t bcp_slash_pos = boot_class_path_[bcp_pos].rfind('/');
-          DCHECK_NE(bcp_slash_pos, std::string::npos);
-          std::string path = boot_class_path_[bcp_pos].substr(0u, bcp_slash_pos + 1u);
-          to_match = path + component;
-          base_location = path + base_name;
-        }
-        if (ExpandLocation(base_location, bcp_pos) == to_match) {
-          break;
-        }
-        ++bcp_pos;
-        if (bcp_pos == bcp_component_count) {
-          *error_msg = StringPrintf("Image component %s does not match a boot class path component",
-                                    component.c_str());
-          return false;
-        }
-      }
-    }
-    base_locations_and_bcp_indexes->emplace_back(base_location, bcp_pos);
-    ++bcp_pos;
-  }
-  return true;
-}
-
-bool ImageSpace::BootImageLayout::ReadHeader(const std::string& base_location,
-                                             const std::string& base_filename,
-                                             size_t bcp_index,
-                                             size_t bcp_component_count,
-                                             /*out*/std::string* error_msg) {
-  DCHECK_LE(next_bcp_index_, bcp_index);
-  DCHECK_LT(bcp_index, bcp_component_count);
-  size_t allowed_component_count = bcp_component_count - bcp_index;
-  DCHECK_LE(total_reservation_size_, kMaxTotalImageReservationSize);
-  size_t allowed_reservation_size = kMaxTotalImageReservationSize - total_reservation_size_;
-
-  std::string actual_filename = ExpandLocation(base_filename, bcp_index);
-  ImageHeader header;
-  if (!ReadSpecificImageHeader(actual_filename.c_str(), &header, error_msg)) {
-    return false;
-  }
-  if (header.GetComponentCount() == 0u ||
-      header.GetComponentCount() > allowed_component_count) {
-    *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
-                                  "expected non-zero and <= %zu",
-                              actual_filename.c_str(),
-                              header.GetComponentCount(),
-                              allowed_component_count);
-    return false;
-  }
-  if (header.GetImageReservationSize() > allowed_reservation_size) {
-    *error_msg = StringPrintf("Reservation size too big in %s: %u > %zu",
-                              actual_filename.c_str(),
-                              header.GetImageReservationSize(),
-                              allowed_reservation_size);
-    return false;
-  }
-
-  if (chunks_.empty()) {
-    base_address_ = reinterpret_cast32<uint32_t>(header.GetImageBegin());
-  }
-  ImageChunk chunk;
-  chunk.base_location = base_location;
-  chunk.base_filename = base_filename;
-  chunk.start_index = bcp_index;
-  chunk.component_count = header.GetComponentCount();
-  chunk.reservation_size = header.GetImageReservationSize();
-  chunk.checksum = header.GetImageChecksum();
-  chunks_.push_back(std::move(chunk));
-  next_bcp_index_ = bcp_index + header.GetComponentCount();
-  total_component_count_ += header.GetComponentCount();
-  total_reservation_size_ += header.GetImageReservationSize();
-  return true;
-}
-
-bool ImageSpace::BootImageLayout::CheckAndRemoveLastChunkChecksum(
-    /*inout*/std::string_view* oat_checksums,
-    /*out*/std::string* error_msg) {
-  DCHECK(oat_checksums != nullptr);
-  DCHECK(!chunks_.empty());
-  const ImageChunk& chunk = chunks_.back();
-  size_t component_count = chunk.component_count;
-  size_t checksum = chunk.checksum;
-  if (!CheckAndRemoveImageChecksum(component_count, checksum, oat_checksums, error_msg)) {
-    DCHECK(!error_msg->empty());
-    return false;
-  }
-  if (oat_checksums->empty()) {
-    if (next_bcp_index_ != boot_class_path_.size()) {
-      *error_msg = StringPrintf("Checksum too short, missing %zu components.",
-                                boot_class_path_.size() - next_bcp_index_);
-      return false;
-    }
-    return true;
-  }
-  if (!StartsWith(*oat_checksums, ":")) {
-    *error_msg = StringPrintf("Missing ':' separator at start of %s",
-                              std::string(*oat_checksums).c_str());
-    return false;
-  }
-  oat_checksums->remove_prefix(1u);
-  if (oat_checksums->empty()) {
-    *error_msg = "Missing checksums after the ':' separator.";
-    return false;
-  }
-  return true;
-}
-
-template <typename FilenameFn>
-bool ImageSpace::BootImageLayout::LoadOrValidate(FilenameFn&& filename_fn,
-                                                 /*inout*/std::string_view* oat_checksums,
-                                                 /*out*/std::string* error_msg) {
-  DCHECK(GetChunks().empty());
-  DCHECK_EQ(GetBaseAddress(), 0u);
-  bool validate = (oat_checksums != nullptr);
-  static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
-  DCHECK(!validate || StartsWith(*oat_checksums, "i"));
-
-  std::vector<std::string> components;
-  Split(image_location_, ':', &components);
-  size_t named_components_count = 0u;
-  if (!VerifyImageLocation(components, &named_components_count, error_msg)) {
-    return false;
-  }
-
-  ArrayRef<const std::string> named_components =
-      ArrayRef<const std::string>(components).SubArray(/*pos=*/ 0u, named_components_count);
-
-  std::vector<std::pair<std::string, size_t>> base_locations_and_bcp_indexes;
-  if (!MatchNamedComponents(named_components, &base_locations_and_bcp_indexes, error_msg)) {
-    return false;
-  }
-
-  // Load the image headers of named components.
-  DCHECK_EQ(base_locations_and_bcp_indexes.size(), named_components.size());
-  const size_t bcp_component_count = boot_class_path_.size();
-  size_t bcp_pos = 0u;
-  for (size_t i = 0, size = named_components.size(); i != size; ++i) {
-    const std::string& base_location = base_locations_and_bcp_indexes[i].first;
-    size_t bcp_index = base_locations_and_bcp_indexes[i].second;
-    if (bcp_index < bcp_pos) {
-      DCHECK_NE(i, 0u);
-      LOG(ERROR) << "Named image component already covered by previous image: " << base_location;
-      continue;
-    }
-    if (validate && bcp_index > bcp_pos) {
-      *error_msg = StringPrintf("End of contiguous boot class path images, remaining checksum: %s",
-                                std::string(*oat_checksums).c_str());
-      return false;
-    }
-    std::string local_error_msg;
-    std::string* err_msg = (i == 0 || validate) ? error_msg : &local_error_msg;
-    std::string base_filename;
-    if (!filename_fn(base_location, &base_filename, err_msg) ||
-        !ReadHeader(base_location, base_filename, bcp_index, bcp_component_count, err_msg)) {
-      if (i == 0u || validate) {
-        return false;
-      }
-      VLOG(image) << "Error reading named image component header for " << base_location
-                  << ", error: " << local_error_msg;
-      bcp_pos = bcp_index + 1u;  // Skip at least this component.
-      DCHECK_GT(bcp_pos, GetNextBcpIndex());
-      continue;
-    }
-    if (validate) {
-      if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
-        return false;
-      }
-      if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
-        return true;  // Let the caller deal with the dex file checksums if any.
-      }
-    }
-    bcp_pos = GetNextBcpIndex();
-  }
-
-  // Look for remaining components if there are any wildcard specifications.
-  ArrayRef<const std::string> search_paths =
-      ArrayRef<const std::string>(components).SubArray(/*pos=*/ named_components_count);
-  if (!search_paths.empty()) {
-    const std::string& primary_base_location = base_locations_and_bcp_indexes[0].first;
-    size_t base_slash_pos = primary_base_location.rfind('/');
-    DCHECK_NE(base_slash_pos, std::string::npos);
-    std::string base_name = primary_base_location.substr(base_slash_pos + 1u);
-    DCHECK(!base_name.empty());
-    while (bcp_pos != bcp_component_count) {
-      const std::string& bcp_component =  boot_class_path_[bcp_pos];
-      bool found = false;
-      for (const std::string& path : search_paths) {
-        std::string base_location;
-        if (path.size() == 1u) {
-          DCHECK_EQ(path, "*");
-          size_t slash_pos = bcp_component.rfind('/');
-          DCHECK_NE(slash_pos, std::string::npos);
-          base_location = bcp_component.substr(0u, slash_pos + 1u) + base_name;
-        } else {
-          DCHECK(EndsWith(path, "/*"));
-          base_location = path.substr(0u, path.size() - 1u) + base_name;
-        }
-        std::string err_msg;  // Ignored.
-        std::string base_filename;
-        if (filename_fn(base_location, &base_filename, &err_msg) &&
-            ReadHeader(base_location, base_filename, bcp_pos, bcp_component_count, &err_msg)) {
-          VLOG(image) << "Found image extension for " << ExpandLocation(base_location, bcp_pos);
-          bcp_pos = GetNextBcpIndex();
-          found = true;
-          if (validate) {
-            if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
-              return false;
-            }
-            if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
-              return true;  // Let the caller deal with the dex file checksums if any.
-            }
-          }
-          break;
-        }
-      }
-      if (!found) {
-        if (validate) {
-          *error_msg = StringPrintf("Missing extension for %s, remaining checksum: %s",
-                                    bcp_component.c_str(),
-                                    std::string(*oat_checksums).c_str());
-          return false;
-        }
-        ++bcp_pos;
-      }
-    }
-  }
-
-  return true;
-}
-
-bool ImageSpace::BootImageLayout::LoadOrValidateFromSystem(InstructionSet image_isa,
-                                                           /*inout*/std::string_view* oat_checksums,
-                                                           /*out*/std::string* error_msg) {
-  auto filename_fn = [image_isa](const std::string& location,
-                                 /*out*/std::string* filename,
-                                 /*out*/std::string* err_msg ATTRIBUTE_UNUSED) {
-    *filename = GetSystemImageFilename(location.c_str(), image_isa);
-    return true;
-  };
-  return LoadOrValidate(filename_fn, oat_checksums, error_msg);
-}
-
-bool ImageSpace::BootImageLayout::LoadOrValidateFromDalvikCache(
-    const std::string& dalvik_cache,
-    /*inout*/std::string_view* oat_checksums,
-    /*out*/std::string* error_msg) {
-  auto filename_fn = [&dalvik_cache](const std::string& location,
-                                     /*out*/std::string* filename,
-                                     /*out*/std::string* err_msg) {
-    return GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), filename, err_msg);
-  };
-  return LoadOrValidate(filename_fn, oat_checksums, error_msg);
-}
-
 class ImageSpace::BootImageLoader {
  public:
   BootImageLoader(const std::vector<std::string>& boot_class_path,
@@ -1927,10 +1398,8 @@
   bool IsZygote() const { return is_zygote_; }
 
   void FindImageFiles() {
-    BootImageLayout layout(image_location_, boot_class_path_);
-    std::string image_location = layout.GetPrimaryImageLocation();
     std::string system_filename;
-    bool found_image = FindImageFilenameImpl(image_location.c_str(),
+    bool found_image = FindImageFilenameImpl(image_location_.c_str(),
                                              image_isa_,
                                              &has_system_,
                                              &system_filename,
@@ -1959,115 +1428,124 @@
 
   bool LoadFromSystem(bool validate_oat_file,
                       size_t extra_reservation_size,
-                      /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+                      /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
                       /*out*/MemMap* extra_reservation,
-                      /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
+                      /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+    TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+    std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
+
+    if (!LoadFromFile(filename,
+                      validate_oat_file,
+                      extra_reservation_size,
+                      &logger,
+                      boot_image_spaces,
+                      extra_reservation,
+                      error_msg)) {
+      return false;
+    }
+
+    if (VLOG_IS_ON(image)) {
+      LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
+          << boot_image_spaces->front();
+      logger.Dump(LOG_STREAM(INFO));
+    }
+    return true;
+  }
 
   bool LoadFromDalvikCache(
       bool validate_oat_file,
       size_t extra_reservation_size,
-      /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+      /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
       /*out*/MemMap* extra_reservation,
-      /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
+      /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+    TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+    DCHECK(DalvikCacheExists());
+
+    if (!LoadFromFile(cache_filename_,
+                      validate_oat_file,
+                      extra_reservation_size,
+                      &logger,
+                      boot_image_spaces,
+                      extra_reservation,
+                      error_msg)) {
+      return false;
+    }
+
+    if (VLOG_IS_ON(image)) {
+      LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
+          << boot_image_spaces->front();
+      logger.Dump(LOG_STREAM(INFO));
+    }
+    return true;
+  }
 
  private:
-  bool LoadImage(
-      const BootImageLayout& layout,
+  bool LoadFromFile(
+      const std::string& filename,
       bool validate_oat_file,
       size_t extra_reservation_size,
       TimingLogger* logger,
-      /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+      /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
       /*out*/MemMap* extra_reservation,
       /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
-    ArrayRef<const BootImageLayout::ImageChunk> chunks = layout.GetChunks();
-    DCHECK(!chunks.empty());
-    const uint32_t base_address = layout.GetBaseAddress();
-    const size_t image_component_count = layout.GetTotalComponentCount();
-    const size_t image_reservation_size = layout.GetTotalReservationSize();
-
-    DCHECK_LE(image_reservation_size, kMaxTotalImageReservationSize);
-    static_assert(kMaxTotalImageReservationSize < std::numeric_limits<uint32_t>::max());
-    if (extra_reservation_size > std::numeric_limits<uint32_t>::max() - image_reservation_size) {
-      // Since the `image_reservation_size` is limited to kMaxTotalImageReservationSize,
-      // the `extra_reservation_size` would have to be really excessive to fail this check.
-      *error_msg = StringPrintf("Excessive extra reservation size: %zu", extra_reservation_size);
+    ImageHeader system_hdr;
+    if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr, error_msg)) {
       return false;
     }
-
-    // Reserve address space. If relocating, choose a random address for ALSR.
-    uint8_t* addr = reinterpret_cast<uint8_t*>(
-        relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : base_address);
-    MemMap image_reservation =
-        ReserveBootImageMemory(addr, image_reservation_size + extra_reservation_size, error_msg);
-    if (!image_reservation.IsValid()) {
+    if (system_hdr.GetComponentCount() == 0u ||
+        system_hdr.GetComponentCount() > boot_class_path_.size()) {
+      *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
+                                    "expected non-zero and <= %zu",
+                                filename.c_str(),
+                                system_hdr.GetComponentCount(),
+                                boot_class_path_.size());
       return false;
     }
-
-    // Load components.
-    std::vector<std::unique_ptr<ImageSpace>> spaces;
-    spaces.reserve(image_component_count);
-    size_t max_image_space_dependencies = 0u;
-    for (size_t i = 0, num_chunks = chunks.size(); i != num_chunks; ++i) {
-      const BootImageLayout::ImageChunk& chunk = chunks[i];
-      std::string extension_error_msg;
-      uint8_t* old_reservation_begin = image_reservation.Begin();
-      size_t old_reservation_size = image_reservation.Size();
-      DCHECK_LE(chunk.reservation_size, old_reservation_size);
-      if (!LoadComponents(chunk,
-                          validate_oat_file,
-                          max_image_space_dependencies,
-                          logger,
-                          &spaces,
-                          &image_reservation,
-                          (i == 0) ? error_msg : &extension_error_msg)) {
-        // Failed to load the chunk. If this is the primary boot image, report the error.
-        if (i == 0) {
-          return false;
-        }
-        // For extension, shrink the reservation (and remap if needed, see below).
-        size_t new_reservation_size = old_reservation_size - chunk.reservation_size;
-        if (new_reservation_size == 0u) {
-          DCHECK_EQ(extra_reservation_size, 0u);
-          DCHECK_EQ(i + 1u, num_chunks);
-          image_reservation.Reset();
-        } else if (old_reservation_begin != image_reservation.Begin()) {
-          // Part of the image reservation has been used and then unmapped when
-          // rollling back the partial boot image extension load. Try to remap
-          // the image reservation. As this should be running single-threaded,
-          // the address range should still be available to mmap().
-          image_reservation.Reset();
-          std::string remap_error_msg;
-          image_reservation = ReserveBootImageMemory(old_reservation_begin,
-                                                     new_reservation_size,
-                                                     &remap_error_msg);
-          if (!image_reservation.IsValid()) {
-            *error_msg = StringPrintf("Failed to remap boot image reservation after failing "
-                                          "to load boot image extension (%s: %s): %s",
-                                      boot_class_path_locations_[chunk.start_index].c_str(),
-                                      extension_error_msg.c_str(),
-                                      remap_error_msg.c_str());
-            return false;
-          }
-        } else {
-          DCHECK_EQ(old_reservation_size, image_reservation.Size());
-          image_reservation.SetSize(new_reservation_size);
-        }
-        LOG(ERROR) << "Failed to load boot image extension "
-            << boot_class_path_locations_[chunk.start_index] << ": " << extension_error_msg;
-      }
-      // Update `max_image_space_dependencies` if all previous BCP components
-      // were covered and loading the current chunk succeeded.
-      if (max_image_space_dependencies == chunk.start_index &&
-          spaces.size() == chunk.start_index + chunk.component_count) {
-        max_image_space_dependencies = chunk.start_index + chunk.component_count;
-      }
-    }
-
+    MemMap image_reservation;
     MemMap local_extra_reservation;
-    if (!RemapExtraReservation(extra_reservation_size,
-                               &image_reservation,
-                               &local_extra_reservation,
-                               error_msg)) {
+    if (!ReserveBootImageMemory(system_hdr.GetImageReservationSize(),
+                                reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin()),
+                                extra_reservation_size,
+                                &image_reservation,
+                                &local_extra_reservation,
+                                error_msg)) {
+      return false;
+    }
+
+    ArrayRef<const std::string> provided_locations(boot_class_path_locations_.data(),
+                                                   system_hdr.GetComponentCount());
+    std::vector<std::string> locations =
+        ExpandMultiImageLocations(provided_locations, image_location_);
+    std::vector<std::string> filenames =
+        ExpandMultiImageLocations(provided_locations, filename);
+    DCHECK_EQ(locations.size(), filenames.size());
+    std::vector<std::unique_ptr<ImageSpace>> spaces;
+    spaces.reserve(locations.size());
+    for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
+      spaces.push_back(Load(locations[i], filenames[i], logger, &image_reservation, error_msg));
+      const ImageSpace* space = spaces.back().get();
+      if (space == nullptr) {
+        return false;
+      }
+      uint32_t expected_component_count = (i == 0u) ? system_hdr.GetComponentCount() : 0u;
+      uint32_t expected_reservation_size = (i == 0u) ? system_hdr.GetImageReservationSize() : 0u;
+      if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
+          !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
+        return false;
+      }
+    }
+    for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
+      if (!OpenOatFile(spaces[i].get(),
+                       boot_class_path_[i],
+                       validate_oat_file,
+                       /*available_dependencies=*/ ArrayRef<const std::unique_ptr<ImageSpace>>(),
+                       logger,
+                       &image_reservation,
+                       error_msg)) {
+        return false;
+      }
+    }
+    if (!CheckReservationExhausted(image_reservation, error_msg)) {
       return false;
     }
 
@@ -2227,7 +1705,6 @@
                                                            simple_relocate_visitor);
 
     // Retrieve the Class.class, Method.class and Constructor.class needed in the loops below.
-    ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots;
     ObjPtr<mirror::Class> class_class;
     ObjPtr<mirror::Class> method_class;
     ObjPtr<mirror::Class> constructor_class;
@@ -2242,8 +1719,9 @@
           kExtension ? source_size - image_size : image_size);
       int32_t class_roots_index = enum_cast<int32_t>(ImageHeader::kClassRoots);
       DCHECK_LT(class_roots_index, image_roots->GetLength<kVerifyNone>());
-      class_roots = ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
-          image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
+      ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
+          ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
+              image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
       if (kExtension) {
         DCHECK(patched_objects->Test(class_roots.Ptr()));
         class_class = GetClassRoot<mirror::Class, kWithoutReadBarrier>(class_roots);
@@ -2375,12 +1853,6 @@
         pos += RoundUp(object->SizeOf<kVerifyNone>(), kObjectAlignment);
       }
     }
-    if (kIsDebugBuild && !kExtension) {
-      // We used just Test() instead of Set() above but we need to use Set()
-      // for class roots to satisfy a DCHECK() for extensions.
-      DCHECK(!patched_objects->Test(class_roots.Ptr()));
-      patched_objects->Set(class_roots.Ptr());
-    }
   }
 
   void MaybeRelocateSpaces(const std::vector<std::unique_ptr<ImageSpace>>& spaces,
@@ -2561,117 +2033,37 @@
     return true;
   }
 
-  bool LoadComponents(const BootImageLayout::ImageChunk& chunk,
-                      bool validate_oat_file,
-                      size_t max_image_space_dependencies,
-                      TimingLogger* logger,
-                      /*inout*/std::vector<std::unique_ptr<ImageSpace>>* spaces,
-                      /*inout*/MemMap* image_reservation,
-                      /*out*/std::string* error_msg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    // Make sure we destroy the spaces we created if we're returning an error.
-    // Note that this can unmap part of the original `image_reservation`.
-    class Guard {
-     public:
-      explicit Guard(std::vector<std::unique_ptr<ImageSpace>>* spaces_in)
-          : spaces_(spaces_in), committed_(spaces_->size()) {}
-      void Commit() {
-        DCHECK_LT(committed_, spaces_->size());
-        committed_ = spaces_->size();
-      }
-      ~Guard() {
-        DCHECK_LE(committed_, spaces_->size());
-        spaces_->resize(committed_);
-      }
-     private:
-      std::vector<std::unique_ptr<ImageSpace>>* const spaces_;
-      size_t committed_;
-    };
-    Guard guard(spaces);
-
-    bool is_extension = (chunk.start_index != 0u);
-    DCHECK_NE(spaces->empty(), is_extension);
-    ArrayRef<const std::string> requested_bcp_locations =
-        ArrayRef<const std::string>(boot_class_path_locations_).SubArray(
-            chunk.start_index, chunk.component_count);
-    std::vector<std::string> locations =
-        ExpandMultiImageLocations(requested_bcp_locations, chunk.base_location, is_extension);
-    std::vector<std::string> filenames =
-        ExpandMultiImageLocations(requested_bcp_locations, chunk.base_filename, is_extension);
-    DCHECK_EQ(locations.size(), filenames.size());
-    for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
-      spaces->push_back(Load(locations[i], filenames[i], logger, image_reservation, error_msg));
-      const ImageSpace* space = spaces->back().get();
-      if (space == nullptr) {
-        return false;
-      }
-      uint32_t expected_component_count = (i == 0u) ? chunk.component_count : 0u;
-      uint32_t expected_reservation_size = (i == 0u) ? chunk.reservation_size : 0u;
-      if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
-          !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
-        return false;
-      }
-      if (i == 0 && chunk.checksum != space->GetImageHeader().GetImageChecksum()) {
-        *error_msg = StringPrintf("Image checksum modified since previously read from %s, "
-                                      "received %u, expected %u",
-                                  space->GetImageFilename().c_str(),
-                                  space->GetImageHeader().GetImageChecksum(),
-                                  chunk.checksum);
-        return false;
-      }
-    }
-    ArrayRef<const std::unique_ptr<ImageSpace>> available_dependencies =
-        ArrayRef<const std::unique_ptr<ImageSpace>>(*spaces).SubArray(/*pos=*/ 0u,
-                                                                      max_image_space_dependencies);
-    for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
-      ImageSpace* space = (*spaces)[spaces->size() - chunk.component_count + i].get();
-      if (!OpenOatFile(space,
-                       boot_class_path_[chunk.start_index + i],
-                       validate_oat_file,
-                       available_dependencies,
-                       logger,
-                       image_reservation,
-                       error_msg)) {
-        return false;
-      }
-    }
-
-    guard.Commit();
-    return true;
-  }
-
-  MemMap ReserveBootImageMemory(uint8_t* addr,
-                                uint32_t reservation_size,
-                                /*out*/std::string* error_msg) {
+  bool ReserveBootImageMemory(uint32_t reservation_size,
+                              uint32_t image_start,
+                              size_t extra_reservation_size,
+                              /*out*/MemMap* image_reservation,
+                              /*out*/MemMap* extra_reservation,
+                              /*out*/std::string* error_msg) {
     DCHECK_ALIGNED(reservation_size, kPageSize);
-    DCHECK_ALIGNED(addr, kPageSize);
-    return MemMap::MapAnonymous("Boot image reservation",
-                                addr,
-                                reservation_size,
-                                PROT_NONE,
-                                /*low_4gb=*/ true,
-                                /*reuse=*/ false,
-                                /*reservation=*/ nullptr,
-                                error_msg);
-  }
-
-  bool RemapExtraReservation(size_t extra_reservation_size,
-                             /*inout*/MemMap* image_reservation,
-                             /*out*/MemMap* extra_reservation,
-                             /*out*/std::string* error_msg) {
-    DCHECK_ALIGNED(extra_reservation_size, kPageSize);
-    DCHECK(!extra_reservation->IsValid());
-    size_t expected_size = image_reservation->IsValid() ? image_reservation->Size() : 0u;
-    if (extra_reservation_size != expected_size) {
-      *error_msg = StringPrintf("Image reservation mismatch after loading boot image: %zu != %zu",
-                                extra_reservation_size,
-                                expected_size);
+    DCHECK_ALIGNED(image_start, kPageSize);
+    DCHECK(!image_reservation->IsValid());
+    DCHECK_LT(extra_reservation_size, std::numeric_limits<uint32_t>::max() - reservation_size);
+    size_t total_size = reservation_size + extra_reservation_size;
+    // If relocating, choose a random address for ALSR.
+    uint32_t addr = relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : image_start;
+    *image_reservation =
+        MemMap::MapAnonymous("Boot image reservation",
+                             reinterpret_cast32<uint8_t*>(addr),
+                             total_size,
+                             PROT_NONE,
+                             /*low_4gb=*/ true,
+                             /*reuse=*/ false,
+                             /*reservation=*/ nullptr,
+                             error_msg);
+    if (!image_reservation->IsValid()) {
       return false;
     }
+    DCHECK(!extra_reservation->IsValid());
     if (extra_reservation_size != 0u) {
-      DCHECK(image_reservation->IsValid());
-      DCHECK_EQ(extra_reservation_size, image_reservation->Size());
-      *extra_reservation = image_reservation->RemapAtEnd(image_reservation->Begin(),
+      DCHECK_ALIGNED(extra_reservation_size, kPageSize);
+      DCHECK_LT(extra_reservation_size, image_reservation->Size());
+      uint8_t* split = image_reservation->End() - extra_reservation_size;
+      *extra_reservation = image_reservation->RemapAtEnd(split,
                                                          "Boot image extra reservation",
                                                          PROT_NONE,
                                                          error_msg);
@@ -2679,17 +2071,27 @@
         return false;
       }
     }
-    DCHECK(!image_reservation->IsValid());
+
     return true;
   }
 
-  const ArrayRef<const std::string> boot_class_path_;
-  const ArrayRef<const std::string> boot_class_path_locations_;
-  const std::string image_location_;
-  const InstructionSet image_isa_;
-  const bool relocate_;
-  const bool executable_;
-  const bool is_zygote_;
+  bool CheckReservationExhausted(const MemMap& image_reservation, /*out*/std::string* error_msg) {
+    if (image_reservation.IsValid()) {
+      *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p",
+                                image_reservation.Begin(),
+                                image_reservation.End());
+      return false;
+    }
+    return true;
+  }
+
+  const std::vector<std::string>& boot_class_path_;
+  const std::vector<std::string>& boot_class_path_locations_;
+  const std::string& image_location_;
+  InstructionSet image_isa_;
+  bool relocate_;
+  bool executable_;
+  bool is_zygote_;
   bool has_system_;
   bool has_cache_;
   bool is_global_cache_;
@@ -2698,68 +2100,6 @@
   std::string cache_filename_;
 };
 
-bool ImageSpace::BootImageLoader::LoadFromSystem(
-    bool validate_oat_file,
-    size_t extra_reservation_size,
-    /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
-    /*out*/MemMap* extra_reservation,
-    /*out*/std::string* error_msg) {
-  TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
-
-  BootImageLayout layout(image_location_, boot_class_path_);
-  if (!layout.LoadFromSystem(image_isa_, error_msg)) {
-    return false;
-  }
-
-  if (!LoadImage(layout,
-                 validate_oat_file,
-                 extra_reservation_size,
-                 &logger,
-                 boot_image_spaces,
-                 extra_reservation,
-                 error_msg)) {
-    return false;
-  }
-
-  if (VLOG_IS_ON(image)) {
-    LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
-        << boot_image_spaces->front();
-    logger.Dump(LOG_STREAM(INFO));
-  }
-  return true;
-}
-
-bool ImageSpace::BootImageLoader::LoadFromDalvikCache(
-    bool validate_oat_file,
-    size_t extra_reservation_size,
-    /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
-    /*out*/MemMap* extra_reservation,
-    /*out*/std::string* error_msg) {
-  TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
-  DCHECK(DalvikCacheExists());
-
-  BootImageLayout layout(image_location_, boot_class_path_);
-  if (!layout.LoadFromDalvikCache(dalvik_cache_, error_msg)) {
-    return false;
-  }
-  if (!LoadImage(layout,
-                 validate_oat_file,
-                 extra_reservation_size,
-                 &logger,
-                 boot_image_spaces,
-                 extra_reservation,
-                 error_msg)) {
-    return false;
-  }
-
-  if (VLOG_IS_ON(image)) {
-    LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
-        << boot_image_spaces->front();
-    logger.Dump(LOG_STREAM(INFO));
-  }
-  return true;
-}
-
 static constexpr uint64_t kLowSpaceValue = 50 * MB;
 static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
 
@@ -2805,7 +2145,7 @@
     bool executable,
     bool is_zygote,
     size_t extra_reservation_size,
-    /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+    /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
     /*out*/MemMap* extra_reservation) {
   ScopedTrace trace(__FUNCTION__);
 
@@ -3048,6 +2388,11 @@
   return true;
 }
 
+static void AppendImageChecksum(const ImageHeader& header, /*inout*/std::string* checksums) {
+  static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
+  StringAppendF(checksums, "i;%u/%08x", header.GetComponentCount(), header.GetImageChecksum());
+}
+
 std::string ImageSpace::GetBootClassPathChecksums(
     ArrayRef<ImageSpace* const> image_spaces,
     ArrayRef<const DexFile* const> boot_class_path) {
@@ -3068,7 +2413,7 @@
     if (image_pos != 0u) {
       boot_image_checksum += ':';
     }
-    AppendImageChecksum(component_count, current_header.GetImageChecksum(), &boot_image_checksum);
+    AppendImageChecksum(current_header, &boot_image_checksum);
     for (size_t component_index = 0; component_index != component_count; ++component_index) {
       const ImageSpace* space = image_spaces[image_pos + component_index];
       const OatFile* oat_file = space->oat_file_non_owned_;
@@ -3091,10 +2436,10 @@
   DCHECK(boot_class_path_tail.empty() ||
          !DexFileLoader::IsMultiDexLocation(boot_class_path_tail.front()->GetLocation().c_str()));
   for (const DexFile* dex_file : boot_class_path_tail) {
+    if (!boot_image_checksum.empty()) {
+      boot_image_checksum += ':';
+    }
     if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
-      if (!boot_image_checksum.empty()) {
-        boot_image_checksum += ':';
-      }
       boot_image_checksum += kDexFileChecksumPrefix;
     }
     StringAppendF(&boot_image_checksum, "/%08x", dex_file->GetLocationChecksum());
@@ -3134,6 +2479,21 @@
   return component_count;
 }
 
+static bool CheckAndRemoveImageChecksum(const ImageHeader& header,
+                                        /*inout*/std::string_view* oat_checksums,
+                                        /*out*/std::string* error_msg) {
+  std::string image_checksum;
+  AppendImageChecksum(header, &image_checksum);
+  if (!StartsWith(*oat_checksums, image_checksum)) {
+    *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
+                              std::string(*oat_checksums).c_str(),
+                              image_checksum.c_str());
+    return false;
+  }
+  oat_checksums->remove_prefix(image_checksum.size());
+  return true;
+}
+
 bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums,
                                               std::string_view oat_boot_class_path,
                                               const std::string& image_location,
@@ -3155,39 +2515,75 @@
     return false;
   }
 
-  size_t bcp_pos = 0u;
-  if (StartsWith(oat_checksums, "i")) {
-    BootImageLayout layout(image_location, boot_class_path);
-    std::string primary_image_location = layout.GetPrimaryImageLocation();
-    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 = false;
-    if (!FindImageFilename(primary_image_location.c_str(),
-                           image_isa,
-                           &system_filename,
-                           &has_system,
-                           &cache_filename,
-                           &dalvik_cache_exists,
-                           &has_cache,
-                           &is_global_cache)) {
-      *error_msg = StringPrintf("Unable to find image file for %s and %s",
-                                image_location.c_str(),
-                                GetInstructionSetString(image_isa));
-      return false;
-    }
+  bool load_extensions = false;  // TODO: Boot image extensions.
+  const std::string& actual_image_location = image_location;  // TODO: Boot image extensions.
+  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 = false;
+  if (!FindImageFilename(actual_image_location.c_str(),
+                         image_isa,
+                         &system_filename,
+                         &has_system,
+                         &cache_filename,
+                         &dalvik_cache_exists,
+                         &has_cache,
+                         &is_global_cache)) {
+    *error_msg = StringPrintf("Unable to find image file for %s and %s",
+                              image_location.c_str(),
+                              GetInstructionSetString(image_isa));
+    return false;
+  }
 
-    DCHECK(has_system || has_cache);
-    bool use_system = (order == ImageSpaceLoadingOrder::kSystemFirst) ? has_system : !has_cache;
-    bool image_checksums_ok = use_system
-        ? layout.ValidateFromSystem(image_isa, &oat_checksums, error_msg)
-        : layout.ValidateFromDalvikCache(cache_filename, &oat_checksums, error_msg);
-    if (!image_checksums_ok) {
+  DCHECK(has_system || has_cache);
+  const std::string& filename = (order == ImageSpaceLoadingOrder::kSystemFirst)
+      ? (has_system ? system_filename : cache_filename)
+      : (has_cache ? cache_filename : system_filename);
+
+  size_t bcp_pos = 0u;
+  while (StartsWith(oat_checksums, "i")) {
+    const std::string& current_filename = filename;
+    if (bcp_pos != 0u) {
+      if (!load_extensions) {
+        *error_msg = "Checksum specifies boot image extension but extensions are not used.";
+        return false;
+      }
+      UNREACHABLE();  // TODO: Boot image extensions.
+    }
+    ImageHeader header;
+    if (!ReadSpecificImageHeader(current_filename.c_str(), &header, error_msg)) {
       return false;
     }
-    bcp_pos = layout.GetNextBcpIndex();
+    size_t component_count = header.GetComponentCount();
+    if (component_count == 0u || component_count > bcp_size - bcp_pos) {
+      *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
+                                    "expected non-zero and <= %zu",
+                                current_filename.c_str(),
+                                header.GetComponentCount(),
+                                bcp_size - bcp_pos);
+      return false;
+    }
+    if (!CheckAndRemoveImageChecksum(header, &oat_checksums, error_msg)) {
+      DCHECK(!error_msg->empty());
+      return false;
+    }
+    bcp_pos += component_count;
+    if (oat_checksums.empty()) {
+      if (bcp_pos != bcp_size) {
+        *error_msg = StringPrintf("Checksum too short, missing %zu components.",
+                                  bcp_size - bcp_pos);
+        return false;
+      }
+      return true;
+    }
+    if (!StartsWith(oat_checksums, ":")) {
+      *error_msg = StringPrintf("Missing ':' separator at start of %s",
+                                std::string(oat_checksums).c_str());
+      return false;
+    }
+    oat_checksums.remove_prefix(1u);
   }
 
   for ( ; bcp_pos != bcp_size; ++bcp_pos) {
@@ -3266,8 +2662,7 @@
     uint32_t component_count = current_header.GetComponentCount();
     DCHECK_NE(component_count, 0u);
     DCHECK_LE(component_count, image_spaces.size() - image_pos);
-    uint32_t checksum = current_header.GetImageChecksum();
-    if (!CheckAndRemoveImageChecksum(component_count, checksum, &oat_checksums, error_msg)) {
+    if (!CheckAndRemoveImageChecksum(current_header, &oat_checksums, error_msg)) {
       DCHECK(!error_msg->empty());
       return false;
     }
@@ -3278,9 +2673,7 @@
         size_t num_dex_files = oat_file->GetOatDexFiles().size();
         CHECK_NE(num_dex_files, 0u);
         const std::string main_location = oat_file->GetOatDexFiles()[0]->GetDexFileLocation();
-        // TODO: Get rid of the weird ResolveRelativeEncodedDexLocation() stuff from oat_file.cc
-        // and enable this check:
-        // CHECK_EQ(main_location, boot_class_path_locations[image_pos + component_index]);
+        CHECK_EQ(main_location, boot_class_path[image_pos + component_index]);
         CHECK(!DexFileLoader::IsMultiDexLocation(main_location.c_str()));
         for (size_t i = 1u; i != num_dex_files; ++i) {
           CHECK(DexFileLoader::IsMultiDexLocation(
@@ -3315,6 +2708,14 @@
 }
 
 std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
+    const std::vector<std::string>& dex_locations,
+    const std::string& image_location,
+    bool boot_image_extension) {
+  return ExpandMultiImageLocations(
+      ArrayRef<const std::string>(dex_locations), image_location, boot_image_extension);
+}
+
+std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
     ArrayRef<const std::string> dex_locations,
     const std::string& image_location,
     bool boot_image_extension) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index f56b42b..837facc 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -39,61 +39,11 @@
     return kSpaceTypeImageSpace;
   }
 
-  // Load boot image spaces for specified boot class path, image location, instruction set, etc.
+  // Load boot image spaces from a primary image file for a specified instruction set.
   //
   // On successful return, the loaded spaces are added to boot_image_spaces (which must be
   // empty on entry) and `extra_reservation` is set to the requested reservation located
   // after the end of the last loaded oat file.
-  //
-  // IMAGE LOCATION
-  //
-  // The "image location" is a colon-separated list that specifies one or more
-  // components by name and may also specify search paths for extensions
-  // corresponding to the remaining boot class path (BCP) extensions.
-  //
-  // The primary boot image can be specified as one of
-  //     <path>/<base-name>
-  //     <base-name>
-  // and the path of the first BCP component is used for the second form.
-  //
-  // Named extension specifications must correspond to an expansion of the
-  // <base-name> with a BCP component (for example boot.art with the BCP
-  // component name <jar-path>/framework.jar expands to boot-framework.art).
-  // They can be similarly specified as one of
-  //     <ext-path>/<ext-name>
-  //     <ext-name>
-  // and must be listed in the order of their corresponding BCP components.
-  //
-  // Search paths for remaining extensions can be specified after named
-  // components as one of
-  //     <search-path>/*
-  //     *
-  // where the second form means that the path of a particular BCP component
-  // should be used to search for that component's boot image extension. These
-  // paths will be searched in the specifed order.
-  //
-  // The actual filename shall be derived from the specified locations using
-  // `GetSystemImageFilename()` or `GetDalvikCacheFilename()`.
-  //
-  // Example image locations:
-  //     /system/framework/boot.art
-  //         - only primary boot image with full path.
-  //     boot.art:boot-framework.art
-  //         - primary and one extension, use BCP component paths.
-  //     /apex/com.android.art/boot.art:*
-  //         - primary with exact location, search for the rest based on BCP
-  //           component paths.
-  //     boot.art:/system/framework/*
-  //         - primary based on BCP component path, search for extensions in
-  //           /system/framework.
-  //     /apex/com.android.art/boot.art:/system/framework/*:*
-  //         - primary with exact location, search for extensions first in
-  //           /system/framework, then in the corresponding BCP component path.
-  //     /apex/com.android.art/boot.art:*:/system/framework/*
-  //         - primary with exact location, search for extensions first in the
-  //           corresponding BCP component path and then in /system/framework.
-  //     /apex/com.android.art/boot.art:*:boot-framework.jar
-  //         - invalid, named components may not follow search paths.
   static bool LoadBootImage(
       const std::vector<std::string>& boot_class_path,
       const std::vector<std::string>& boot_class_path_locations,
@@ -104,7 +54,7 @@
       bool executable,
       bool is_zygote,
       size_t extra_reservation_size,
-      /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+      /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
       /*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Try to open an existing app image space.
@@ -217,7 +167,7 @@
 
   // Expand a single image location to multi-image locations based on the dex locations.
   static std::vector<std::string> ExpandMultiImageLocations(
-      ArrayRef<const std::string> dex_locations,
+      const std::vector<std::string>& dex_locations,
       const std::string& image_location,
       bool boot_image_extension = false);
 
@@ -283,7 +233,12 @@
   friend class Space;
 
  private:
-  class BootImageLayout;
+  // Internal overload that takes ArrayRef<> instead of vector<>.
+  static std::vector<std::string> ExpandMultiImageLocations(
+      ArrayRef<const std::string> dex_locations,
+      const std::string& image_location,
+      bool boot_image_extension = false);
+
   class BootImageLoader;
   template <typename ReferenceVisitor>
   class ClassTableVisitor;
diff --git a/runtime/image.cc b/runtime/image.cc
index 08b81c1..256b957 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -93,13 +93,6 @@
   }
 }
 
-bool ImageHeader::IsAppImage() const {
-  // Unlike boot image and boot image extensions which include address space for
-  // oat files in their reservation size, app images are loaded separately from oat
-  // files and their reservation size is the image size rounded up to full page.
-  return image_reservation_size_ == RoundUp(image_size_, kPageSize);
-}
-
 bool ImageHeader::IsValid() const {
   if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) {
     return false;
diff --git a/runtime/image.h b/runtime/image.h
index a2d163a..13bf112 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -354,7 +354,11 @@
     return data_size_;
   }
 
-  bool IsAppImage() const;
+  bool IsAppImage() const {
+    // App images currently require a boot image, if the size is non zero then it is an app image
+    // header.
+    return boot_image_size_ != 0u;
+  }
 
   // Visit mirror::Objects in the section starting at base.
   // TODO: Delete base parameter if it is always equal to GetImageBegin.
diff --git a/runtime/oat.h b/runtime/oat.h
index 7817bd3..20ea5d3 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: Boot image extension.
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '4', '\0' } };
+  // Last oat version changed reason: Revert^3 Boot image extension.
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '5', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 9ef5fbb..4b5d5c32 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -855,18 +855,14 @@
     CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ);
     // Make sure the file lists a boot image dependency, otherwise the .data.bimg.rel.ro
     // section is bogus. The full dependency is checked before the code is executed.
-    // We cannot do this check if we do not have a key-value store, i.e. for secondary
-    // oat files for boot image extensions.
-    if (GetOatHeader().GetKeyValueStoreSize() != 0u) {
-      const char* boot_class_path_checksum =
-          GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
-      if (boot_class_path_checksum == nullptr ||
-          boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
-        *error_msg = StringPrintf("Oat file '%s' contains .data.bimg.rel.ro section "
-                                      "without boot image dependency.",
-                                  GetLocation().c_str());
-        return false;
-      }
+    const char* boot_class_path_checksum =
+        GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
+    if (boot_class_path_checksum == nullptr ||
+        boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
+      *error_msg = StringPrintf("Oat file '%s' contains .data.bimg.rel.ro section "
+                                    "without boot image dependency.",
+                                GetLocation().c_str());
+      return false;
     }
   }
 
diff --git a/test/run-test b/test/run-test
index 72e7562..eeeefbb 100755
--- a/test/run-test
+++ b/test/run-test
@@ -672,12 +672,12 @@
 elif [ "$runtime" = "art" ]; then
     if [ "$target_mode" = "no" ]; then
         guess_host_arch_name
-        run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art:*")
+        run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art")
         run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
     else
         guess_target_arch_name
         run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
-        run_args+=(--boot "/data/art-test/core${image_suffix}.art:/data/art-test/*")
+        run_args+=(--boot "/data/art-test/core${image_suffix}.art")
     fi
     if [ "$relocate" = "yes" ]; then
       run_args+=(--relocate)