Fail dex2oat when generating app image without boot image.

When the boot image is unavailable, an app image or a boot image
extension cannot be generated. Before this change, dex2oat crashes at
`CHECK_NE(image_begin, 0U)` in `ImageWriter`. After this change, dex2oat
returns early with a user friendly error message.

Bug: 211973309
Bug: 215665835
Test: atest art_standalone_dex2oat_tests
Change-Id: Ic6ca2cdade08b8f7c7ae4d06fc52aedafd71beab
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 2d78590..154b1ca 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1502,6 +1502,13 @@
     if (!CreateRuntime(std::move(runtime_options))) {
       return dex2oat::ReturnCode::kCreateRuntime;
     }
+    if (runtime_->GetHeap()->GetBootImageSpaces().empty() &&
+        (IsBootImageExtension() || IsAppImage())) {
+      LOG(ERROR) << "Cannot create "
+                 << (IsBootImageExtension() ? "boot image extension" : "app image")
+                 << " without a primary boot image.";
+      return dex2oat::ReturnCode::kOther;
+    }
     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.
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b340c80..7b6440f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -872,6 +872,21 @@
   RunTest(/*app_image=*/ true);
 }
 
+TEST_F(Dex2oatLayoutTest, TestLayoutAppImageMissingBootImage) {
+  std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+  std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+  std::string app_image_file = GetOdexDir() + "/DexOdexNoOat.art";
+  Copy(GetDexSrc2(), dex_location);
+
+  CompileProfileOdex(dex_location,
+                     odex_location,
+                     app_image_file,
+                     /*use_fd=*/ false,
+                     /*num_profile_classes=*/ 1,
+                     /*extra_args=*/ {"--boot-image=/nonx/boot.art"},
+                     /*expect_success=*/ false);
+}
+
 TEST_F(Dex2oatLayoutTest, TestLayoutMultipleProfiles) {
   std::string dex_location = GetScratchDir() + "/Dex.jar";
   std::string odex_location = GetOdexDir() + "/Dex.odex";