ART: Add support for patching and loading OAT files compiled with PIC

* Images (.art) compiled with pic now have a new field added.
* isDexOptNeeded will now skip patch-ing for apps compiled PIC
* First-boot patching now only copies boot.art, boot.oat is linked

As a result, all system preopted dex files (with --compile-pic) no
longer take up any space in /data/dalvik-cache/<isa>.

(cherry-picked from AOSP master
46774767fcf7780d1455e755729198648d08742e)

Conflicts (from aosp master):
	compiler/image_test.cc
	compiler/image_writer.cc
	compiler/image_writer.h
	compiler/oat_test.cc
	dex2oat/dex2oat.cc
	oatdump/oatdump.cc
	runtime/elf_file.cc
	runtime/elf_file.h
	runtime/elf_file_impl.h
	runtime/oat_file.cc
	runtime/oat_file.h

Bug: 18035729
Change-Id: Ie1acad81a0fd8b2f24e1f3f07a06e6fdb548be62
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
index e4c957a..e8ccd67 100644
--- a/compiler/elf_patcher.cc
+++ b/compiler/elf_patcher.cc
@@ -44,7 +44,7 @@
   const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
   if (oat_file == nullptr) {
     CHECK(Runtime::Current()->IsCompiler());
-    oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+    oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, error_msg);
     if (oat_file == nullptr) {
       *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
                                 error_msg->c_str());
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 355036b..0aef512 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -98,7 +98,7 @@
   {
     ImageWriter writer(*compiler_driver_.get());
     bool success_image = writer.Write(image_file.GetFilename(), requested_image_base,
-                                      dup_oat->GetPath(), dup_oat->GetPath());
+                                      dup_oat->GetPath(), dup_oat->GetPath(), /*compile_pic*/false);
     ASSERT_TRUE(success_image);
     bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
     ASSERT_TRUE(success_fixup);
@@ -210,7 +210,8 @@
                              oat_file_begin,
                              oat_data_begin,
                              oat_data_end,
-                             oat_file_end);
+                             oat_file_end,
+                             /*compile_pic*/false);
     ASSERT_TRUE(image_header.IsValid());
 
     char* magic = const_cast<char*>(image_header.GetMagic());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3ea4e5e..815bad3 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -71,11 +71,13 @@
 bool ImageWriter::Write(const std::string& image_filename,
                         uintptr_t image_begin,
                         const std::string& oat_filename,
-                        const std::string& oat_location) {
+                        const std::string& oat_location,
+                        bool compile_pic) {
   CHECK(!image_filename.empty());
 
   CHECK_NE(image_begin, 0U);
   image_begin_ = reinterpret_cast<byte*>(image_begin);
+  compile_pic_ = compile_pic;
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
@@ -571,7 +573,8 @@
                            PointerToLowMemUInt32(oat_file_begin),
                            PointerToLowMemUInt32(oat_data_begin_),
                            PointerToLowMemUInt32(oat_data_end),
-                           PointerToLowMemUInt32(oat_file_end));
+                           PointerToLowMemUInt32(oat_file_end),
+                           compile_pic_);
   memcpy(image_->Begin(), &image_header, sizeof(image_header));
 
   // Note that image_end_ is left at end of used space
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e8bcf7f..61365fe 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -24,6 +24,7 @@
 #include <set>
 #include <string>
 
+#include "base/macros.h"
 #include "driver/compiler_driver.h"
 #include "mem_map.h"
 #include "oat_file.h"
@@ -35,21 +36,23 @@
 namespace art {
 
 // Write a Space built during compilation for use during execution.
-class ImageWriter {
+class ImageWriter FINAL {
  public:
   explicit ImageWriter(const CompilerDriver& compiler_driver)
       : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
         oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
         interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
         portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
-        quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {}
+        quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
+        compile_pic_(false) {}
 
   ~ImageWriter() {}
 
   bool Write(const std::string& image_filename,
              uintptr_t image_begin,
              const std::string& oat_filename,
-             const std::string& oat_location)
+             const std::string& oat_location,
+             bool compile_pic)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
   uintptr_t GetOatDataBegin() {
@@ -97,8 +100,8 @@
     // different .o ELF objects.
     DCHECK_LT(offset, oat_file_->Size());
 #endif
-    if (offset == 0) {
-      return NULL;
+    if (offset == 0u) {
+      return nullptr;
     }
     return oat_data_begin_ + offset;
   }
@@ -199,6 +202,7 @@
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
+  bool compile_pic_;
 
   friend class FixupVisitor;
   friend class FixupClassVisitor;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 11d1728..7c1f6c5 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -135,8 +135,8 @@
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
   std::string error_msg;
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false,
-                                            &error_msg));
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
+                                                  nullptr, false, &error_msg));
   ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
   const OatHeader& oat_header = oat_file->GetOatHeader();
   ASSERT_TRUE(oat_header.IsValid());
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6a4df49..21ca987 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -155,6 +155,9 @@
   UsageError("      Example: --instruction-set-features=div");
   UsageError("      Default: default");
   UsageError("");
