Do not execute OAT files that require app images that cannot be loaded am: bc89ed42d6

Change-Id: Ie8c2a70b39e0484f2e4f0335d9116c34badf52af
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e767224..c0f73c8 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -521,6 +521,9 @@
   UsageError("");
   UsageError("  --max-image-block-size=<size>: Maximum solid block size for compressed images.");
   UsageError("");
+  UsageError("  --compile-individually: Compiles dex files individually, unloading classes in");
+  UsageError("      between compiling each file.");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -824,7 +827,8 @@
       timings_(timings),
       force_determinism_(false),
       check_linkage_conditions_(false),
-      crash_on_linkage_violation_(false)
+      crash_on_linkage_violation_(false),
+      compile_individually_(false)
       {}
 
   ~Dex2Oat() {
@@ -1215,6 +1219,7 @@
     key_value_store_->Put(OatHeader::kCompilerFilter,
                           CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
     key_value_store_->Put(OatHeader::kConcurrentCopying, kUseReadBarrier);
+    key_value_store_->Put(OatHeader::kRequiresImage, compiler_options_->IsGeneratingImage());
     if (invocation_file_.get() != -1) {
       std::ostringstream oss;
       for (int i = 0; i < argc; ++i) {
@@ -1375,6 +1380,7 @@
     if (args.Exists(M::ForceDeterminism)) {
       force_determinism_ = true;
     }
+    AssignTrueIfExists(args, M::CompileIndividually, &compile_individually_);
 
     if (args.Exists(M::Base)) {
       ParseBase(*args.Get(M::Base));
@@ -2013,17 +2019,17 @@
   }
 
   bool ShouldCompileDexFilesIndividually() const {
-    // Compile individually if we are:
-    // 1. not building an image,
-    // 2. not verifying a vdex file,
-    // 3. using multidex,
+    // Compile individually if we are specifically asked to, or
+    // 1. not building an image, and
+    // 2. not verifying a vdex file, and
+    // 3. using multidex, and
     // 4. not doing any AOT compilation.
     // This means extract, no-vdex verify, and quicken, will use the individual compilation
     // mode (to reduce RAM used by the compiler).
-    return !IsImage() &&
-        !update_input_vdex_ &&
-        compiler_options_->dex_files_for_oat_file_.size() > 1 &&
-        !CompilerFilter::IsAotCompilationEnabled(compiler_options_->GetCompilerFilter());
+    return compile_individually_ ||
+           (!IsImage() && !update_input_vdex_ &&
+            compiler_options_->dex_files_for_oat_file_.size() > 1 &&
+            !CompilerFilter::IsAotCompilationEnabled(compiler_options_->GetCompilerFilter()));
   }
 
   uint32_t GetCombinedChecksums() const {
@@ -3097,6 +3103,9 @@
   // The reason for invoking the compiler.
   std::string compilation_reason_;
 
+  // Whether to force individual compilation.
+  bool compile_individually_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index d282c3b..81b9c98 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -262,7 +262,9 @@
           .IntoKey(M::RuntimeOptions)
       .Define("--compilation-reason=_")
           .WithType<std::string>()
-          .IntoKey(M::CompilationReason);
+          .IntoKey(M::CompilationReason)
+      .Define("--compile-individually")
+          .IntoKey(M::CompileIndividually);
 
   AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
 
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 805a13e..8b018bf 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -94,5 +94,7 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    CompilationReason)
 DEX2OAT_OPTIONS_KEY (Unit,                           CheckLinkageConditions)
 DEX2OAT_OPTIONS_KEY (Unit,                           CrashOnLinkageViolation)
+DEX2OAT_OPTIONS_KEY (Unit,                           CompileIndividually)
+
 
 #undef DEX2OAT_OPTIONS_KEY
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 54d5196..81fd09a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1308,7 +1308,6 @@
   const std::string unload_vdex_name = out_dir + "/unload.vdex";
   const std::string no_unload_oat_name = out_dir + "/nounload.oat";
   const std::string no_unload_vdex_name = out_dir + "/nounload.vdex";
-  const std::string app_image_name = out_dir + "/unload.art";
   std::string error_msg;
   const std::vector<gc::space::ImageSpace*>& spaces = runtime->GetHeap()->GetBootImageSpaces();
   ASSERT_GT(spaces.size(), 0u);
@@ -1336,7 +1335,7 @@
       base_oat_name,
       CompilerFilter::Filter::kQuicken,
       &error_msg,
-      {"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name});
+      {"--force-determinism", "--avoid-storing-invocation", "--compile-individually"});
   ASSERT_EQ(res2, 0);
   Copy(base_oat_name, no_unload_oat_name);
   Copy(base_vdex_name, no_unload_vdex_name);
@@ -1353,10 +1352,6 @@
       << unload_oat_name << " " << no_unload_oat_name;
   EXPECT_EQ(unload_vdex->Compare(no_unload_vdex.get()), 0)
       << unload_vdex_name << " " << no_unload_vdex_name;
-  // App image file.
-  std::unique_ptr<File> app_image_file(OS::OpenFileForReading(app_image_name.c_str()));
-  ASSERT_TRUE(app_image_file != nullptr);
-  EXPECT_GT(app_image_file->GetLength(), 0u);
 }
 
 // Test that dexlayout section info is correctly written to the oat file for profile based
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 723bf49..52e0dd1 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -396,6 +396,10 @@
   return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
 }
 
+bool OatHeader::RequiresImage() const {
+  return IsKeyEnabled(OatHeader::kRequiresImage);
+}
+
 CompilerFilter::Filter OatHeader::GetCompilerFilter() const {
   CompilerFilter::Filter filter;
   const char* key_value = GetStoreValueByKey(kCompilerFilter);
diff --git a/runtime/oat.h b/runtime/oat.h
index c1659e4..0645e4c 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: Change x86-64 @CriticalNative hidden arg register to RAX.
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '1', '\0' } };
+  // Last oat version changed reason: Add requires-image flag.
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '2', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
@@ -44,6 +44,7 @@
   static constexpr const char* kBootClassPathChecksumsKey = "bootclasspath-checksums";
   static constexpr const char* kConcurrentCopying = "concurrent-copying";
   static constexpr const char* kCompilationReasonKey = "compilation-reason";
+  static constexpr const char* kRequiresImage = "requires-image";
 
   static constexpr const char kTrueValue[] = "true";
   static constexpr const char kFalseValue[] = "false";
@@ -102,6 +103,7 @@
   bool IsNativeDebuggable() const;
   CompilerFilter::Filter GetCompilerFilter() const;
   bool IsConcurrentCopying() const;
+  bool RequiresImage() const;
 
  private:
   bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 346c112..a7054d3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -2124,6 +2124,8 @@
   return oat_dex_file->GetOatClass(class_def_idx);
 }
 
