ART: Add pic flag to oat header store

Add the compile-time PIC flag to the oat-header key-value store.
Ignore image offset and patch delta when loading PIC oat files.

Change-Id: Ie1f1ef37125386a968228033d1e2bec565315510
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 98712cd..1eb5718 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -774,7 +774,7 @@
   *parsed_value = value;
 }
 
-static int dex2oat(int argc, char** argv) {
+static void b13564922() {
 #if defined(__linux__) && defined(__arm__)
   int major, minor;
   struct utsname uts;
@@ -792,6 +792,10 @@
     }
   }
 #endif
+}
+
+static int dex2oat(int argc, char** argv) {
+  b13564922();
 
   original_argc = argc;
   original_argv = argv;
@@ -1414,17 +1418,20 @@
       new SafeMap<std::string, std::string>());
 
   // Insert some compiler things.
-  std::ostringstream oss;
-  for (int i = 0; i < argc; ++i) {
-    if (i > 0) {
-      oss << ' ';
+  {
+    std::ostringstream oss;
+    for (int i = 0; i < argc; ++i) {
+      if (i > 0) {
+        oss << ' ';
+      }
+      oss << argv[i];
     }
-    oss << argv[i];
+    key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+    oss.str("");  // Reset.
+    oss << kRuntimeISA;
+    key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
+    key_value_store->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
   }
-  key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
-  oss.str("");  // Reset.
-  oss << kRuntimeISA;
-  key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
 
   dex2oat->Compile(boot_image_option,
                    dex_files,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0af8bd2..d6d9583 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1345,11 +1345,11 @@
   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
+    // There is a high probability that both these oat files map similar/the same address
     // spaces so we must scope them like this so they each gets its turn.
     std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
                                                          executable, &odex_error_msg));
-    if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
+    if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
                                                        &odex_checksum_verified,
                                                        &odex_error_msg)) {
       return odex_oat_file.release();
@@ -1371,7 +1371,7 @@
   if (have_dalvik_cache) {
     std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
                                                           executable, &cache_error_msg));
-    if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
+    if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
                                                         &cache_checksum_verified,
                                                         &cache_error_msg)) {
       return cache_oat_file.release();
@@ -1465,13 +1465,15 @@
                                                 const std::string& image_location,
                                                 InstructionSet isa,
                                                 std::string* error_msg) {
-  if (!Runtime::Current()->GetHeap()->HasImageSpace()) {
+  Runtime* runtime = Runtime::Current();
+  DCHECK(runtime != nullptr);
+  if (!runtime->GetHeap()->HasImageSpace()) {
     // We don't have an image space so there is no point in trying to patchoat.
     LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
                  << "running without an image. Attempting to use oat file for interpretation.";
     return GetInterpretedOnlyOat(input_oat, isa, error_msg);
   }
-  if (!Runtime::Current()->IsDex2OatEnabled()) {
+  if (!runtime->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 "
@@ -1479,7 +1481,7 @@
     return GetInterpretedOnlyOat(input_oat, isa, error_msg);
   }
   Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
-  std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+  std::string patchoat(runtime->GetPatchoatExecutable());
 
   std::string isa_arg("--instruction-set=");
   isa_arg += GetInstructionSetString(isa);
@@ -1502,9 +1504,10 @@
   bool success = Exec(argv, error_msg);
   if (success) {
     std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr,
-                                                  !Runtime::Current()->IsCompiler(), error_msg));
+                                                  !runtime->IsCompiler(), error_msg));
     bool checksum_verified = false;
-    if (output.get() != nullptr && CheckOatFile(output.get(), isa, &checksum_verified, error_msg)) {
+    if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
+                                                error_msg)) {
       return output.release();
     } else if (output.get() != nullptr) {
       *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
@@ -1515,7 +1518,7 @@
                                 "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()) {
+  } else if (!runtime->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 "
@@ -1529,27 +1532,9 @@
   return nullptr;
 }
 