+  UsageError("  --compile-pic: Force indirect use of code, methods, and classes");
+  UsageError("      Default: disabled");
+  UsageError("");
   UsageError("  --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
   UsageError("      set.");
   UsageError("      Example: --compiler-backend=Portable");
@@ -453,7 +456,8 @@
     {
       // ImageWriter is scoped so it can free memory before doing FixupElf
       ImageWriter image_writer(compiler);
-      if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
+      if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location,
+                              compiler_options_->GetCompilePic())) {
         LOG(ERROR) << "Failed to create image file " << image_filename;
         return false;
       }
@@ -465,10 +469,14 @@
       PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
       return false;
     }
-    if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
-      LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
-      return false;
+    // Do not fix up the ELF file if we are --compile-pic
+    if (!compiler_options_->GetCompilePic()) {
+      if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
+        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+        return false;
+      }
     }
+
     return true;
   }
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 9cf7494..883479e 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -984,6 +984,8 @@
 
     os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
 
+    os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
+
     {
       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
       Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
@@ -1034,7 +1036,7 @@
     std::string error_msg;
     const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
     if (oat_file == nullptr) {
-      oat_file = OatFile::Open(oat_location, oat_location, nullptr, false, &error_msg);
+      oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
       if (oat_file == nullptr) {
         os << "NOT FOUND: " << error_msg << "\n";
         return false;
@@ -1762,7 +1764,7 @@
   if (oat_filename != nullptr) {
     std::string error_msg;
     OatFile* oat_file =
-        OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+        OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
     if (oat_file == nullptr) {
       fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
       return EXIT_FAILURE;
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 74f6779..b9637d0 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -71,7 +71,7 @@
   bool has_system = false;
   bool has_cache = false;
   // image_location = /system/framework/boot.art
-  // system_image_location = /system/framework/<image_isa>/boot.art
+  // system_image_filename = /system/framework/<image_isa>/boot.art
   std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
   if (OS::FileExists(system_filename.c_str())) {
     has_system = true;
@@ -130,6 +130,7 @@
                << " for location " << image_location;
     return false;
   }
+
   int64_t image_len = input_image->GetLength();
   if (image_len < 0) {
     LOG(ERROR) << "Error while getting image length";
@@ -142,6 +143,10 @@
     return false;
   }
 
+  /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+  // Nothing special to do right now since the image always needs to get patched.
+  // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
   // Set up the runtime
   RuntimeOptions options;
   NoopCompilerCallbacks callbacks;
@@ -186,9 +191,11 @@
   return true;
 }
 
-bool PatchOat::Patch(const File* input_oat, const std::string& image_location, off_t delta,
+bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
                      File* output_oat, File* output_image, InstructionSet isa,
-                     TimingLogger* timings) {
+                     TimingLogger* timings,
+                     bool output_oat_opened_from_fd,
+                     bool new_oat_out) {
   CHECK(Runtime::Current() == nullptr);
   CHECK(output_image != nullptr);
   CHECK_GE(output_image->Fd(), 0);
@@ -231,6 +238,10 @@
     LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
   }
 
+  /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+  // Nothing special to do right now since the image always needs to get patched.
+  // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
   // Set up the runtime
   RuntimeOptions options;
   NoopCompilerCallbacks callbacks;
@@ -260,17 +271,37 @@
   }
   gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
 
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
                                              PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
   if (elf.get() == nullptr) {
     LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
     return false;
   }
 
+  bool skip_patching_oat = false;
+  MaybePic is_oat_pic = IsOatPic(elf.get());
+  if (is_oat_pic >= ERROR_FIRST) {
+    // Error logged by IsOatPic
+    return false;
+  } else if (is_oat_pic == PIC) {
+    // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+    if (!ReplaceOatFileWithSymlink(input_oat->GetPath(),
+                                   output_oat->GetPath(),
+                                   output_oat_opened_from_fd,
+                                   new_oat_out)) {
+      // Errors already logged by above call.
+      return false;
+    }
+    // Don't patch the OAT, since we just symlinked it. Image still needs patching.
+    skip_patching_oat = true;
+  } else {
+    CHECK(is_oat_pic == NOT_PIC);
+  }
+
   PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
              delta, timings);
   t.NewTiming("Patching files");
