summaryrefslogtreecommitdiff
path: root/patchoat/patchoat.cc
diff options
context:
space:
mode:
Diffstat (limited to 'patchoat/patchoat.cc')
-rw-r--r--patchoat/patchoat.cc558
1 files changed, 495 insertions, 63 deletions
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index eb648cba18..d9cefee611 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -15,6 +15,7 @@
*/
#include "patchoat.h"
+#include <openssl/sha.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
@@ -24,12 +25,14 @@
#include <string>
#include <vector>
+#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/dumpable.h"
+#include "base/file_utils.h"
#include "base/logging.h" // For InitLogging.
#include "base/memory_tool.h"
#include "base/scoped_flock.h"
@@ -42,6 +45,7 @@
#include "gc/space/image_space.h"
#include "image-inl.h"
#include "intern_table.h"
+#include "leb128.h"
#include "mirror/dex_cache.h"
#include "mirror/executable.h"
#include "mirror/method.h"
@@ -58,6 +62,8 @@
namespace art {
+using android::base::StringPrintf;
+
static const OatHeader* GetOatHeader(const ElfFile* elf_file) {
uint64_t off = 0;
if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) {
@@ -120,11 +126,254 @@ static bool SymlinkFile(const std::string& input_filename, const std::string& ou
return true;
}
+bool PatchOat::GeneratePatch(
+ const MemMap& original,
+ const MemMap& relocated,
+ std::vector<uint8_t>* output,
+ std::string* error_msg) {
+ // FORMAT of the patch (aka image relocation) file:
+ // * SHA-256 digest (32 bytes) of original/unrelocated file (e.g., the one from /system)
+ // * List of monotonically increasing offsets (max value defined by uint32_t) at which relocations
+ // occur.
+ // Each element is represented as the delta from the previous offset in the list (first element
+ // is a delta from 0). Each delta is encoded using unsigned LEB128: little-endian
+ // variable-length 7 bits per byte encoding, where all bytes have the highest bit (0x80) set
+ // except for the final byte which does not have that bit set. For example, 0x3f is offset 0x3f,
+ // whereas 0xbf 0x05 is offset (0x3f & 0x7f) | (0x5 << 7) which is 0x2bf. Most deltas end up
+ // being encoding using just one byte, achieving ~4x decrease in relocation file size compared
+ // to the encoding where offsets are stored verbatim, as uint32_t.
+
+ size_t original_size = original.Size();
+ size_t relocated_size = relocated.Size();
+ if (original_size != relocated_size) {
+ *error_msg =
+ StringPrintf(
+ "Original and relocated image sizes differ: %zu vs %zu", original_size, relocated_size);
+ return false;
+ }
+ if ((original_size % 4) != 0) {
+ *error_msg = StringPrintf("Image size not multiple of 4: %zu", original_size);
+ return false;
+ }
+ if (original_size > UINT32_MAX) {
+ *error_msg = StringPrintf("Image too large: %zu" , original_size);
+ return false;
+ }
+
+ const ImageHeader& relocated_header =
+ *reinterpret_cast<const ImageHeader*>(relocated.Begin());
+ // Offsets are supposed to differ between original and relocated by this value
+ off_t expected_diff = relocated_header.GetPatchDelta();
+ if (expected_diff == 0) {
+ // Can't identify offsets which are supposed to differ due to relocation
+ *error_msg = "Relocation delta is 0";
+ return false;
+ }
+
+ // Output the SHA-256 digest of the original
+ output->resize(SHA256_DIGEST_LENGTH);
+ const uint8_t* original_bytes = original.Begin();
+ SHA256(original_bytes, original_size, output->data());
+
+ // Output the list of offsets at which the original and patched images differ
+ size_t last_diff_offset = 0;
+ size_t diff_offset_count = 0;
+ const uint8_t* relocated_bytes = relocated.Begin();
+ for (size_t offset = 0; offset < original_size; offset += 4) {
+ uint32_t original_value = *reinterpret_cast<const uint32_t*>(original_bytes + offset);
+ uint32_t relocated_value = *reinterpret_cast<const uint32_t*>(relocated_bytes + offset);
+ off_t diff = relocated_value - original_value;
+ if (diff == 0) {
+ continue;
+ } else if (diff != expected_diff) {
+ *error_msg =
+ StringPrintf(
+ "Unexpected diff at offset %zu. Expected: %jd, but was: %jd",
+ offset,
+ (intmax_t) expected_diff,
+ (intmax_t) diff);
+ return false;
+ }
+
+ uint32_t offset_diff = offset - last_diff_offset;
+ last_diff_offset = offset;
+ diff_offset_count++;
+
+ EncodeUnsignedLeb128(output, offset_diff);
+ }
+
+ if (diff_offset_count == 0) {
+ *error_msg = "Original and patched images are identical";
+ return false;
+ }
+
+ return true;
+}
+
+static bool WriteRelFile(
+ const MemMap& original,
+ const MemMap& relocated,
+ const std::string& rel_filename,
+ std::string* error_msg) {
+ std::vector<uint8_t> output;
+ if (!PatchOat::GeneratePatch(original, relocated, &output, error_msg)) {
+ return false;
+ }
+
+ std::unique_ptr<File> rel_file(OS::CreateEmptyFileWriteOnly(rel_filename.c_str()));
+ if (rel_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to create/open output file %s", rel_filename.c_str());
+ return false;
+ }
+ if (!rel_file->WriteFully(output.data(), output.size())) {
+ *error_msg = StringPrintf("Failed to write to %s", rel_filename.c_str());
+ return false;
+ }
+ if (rel_file->FlushCloseOrErase() != 0) {
+ *error_msg = StringPrintf("Failed to flush and close %s", rel_filename.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+static bool CheckImageIdenticalToOriginalExceptForRelocation(
+ const std::string& relocated_filename,
+ const std::string& original_filename,
+ std::string* error_msg) {
+ *error_msg = "";
+ std::string rel_filename = original_filename + ".rel";
+ std::unique_ptr<File> rel_file(OS::OpenFileForReading(rel_filename.c_str()));
+ if (rel_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to open image relocation file %s", rel_filename.c_str());
+ return false;
+ }
+ int64_t rel_size = rel_file->GetLength();
+ if (rel_size < 0) {
+ *error_msg = StringPrintf("Error while getting size of image relocation file %s",
+ rel_filename.c_str());
+ return false;
+ }
+ std::unique_ptr<uint8_t[]> rel(new uint8_t[rel_size]);
+ if (!rel_file->ReadFully(rel.get(), rel_size)) {
+ *error_msg = StringPrintf("Failed to read image relocation file %s", rel_filename.c_str());
+ return false;
+ }
+
+ std::unique_ptr<File> image_file(OS::OpenFileForReading(relocated_filename.c_str()));
+ if (image_file.get() == nullptr) {
+ *error_msg = StringPrintf("Unable to open relocated image file %s",
+ relocated_filename.c_str());
+ return false;
+ }
+
+ int64_t image_size = image_file->GetLength();
+ if (image_size < 0) {
+ *error_msg = StringPrintf("Error while getting size of relocated image file %s",
+ relocated_filename.c_str());
+ return false;
+ }
+ if ((image_size % 4) != 0) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image file %s size not multiple of 4: %" PRId64,
+ relocated_filename.c_str(), image_size);
+ return false;
+ }
+ if (image_size > std::numeric_limits<uint32_t>::max()) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image file %s too large: %" PRId64, relocated_filename.c_str(), image_size);
+ return false;
+ }
+
+ std::unique_ptr<uint8_t[]> image(new uint8_t[image_size]);
+ if (!image_file->ReadFully(image.get(), image_size)) {
+ *error_msg = StringPrintf("Failed to read relocated image file %s", relocated_filename.c_str());
+ return false;
+ }
+
+ const uint8_t* original_image_digest = rel.get();
+ if (rel_size < SHA256_DIGEST_LENGTH) {
+ *error_msg = StringPrintf("Malformed image relocation file %s: too short",
+ rel_filename.c_str());
+ return false;
+ }
+
+ const ImageHeader& image_header = *reinterpret_cast<const ImageHeader*>(image.get());
+ off_t expected_diff = image_header.GetPatchDelta();
+
+ if (expected_diff == 0) {
+ *error_msg = StringPrintf("Unsuported patch delta of zero in %s",
+ relocated_filename.c_str());
+ return false;
+ }
+
+ // Relocated image is expected to differ from the original due to relocation.
+ // Unrelocate the image in memory to compensate.
+ uint8_t* image_start = image.get();
+ const uint8_t* rel_end = &rel[rel_size];
+ const uint8_t* rel_ptr = &rel[SHA256_DIGEST_LENGTH];
+ // The remaining .rel file consists of offsets at which relocation should've occurred.
+ // For each offset, we "unrelocate" the image by subtracting the expected relocation
+ // diff value (as specified in the image header).
+ //
+ // Each offset is encoded as a delta/diff relative to the previous offset. With the
+ // very first offset being encoded relative to offset 0.
+ // Deltas are encoded using little-endian 7 bits per byte encoding, with all bytes except
+ // the last one having the highest bit set.
+ uint32_t offset = 0;
+ while (rel_ptr != rel_end) {
+ uint32_t offset_delta = 0;
+ if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) {
+ offset += offset_delta;
+ uint32_t *image_value = reinterpret_cast<uint32_t*>(image_start + offset);
+ *image_value -= expected_diff;
+ } else {
+ *error_msg =
+ StringPrintf(
+ "Malformed image relocation file %s: "
+ "last byte has it's most significant bit set",
+ rel_filename.c_str());
+ return false;
+ }
+ }
+
+ // Image in memory is now supposed to be identical to the original. We
+ // confirm this by comparing the digest of the in-memory image to the expected
+ // digest from relocation file.
+ uint8_t image_digest[SHA256_DIGEST_LENGTH];
+ SHA256(image.get(), image_size, image_digest);
+ if (memcmp(image_digest, original_image_digest, SHA256_DIGEST_LENGTH) != 0) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image %s does not match the original %s after unrelocation",
+ relocated_filename.c_str(),
+ original_filename.c_str());
+ return false;
+ }
+
+ // Relocated image is identical to the original, once relocations are taken into account
+ return true;
+}
+
bool PatchOat::Patch(const std::string& image_location,
off_t delta,
- const std::string& output_directory,
+ const std::string& output_image_directory,
+ const std::string& output_image_relocation_directory,
InstructionSet isa,
TimingLogger* timings) {
+ bool output_image = !output_image_directory.empty();
+ bool output_image_relocation = !output_image_relocation_directory.empty();
+ if ((!output_image) && (!output_image_relocation)) {
+ // Nothing to do
+ return true;
+ }
+ if ((output_image_relocation) && (delta == 0)) {
+ LOG(ERROR) << "Cannot output image relocation information when requested relocation delta is 0";
+ return false;
+ }
+
CHECK(Runtime::Current() == nullptr);
CHECK(!image_location.empty()) << "image file must have a filename.";
@@ -221,32 +470,35 @@ bool PatchOat::Patch(const std::string& image_location,
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 == NOT_PIC) {
- LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath();
- return false;
- } else {
- CHECK(is_oat_pic == PIC);
-
- // Create a symlink.
- std::string converted_image_filename = space->GetImageLocation();
- std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
- std::string output_image_filename = output_directory +
- (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
- converted_image_filename;
- std::string output_vdex_filename =
- ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
- std::string output_oat_filename =
- ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
-
- if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
- output_oat_filename) ||
- !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
- // Errors already logged by above call.
+ if (output_image) {
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
+ return false;
+ } else if (is_oat_pic == NOT_PIC) {
+ LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath();
return false;
+ } else {
+ CHECK(is_oat_pic == PIC);
+
+ // Create a symlink.
+ std::string converted_image_filename = space->GetImageLocation();
+ std::replace(
+ converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
+ std::string output_image_filename = output_image_directory +
+ (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
+ converted_image_filename;
+ std::string output_vdex_filename =
+ ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
+ std::string output_oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
+
+ if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
+ output_oat_filename) ||
+ !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
+ // Errors already logged by above call.
+ return false;
+ }
}
}
@@ -267,28 +519,72 @@ bool PatchOat::Patch(const std::string& image_location,
}
}
- // Write the patched image spaces.
- for (size_t i = 0; i < spaces.size(); ++i) {
- gc::space::ImageSpace* space = spaces[i];
+ if (output_image) {
+ // Write the patched image spaces.
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
- t.NewTiming("Writing image");
- std::string converted_image_filename = space->GetImageLocation();
- std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
- std::string output_image_filename = output_directory +
- (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
- converted_image_filename;
- std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str()));
- if (output_image_file.get() == nullptr) {
- LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
- return false;
+ t.NewTiming("Writing image");
+ std::string converted_image_filename = space->GetImageLocation();
+ std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
+ std::string output_image_filename = output_image_directory +
+ (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
+ converted_image_filename;
+ std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str()));
+ if (output_image_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
+ return false;
+ }
+
+ PatchOat& p = space_to_patchoat_map.find(space)->second;
+
+ bool success = p.WriteImage(output_image_file.get());
+ success = FinishFile(output_image_file.get(), success);
+ if (!success) {
+ return false;
+ }
}
+ }
- PatchOat& p = space_to_patchoat_map.find(space)->second;
+ if (output_image_relocation) {
+ // Write the image relocation information for each space.
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+
+ t.NewTiming("Writing image relocation");
+ std::string original_image_filename(space->GetImageLocation() + ".rel");
+ std::string image_relocation_filename =
+ output_image_relocation_directory
+ + (android::base::StartsWith(original_image_filename, "/") ? "" : "/")
+ + original_image_filename.substr(original_image_filename.find_last_of("/"));
+ File& input_image = *space_to_file_map.find(space)->second;
+ int64_t input_image_size = input_image.GetLength();
+ if (input_image_size < 0) {
+ LOG(ERROR) << "Error while getting input image size";
+ return false;
+ }
+ std::string error_msg;
+ std::unique_ptr<MemMap> original(MemMap::MapFile(input_image_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ input_image.Fd(),
+ 0,
+ /*low_4gb*/false,
+ input_image.GetPath().c_str(),
+ &error_msg));
+ if (original.get() == nullptr) {
+ LOG(ERROR) << "Unable to map image file " << input_image.GetPath() << " : " << error_msg;
+ return false;
+ }
- bool success = p.WriteImage(output_image_file.get());
- success = FinishFile(output_image_file.get(), success);
- if (!success) {
- return false;
+ PatchOat& p = space_to_patchoat_map.find(space)->second;
+ const MemMap* relocated = p.image_;
+
+ if (!WriteRelFile(*original, *relocated, image_relocation_filename, &error_msg)) {
+ LOG(ERROR) << "Failed to create image relocation file " << image_relocation_filename
+ << ": " << error_msg;
+ return false;
+ }
}
}
@@ -301,6 +597,86 @@ bool PatchOat::Patch(const std::string& image_location,
return true;
}
+bool PatchOat::Verify(const std::string& image_location,
+ const std::string& output_image_directory,
+ InstructionSet isa,
+ TimingLogger* timings) {
+ if (image_location.empty()) {
+ LOG(ERROR) << "Original image file not provided";
+ return false;
+ }
+ if (output_image_directory.empty()) {
+ LOG(ERROR) << "Relocated image directory not provided";
+ return false;
+ }
+
+ TimingLogger::ScopedTiming t("Runtime Setup", timings);
+
+ CHECK_NE(isa, InstructionSet::kNone);
+ const char* isa_name = GetInstructionSetString(isa);
+
+ // Set up the runtime
+ RuntimeOptions options;
+ NoopCompilerCallbacks callbacks;
+ options.push_back(std::make_pair("compilercallbacks", &callbacks));
+ std::string img = "-Ximage:" + image_location;
+ options.push_back(std::make_pair(img.c_str(), nullptr));
+ options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name)));
+ options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+ if (!Runtime::Create(options, false)) {
+ LOG(ERROR) << "Unable to initialize runtime";
+ return false;
+ }
+ std::unique_ptr<Runtime> runtime(Runtime::Current());
+
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
+ // give it away now and then switch to a more manageable ScopedObjectAccess.
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+ ScopedObjectAccess soa(Thread::Current());
+
+ t.NewTiming("Image Verification setup");
+ std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
+
+ // TODO: Check that no other .rel files exist in the original dir
+
+ bool success = true;
+ std::string image_location_dir = android::base::Dirname(image_location);
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+ std::string image_filename = space->GetImageLocation();
+
+ std::string relocated_image_filename;
+ std::string error_msg;
+ if (!GetDalvikCacheFilename(image_filename.c_str(),
+ output_image_directory.c_str(), &relocated_image_filename, &error_msg)) {
+ LOG(ERROR) << "Failed to find relocated image file name: " << error_msg;
+ success = false;
+ break;
+ }
+ // location: /system/framework/boot.art
+ // isa: arm64
+ // basename: boot.art
+ // original: /system/framework/arm64/boot.art
+ // relocation: /system/framework/arm64/boot.art.rel
+ std::string original_image_filename = GetSystemImageFilename(image_filename.c_str(), isa);
+
+ if (!CheckImageIdenticalToOriginalExceptForRelocation(
+ relocated_image_filename, original_image_filename, &error_msg)) {
+ LOG(ERROR) << error_msg;
+ success = false;
+ break;
+ }
+ }
+
+ if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+ // We want to just exit on non-debug builds, not bringing the runtime down
+ // in an orderly fashion. So release the following fields.
+ runtime.release();
+ }
+
+ return success;
+}
+
bool PatchOat::WriteImage(File* out) {
TimingLogger::ScopedTiming t("Writing image File", timings_);
std::string error_msg;
@@ -739,9 +1115,14 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched");
UsageError(" image file to.");
UsageError("");
+ UsageError(" --output-image-relocation-file=<file.art.rel>: Specifies the exact file to write");
+ UsageError(" the image relocation information to.");
+ UsageError("");
UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
UsageError(" This value may be negative.");
UsageError("");
+ UsageError(" --verify: Verify an existing patched file instead of creating one.");
+ UsageError("");
UsageError(" --dump-timings: dump out patch timing information");
UsageError("");
UsageError(" --no-dump-timings: do not dump out patch timing information");
@@ -750,16 +1131,17 @@ NO_RETURN static void Usage(const char *fmt, ...) {
exit(EXIT_FAILURE);
}
-static int patchoat_image(TimingLogger& timings,
- InstructionSet isa,
- const std::string& input_image_location,
- const std::string& output_image_filename,
- off_t base_delta,
- bool base_delta_set,
- bool debug) {
+static int patchoat_patch_image(TimingLogger& timings,
+ InstructionSet isa,
+ const std::string& input_image_location,
+ const std::string& output_image_directory,
+ const std::string& output_image_relocation_filename,
+ off_t base_delta,
+ bool base_delta_set,
+ bool debug) {
CHECK(!input_image_location.empty());
- if (output_image_filename.empty()) {
- Usage("Image patching requires --output-image-file");
+ if ((output_image_directory.empty()) && (output_image_relocation_filename.empty())) {
+ Usage("Image patching requires --output-image-file or --output-image-relocation-file");
}
if (!base_delta_set) {
@@ -778,9 +1160,37 @@ static int patchoat_image(TimingLogger& timings,
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
- std::string output_directory =
- output_image_filename.substr(0, output_image_filename.find_last_of('/'));
- bool ret = PatchOat::Patch(input_image_location, base_delta, output_directory, isa, &timings);
+ std::string output_image_relocation_directory =
+ output_image_relocation_filename.substr(
+ 0, output_image_relocation_filename.find_last_of('/'));
+ bool ret =
+ PatchOat::Patch(
+ input_image_location,
+ base_delta,
+ output_image_directory,
+ output_image_relocation_directory,
+ isa,
+ &timings);
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Exiting with return ... " << ret;
+ }
+ return ret ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int patchoat_verify_image(TimingLogger& timings,
+ InstructionSet isa,
+ const std::string& input_image_location,
+ const std::string& output_image_directory) {
+ CHECK(!input_image_location.empty());
+ TimingLogger::ScopedTiming pt("verify image and oat", &timings);
+
+ bool ret =
+ PatchOat::Verify(
+ input_image_location,
+ output_image_directory,
+ isa,
+ &timings);
if (kIsDebugBuild) {
LOG(INFO) << "Exiting with return ... " << ret;
@@ -811,9 +1221,11 @@ static int patchoat(int argc, char **argv) {
InstructionSet isa = InstructionSet::kNone;
std::string input_image_location;
std::string output_image_filename;
+ std::string output_image_relocation_filename;
off_t base_delta = 0;
bool base_delta_set = false;
bool dump_timings = kIsDebugBuild;
+ bool verify = false;
for (int i = 0; i < argc; ++i) {
const StringPiece option(argv[i]);
@@ -832,6 +1244,9 @@ static int patchoat(int argc, char **argv) {
input_image_location = option.substr(strlen("--input-image-location=")).data();
} else if (option.starts_with("--output-image-file=")) {
output_image_filename = option.substr(strlen("--output-image-file=")).data();
+ } else if (option.starts_with("--output-image-relocation-file=")) {
+ output_image_relocation_filename =
+ option.substr(strlen("--output-image-relocation-file=")).data();
} else if (option.starts_with("--base-offset-delta=")) {
const char* base_delta_str = option.substr(strlen("--base-offset-delta=")).data();
base_delta_set = true;
@@ -842,23 +1257,40 @@ static int patchoat(int argc, char **argv) {
dump_timings = true;
} else if (option == "--no-dump-timings") {
dump_timings = false;
+ } else if (option == "--verify") {
+ verify = true;
} else {
Usage("Unknown argument %s", option.data());
}
}
+ // TODO: Have calls to patchoat pass in the output_image directory instead of
+ // the output_image_filename.
+ std::string output_image_directory;
+ if (!output_image_filename.empty())
+ output_image_directory = android::base::Dirname(output_image_filename);
+
// The instruction set is mandatory. This simplifies things...
if (!isa_set) {
Usage("Instruction set must be set.");
}
- int ret = patchoat_image(timings,
- isa,
- input_image_location,
- output_image_filename,
- base_delta,
- base_delta_set,
- debug);
+ int ret;
+ if (verify) {
+ ret = patchoat_verify_image(timings,
+ isa,
+ input_image_location,
+ output_image_directory);
+ } else {
+ ret = patchoat_patch_image(timings,
+ isa,
+ input_image_location,
+ output_image_directory,
+ output_image_relocation_filename,
+ base_delta,
+ base_delta_set,
+ debug);
+ }
timings.EndTiming();
if (dump_timings) {