Make apps able to run with a failing patchoat

Bug: 17000769

Change-Id: I0a1a4dc7f5d4bb268530840302ecfb1555231e05
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f31e273..7aef8fa 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -851,7 +851,8 @@
       // We opened the oat file, so we must register it.
       RegisterOatFile(oat_file);
     }
-    return true;
+    // If the file isn't executable we failed patchoat but did manage to get the dex files.
+    return oat_file->IsExecutable();
   } else {
     if (needs_registering) {
       // We opened it, delete it.
@@ -1136,11 +1137,18 @@
     error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
                                        dex_location));
     return nullptr;
-  } else if (!VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+  } else if (oat_file->IsExecutable() &&
+             !VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
     error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
                                        "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
                                        error_msg.c_str()));
     return nullptr;
+  } else if (!oat_file->IsExecutable() &&
+             !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.",
+                                       oat_file->GetLocation().c_str(), dex_location));
+    return nullptr;
   } else {
     return oat_file.release();
   }
@@ -1310,11 +1318,35 @@
   return ret;
 }
 
+const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path,
+                                                  InstructionSet isa,
+                                                  std::string* error_msg) {
+  // We open it non-executable
+  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, NULL, false, error_msg));
+  if (output.get() == nullptr) {
+    return nullptr;
+  }
+  if (VerifyOatImageChecksum(output.get(), isa)) {
+    return output.release();
+  } else {
+    *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
+                              oat_path.c_str());
+    return nullptr;
+  }
+}
+
 const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
                                                 const std::string& output_oat,
                                                 const std::string& image_location,
                                                 InstructionSet isa,
                                                 std::string* error_msg) {
+  if (!Runtime::Current()->IsDex2OatEnabled()) {
+    // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
+    // input_oat but make sure we only do interpretation on it's dex files.
+    LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
+                 << "disabled. Attempting to use oat file for interpretation";
+    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
+  }
   Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
   std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
 
@@ -1352,6 +1384,12 @@
                                 "but was unable to open output file '%s': %s",
                                 input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
     }
+  } else if (!Runtime::Current()->IsCompiler()) {
+    // patchoat failed which means we probably don't have enough room to place the output oat file,
+    // instead of failing we should just run the interpreter from the dex files in the input oat.
+    LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
+                 << "for interpretation. patchoat failure was: " << *error_msg;
+    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
   } else {
     *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
                               "failed: %s", input_oat.c_str(), output_oat.c_str(),
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9ae3862..5694149 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -573,6 +573,10 @@
                                             std::vector<std::string>* error_msg)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
+  const OatFile* GetInterpretedOnlyOat(const std::string& oat_path,
+                                       InstructionSet isa,
+                                       std::string* error_msg);
+
   const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
                                      const std::string& image_location, InstructionSet isa,
                                      std::string* error_msg)
@@ -752,6 +756,7 @@
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
   friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
   friend class NoDex2OatTest;  // for FindOpenedOatFileForDexFile
+  friend class NoPatchoatTest;  // for FindOpenedOatFileForDexFile
   FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
   FRIEND_TEST(mirror::DexCacheTest, Open);
   FRIEND_TEST(ExceptionTest, FindExceptionHandler);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b74b10f..7d9922d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -45,7 +45,7 @@
                              std::string* error_msg) {
   CHECK(!oat_contents.empty()) << location;
   CheckLocation(location);
-  std::unique_ptr<OatFile> oat_file(new OatFile(location));
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
   oat_file->begin_ = &oat_contents[0];
   oat_file->end_ = &oat_contents[oat_contents.size()];
   return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
@@ -97,7 +97,7 @@
                              const std::string& location,
                              byte* requested_base,
                              std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(new OatFile(location));
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, true));
   bool success = oat_file->Dlopen(elf_filename, requested_base, error_msg);
   if (!success) {
     return nullptr;
@@ -111,7 +111,7 @@
                               bool writable,
                               bool executable,
                               std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(new OatFile(location));
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
   bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
   if (!success) {
     CHECK(!error_msg->empty());
@@ -120,8 +120,9 @@
   return oat_file.release();
 }
 
-OatFile::OatFile(const std::string& location)
-    : location_(location), begin_(NULL), end_(NULL), dlopen_handle_(NULL),
+OatFile::OatFile(const std::string& location, bool is_executable)
+    : location_(location), begin_(NULL), end_(NULL), is_executable_(is_executable),
+      dlopen_handle_(NULL),
       secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) {
   CHECK(!location_.empty());
 }
@@ -533,10 +534,15 @@
     methods_pointer_index = num_set_bits;
   }
   const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index];
-  return OatMethod(
-      oat_file_->Begin(),
-      oat_method_offsets.code_offset_,
-      oat_method_offsets.gc_map_offset_);
+  if (oat_file_->IsExecutable() || Runtime::Current()->IsCompiler()) {
+    return OatMethod(
+        oat_file_->Begin(),
+        oat_method_offsets.code_offset_,
+        oat_method_offsets.gc_map_offset_);
+  } else {
+    // We aren't allowed to use the compiled code. We just force it down the interpreted version.
+    return OatMethod(oat_file_->Begin(), 0, 0);
+  }
 }
 
 
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 508bfc2..8535bf4 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -64,6 +64,10 @@
 
   ~OatFile();
 
