diff options
Diffstat (limited to 'patchoat/patchoat.cc')
| -rw-r--r-- | patchoat/patchoat.cc | 291 |
1 files changed, 207 insertions, 84 deletions
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 85b4e6df32..eed20da178 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -17,11 +17,14 @@ #include <stdio.h> #include <stdlib.h> +#include <sys/file.h> #include <sys/stat.h> +#include <unistd.h> #include <string> #include <vector> +#include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/stringprintf.h" #include "elf_utils.h" @@ -63,6 +66,47 @@ static InstructionSet ElfISAToInstructionSet(Elf32_Word isa) { } } +static bool LocationToFilename(const std::string& location, InstructionSet isa, + std::string* filename) { + bool has_system = false; + bool has_cache = false; + // image_location = /system/framework/boot.art + // system_image_location = /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; + } + + bool have_android_data = false; + bool dalvik_cache_exists = false; + std::string dalvik_cache; + GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache, + &have_android_data, &dalvik_cache_exists); + + std::string cache_filename; + if (have_android_data && dalvik_cache_exists) { + // Always set output location even if it does not exist, + // so that the caller knows where to create the image. + // + // image_location = /system/framework/boot.art + // *image_filename = /data/dalvik-cache/<image_isa>/boot.art + std::string error_msg; + if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), + &cache_filename, &error_msg)) { + has_cache = true; + } + } + if (has_system) { + *filename = system_filename; + return true; + } else if (has_cache) { + *filename = cache_filename; + return true; + } else { + return false; + } +} + bool PatchOat::Patch(const std::string& image_location, off_t delta, File* output_image, InstructionSet isa, TimingLogger* timings) { @@ -74,10 +118,15 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta, TimingLogger::ScopedTiming t("Runtime Setup", timings); const char *isa_name = GetInstructionSetString(isa); - std::string image_filename(GetSystemImageFilename(image_location.c_str(), isa)); + std::string image_filename; + if (!LocationToFilename(image_location, isa, &image_filename)) { + LOG(ERROR) << "Unable to find image at location " << image_location; + return false; + } std::unique_ptr<File> input_image(OS::OpenFileForReading(image_filename.c_str())); if (input_image.get() == nullptr) { - LOG(ERROR) << "unable to open input image file."; + LOG(ERROR) << "unable to open input image file at " << image_filename + << " for location " << image_location; return false; } int64_t image_len = input_image->GetLength(); @@ -125,7 +174,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta, delta, timings); t.NewTiming("Patching files"); if (!p.PatchImage()) { - LOG(INFO) << "Failed to patch image file " << input_image->GetPath(); + LOG(ERROR) << "Failed to patch image file " << input_image->GetPath(); return false; } @@ -159,10 +208,15 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o isa = ElfISAToInstructionSet(elf_hdr.e_machine); } const char* isa_name = GetInstructionSetString(isa); - std::string image_filename(GetSystemImageFilename(image_location.c_str(), isa)); + std::string image_filename; + if (!LocationToFilename(image_location, isa, &image_filename)) { + LOG(ERROR) << "Unable to find image at location " << image_location; + return false; + } std::unique_ptr<File> input_image(OS::OpenFileForReading(image_filename.c_str())); if (input_image.get() == nullptr) { - LOG(ERROR) << "unable to open input image file."; + LOG(ERROR) << "unable to open input image file at " << image_filename + << " for location " << image_location; return false; } int64_t image_len = input_image->GetLength(); @@ -216,11 +270,11 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o delta, timings); t.NewTiming("Patching files"); if (!p.PatchElf()) { - LOG(INFO) << "Failed to patch oat file " << input_oat->GetPath(); + LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath(); return false; } if (!p.PatchImage()) { - LOG(INFO) << "Failed to patch image file " << input_image->GetPath(); + LOG(ERROR) << "Failed to patch image file " << input_image->GetPath(); return false; } @@ -236,6 +290,7 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o bool PatchOat::WriteElf(File* out) { TimingLogger::ScopedTiming t("Writing Elf File", timings_); + CHECK(oat_file_.get() != nullptr); CHECK(out != nullptr); size_t expect = oat_file_->Size(); @@ -250,6 +305,11 @@ bool PatchOat::WriteElf(File* out) { bool PatchOat::WriteImage(File* out) { TimingLogger::ScopedTiming t("Writing image File", timings_); + std::string error_msg; + + ScopedFlock img_flock; + img_flock.Init(out, &error_msg); + CHECK(image_ != nullptr); CHECK(out != nullptr); size_t expect = image_->Size(); @@ -406,16 +466,13 @@ bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogge return true; } -bool PatchOat::CheckOatFile() { - Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches"); - if (patches_sec == nullptr) { - return false; - } - if (patches_sec->sh_type != SHT_OAT_PATCH) { +template <typename ptr_t> +bool PatchOat::CheckOatFile(const Elf32_Shdr& patches_sec) { + if (patches_sec.sh_type != SHT_OAT_PATCH) { return false; } - uintptr_t* patches = reinterpret_cast<uintptr_t*>(oat_file_->Begin() + patches_sec->sh_offset); - uintptr_t* patches_end = patches + (patches_sec->sh_size/sizeof(uintptr_t)); + ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file_->Begin() + patches_sec.sh_offset); + ptr_t* patches_end = patches + (patches_sec.sh_size / sizeof(ptr_t)); Elf32_Shdr* oat_data_sec = oat_file_->FindSectionByName(".rodata"); Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text"); if (oat_data_sec == nullptr) { @@ -437,19 +494,50 @@ bool PatchOat::CheckOatFile() { return true; } +bool PatchOat::PatchOatHeader() { + Elf32_Shdr *rodata_sec = oat_file_->FindSectionByName(".rodata"); + if (rodata_sec == nullptr) { + return false; + } + OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file_->Begin() + rodata_sec->sh_offset); + if (!oat_header->IsValid()) { + LOG(ERROR) << "Elf file " << oat_file_->GetFile().GetPath() << " has an invalid oat header"; + return false; + } + oat_header->RelocateOat(delta_); + return true; +} + bool PatchOat::PatchElf() { - TimingLogger::ScopedTiming t("Fixup Elf Headers", timings_); + TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_); + if (!PatchTextSection()) { + return false; + } + + if (!PatchOatHeader()) { + return false; + } + + bool need_fixup = false; + t.NewTiming("Fixup Elf Headers"); // Fixup Phdr's for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) { Elf32_Phdr& hdr = oat_file_->GetProgramHeader(i); - if (hdr.p_vaddr != 0) { + if (hdr.p_vaddr != 0 && hdr.p_vaddr != hdr.p_offset) { + need_fixup = true; hdr.p_vaddr += delta_; } - if (hdr.p_paddr != 0) { + if (hdr.p_paddr != 0 && hdr.p_paddr != hdr.p_offset) { + need_fixup = true; hdr.p_paddr += delta_; } } - // Fixup Shdr's + if (!need_fixup) { + // This was never passed through ElfFixup so all headers/symbols just have their offset as + // their addr. Therefore we do not need to update these parts. + return true; + } + t.NewTiming("Fixup Section Headers"); for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) { Elf32_Shdr& hdr = oat_file_->GetSectionHeader(i); if (hdr.sh_addr != 0) { @@ -457,7 +545,7 @@ bool PatchOat::PatchElf() { } } - // Fixup Dynamics. + t.NewTiming("Fixup Dynamics"); for (Elf32_Word i = 0; i < oat_file_->GetDynamicNum(); i++) { Elf32_Dyn& dyn = oat_file_->GetDynamic(i); if (IsDynamicSectionPointer(dyn.d_tag, oat_file_->GetHeader().e_machine)) { @@ -481,12 +569,6 @@ bool PatchOat::PatchElf() { } } - t.NewTiming("Fixup Elf Text Section"); - // Fixup text - if (!PatchTextSection()) { - return false; - } - return true; } @@ -511,13 +593,31 @@ bool PatchOat::PatchSymbols(Elf32_Shdr* section) { bool PatchOat::PatchTextSection() { Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches"); if (patches_sec == nullptr) { - LOG(INFO) << ".oat_patches section not found. Aborting patch"; + LOG(ERROR) << ".oat_patches section not found. Aborting patch"; return false; } - DCHECK(CheckOatFile()) << "Oat file invalid"; - CHECK_EQ(patches_sec->sh_type, SHT_OAT_PATCH) << "Unexpected type of .oat_patches"; - uintptr_t* patches = reinterpret_cast<uintptr_t*>(oat_file_->Begin() + patches_sec->sh_offset); - uintptr_t* patches_end = patches + (patches_sec->sh_size/sizeof(uintptr_t)); + if (patches_sec->sh_type != SHT_OAT_PATCH) { + LOG(ERROR) << "Unexpected type of .oat_patches"; + return false; + } + + switch (patches_sec->sh_entsize) { + case sizeof(uint32_t): + return PatchTextSection<uint32_t>(*patches_sec); + case sizeof(uint64_t): + return PatchTextSection<uint64_t>(*patches_sec); + default: + LOG(ERROR) << ".oat_patches Entsize of " << patches_sec->sh_entsize << "bits " + << "is not valid"; + return false; + } +} + +template <typename ptr_t> +bool PatchOat::PatchTextSection(const Elf32_Shdr& patches_sec) { + DCHECK(CheckOatFile<ptr_t>(patches_sec)) << "Oat file invalid"; + ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file_->Begin() + patches_sec.sh_offset); + ptr_t* patches_end = patches + (patches_sec.sh_size / sizeof(ptr_t)); Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text"); CHECK(oat_text_sec != nullptr); byte* to_patch = oat_file_->Begin() + oat_text_sec->sh_offset; @@ -529,7 +629,6 @@ bool PatchOat::PatchTextSection() { CHECK_LT(reinterpret_cast<uintptr_t>(patch_loc), to_patch_end); *patch_loc += delta_; } - return true; } @@ -585,9 +684,6 @@ static void Usage(const char *fmt, ...) { UsageError(" --output-oat-file=<file.oat>: Specifies the exact file to write the patched oat"); UsageError(" file to."); UsageError(""); - UsageError(" --output-oat-location=<file.oat>: Specifies the 'location' to write the patched"); - UsageError(" oat file to. If used one must also specify the --instruction-set"); - UsageError(""); UsageError(" --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the"); UsageError(" the patched oat file to."); UsageError(""); @@ -597,9 +693,6 @@ static void Usage(const char *fmt, ...) { UsageError(" --output-image-fd=<file-descriptor>: Specifies the file-descriptor to write the"); UsageError(" the patched image file to."); UsageError(""); - UsageError(" --output-image-location=<file.art>: Specifies the 'location' to write the patched"); - UsageError(" image file to. If used one must also specify the --instruction-set"); - UsageError(""); UsageError(" --orig-base-offset=<original-base-offset>: Specify the base offset the input file"); UsageError(" was compiled with. This is needed if one is specifying a --base-offset"); UsageError(""); @@ -614,7 +707,12 @@ static void Usage(const char *fmt, ...) { 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(" --instruction-set flag."); + UsageError(" --instruction-set flag. It will search for this image in the same way that"); + UsageError(" is done when loading one."); + UsageError(""); + UsageError(" --lock-output: Obtain a flock on output oat file before starting."); + UsageError(""); + UsageError(" --no-lock-output: Do not attempt to obtain a flock on output oat file."); UsageError(""); UsageError(" --dump-timings: dump out patch timing information"); UsageError(""); @@ -658,7 +756,15 @@ static File* CreateOrOpen(const char* name, bool* created) { return OS::OpenFileReadWrite(name); } else { *created = true; - return OS::CreateEmptyFile(name); + std::unique_ptr<File> f(OS::CreateEmptyFile(name)); + if (f.get() != nullptr) { + if (fchmod(f->Fd(), 0644) != 0) { + PLOG(ERROR) << "Unable to make " << name << " world readable"; + unlink(name); + return nullptr; + } + } + return f.release(); } } @@ -690,11 +796,9 @@ static int patchoat(int argc, char **argv) { bool have_input_oat = false; std::string input_image_location; std::string output_oat_filename; - std::string output_oat_location; int output_oat_fd = -1; bool have_output_oat = false; std::string output_image_filename; - std::string output_image_location; int output_image_fd = -1; bool have_output_image = false; uintptr_t base_offset = 0; @@ -706,6 +810,7 @@ static int patchoat(int argc, char **argv) { std::string patched_image_filename; std::string patched_image_location; bool dump_timings = kIsDebugBuild; + bool lock_output = true; for (int i = 0; i < argc; i++) { const StringPiece option(argv[i]); @@ -756,24 +861,15 @@ static int patchoat(int argc, char **argv) { } } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); - } else if (option.starts_with("--output-oat-location=")) { - if (have_output_oat) { - Usage("Only one of --output-oat-file, --output-oat-location and --output-oat-fd may " - "be used."); - } - have_output_oat = true; - output_oat_location = option.substr(strlen("--output-oat-location=")).data(); } else if (option.starts_with("--output-oat-file=")) { if (have_output_oat) { - Usage("Only one of --output-oat-file, --output-oat-location and --output-oat-fd may " - "be used."); + Usage("Only one of --output-oat-file, and --output-oat-fd may be used."); } have_output_oat = true; output_oat_filename = option.substr(strlen("--output-oat-file=")).data(); } else if (option.starts_with("--output-oat-fd=")) { if (have_output_oat) { - Usage("Only one of --output-oat-file, --output-oat-location and --output-oat-fd may " - "be used."); + Usage("Only one of --output-oat-file, --output-oat-fd may be used."); } have_output_oat = true; const char* oat_fd_str = option.substr(strlen("--output-oat-fd=")).data(); @@ -783,24 +879,15 @@ static int patchoat(int argc, char **argv) { if (output_oat_fd < 0) { Usage("--output-oat-fd pass a negative value %d", output_oat_fd); } - } else if (option.starts_with("--output-image-location=")) { - if (have_output_image) { - Usage("Only one of --output-image-file, --output-image-location and --output-image-fd may " - "be used."); - } - have_output_image = true; - output_image_location= option.substr(strlen("--output-image-location=")).data(); } else if (option.starts_with("--output-image-file=")) { if (have_output_image) { - Usage("Only one of --output-image-file, --output-image-location and --output-image-fd may " - "be used."); + Usage("Only one of --output-image-file, and --output-image-fd may be used."); } have_output_image = true; output_image_filename = option.substr(strlen("--output-image-file=")).data(); } else if (option.starts_with("--output-image-fd=")) { if (have_output_image) { - Usage("Only one of --output-image-file, --output-image-location and --output-image-fd " - "may be used."); + Usage("Only one of --output-image-file, and --output-image-fd may be used."); } have_output_image = true; const char* image_fd_str = option.substr(strlen("--output-image-fd=")).data(); @@ -833,6 +920,10 @@ static int patchoat(int argc, char **argv) { patched_image_location = option.substr(strlen("--patched-image-location=")).data(); } else if (option.starts_with("--patched-image-file=")) { patched_image_filename = option.substr(strlen("--patched-image-file=")).data(); + } else if (option == "--lock-output") { + lock_output = true; + } else if (option == "--no-lock-output") { + lock_output = false; } else if (option == "--dump-timings") { dump_timings = true; } else if (option == "--no-dump-timings") { @@ -882,34 +973,36 @@ static int patchoat(int argc, char **argv) { if (!isa_set) { Usage("specifying a location requires specifying an instruction set"); } - input_oat_filename = GetSystemImageFilename(input_oat_location.c_str(), isa); - if (debug) { - LOG(INFO) << "Using input-oat-file " << input_oat_filename; + 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 (!output_oat_location.empty()) { - if (!isa_set) { - Usage("specifying a location requires specifying an instruction set"); - } - output_oat_filename = GetSystemImageFilename(output_oat_location.c_str(), isa); if (debug) { - LOG(INFO) << "Using output-oat-file " << output_oat_filename; + LOG(INFO) << "Using input-oat-file " << input_oat_filename; } } - if (!output_image_location.empty()) { + if (!patched_image_location.empty()) { if (!isa_set) { Usage("specifying a location requires specifying an instruction set"); } - output_image_filename = GetSystemImageFilename(output_image_location.c_str(), isa); - if (debug) { - LOG(INFO) << "Using output-image-file " << output_image_filename; + std::string system_filename; + bool has_system = false; + std::string cache_filename; + bool has_cache = false; + bool has_android_data_unused = false; + if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa, + &system_filename, &has_system, &cache_filename, + &has_android_data_unused, &has_cache)) { + Usage("Unable to determine image file for location %s", patched_image_location.c_str()); } - } - if (!patched_image_location.empty()) { - if (!isa_set) { - Usage("specifying a location requires specifying an instruction set"); + 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()); } - patched_image_filename = GetSystemImageFilename(patched_image_location.c_str(), isa); if (debug) { LOG(INFO) << "Using patched-image-file " << patched_image_filename; } @@ -950,6 +1043,9 @@ static int patchoat(int argc, char **argv) { CHECK(!input_image_location.empty()); if (output_image_fd != -1) { + if (output_image_filename.empty()) { + output_image_filename = "output-image-file"; + } output_image.reset(new File(output_image_fd, output_image_filename)); } else { CHECK(!output_image_filename.empty()); @@ -961,13 +1057,22 @@ static int patchoat(int argc, char **argv) { if (have_oat_files) { 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)); } 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 (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)); } else { CHECK(!output_oat_filename.empty()); @@ -993,8 +1098,26 @@ static int patchoat(int argc, char **argv) { } }; + if ((have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) || + (have_image_files && output_image.get() == nullptr)) { + cleanup(false); + return EXIT_FAILURE; + } + + ScopedFlock output_oat_lock; + if (lock_output) { + std::string error_msg; + if (have_oat_files && !output_oat_lock.Init(output_oat.get(), &error_msg)) { + LOG(ERROR) << "Unable to lock output oat " << output_image->GetPath() << ": " << error_msg; + cleanup(false); + return EXIT_FAILURE; + } + } + if (debug) { - LOG(INFO) << "moving offset by " << base_delta << " (0x" << std::hex << base_delta << ") bytes"; + LOG(INFO) << "moving offset by " << base_delta + << " (0x" << std::hex << base_delta << ") bytes or " + << std::dec << (base_delta/kPageSize) << " pages."; } bool ret; |