Make patchoat match offset when given a patched image

Previously if we gave patchoat a patched image and a already patched
(but improperly relocated) oat file it would not correctly patch the
oat file to the same offset as the image.

Bug: 22599792
Bug: 23119724
Change-Id: I8773022bd75c2e0b7eb529893b147cbd8792bcad
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 283eea9..6cd391f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -92,6 +92,32 @@
   }
 }
 
+static const OatHeader* GetOatHeader(const ElfFile* elf_file) {
+  uint64_t off = 0;
+  if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) {
+    return nullptr;
+  }
+
+  OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf_file->Begin() + off);
+  return oat_header;
+}
+
+// This function takes an elf file and reads the current patch delta value
+// encoded in its oat header value
+static bool ReadOatPatchDelta(const ElfFile* elf_file, off_t* delta, std::string* error_msg) {
+  const OatHeader* oat_header = GetOatHeader(elf_file);
+  if (oat_header == nullptr) {
+    *error_msg = "Unable to get oat header from elf file.";
+    return false;
+  }
+  if (!oat_header->IsValid()) {
+    *error_msg = "Elf file has an invalid oat header";
+    return false;
+  }
+  *delta = oat_header->GetImagePatchDelta();
+  return true;
+}
+
 bool PatchOat::Patch(const std::string& image_location, off_t delta,
                      File* output_image, InstructionSet isa,
                      TimingLogger* timings) {
@@ -585,25 +611,6 @@
   copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object);
 }
 
-const OatHeader* PatchOat::GetOatHeader(const ElfFile* elf_file) {
-  if (elf_file->Is64Bit()) {
-    return GetOatHeader<ElfFileImpl64>(elf_file->GetImpl64());
-  } else {
-    return GetOatHeader<ElfFileImpl32>(elf_file->GetImpl32());
-  }
-}
-
-template <typename ElfFileImpl>
-const OatHeader* PatchOat::GetOatHeader(const ElfFileImpl* 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);
@@ -871,11 +878,11 @@
   UsageError("  --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
   UsageError("      This value may be negative.");
   UsageError("");
-  UsageError("  --patched-image-file=<file.art>: Use the same patch delta as was used to patch");
-  UsageError("      the given image file.");
+  UsageError("  --patched-image-file=<file.art>: Relocate the oat file to be the same as the");
+  UsageError("      given image file.");
   UsageError("");
-  UsageError("  --patched-image-location=<file.art>: Use the same patch delta as was used to");
-  UsageError("      patch the given image location. If used one must also specify the");
+  UsageError("  --patched-image-location=<file.art>: Relocate the oat file to be the same as the");
+  UsageError("      image at the given location. If used one must also specify the");
   UsageError("      --instruction-set flag. It will search for this image in the same way that");
   UsageError("      is done when loading one.");
   UsageError("");
@@ -991,6 +998,7 @@
   bool orig_base_offset_set = false;
   off_t base_delta = 0;
   bool base_delta_set = false;
+  bool match_delta = false;
   std::string patched_image_filename;
   std::string patched_image_location;
   bool dump_timings = kIsDebugBuild;
@@ -1189,7 +1197,11 @@
       base_delta_set = true;
       base_delta = base_offset - orig_base_offset;
     } else if (!patched_image_filename.empty()) {
+      if (have_image_files) {
+        Usage("--patched-image-location should not be used when patching other images");
+      }
       base_delta_set = true;
+      match_delta = true;
       std::string error_msg;
       if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) {
         Usage(error_msg.c_str(), patched_image_filename.c_str());
@@ -1307,6 +1319,32 @@
     return EXIT_FAILURE;
   }
 
+  if (match_delta) {
+    CHECK(!have_image_files);  // We will not do this with images.
+    std::string error_msg;
+    // Figure out what the current delta is so we can match it to the desired delta.
+    std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE,
+                                               &error_msg));
+    off_t current_delta = 0;
+    if (elf.get() == nullptr) {
+      LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
+      cleanup(false);
+      return EXIT_FAILURE;
+    } else if (!ReadOatPatchDelta(elf.get(), &current_delta, &error_msg)) {
+      LOG(ERROR) << "Unable to get current delta: " << error_msg;
+      cleanup(false);
+      return EXIT_FAILURE;
+    }
+    // Before this line base_delta is the desired final delta. We need it to be the actual amount to
+    // change everything by. We subtract the current delta from it to make it this.
+    base_delta -= current_delta;
+    if (!IsAligned<kPageSize>(base_delta)) {
+      LOG(ERROR) << "Given image file was relocated by an illegal delta";
+      cleanup(false);
+      return false;
+    }
+  }
+
   if (debug) {
     LOG(INFO) << "moving offset by " << base_delta
               << " (0x" << std::hex << base_delta << ") bytes or "
@@ -1333,18 +1371,18 @@
                           new_oat_out);
     // The order here doesn't matter. If the first one is successfully saved and the second one
     // erased, ImageSpace will still detect a problem and not use the files.
-    ret = ret && FinishFile(output_image.get(), ret);
-    ret = ret && FinishFile(output_oat.get(), ret);
+    ret = FinishFile(output_image.get(), ret);
+    ret = FinishFile(output_oat.get(), ret);
   } else if (have_oat_files) {
     TimingLogger::ScopedTiming pt("patch oat", &timings);
     ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
                           output_oat_fd >= 0,  // was it opened from FD?
                           new_oat_out);
-    ret = ret && FinishFile(output_oat.get(), ret);
+    ret = FinishFile(output_oat.get(), ret);
   } else if (have_image_files) {
     TimingLogger::ScopedTiming pt("patch image", &timings);
     ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
-    ret = ret && FinishFile(output_image.get(), ret);
+    ret = FinishFile(output_image.get(), ret);
   } else {
     CHECK(false);
     ret = true;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 43cdaea..87ecc61 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -163,13 +163,6 @@
     return ret;
   }
 
-  // Look up the oat header from any elf file.
-  static const OatHeader* GetOatHeader(const ElfFile* elf_file);
-
-  // Templatized version to actually look up the oat header
-  template <typename ElfFileImpl>
-  static const OatHeader* GetOatHeader(const ElfFileImpl* 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/elf_file.cc b/runtime/elf_file.cc
index 9fd8c87..723ee74 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1868,7 +1868,8 @@
   DELEGATE_TO_IMPL(GetFile);
 }
 
-bool ElfFile::GetSectionOffsetAndSize(const char* section_name, uint64_t* offset, uint64_t* size) {
+bool ElfFile::GetSectionOffsetAndSize(const char* section_name, uint64_t* offset,
+                                      uint64_t* size) const {
   if (elf32_.get() == nullptr) {
     CHECK(elf64_.get() != nullptr);
 
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 48cb4b8..1188c97 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -60,7 +60,7 @@
 
   const File& GetFile() const;
 
-  bool GetSectionOffsetAndSize(const char* section_name, uint64_t* offset, uint64_t* size);
+  bool GetSectionOffsetAndSize(const char* section_name, uint64_t* offset, uint64_t* size) const;
 
   uint64_t FindSymbolAddress(unsigned section_type,
                              const std::string& symbol_name,