+  bool IsExecutable() const {
+    return is_executable_;
+  }
+
   ElfFile* GetElfFile() const {
     CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
         << "Cannot get an elf file from " << GetLocation();
@@ -270,7 +274,7 @@
                               bool executable,
                               std::string* error_msg);
 
-  explicit OatFile(const std::string& filename);
+  explicit OatFile(const std::string& filename, bool executable);
   bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
   bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
                    std::string* error_msg);
@@ -287,6 +291,9 @@
   // Pointer to end of oat region for bounds checking.
   const byte* end_;
 
+  // Was this oat_file loaded executable?
+  const bool is_executable_;
+
   // Backing memory map for oat file during when opened by ElfWriter during initial compilation.
   std::unique_ptr<MemMap> mem_map_;
 
diff --git a/test/117-nopatchoat/expected.txt b/test/117-nopatchoat/expected.txt
new file mode 100644
index 0000000..a1293ae
--- /dev/null
+++ b/test/117-nopatchoat/expected.txt
@@ -0,0 +1,9 @@
+Run without dex2oat/patchoat
+dex2oat & patchoat are disabled, has oat is true, has executable oat is false.
+This is a function call
+Run with dexoat/patchoat
+dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+This is a function call
+Run default
+dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+This is a function call
diff --git a/test/117-nopatchoat/info.txt b/test/117-nopatchoat/info.txt
new file mode 100644
index 0000000..aa9f57c
--- /dev/null
+++ b/test/117-nopatchoat/info.txt
@@ -0,0 +1 @@
+Test that disables patchoat'ing the application.
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
new file mode 100644
index 0000000..ced7f6e
--- /dev/null
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "class_linker.h"
+#include "dex_file-inl.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+class NoPatchoatTest {
+ public:
+  static bool hasExecutableOat(jclass cls) {
+    ScopedObjectAccess soa(Thread::Current());
+    mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+    const DexFile& dex_file = klass->GetDexFile();
+    const OatFile* oat_file =
+        Runtime::Current()->GetClassLinker()->FindOpenedOatFileForDexFile(dex_file);
+    return oat_file != nullptr && oat_file->IsExecutable();
+  }
+};
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasExecutableOat(JNIEnv*, jclass cls) {
+  return NoPatchoatTest::hasExecutableOat(cls);
+}
+
+}  // namespace art
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
new file mode 100755
index 0000000..a7c96a0
--- /dev/null
+++ b/test/117-nopatchoat/run
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ensure flags includes prebuild and relocate. It doesn't make sense unless we
+# have a oat file we want to relocate.
+# TODO Unfortunately we have no way to force prebuild on for both host and target (or skip if not on).
+flags="${@/--relocate/}"
+flags="${flags/--no-relocate/}"
+flags="${flags} --relocate"
+
+# Make sure we can run without relocation
+echo "Run without dex2oat/patchoat"
+# /bin/false is actually not even there for either, so the exec will fail.
+# Unfortunately there is no equivalent to /bin/false in android.
+${RUN} ${flags} --runtime-option -Xnodex2oat
+
+# Make sure we can run with the oat file.
+echo "Run with dexoat/patchoat"
+${RUN} ${flags} --runtime-option -Xdex2oat
+
+# Make sure we can run with the default settings.
+echo "Run default"
+${RUN} ${flags}
diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java
new file mode 100644
index 0000000..f3f91ce
--- /dev/null
+++ b/test/117-nopatchoat/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println(
+        "dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") +
+        ", has oat is " + hasOat() + ", has executable oat is " + hasExecutableOat() + ".");
+
+    if (!hasOat() && isDex2OatEnabled()) {
+      throw new Error("Application with dex2oat enabled runs without an oat file");
+    }
+
+    System.out.println(functionCall());
+  }
+
+  public static String functionCall() {
+    String arr[] = {"This", "is", "a", "function", "call"};
+    String ret = "";
+    for (int i = 0; i < arr.length; i++) {
+      ret = ret + arr[i] + " ";
+    }
+    return ret.substring(0, ret.length() - 1);
+  }
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  private native static boolean isDex2OatEnabled();
+
+  private native static boolean hasOat();
+
+  private native static boolean hasExecutableOat();
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 3871b28..caaf649 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -24,7 +24,8 @@
   004-ReferenceMap/stack_walk_refmap_jni.cc \
   004-StackWalk/stack_walk_jni.cc \
   004-UnsafeTest/unsafe_test.cc \
-  116-nodex2oat/nodex2oat.cc
+  116-nodex2oat/nodex2oat.cc \
+  117-nopatchoat/nopatchoat.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 0cf9e16..ce0eb3f 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -124,6 +124,42 @@
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-no-prebuild)
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-no-prebuild)
 
+# NB 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
+# On host this is patched around by changing a run flag but we cannot do this on the target due to
+# a different run-script.
+TEST_ART_TARGET_BROKEN_PREBUILD_RUN_TESTS := \
+  116-nodex2oat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-trace,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcverify,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcstress,-prebuild))
+
+# NB 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without --prebuild --relocate
+TEST_ART_BROKEN_RELOCATE_TESTS := \
+  117-nopatchoat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+
+TEST_ART_BROKEN_NORELOCATE_TESTS := \
+  117-nopatchoat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+
+TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
+  117-nopatchoat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
+
 # The path where build only targets will be output, e.g.
 # out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA
 art_run_tests_dir := $(call intermediates-dir-for,PACKAGING,art-run-tests)/DATA