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(), ¤t_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,