-  if (!p.PatchElf()) {
+  if (!skip_patching_oat && !p.PatchElf()) {
     LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
     return false;
   }
@@ -280,10 +311,12 @@
   }
 
   t.NewTiming("Writing files");
-  if (!p.WriteElf(output_oat)) {
+  if (!skip_patching_oat && !p.WriteElf(output_oat)) {
+    LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath();
     return false;
   }
   if (!p.WriteImage(output_image)) {
+    LOG(ERROR) << "Failed to write image file " << input_image->GetPath();
     return false;
   }
   return true;
@@ -323,6 +356,83 @@
   }
 }
 
+bool PatchOat::IsImagePic(const ImageHeader& image_header, const std::string& image_path) {
+  if (!image_header.CompilePic()) {
+    if (kIsDebugBuild) {
+      LOG(INFO) << "image at location " << image_path << " was *not* compiled pic";
+    }
+    return false;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "image at location " << image_path << " was compiled PIC";
+  }
+
+  return true;
+}
+
+PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) {
+  if (oat_in == nullptr) {
+    LOG(ERROR) << "No ELF input oat fie available";
+    return ERROR_OAT_FILE;
+  }
+
+  const std::string& file_path = oat_in->GetFile().GetPath();
+
+  const OatHeader* oat_header = GetOatHeader(oat_in);
+  if (oat_header == nullptr) {
+    LOG(ERROR) << "Failed to find oat header in oat file " << file_path;
+    return ERROR_OAT_FILE;
+  }
+
+  if (!oat_header->IsValid()) {
+    LOG(ERROR) << "Elf file " << file_path << " has an invalid oat header";
+    return ERROR_OAT_FILE;
+  }
+
+  bool is_pic = oat_header->IsPic();
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Oat file at " << file_path << " is " << (is_pic ? "PIC" : "not pic");
+  }
+
+  return is_pic ? PIC : NOT_PIC;
+}
+
+bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+                                         const std::string& output_oat_filename,
+                                         bool output_oat_opened_from_fd,
+                                         bool new_oat_out) {
+  // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD.
+  if (output_oat_opened_from_fd) {
+    // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC?
+    LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC";
+    return false;
+  }
+
+  // Image was PIC. Create symlink where the oat is supposed to go.
+  if (!new_oat_out) {
+    LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite";
+    return false;
+  }
+
+  // Delete the original file, since we won't need it.
+  TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
+
+  // Create a symlink from the old oat to the new oat
+  if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) {
+    int err = errno;
+    LOG(ERROR) << "Failed to create symlink at " << output_oat_filename
+               << " error(" << err << "): " << strerror(err);
+    return false;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename;
+  }
+
+  return true;
+}
+
 bool PatchOat::PatchImage() {
   ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
   CHECK_GT(image_->Size(), sizeof(ImageHeader));
@@ -388,6 +498,16 @@
   }
 }
 
+const OatHeader* PatchOat::GetOatHeader(const ElfFile* elf_file) {
+  auto rodata_sec = elf_file->FindSectionByName(".rodata");
+  if (rodata_sec == nullptr) {
+    return nullptr;
+  }
+
+  OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf_file->Begin() + rodata_sec->sh_offset);
+  return oat_header;
+}
+
 // Called by BitmapCallback
 void PatchOat::VisitObject(mirror::Object* object) {
   mirror::Object* copy = RelocatedCopyOf(object);
@@ -441,7 +561,8 @@
   }
 }
 
-bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings) {
+bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings,
+                     bool output_oat_opened_from_fd, bool new_oat_out) {
   CHECK(input_oat != nullptr);
   CHECK(output_oat != nullptr);
   CHECK_GE(input_oat->Fd(), 0);
@@ -449,13 +570,28 @@
   TimingLogger::ScopedTiming t("Setup Oat File Patching", timings);
 
   std::string error_msg;
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
                                              PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
   if (elf.get() == nullptr) {
     LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
     return false;
   }
 
+  MaybePic is_oat_pic = IsOatPic(elf.get());
+  if (is_oat_pic >= ERROR_FIRST) {
+    // Error logged by IsOatPic
+    return false;
+  } else if (is_oat_pic == PIC) {
+    // Do not need to do ELF-file patching. Create a symlink and skip the rest.
+    // Any errors will be logged by the function call.
+    return ReplaceOatFileWithSymlink(input_oat->GetPath(),
+                                     output_oat->GetPath(),
+                                     output_oat_opened_from_fd,
+                                     new_oat_out);
+  } else {
+    CHECK(is_oat_pic == NOT_PIC);
+  }
+
   PatchOat p(elf.release(), delta, timings);
   t.NewTiming("Patch Oat file");
   if (!p.PatchElf()) {
@@ -1045,11 +1181,17 @@
         input_oat_filename = "input-oat-file";
       }
       input_oat.reset(new File(input_oat_fd, input_oat_filename));
+      if (input_oat == nullptr) {
+        // Unlikely, but ensure exhaustive logging in non-0 exit code case
+        LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
+      }
     } else {
       CHECK(!input_oat_filename.empty());
       input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
-      if (input_oat.get() == nullptr) {
-        LOG(ERROR) << "Could not open input oat file: " << strerror(errno);
+      if (input_oat == nullptr) {
+        int err = errno;
+        LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
+                   << ": " << strerror(err) << "(" << err << ")";
       }
     }
 
@@ -1058,12 +1200,22 @@
         output_oat_filename = "output-oat-file";
       }
       output_oat.reset(new File(output_oat_fd, output_oat_filename));
+      if (output_oat == nullptr) {
+        // Unlikely, but ensure exhaustive logging in non-0 exit code case
+        LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
+      }
     } else {
       CHECK(!output_oat_filename.empty());
       output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
+      if (output_oat == nullptr) {
+        int err = errno;
+        LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
+                   << ": " << strerror(err) << "(" << err << ")";
+      }
     }
   }
 
+  // TODO: get rid of this.
   auto cleanup = [&output_image_filename, &output_oat_filename,
                   &new_oat_out, &new_image_out, &timings, &dump_timings](bool success) {
     timings.EndTiming();
@@ -1080,14 +1232,29 @@
     if (dump_timings) {
       LOG(INFO) << Dumpable<TimingLogger>(timings);
     }
+
+    if (kIsDebugBuild) {
+      LOG(INFO) << "Cleaning up.. success? " << success;
+    }
   };
 
-  if ((have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) ||
-      (have_image_files && output_image.get() == nullptr)) {
+  if (have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) {
+    LOG(ERROR) << "Failed to open input/output oat files";
+    cleanup(false);
+    return EXIT_FAILURE;
+  } else if (have_image_files && output_image.get() == nullptr) {
+    LOG(ERROR) << "Failed to open output image file";
     cleanup(false);
     return EXIT_FAILURE;
   }
 
+  if (debug) {
+    LOG(INFO) << "moving offset by " << base_delta
+              << " (0x" << std::hex << base_delta << ") bytes or "
+              << std::dec << (base_delta/kPageSize) << " pages.";
+  }
+
+  // TODO: is it going to be promatic to unlink a file that was flock-ed?
   ScopedFlock output_oat_lock;
   if (lock_output) {
     std::string error_msg;
@@ -1098,24 +1265,28 @@
     }
   }
 
