Support booting without functioning boot.oat/art patchoat.

Bug: 17000769

Change-Id: I89c26a905af12ea288742368c2c038afd57a879a
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c696089..4123fc6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1168,6 +1168,7 @@
                                        error_msg.c_str()));
     return nullptr;
   } else if (!oat_file->IsExecutable() &&
+             Runtime::Current()->GetHeap()->HasImageSpace() &&
              !VerifyOatImageChecksum(oat_file.get(), isa)) {
     error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for "
                                        "dex location '%s'. Image checksum incorrect.",
@@ -1250,6 +1251,7 @@
   std::string odex_error_msg;
   bool should_patch_system = false;
   bool odex_checksum_verified = false;
+  bool have_system_odex = false;
   {
     // There is a high probability that these both these oat files map similar/the same address
     // spaces so we must scope them like this so they each gets its turn.
@@ -1260,14 +1262,18 @@
                                                        &odex_error_msg)) {
       error_msgs->push_back(odex_error_msg);
       return odex_oat_file.release();
-    } else if (odex_checksum_verified) {
-      // We can just relocate
-      should_patch_system = true;
-      odex_error_msg = "Image Patches are incorrect";
+    } else {
+      if (odex_checksum_verified) {
+        // We can just relocate
+        should_patch_system = true;
+        odex_error_msg = "Image Patches are incorrect";
+      }
+      if (odex_oat_file.get() != nullptr) {
+        have_system_odex = true;
+      }
     }
   }
 
-
   std::string cache_error_msg;
   bool should_patch_cache = false;
   bool cache_checksum_verified = false;
@@ -1303,6 +1309,8 @@
         CHECK(have_dalvik_cache);
         ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
       }
+    } else if (have_system_odex) {
+      ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg);
     }
   }
   if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
@@ -1352,7 +1360,8 @@
   if (output.get() == nullptr) {
     return nullptr;
   }
-  if (VerifyOatImageChecksum(output.get(), isa)) {
+  if (!Runtime::Current()->GetHeap()->HasImageSpace() ||
+      VerifyOatImageChecksum(output.get(), isa)) {
     return output.release();
   } else {
     *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 50dfe21..d179a96 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -24,6 +24,7 @@
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "elf_file.h"
+#include "elf_utils.h"
 #include "oat.h"
 #include "mirror/art_method.h"
 #include "mirror/art_method-inl.h"
@@ -40,6 +41,17 @@
   CHECK(!location.empty());
 }
 
+OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
+                                  const std::string& location,
+                                  std::string* error_msg) {
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
+  oat_file->elf_file_.reset(elf_file);
+  Elf32_Shdr* hdr = elf_file->FindSectionByName(".rodata");
+  oat_file->begin_ = elf_file->Begin() + hdr->sh_offset;
+  oat_file->end_ = elf_file->Begin() + hdr->sh_size + hdr->sh_offset;
+  return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
+}
+
 OatFile* OatFile::OpenMemory(std::vector<uint8_t>& oat_contents,
                              const std::string& location,
                              std::string* error_msg) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 8535bf4..5b24e8f 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -40,6 +40,10 @@
 
 class OatFile {
  public:
+  // Opens an oat file contained within the given elf file. This is always opened as
+  // non-executable at the moment.
+  static OatFile* OpenWithElfFile(ElfFile* elf_file, const std::string& location,
+                                  std::string* error_msg);
   // Open an oat file. Returns NULL on failure.  Requested base can
   // optionally be used to request where the file should be loaded.
   static OatFile* Open(const std::string& filename,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3fc6ad5..d05d031 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -46,6 +46,7 @@
 #include "atomic.h"
 #include "class_linker.h"
 #include "debugger.h"
+#include "elf_file.h"
 #include "fault_handler.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/heap.h"
@@ -66,6 +67,7 @@
 #include "native_bridge_art_interface.h"
 #include "parsed_options.h"
 #include "oat_file.h"
+#include "os.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
 #include "ScopedLocalRef.h"
@@ -547,9 +549,74 @@
   VLOG(startup) << "Runtime::StartDaemonThreads exiting";
 }
 
+static bool OpenDexFilesFromImage(const std::vector<std::string>& dex_filenames,
+                                  const std::string& image_location,
+                                  std::vector<const DexFile*>& dex_files,
+                                  size_t* failures) {
+  std::string system_filename;
+  bool has_system = false;
+  std::string cache_filename_unused;
+  bool dalvik_cache_exists_unused;
+  bool has_cache_unused;
+  bool found_image = gc::space::ImageSpace::FindImageFilename(image_location.c_str(),
+                                                              kRuntimeISA,
+                                                              &system_filename,
+                                                              &has_system,
+                                                              &cache_filename_unused,
+                                                              &dalvik_cache_exists_unused,
+                                                              &has_cache_unused);
+  *failures = 0;
+  if (!found_image || !has_system) {
+    return false;
+  }
+  std::string error_msg;
+  // We are falling back to non-executable use of the oat file because patching failed, presumably
+  // due to lack of space.
+  std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str());
+  std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location.c_str());
+  std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str()));
+  if (file.get() == nullptr) {
+    return false;
+  }
+  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
+  if (elf_file.get() == nullptr) {
+    return false;
+  }
+  std::unique_ptr<OatFile> oat_file(OatFile::OpenWithElfFile(elf_file.release(), oat_location,
+                                                             &error_msg));
+  if (oat_file.get() == nullptr) {
+    LOG(INFO) << "Unable to use '" << oat_filename << "' because " << error_msg;
+    return false;
+  }
+
+  std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file->GetOatDexFiles();
+  for (size_t i = 0; i < oat_dex_files.size(); i++) {
+    const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+    if (oat_dex_file == nullptr) {
+      *failures += 1;
+      continue;
+    }
+    const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
+    if (dex_file == nullptr) {
+      *failures += 1;
+    } else {
+      dex_files.push_back(dex_file);
+    }
+  }
+  Runtime::Current()->GetClassLinker()->RegisterOatFile(oat_file.release());
+  return true;
+}
+
+
 static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
+                           const std::string& image_location,
                            std::vector<const DexFile*>& dex_files) {
   size_t failure_count = 0;
+  if (!image_location.empty() && OpenDexFilesFromImage(dex_filenames, image_location, dex_files,
+                                                       &failure_count)) {
+    return failure_count;
+  }
+  failure_count = 0;
   for (size_t i = 0; i < dex_filenames.size(); i++) {
     const char* dex_filename = dex_filenames[i].c_str();
     std::string error_msg;
@@ -713,7 +780,7 @@
     std::vector<std::string> dex_filenames;
     Split(boot_class_path_string_, ':', dex_filenames);
     std::vector<const DexFile*> boot_class_path;
-    OpenDexFiles(dex_filenames, boot_class_path);
+    OpenDexFiles(dex_filenames, options->image_, boot_class_path);
     class_linker_->InitWithoutImage(boot_class_path);
     // TODO: Should we move the following to InitWithoutImage?
     SetInstructionSet(kRuntimeISA);