-int32_t ClassLinker::GetRequiredDelta(const OatFile* oat_file, InstructionSet isa) {
-  Runtime* runtime = Runtime::Current();
-  int32_t real_patch_delta;
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  CHECK(image_space != nullptr);
-  if (isa == Runtime::Current()->GetInstructionSet()) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    real_patch_delta = image_header.GetPatchDelta();
-  } else {
-    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
-        image_space->GetImageLocation().c_str(), isa));
-    real_patch_delta = image_header->GetPatchDelta();
-  }
-  const OatHeader& oat_header = oat_file->GetOatHeader();
-  return real_patch_delta - oat_header.GetImagePatchDelta();
-}
-
-bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
                                bool* checksum_verified,
                                std::string* error_msg) {
-  Runtime* runtime = Runtime::Current();
   const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
   if (image_space == nullptr) {
     *error_msg = "No image space present";
@@ -1581,19 +1566,30 @@
                   real_image_checksum, oat_image_checksum);
   }
 
-  void* oat_image_oat_offset =
-      reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
-  bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
-  if (!offset_verified) {
-    StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
-                  real_image_oat_offset, oat_image_oat_offset);
-  }
+  bool offset_verified;
+  bool patch_delta_verified;
 
-  int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
-  bool patch_delta_verified = oat_patch_delta == real_patch_delta;
-  if (!patch_delta_verified) {
-    StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, received 0x%x)",
-                  real_patch_delta, oat_patch_delta);
+  if (!oat_file->IsPic()) {
+    // If an oat file is not PIC, we need to check that the image is at the expected location and
+    // patched in the same way.
+    void* oat_image_oat_offset =
+        reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
+    offset_verified = oat_image_oat_offset == real_image_oat_offset;
+    if (!offset_verified) {
+      StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
+                    real_image_oat_offset, oat_image_oat_offset);
+    }
+
+    int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+    patch_delta_verified = oat_patch_delta == real_patch_delta;
+    if (!patch_delta_verified) {
+      StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, "
+                    "received 0x%x)", real_patch_delta, oat_patch_delta);
+    }
+  } else {
+    // If an oat file is PIC, we ignore offset and patching delta.
+    offset_verified = true;
+    patch_delta_verified = true;
   }
 
   bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 51cd730..8034d62 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -50,8 +50,8 @@
 template<class T> class Handle;
 class InternTable;
 template<class T> class ObjectLock;
+class Runtime;
 class ScopedObjectAccessAlreadyRunnable;
-template<class T> class Handle;
 template<size_t kNumReferences> class PACKED(4) StackHandleScope;
 
 typedef bool (ClassVisitor)(mirror::Class* c, void* arg);
@@ -631,9 +631,8 @@
                                      std::string* error_msg)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
-  bool CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+  bool CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
                     bool* checksum_verified, std::string* error_msg);
-  int32_t GetRequiredDelta(const OatFile* oat_file, InstructionSet isa);
 
   // Note: will not register the oat file.
   const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
diff --git a/runtime/oat.h b/runtime/oat.h
index 92b98b1..5de4403 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -35,6 +35,7 @@
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDex2OatHostKey = "dex2oat-host";
+  static constexpr const char* kPicKey = "pic";
 
   static OatHeader* Create(InstructionSet instruction_set,
                            const InstructionSetFeatures* instruction_set_features,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 03a398e..0f0e33c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -72,7 +72,7 @@
                        bool executable,
                        std::string* error_msg) {
   CHECK(!filename.empty()) << location;
-  CheckLocation(filename);
+  CheckLocation(location);
   std::unique_ptr<OatFile> ret;
   if (kUsePortableCompiler && executable) {
     // If we are using PORTABLE, use dlopen to deal with relocations.
@@ -592,4 +592,10 @@
   method->SetNativeGcMap(GetNativeGcMap());  // Used by native methods in work around JNI mode.
 }
 
+bool OatFile::IsPic() const {
+  const char* pic_string = GetOatHeader().GetStoreValueByKey(OatHeader::kPicKey);
+  return (pic_string != nullptr && strncmp(pic_string, "true", 5) == 0);
+  // TODO: Check against oat_patches. b/18144996
+}
+
 }  // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 734b9b3..ad6871d 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -72,6 +72,8 @@
     return is_executable_;
   }
 
+  bool IsPic() const;
+
   ElfFile* GetElfFile() const {
     CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
         << "Cannot get an elf file from " << GetLocation();