-  if (debug) {
-    LOG(INFO) << "moving offset by " << base_delta
-              << " (0x" << std::hex << base_delta << ") bytes or "
-              << std::dec << (base_delta/kPageSize) << " pages.";
-  }
-
   bool ret;
   if (have_image_files && have_oat_files) {
     TimingLogger::ScopedTiming pt("patch image and oat", &timings);
     ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta,
-                          output_oat.get(), output_image.get(), isa, &timings);
+                          output_oat.get(), output_image.get(), isa, &timings,
+                          output_oat_fd >= 0,  // was it opened from FD?
+                          new_oat_out);
   } else if (have_oat_files) {
     TimingLogger::ScopedTiming pt("patch oat", &timings);
-    ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings);
-  } else {
+    ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
+                          output_oat_fd >= 0,  // was it opened from FD?
+                          new_oat_out);
+  } else if (have_image_files) {
     TimingLogger::ScopedTiming pt("patch image", &timings);
-    CHECK(have_image_files);
     ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
+  } else {
+    CHECK(false);
+    ret = true;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Exiting with return ... " << ret;
   }
   cleanup(ret);
   return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 6960d3b..21041fb 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -30,6 +30,7 @@
 namespace art {
 
 class ImageHeader;
+class OatHeader;
 
 namespace mirror {
 class Object;
@@ -40,14 +41,21 @@
 
 class PatchOat {
  public:
-  static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings);
+  // Patch only the oat file
+  static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings,
+                    bool output_oat_opened_from_fd,  // Was this using --oatput-oat-fd ?
+                    bool new_oat_out);               // Output oat was a new file created by us?
 
+  // Patch only the image (art file)
   static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa,
                     TimingLogger* timings);
 
-  static bool Patch(const File* oat_in, const std::string& art_location,
+  // Patch both the image and the oat file
+  static bool Patch(File* oat_in, const std::string& art_location,
                     off_t delta, File* oat_out, File* art_out, InstructionSet isa,
-                    TimingLogger* timings);
+                    TimingLogger* timings,
+                    bool output_oat_opened_from_fd,  // Was this using --oatput-oat-fd ?
+                    bool new_oat_out);               // Output oat was a new file created by us?
 
  private:
   // Takes ownership only of the ElfFile. All other pointers are only borrowed.
@@ -63,6 +71,26 @@
         delta_(delta), timings_(timings) {}
   ~PatchOat() {}
 
+  // Was the .art image at image_path made with --compile-pic ?
+  static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path);
+
+  enum MaybePic {
+      NOT_PIC,            // Code not pic. Patch as usual.
+      PIC,                // Code was pic. Create symlink; skip OAT patching.
+      ERROR_OAT_FILE,     // Failed to symlink oat file
+      ERROR_FIRST = ERROR_OAT_FILE,
+  };
+
+  // Was the .oat image at oat_in made with --compile-pic ?
+  static MaybePic IsOatPic(const ElfFile* oat_in);
+
+  // Attempt to replace the file with a symlink
+  // Returns false if it fails
+  static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+                                        const std::string& output_oat_filename,
+                                        bool output_oat_opened_from_fd,
+                                        bool new_oat_out);  // Output oat was newly created?
+
   static void BitmapCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     reinterpret_cast<PatchOat*>(arg)->VisitObject(obj);
@@ -90,6 +118,9 @@
   mirror::Object* RelocatedCopyOf(mirror::Object*);
   mirror::Object* RelocatedAddressOf(mirror::Object* obj);
 
+  // Look up the oat header from any elf file.
+  static const OatHeader* GetOatHeader(const ElfFile* elf_file);
+
   // Walks through the old image and patches the mmap'd copy of it to the new offset. It does not
   // change the heap.
   class PatchVisitor {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 079d958..c3c7369 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -965,7 +965,7 @@
                                                                uint32_t dex_location_checksum,
                                                                const char* oat_location,
                                                                std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
                                             !Runtime::Current()->IsCompiler(),
                                             error_msg));
   if (oat_file.get() == nullptr) {
@@ -1037,7 +1037,7 @@
     error_msgs->push_back(error_msg);
     return nullptr;
   }
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
                                             !Runtime::Current()->IsCompiler(),
                                             &error_msg));
   if (oat_file.get() == nullptr) {
@@ -1291,6 +1291,7 @@
     // 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,
+                                                         nullptr,
                                                          executable, &odex_error_msg));
     if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
                                                        &odex_checksum_verified,
@@ -1314,6 +1315,7 @@
   bool cache_checksum_verified = false;
   if (have_dalvik_cache) {
     std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
+                                                          nullptr,
                                                           executable, &cache_error_msg));
     if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
                                                         &cache_checksum_verified,
@@ -1391,7 +1393,7 @@
                                                   InstructionSet isa,
                                                   std::string* error_msg) {
   // We open it non-executable
-  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, false, error_msg));
+  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
   if (output.get() == nullptr) {
     return nullptr;
   }
