in patchoat/patchoat.cc [1026:1228]
static int patchoat_oat(TimingLogger& timings,
InstructionSet isa,
const std::string& patched_image_location,
off_t base_delta,
bool base_delta_set,
int input_oat_fd,
const std::string& input_oat_location,
std::string input_oat_filename,
bool have_input_oat,
int output_oat_fd,
std::string output_oat_filename,
bool have_output_oat,
bool lock_output,
bool debug) {
{
// Only 1 of these may be set.
uint32_t cnt = 0;
cnt += (base_delta_set) ? 1 : 0;
cnt += (!patched_image_location.empty()) ? 1 : 0;
if (cnt > 1) {
Usage("Only one of --base-offset-delta or --patched-image-location may be used.");
} else if (cnt == 0) {
Usage("Must specify --base-offset-delta or --patched-image-location.");
}
}
if (!have_input_oat || !have_output_oat) {
Usage("Both input and output oat must be supplied to patch an app odex.");
}
if (!input_oat_location.empty()) {
if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) {
Usage("Unable to find filename for input oat location %s", input_oat_location.c_str());
}
if (debug) {
LOG(INFO) << "Using input-oat-file " << input_oat_filename;
}
}
bool match_delta = false;
if (!patched_image_location.empty()) {
std::string system_filename;
bool has_system = false;
std::string cache_filename;
bool has_cache = false;
bool has_android_data_unused = false;
bool is_global_cache = false;
if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
&system_filename, &has_system, &cache_filename,
&has_android_data_unused, &has_cache,
&is_global_cache)) {
Usage("Unable to determine image file for location %s", patched_image_location.c_str());
}
std::string patched_image_filename;
if (has_cache) {
patched_image_filename = cache_filename;
} else if (has_system) {
LOG(WARNING) << "Only image file found was in /system for image location "
<< patched_image_location;
patched_image_filename = system_filename;
} else {
Usage("Unable to determine image file for location %s", patched_image_location.c_str());
}
if (debug) {
LOG(INFO) << "Using patched-image-file " << patched_image_filename;
}
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());
}
}
if (!IsAligned<kPageSize>(base_delta)) {
Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
}
// Do we need to cleanup output files if we fail?
bool new_oat_out = false;
std::unique_ptr<File> input_oat;
std::unique_ptr<File> output_oat;
if (input_oat_fd != -1) {
if (input_oat_filename.empty()) {
input_oat_filename = "input-oat-file";
}
input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
if (input_oat_fd == output_oat_fd) {
input_oat.get()->DisableAutoClose();
}
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;
return EXIT_FAILURE;
}
} else {
CHECK(!input_oat_filename.empty());
input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
if (input_oat == nullptr) {
int err = errno;
LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
<< ": " << strerror(err) << "(" << err << ")";
return EXIT_FAILURE;
}
}
std::string error_msg;
std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE, &error_msg));
if (elf.get() == nullptr) {
LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
return EXIT_FAILURE;
}
if (!elf->HasSection(".text.oat_patches")) {
LOG(ERROR) << "missing oat patch section in input oat file " << input_oat->GetPath();
return EXIT_FAILURE;
}
if (output_oat_fd != -1) {
if (output_oat_filename.empty()) {
output_oat_filename = "output-oat-file";
}
output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
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_oat_filename, &new_oat_out](bool success) {
if (!success) {
if (new_oat_out) {
CHECK(!output_oat_filename.empty());
unlink(output_oat_filename.c_str());
}
}
if (kIsDebugBuild) {
LOG(INFO) << "Cleaning up.. success? " << success;
}
};
if (output_oat.get() == nullptr) {
cleanup(false);
return EXIT_FAILURE;
}
if (match_delta) {
// Figure out what the current delta is so we can match it to the desired delta.
off_t current_delta = 0;
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 "
<< std::dec << (base_delta/kPageSize) << " pages.";
}
ScopedFlock output_oat_lock;
if (lock_output) {
if (!output_oat_lock.Init(output_oat.get(), &error_msg)) {
LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg;
cleanup(false);
return EXIT_FAILURE;
}
}
TimingLogger::ScopedTiming pt("patch oat", &timings);
bool 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 = FinishFile(output_oat.get(), ret);
if (kIsDebugBuild) {
LOG(INFO) << "Exiting with return ... " << ret;
}
cleanup(ret);
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}