+bool OatFile::RequiresImage() const { return GetOatHeader().RequiresImage(); }
+
 static void DCheckIndexToBssMapping(const OatFile* oat_file,
                                     uint32_t number_of_indexes,
                                     size_t slot_size,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index dce34d9..ee5aede 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -378,6 +378,9 @@
     return external_dex_files_.empty();
   }
 
+  // Returns whether an image (e.g. app image) is required to safely execute this OAT file.
+  bool RequiresImage() const;
+
  protected:
   OatFile(const std::string& filename, bool executable);
 
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index ed47ca3..7b1a5eb 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -1437,6 +1437,7 @@
 TEST_F(OatFileAssistantTest, GetDexLocation) {
   std::string dex_location = GetScratchDir() + "/TestDex.jar";
   std::string oat_location = GetOdexDir() + "/TestDex.odex";
+  std::string art_location = GetOdexDir() + "/TestDex.art";
 
   // Start the runtime to initialize the system's class loader.
   Thread::Current()->TransitionFromSuspendedToRunnable();
@@ -1463,6 +1464,7 @@
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--dex-location=TestDex.jar");
     args.push_back("--oat-file=" + oat_location);
+    args.push_back("--app-image-file=" + art_location);
     std::string error_msg;
     ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg;
   }
@@ -1490,6 +1492,7 @@
   odex_dir = odex_dir + std::string(GetInstructionSetString(kRuntimeISA));
   mkdir(odex_dir.c_str(), 0700);
   std::string oat_location = odex_dir + "/" + filebase + ".odex";
+  std::string art_location = odex_dir + "/" + filebase + ".art";
   // Clean up in case previous run crashed.
   remove(oat_location.c_str());
 
@@ -1527,6 +1530,7 @@
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--dex-location=" + filebase + ".jar");
     args.push_back("--oat-file=" + oat_location);
+    args.push_back("--app-image-file=" + art_location);
     std::string error_msg;
     ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg;
   }
@@ -1554,6 +1558,41 @@
   EXPECT_EQ(0, remove(oat_location.c_str()));
 }
 