@@ -1448,7 +1450,7 @@
   LOG(INFO) << "Relocate Oat File: " << command_line;
   bool success = Exec(argv, error_msg);
   if (success) {
-    std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr,
+    std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
                                                   !runtime->IsCompiler(), error_msg));
     bool checksum_verified = false;
     if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
@@ -1549,7 +1551,7 @@
     return oat_file;
   }
 
-  return OatFile::Open(oat_location, oat_location, nullptr, !Runtime::Current()->IsCompiler(),
+  return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
                        error_msg);
 }
 
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0ab6394..6ec0712 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -106,7 +106,7 @@
   delete entry;
 }
 
-ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
+ElfFile::ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
   : file_(file),
     writable_(writable),
     program_header_only_(program_header_only),
@@ -124,13 +124,15 @@
     symtab_symbol_table_(nullptr),
     dynsym_symbol_table_(nullptr),
     jit_elf_image_(nullptr),
-    jit_gdb_entry_(nullptr) {
+    jit_gdb_entry_(nullptr),
+    requested_base_(requested_base) {
   CHECK(file != nullptr);
 }
 
 ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
-                       std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only));
+                       std::string* error_msg, uint8_t* requested_base) {
+  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only,
+                                    requested_base));
   int prot;
   int flags;
   if (writable) {
@@ -147,7 +149,8 @@
 }
 
 ElfFile* ElfFile::Open(File* file, int prot, int flags, std::string* error_msg) {
-  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false));
+  std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false,
+                                    /*requested_base*/nullptr));
   if (!elf_file->Setup(prot, flags, error_msg)) {
     return nullptr;
   }
@@ -757,6 +760,8 @@
   }
   const Elf32_Sym* sym = FindDynamicSymbol(symbol_name);
   if (sym != nullptr) {
+    // TODO: we need to change this to calculate base_address_ in ::Open,
+    // otherwise it will be wrongly 0 if ::Load has not yet been called.
     return base_address_ + sym->st_value;
   } else {
     return nullptr;
@@ -1094,12 +1099,16 @@
     }
     size_t file_length = static_cast<size_t>(temp_file_length);
     if (!reserved) {
-      byte* reserve_base = ((program_header->p_vaddr != 0) ?
-                            reinterpret_cast<byte*>(program_header->p_vaddr) : nullptr);
+      uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
+      uint8_t* reserve_base_override = reserve_base;
+      // Override the base (e.g. when compiling with --compile-pic)
+      if (requested_base_ != nullptr) {
+        reserve_base_override = requested_base_;
+      }
       std::string reservation_name("ElfFile reservation for ");
       reservation_name += file_->GetPath();
       std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
-                                                           reserve_base,
+                                                           reserve_base_override,
                                                            GetLoadedSize(), PROT_NONE, false,
                                                            error_msg));
       if (reserve.get() == nullptr) {
@@ -1108,9 +1117,15 @@
         return false;
       }
       reserved = true;
-      if (reserve_base == nullptr) {
-        base_address_ = reserve->Begin();
-      }
+
+      // Base address is the difference of actual mapped location and the p_vaddr
+      base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
+                       - reinterpret_cast<uintptr_t>(reserve_base));
+      // By adding the p_vaddr of a section/symbol to base_address_ we will always get the
+      // dynamic memory address of where that object is actually mapped
+      //
+      // TODO: base_address_ needs to be calculated in ::Open, otherwise
+      // FindDynamicSymbolAddress returns the wrong values until Load is called.
       segments_.push_back(reserve.release());
     }
     // empty segment, nothing to map
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 985be76..9c0bc9a 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -40,7 +40,8 @@
 // ELFObjectFile.
 class ElfFile {
  public:
-  static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
+  static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+                       uint8_t* requested_base = nullptr);  // TODO: move arg to before error_msg.
   // Open with specific mmap flags, Always maps in the whole file, not just the
   // program header sections.
   static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
@@ -52,10 +53,12 @@
     return *file_;
   }
 
+  // The start of the memory map address range for this ELF file.
   byte* Begin() const {
     return map_->Begin();
   }
 
+  // The end of the memory map address range for this ELF file.
   byte* End() const {
     return map_->End();
   }
@@ -109,7 +112,7 @@
   bool Load(bool executable, std::string* error_msg);
 
  private:
-  ElfFile(File* file, bool writable, bool program_header_only);
+  ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
 
   bool Setup(int prot, int flags, std::string* error_msg);
 