+// Make sure OAT files that require app images are not loaded as executable.
+TEST_F(OatFileAssistantTest, LoadOatNoArt) {
+  std::string dex_location = GetScratchDir() + "/TestDex.jar";
+  std::string odex_location = GetOdexDir() + "/TestDex.odex";
+  std::string art_location = GetOdexDir() + "/TestDex.art";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location,
+                      odex_location,
+                      CompilerFilter::kSpeed,
+                      "install",
+                      {
+                          "--app-image-file=" + art_location,
+                      });
+
+  unlink(art_location.c_str());
+
+  std::vector<std::string> error_msgs;
+  const OatFile* oat_file = nullptr;
+
+  // Start the runtime to initialize the system's class loader.
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  runtime_->Start();
+
+  const auto dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
+      dex_location.c_str(),
+      Runtime::Current()->GetSystemClassLoader(),
+      /*dex_elements=*/nullptr,
+      &oat_file,
+      &error_msgs);
+
+  EXPECT_FALSE(dex_files.empty());
+  EXPECT_NE(oat_file, nullptr);
+  EXPECT_FALSE(oat_file->IsExecutable());
+}
+
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index ff743d5..14ec631 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -492,13 +492,14 @@
   const OatFile* source_oat_file = nullptr;
   CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions;
   std::string error_msg;
+  bool accept_oat_file = false;
   if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
     // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
     // could load oat files without checking the classpath, which would be incorrect.
     // Take the file only if it has no collisions, or we must take it because of preopting.
     check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg);
-    bool accept_oat_file = AcceptOatFile(check_collision_result);
+    accept_oat_file = AcceptOatFile(check_collision_result);
     if (!accept_oat_file) {
       // Failed the collision check. Print warning.
       if (runtime->IsDexFileFallbackEnabled()) {
@@ -531,30 +532,24 @@
 
       LOG(WARNING) << error_msg;
     }
-
-    if (accept_oat_file) {
-      VLOG(class_linker) << "Registering " << oat_file->GetLocation();
-      source_oat_file = RegisterOatFile(std::move(oat_file));
-      *out_oat_file = source_oat_file;
-    }
   }
 
   std::vector<std::unique_ptr<const DexFile>> dex_files;
 
   // Load the dex files from the oat file.
-  if (source_oat_file != nullptr) {
-    bool added_image_space = false;
-    if (source_oat_file->IsExecutable()) {
+  bool added_image_space = false;
+  if (accept_oat_file) {
+    if (oat_file->IsExecutable()) {
       ScopedTrace app_image_timing("AppImage:Loading");
 
       // We need to throw away the image space if we are debuggable but the oat-file source of the
       // image is not otherwise we might get classes with inlined methods or other such things.
       std::unique_ptr<gc::space::ImageSpace> image_space;
       if (ShouldLoadAppImage(check_collision_result,
-                             source_oat_file,
+                             oat_file.get(),
                              context.get(),
                              &error_msg)) {
-        image_space = oat_file_assistant.OpenImageSpace(source_oat_file);
+        image_space = oat_file_assistant.OpenImageSpace(oat_file.get());
       }
       if (image_space != nullptr) {
         ScopedObjectAccess soa(self);
@@ -608,7 +603,18 @@
     }
     if (!added_image_space) {
       DCHECK(dex_files.empty());
-      dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
+
+      if (oat_file->RequiresImage()) {
+        // If we could not load the image, but the OAT file requires it, we have to reload the OAT
+        // file as non-executable.
+        OatFileAssistant nonexecutable_oat_file_assistant(dex_location,
+                                                          kRuntimeISA,
+                                                          /*load_executable=*/false,
+                                                          only_use_system_oat_files_);
+        oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release());
+      }
+
+      dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location);
 
       // Register for tracking.
       for (const auto& dex_file : dex_files) {
@@ -616,13 +622,17 @@
       }
     }
     if (dex_files.empty()) {
-      error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
+      error_msgs->push_back("Failed to open dex files from " + oat_file->GetLocation());
     } else {
       // Opened dex files from an oat file, madvise them to their loaded state.
        for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
          OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad);
        }
     }
+
+    VLOG(class_linker) << "Registering " << oat_file->GetLocation();
+    source_oat_file = RegisterOatFile(std::move(oat_file));
+    *out_oat_file = source_oat_file;
   }
 
   // Fall back to running out of the original dex file if we couldn't load any