@@ -200,6 +203,9 @@
   JITCodeEntry* jit_gdb_entry_;
   std::unique_ptr<ElfFile> gdb_file_mapping_;
   void GdbJITSupport();
+
+  // When not-null, override the base vaddr we memory map LOAD segments into.
+  uint8_t* requested_base_;
 };
 
 }  // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index cf54991..e691518 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -668,7 +668,10 @@
   const ImageHeader& image_header = GetImageHeader();
   std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
 
+  CHECK(image_header.GetOatDataBegin() != nullptr);
+
   OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
+                                    image_header.GetOatFileBegin(),
                                     !Runtime::Current()->IsCompiler(), error_msg);
   if (oat_file == NULL) {
     *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
@@ -684,7 +687,7 @@
   }
   int32_t image_patch_delta = image_header.GetPatchDelta();
   int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
-  if (oat_patch_delta != image_patch_delta) {
+  if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
     // We should have already relocated by this point. Bail out.
     *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
                               "in image %s", oat_patch_delta, image_patch_delta, GetName());
diff --git a/runtime/image.cc b/runtime/image.cc
index f451df9..2a69128 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -35,7 +35,8 @@
                          uint32_t oat_file_begin,
                          uint32_t oat_data_begin,
                          uint32_t oat_data_end,
-                         uint32_t oat_file_end)
+                         uint32_t oat_file_end,
+                         bool compile_pic)
   : image_begin_(image_begin),
     image_size_(image_size),
     image_bitmap_offset_(image_bitmap_offset),
@@ -46,7 +47,8 @@
     oat_data_end_(oat_data_end),
     oat_file_end_(oat_file_end),
     patch_delta_(0),
-    image_roots_(image_roots) {
+    image_roots_(image_roots),
+    compile_pic_(compile_pic) {
   CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
   CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
   CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index e7f5552..a77aec4 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -28,7 +28,7 @@
 // header of image files written by ImageWriter, read and validated by Space.
 class PACKED(4) ImageHeader {
  public:
-  ImageHeader() {}
+  ImageHeader() : compile_pic_(0) {}
 
   ImageHeader(uint32_t image_begin,
               uint32_t image_size_,
@@ -39,7 +39,8 @@
               uint32_t oat_file_begin,
               uint32_t oat_data_begin,
               uint32_t oat_data_end,
-              uint32_t oat_file_end);
+              uint32_t oat_file_end,
+              bool compile_pic_);
 
   bool IsValid() const;
   const char* GetMagic() const;
@@ -121,6 +122,10 @@
 
   void RelocateImage(off_t delta);
 
+  bool CompilePic() const {
+    return compile_pic_ != 0;
+  }
+
  private:
   static const byte kImageMagic[4];
   static const byte kImageVersion[4];
@@ -162,6 +167,9 @@
   // Absolute address of an Object[] of objects needed to reinitialize from an image.
   uint32_t image_roots_;
 
+  // Boolean (0 or 1) to denote if the image was compiled with --compile-pic option
+  const uint32_t compile_pic_;
+
   friend class ImageWriter;
 };
 
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 01c8978..2bb59e2 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -287,9 +287,11 @@
 
 template <const bool kVerboseLogging, const bool kReasonLogging>
 static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
-                                   InstructionSet target_instruction_set) {
+                                   InstructionSet target_instruction_set,
+                                   bool* oat_is_pic) {
   std::string error_msg;
   std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
+                                                        nullptr,
                                                         false, &error_msg));
   if (oat_file.get() == nullptr) {
     if (kReasonLogging) {
@@ -299,6 +301,11 @@
     error_msg.clear();
     return kDexoptNeeded;
   }
+
+  // Pass-up the information about if this is PIC.
+  // TODO: Refactor this function to be less complicated.
+  *oat_is_pic = oat_file->IsPic();
+
   bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
   uint32_t location_checksum = 0;
   const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
@@ -526,10 +533,16 @@
   // Lets try the cache first (since we want to load from there since thats where the relocated
   // versions will be).
   if (have_cache_filename && !force_system_only) {
+    bool oat_is_pic;
     // We can use the dalvik-cache if we find a good file.
     dalvik_cache_decision =
         IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
-                                                               target_instruction_set);
+                                                               target_instruction_set, &oat_is_pic);
+
+    // Apps that are compiled with --compile-pic never need to be patchoat-d
+    if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
+      dalvik_cache_decision = kUpToDate;
+    }
     // We will only return DexOptNeeded if both the cache and system return it.
     if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
       CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
@@ -539,12 +552,18 @@
     // We couldn't find one thats easy. We should now try the system.
   }
 
+  bool oat_is_pic;
   jbyte system_decision =
       IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
-                                                             target_instruction_set);
+                                                             target_instruction_set, &oat_is_pic);
   CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
       << "May not return PatchoatNeeded when patching is disabled.";
 
+  // Apps that are compiled with --compile-pic never need to be patchoat-d
+  if (oat_is_pic && system_decision == kPatchoatNeeded) {
+    system_decision = kUpToDate;
+  }
+
   if (require_system_version && system_decision == kPatchoatNeeded
                              && dalvik_cache_decision == kUpToDate) {
     // We have a version from system relocated to the cache. Return it.
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ede108c..9d11de8 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -470,6 +470,12 @@
   return sizeof(OatHeader) + key_value_store_size_;
 }
 
+bool OatHeader::IsPic() const {
+  const char* pic_string = GetStoreValueByKey(OatHeader::kPicKey);
+  static const char kTrue[] = "true";
+  return (pic_string != nullptr && strncmp(pic_string, kTrue, sizeof(kTrue)) == 0);
+}
+
 void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
   char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
   if (key_value_store != nullptr) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 139f1cc..a5cb4bc 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -104,6 +104,7 @@
   bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const;
 
   size_t GetHeaderSize() const;
+  bool IsPic() const;
 
  private:
   OatHeader(InstructionSet instruction_set,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index be50199..9ba860c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -67,6 +67,7 @@
 OatFile* OatFile::Open(const std::string& filename,
                        const std::string& location,
                        byte* requested_base,
+                       uint8_t* oat_file_begin,
                        bool executable,
                        std::string* error_msg) {
   CHECK(!filename.empty()) << location;
@@ -91,7 +92,8 @@
       *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
       return nullptr;
     }
-    ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg));
+    ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+                          error_msg));
 
     // It would be nice to unlink here. But we might have opened the file created by the
     // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -102,12 +104,12 @@
 
 OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
   CheckLocation(location);
-  return OpenElfFile(file, location, NULL, true, false, error_msg);
+  return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
 }
 
 OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
   CheckLocation(location);
-  return OpenElfFile(file, location, NULL, false, false, error_msg);
+  return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
 }
 
 OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
@@ -125,11 +127,13 @@
 OatFile* OatFile::OpenElfFile(File* file,
                               const std::string& location,
                               byte* requested_base,
+                              uint8_t* oat_file_begin,
                               bool writable,
                               bool executable,
                               std::string* error_msg) {
   std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
-  bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
+  bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
+                                       error_msg);
   if (!success) {
     CHECK(!error_msg->empty());
     return nullptr;
@@ -188,9 +192,12 @@
   return Setup(error_msg);
 }
 
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, uint8_t* oat_file_begin,
+                          bool writable, bool executable,
                           std::string* error_msg) {
-  elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+  // TODO: rename requested_base to oat_data_begin
+  elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
+                                oat_file_begin));
   if (elf_file_.get() == nullptr) {
     DCHECK(!error_msg->empty());
     return false;
@@ -600,8 +607,7 @@
 }
 
 bool OatFile::IsPic() const {
-  const char* pic_string = GetOatHeader().GetStoreValueByKey(OatHeader::kPicKey);
-  return (pic_string != nullptr && strncmp(pic_string, "true", 5) == 0);
+  return GetOatHeader().IsPic();
   // TODO: Check against oat_patches. b/18144996
 }
 
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index a4c8da9..488988e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -49,6 +49,7 @@
   static OatFile* Open(const std::string& filename,
                        const std::string& location,
                        byte* requested_base,
+                       uint8_t* oat_file_begin,
                        bool executable,
                        std::string* error_msg);
 
@@ -305,13 +306,16 @@
   static OatFile* OpenElfFile(File* file,
                               const std::string& location,
                               byte* requested_base,
+                              uint8_t* oat_file_begin,  // Override base if not null
                               bool writable,
                               bool executable,
                               std::string* error_msg);
 
   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,
+  bool ElfFileOpen(File* file, byte* requested_base,
+                   uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
+                   bool writable, bool executable,
                    std::string* error_msg);
   bool Setup(std::string* error_msg);