diff options
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | build/Android.common_path.mk | 1 | ||||
-rw-r--r-- | build/Android.gtest.mk | 16 | ||||
-rw-r--r-- | patchoat/Android.bp | 64 | ||||
-rw-r--r-- | patchoat/patchoat.cc | 1324 | ||||
-rw-r--r-- | patchoat/patchoat.h | 201 | ||||
-rw-r--r-- | patchoat/patchoat_test.cc | 617 | ||||
-rw-r--r-- | runtime/dexopt_test.cc | 30 | ||||
-rw-r--r-- | runtime/dexopt_test.h | 5 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 2 | ||||
-rw-r--r-- | runtime/runtime.cc | 10 | ||||
-rw-r--r-- | runtime/runtime.h | 4 | ||||
-rw-r--r-- | test/117-nopatchoat/expected.txt | 3 | ||||
-rw-r--r-- | test/117-nopatchoat/info.txt | 1 | ||||
-rw-r--r-- | test/117-nopatchoat/nopatchoat.cc | 59 | ||||
-rwxr-xr-x | test/117-nopatchoat/run | 37 | ||||
-rw-r--r-- | test/117-nopatchoat/src/Main.java | 44 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rwxr-xr-x | test/etc/run-test-jar | 13 | ||||
-rw-r--r-- | test/knownfailures.json | 23 | ||||
-rwxr-xr-x | test/run-test | 14 | ||||
-rwxr-xr-x | test/testrunner/testrunner.py | 4 |
22 files changed, 16 insertions, 2461 deletions
diff --git a/Android.mk b/Android.mk index 0c20973edc..e27c3db996 100644 --- a/Android.mk +++ b/Android.mk @@ -341,7 +341,6 @@ LOCAL_REQUIRED_MODULES := \ libart-compiler \ libopenjdkjvm \ libopenjdkjvmti \ - patchoat \ profman \ libadbconnection \ @@ -367,7 +366,6 @@ LOCAL_REQUIRED_MODULES += \ libopenjdkd \ libopenjdkjvmd \ libopenjdkjvmtid \ - patchoatd \ profmand \ libadbconnectiond \ @@ -451,7 +449,7 @@ build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TAR # Also include libartbenchmark, we always include it when running golem. # libstdc++ is needed when building for ART_TARGET_LINUX. ART_TARGET_SHARED_LIBRARY_BENCHMARK := $(TARGET_OUT_SHARED_LIBRARIES)/libartbenchmark.so -build-art-target-golem: dex2oat dalvikvm patchoat linker libstdc++ \ +build-art-target-golem: dex2oat dalvikvm linker libstdc++ \ $(TARGET_OUT_EXECUTABLES)/art \ $(TARGET_OUT)/etc/public.libraries.txt \ $(ART_TARGET_DEX_DEPENDENCIES) \ diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk index 3247e54ab2..96d3648ff6 100644 --- a/build/Android.common_path.mk +++ b/build/Android.common_path.mk @@ -108,7 +108,6 @@ ART_CORE_DEBUGGABLE_EXECUTABLES := \ dexoptanalyzer \ imgdiag \ oatdump \ - patchoat \ profman \ ART_CORE_EXECUTABLES := \ diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 20f20c9f7b..0523f1827f 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -191,7 +191,6 @@ ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncom ART_GTEST_oat_test_DEX_DEPS := Main ART_GTEST_oat_writer_test_DEX_DEPS := Main ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY -ART_GTEST_patchoat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_proxy_test_DEX_DEPS := Interfaces ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex @@ -214,14 +213,12 @@ ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(HOST_CORE_IMAGE_optimizing_64) \ $(HOST_CORE_IMAGE_optimizing_32) \ $(HOST_CORE_IMAGE_interpreter_64) \ - $(HOST_CORE_IMAGE_interpreter_32) \ - patchoatd-host + $(HOST_CORE_IMAGE_interpreter_32) ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_optimizing_64) \ $(TARGET_CORE_IMAGE_optimizing_32) \ $(TARGET_CORE_IMAGE_interpreter_64) \ - $(TARGET_CORE_IMAGE_interpreter_32) \ - patchoatd-target + $(TARGET_CORE_IMAGE_interpreter_32) ART_GTEST_oat_file_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) @@ -348,11 +345,6 @@ ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \ ART_GTEST_oatdump_app_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) \ dex2oatd-target -ART_GTEST_patchoat_test_HOST_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) -ART_GTEST_patchoat_test_TARGET_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) - # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -382,7 +374,6 @@ ART_TEST_MODULES := \ art_libdexfile_tests \ art_libprofile_tests \ art_oatdump_tests \ - art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -733,9 +724,6 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := ART_GTEST_dex2oat_image_test_HOST_DEPS := ART_GTEST_dex2oat_image_test_TARGET_DEPS := ART_GTEST_object_test_DEX_DEPS := -ART_GTEST_patchoat_test_DEX_DEPS := -ART_GTEST_patchoat_test_HOST_DEPS := -ART_GTEST_patchoat_test_TARGET_DEPS := ART_GTEST_proxy_test_DEX_DEPS := ART_GTEST_reflection_test_DEX_DEPS := ART_GTEST_stub_test_DEX_DEPS := diff --git a/patchoat/Android.bp b/patchoat/Android.bp deleted file mode 100644 index 13c8f475f5..0000000000 --- a/patchoat/Android.bp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (C) 2014 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -cc_defaults { - name: "patchoat-defaults", - host_supported: true, - defaults: ["art_defaults"], - srcs: ["patchoat.cc"], - target: { - android: { - compile_multilib: "prefer32", - }, - }, - shared_libs: [ - "libartbase", - "libbase", - "libcrypto", // For computing the digest of image file - ], -} - -art_cc_binary { - name: "patchoat", - defaults: ["patchoat-defaults"], - shared_libs: [ - "libart", - ], -} - -art_cc_binary { - name: "patchoatd", - defaults: [ - "art_debug_defaults", - "patchoat-defaults", - ], - shared_libs: [ - "libartd", - ], -} - -art_cc_test { - name: "art_patchoat_tests", - defaults: [ - "art_gtest_defaults", - ], - srcs: [ - "patchoat_test.cc", - ], - shared_libs: [ - "libcrypto", // For computing the digest of image file - ], -} diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc deleted file mode 100644 index 5d38e8bfed..0000000000 --- a/patchoat/patchoat.cc +++ /dev/null @@ -1,1324 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "patchoat.h" - -#include <openssl/sha.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/file.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "android-base/file.h" -#include <android-base/parseint.h> -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "art_field-inl.h" -#include "art_method-inl.h" -#include "base/bit_memory_region.h" -#include "base/dumpable.h" -#include "base/file_utils.h" -#include "base/leb128.h" -#include "base/logging.h" // For InitLogging. -#include "base/mutex.h" -#include "base/memory_region.h" -#include "base/memory_tool.h" -#include "base/os.h" -#include "base/scoped_flock.h" -#include "base/stringpiece.h" -#include "base/unix_file/fd_file.h" -#include "base/unix_file/random_access_file_utils.h" -#include "base/utils.h" -#include "class_root.h" -#include "elf_file.h" -#include "elf_file_impl.h" -#include "elf_utils.h" -#include "gc/space/image_space.h" -#include "image-inl.h" -#include "intern_table.h" -#include "mirror/dex_cache.h" -#include "mirror/executable.h" -#include "mirror/method.h" -#include "mirror/object-inl.h" -#include "mirror/object-refvisitor-inl.h" -#include "mirror/reference.h" -#include "noop_compiler_callbacks.h" -#include "offsets.h" -#include "runtime.h" -#include "scoped_thread_state_change-inl.h" -#include "thread.h" - -namespace art { - -using android::base::StringPrintf; - -namespace { - -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; -} - -static File* CreateOrOpen(const char* name) { - if (OS::FileExists(name)) { - return OS::OpenFileReadWrite(name); - } else { - 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(); - } -} - -// Either try to close the file (close=true), or erase it. -static bool FinishFile(File* file, bool close) { - if (close) { - if (file->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close file."; - return false; - } - return true; - } else { - file->Erase(); - return false; - } -} - -static bool SymlinkFile(const std::string& input_filename, const std::string& output_filename) { - if (input_filename == output_filename) { - // Input and output are the same, nothing to do. - return true; - } - - // Unlink the original filename, since we are overwriting it. - unlink(output_filename.c_str()); - - // Create a symlink from the source file to the target path. - if (symlink(input_filename.c_str(), output_filename.c_str()) < 0) { - PLOG(ERROR) << "Failed to create symlink " << output_filename << " -> " << input_filename; - return false; - } - - if (kIsDebugBuild) { - LOG(INFO) << "Created symlink " << output_filename << " -> " << input_filename; - } - - return true; -} - -// Holder class for runtime options and related objects. -class PatchoatRuntimeOptionsHolder { - public: - PatchoatRuntimeOptionsHolder(const std::string& image_location, InstructionSet isa) { - options_.push_back(std::make_pair("compilercallbacks", &callbacks_)); - img_ = "-Ximage:" + image_location; - options_.push_back(std::make_pair(img_.c_str(), nullptr)); - isa_name_ = GetInstructionSetString(isa); - options_.push_back(std::make_pair("imageinstructionset", - reinterpret_cast<const void*>(isa_name_.c_str()))); - options_.push_back(std::make_pair("-Xno-sig-chain", nullptr)); - // We do not want the runtime to attempt to patch the image. - options_.push_back(std::make_pair("-Xnorelocate", nullptr)); - // Don't try to compile. - options_.push_back(std::make_pair("-Xnoimage-dex2oat", nullptr)); - // Do not accept broken image. - options_.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr)); - } - - const RuntimeOptions& GetRuntimeOptions() { - return options_; - } - - private: - RuntimeOptions options_; - NoopCompilerCallbacks callbacks_; - std::string isa_name_; - std::string img_; -}; - -} // namespace - -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 > 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; - } - - const ImageHeader* image_header = reinterpret_cast<const ImageHeader*>(original.Begin()); - if (image_header->GetStorageMode() != ImageHeader::kStorageModeUncompressed) { - *error_msg = "Unexpected compressed image."; - return false; - } - if (image_header->IsAppImage()) { - *error_msg = "Unexpected app image."; - return false; - } - if (image_header->GetPointerSize() != PointerSize::k32 && - image_header->GetPointerSize() != PointerSize::k64) { - *error_msg = "Unexpected pointer size."; - return false; - } - static_assert(sizeof(GcRoot<mirror::Object>) == sizeof(mirror::HeapReference<mirror::Object>), - "Expecting heap GC roots and references to have the same size."); - DCHECK_LE(sizeof(GcRoot<mirror::Object>), static_cast<size_t>(image_header->GetPointerSize())); - - const size_t image_bitmap_offset = RoundUp(sizeof(ImageHeader) + image_header->GetDataSize(), - kPageSize); - const size_t end_of_bitmap = image_bitmap_offset + image_header->GetImageBitmapSection().Size(); - const ImageSection& relocation_section = image_header->GetImageRelocationsSection(); - MemoryRegion relocations_data(original.Begin() + end_of_bitmap, relocation_section.Size()); - size_t image_end = image_header->GetClassTableSection().End(); - if (!IsAligned<sizeof(GcRoot<mirror::Object>)>(image_end)) { - *error_msg = StringPrintf("Unaligned image end: %zu", image_end); - return false; - } - size_t num_indexes = image_end / sizeof(GcRoot<mirror::Object>); - if (relocation_section.Size() != BitsToBytesRoundUp(num_indexes)) { - *error_msg = StringPrintf("Unexpected size of relocation section: %zu expected: %zu", - static_cast<size_t>(relocation_section.Size()), - BitsToBytesRoundUp(num_indexes)); - return false; - } - BitMemoryRegion relocation_bitmap(relocations_data, /* bit_offset */ 0u, num_indexes); - - // 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()); - - // Check the list of offsets at which the original and patched images differ. - size_t diff_offset_count = 0; - const uint8_t* relocated_bytes = relocated.Begin(); - for (size_t index = 0; index != num_indexes; ++index) { - size_t offset = index * sizeof(GcRoot<mirror::Object>); - 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) { - CHECK(!relocation_bitmap.LoadBit(index)); - 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; - } - CHECK(relocation_bitmap.LoadBit(index)); - diff_offset_count++; - } - size_t tail_bytes = original_size - image_end; - CHECK_EQ(memcmp(original_bytes + image_end, relocated_bytes + image_end, tail_bytes), 0); - - 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; - } - if (rel_size != SHA256_DIGEST_LENGTH) { - *error_msg = StringPrintf("Unexpected size of image relocation file %s: %" PRId64 - ", expected %zu", - rel_filename.c_str(), - rel_size, - static_cast<size_t>(SHA256_DIGEST_LENGTH)); - 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 (static_cast<uint64_t>(image_size) < sizeof(ImageHeader)) { - *error_msg = - StringPrintf( - "Relocated image file %s too small: %" 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 ImageHeader& image_header = *reinterpret_cast<const ImageHeader*>(image.get()); - if (image_header.GetStorageMode() != ImageHeader::kStorageModeUncompressed) { - *error_msg = StringPrintf("Unsuported compressed image file %s", - relocated_filename.c_str()); - return false; - } - size_t image_end = image_header.GetClassTableSection().End(); - if (image_end > static_cast<uint64_t>(image_size) || !IsAligned<4u>(image_end)) { - *error_msg = StringPrintf("Heap size too big or unaligned in image file %s: %zu", - relocated_filename.c_str(), - image_end); - return false; - } - size_t number_of_relocation_locations = image_end / 4u; - const ImageSection& relocation_section = image_header.GetImageRelocationsSection(); - if (relocation_section.Size() != BitsToBytesRoundUp(number_of_relocation_locations)) { - *error_msg = StringPrintf("Unexpected size of relocation section in image file %s: %zu" - " expected: %zu", - relocated_filename.c_str(), - static_cast<size_t>(relocation_section.Size()), - BitsToBytesRoundUp(number_of_relocation_locations)); - return false; - } - if (relocation_section.End() != image_size) { - *error_msg = StringPrintf("Relocation section does not end at file end in image file %s: %zu" - " expected: %" PRId64, - relocated_filename.c_str(), - static_cast<size_t>(relocation_section.End()), - image_size); - return false; - } - - 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. - MemoryRegion relocations(image.get() + relocation_section.Offset(), relocation_section.Size()); - BitMemoryRegion relocation_bitmask(relocations, - /* bit_offset */ 0u, - number_of_relocation_locations); - for (size_t index = 0; index != number_of_relocation_locations; ++index) { - if (relocation_bitmask.LoadBit(index)) { - uint32_t* image_value = reinterpret_cast<uint32_t*>(image.get() + index * 4u); - *image_value -= expected_diff; - } - } - - // 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, rel.get(), 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; -} - -static bool VerifySymlink(const std::string& intended_target, const std::string& link_name) { - std::string actual_target; - if (!android::base::Readlink(link_name, &actual_target)) { - PLOG(ERROR) << "Readlink on " << link_name << " failed."; - return false; - } - return actual_target == intended_target; -} - -static bool VerifyVdexAndOatSymlinks(const std::string& input_image_filename, - const std::string& output_image_filename) { - return VerifySymlink(ImageHeader::GetVdexLocationFromImageLocation(input_image_filename), - ImageHeader::GetVdexLocationFromImageLocation(output_image_filename)) - && VerifySymlink(ImageHeader::GetOatLocationFromImageLocation(input_image_filename), - ImageHeader::GetOatLocationFromImageLocation(output_image_filename)); -} - -bool PatchOat::CreateVdexAndOatSymlinks(const std::string& input_image_filename, - const std::string& output_image_filename) { - std::string input_vdex_filename = - ImageHeader::GetVdexLocationFromImageLocation(input_image_filename); - std::string input_oat_filename = - ImageHeader::GetOatLocationFromImageLocation(input_image_filename); - - std::unique_ptr<File> input_oat_file(OS::OpenFileForReading(input_oat_filename.c_str())); - if (input_oat_file.get() == nullptr) { - LOG(ERROR) << "Unable to open input oat file at " << input_oat_filename; - return false; - } - std::string error_msg; - std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat_file.get(), - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - &error_msg)); - if (elf == nullptr) { - LOG(ERROR) << "Unable to open oat file " << input_oat_filename << " : " << error_msg; - return false; - } - - const OatHeader* oat_header = GetOatHeader(elf.get()); - if (oat_header == nullptr) { - LOG(ERROR) << "Failed to find oat header in oat file " << input_oat_filename; - return false; - } - - if (!oat_header->IsValid()) { - LOG(ERROR) << "Elf file " << input_oat_filename << " has an invalid oat header"; - return false; - } - - std::string output_vdex_filename = - ImageHeader::GetVdexLocationFromImageLocation(output_image_filename); - std::string output_oat_filename = - ImageHeader::GetOatLocationFromImageLocation(output_image_filename); - - return SymlinkFile(input_oat_filename, output_oat_filename) && - SymlinkFile(input_vdex_filename, output_vdex_filename); -} - -bool PatchOat::Patch(const std::string& image_location, - off_t delta, - 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."; - - TimingLogger::ScopedTiming t("Runtime Setup", timings); - - CHECK_NE(isa, InstructionSet::kNone); - - // Set up the runtime - PatchoatRuntimeOptionsHolder options_holder(image_location, isa); - if (!Runtime::Create(options_holder.GetRuntimeOptions(), 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()); - - std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); - std::map<gc::space::ImageSpace*, MemMap> space_to_memmap_map; - - for (size_t i = 0; i < spaces.size(); ++i) { - t.NewTiming("Image Patching setup"); - gc::space::ImageSpace* space = spaces[i]; - std::string input_image_filename = space->GetImageFilename(); - std::unique_ptr<File> input_image(OS::OpenFileForReading(input_image_filename.c_str())); - if (input_image.get() == nullptr) { - LOG(ERROR) << "Unable to open input image file at " << input_image_filename; - return false; - } - - int64_t image_len = input_image->GetLength(); - if (image_len < 0) { - LOG(ERROR) << "Error while getting image length"; - return false; - } - ImageHeader image_header; - if (sizeof(image_header) != input_image->Read(reinterpret_cast<char*>(&image_header), - sizeof(image_header), 0)) { - LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath(); - } - - /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath()); - // Nothing special to do right now since the image always needs to get patched. - // Perhaps in some far-off future we may have images with relative addresses that are true-PIC. - - // Create the map where we will write the image patches to. - std::string error_msg; - MemMap image = MemMap::MapFile(image_len, - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - input_image->Fd(), - 0, - /*low_4gb*/false, - input_image->GetPath().c_str(), - &error_msg); - if (!image.IsValid()) { - LOG(ERROR) << "Unable to map image file " << input_image->GetPath() << " : " << error_msg; - return false; - } - - - space_to_memmap_map.emplace(space, std::move(image)); - PatchOat p = PatchOat(isa, - &space_to_memmap_map[space], - space->GetLiveBitmap(), - space->GetMemMap(), - delta, - &space_to_memmap_map, - timings); - - t.NewTiming("Patching image"); - if (!p.PatchImage(i == 0)) { - LOG(ERROR) << "Failed to patch image file " << input_image_filename; - return false; - } - - // Write the patched image spaces. - if (output_image) { - std::string output_image_filename; - if (!GetDalvikCacheFilename(space->GetImageLocation().c_str(), - output_image_directory.c_str(), - &output_image_filename, - &error_msg)) { - LOG(ERROR) << "Failed to find relocated image file name: " << error_msg; - return false; - } - - if (!CreateVdexAndOatSymlinks(input_image_filename, output_image_filename)) - return false; - - t.NewTiming("Writing image"); - 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; - } - - bool success = p.WriteImage(output_image_file.get()); - success = FinishFile(output_image_file.get(), success); - if (!success) { - return false; - } - } - - if (output_image_relocation) { - 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('/')); - int64_t input_image_size = input_image->GetLength(); - if (input_image_size < 0) { - LOG(ERROR) << "Error while getting input image size"; - return false; - } - 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.IsValid()) { - LOG(ERROR) << "Unable to map image file " << input_image->GetPath() << " : " << error_msg; - return false; - } - - 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; - } - } - } - - if (!kIsDebugBuild && !(kRunningOnMemoryTool && 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 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); - - // Set up the runtime - PatchoatRuntimeOptionsHolder options_holder(image_location, isa); - if (!Runtime::Create(options_holder.GetRuntimeOptions(), 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 relocated_image_filename; - std::string error_msg; - if (!GetDalvikCacheFilename(space->GetImageLocation().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(space->GetImageLocation().c_str(), isa); - - if (!CheckImageIdenticalToOriginalExceptForRelocation( - relocated_image_filename, original_image_filename, &error_msg)) { - LOG(ERROR) << error_msg; - success = false; - break; - } - - if (!VerifyVdexAndOatSymlinks(original_image_filename, relocated_image_filename)) { - LOG(ERROR) << "Verification of vdex and oat symlinks for " - << space->GetImageLocation() << " failed."; - success = false; - break; - } - } - - if (!kIsDebugBuild && !(kRunningOnMemoryTool && 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) { - CHECK(out != nullptr); - TimingLogger::ScopedTiming t("Writing image File", timings_); - std::string error_msg; - - // No error checking here, this is best effort. The locking may or may not - // succeed and we don't really care either way. - ScopedFlock img_flock = LockedFile::DupOf(out->Fd(), out->GetPath(), - true /* read_only_mode */, &error_msg); - - CHECK(image_ != nullptr); - size_t expect = image_->Size(); - if (out->WriteFully(reinterpret_cast<char*>(image_->Begin()), expect) && - out->SetLength(expect) == 0) { - return true; - } else { - LOG(ERROR) << "Writing to image file " << out->GetPath() << " failed."; - return false; - } -} - -bool PatchOat::IsImagePic(const ImageHeader& image_header, const std::string& image_path) { - if (!image_header.CompilePic()) { - if (kIsDebugBuild) { - LOG(INFO) << "image at location " << image_path << " was *not* compiled pic"; - } - return false; - } - - if (kIsDebugBuild) { - LOG(INFO) << "image at location " << image_path << " was compiled PIC"; - } - - return true; -} - -class PatchOat::PatchOatArtFieldVisitor : public ArtFieldVisitor { - public: - explicit PatchOatArtFieldVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {} - - void Visit(ArtField* field) override REQUIRES_SHARED(Locks::mutator_lock_) { - ArtField* const dest = patch_oat_->RelocatedCopyOf(field); - dest->SetDeclaringClass( - patch_oat_->RelocatedAddressOfPointer(field->GetDeclaringClass().Ptr())); - } - - private: - PatchOat* const patch_oat_; -}; - -void PatchOat::PatchArtFields(const ImageHeader* image_header) { - PatchOatArtFieldVisitor visitor(this); - image_header->VisitPackedArtFields(&visitor, heap_->Begin()); -} - -class PatchOat::PatchOatArtMethodVisitor : public ArtMethodVisitor { - public: - explicit PatchOatArtMethodVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {} - - void Visit(ArtMethod* method) override REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* const dest = patch_oat_->RelocatedCopyOf(method); - patch_oat_->FixupMethod(method, dest); - } - - private: - PatchOat* const patch_oat_; -}; - -void PatchOat::PatchArtMethods(const ImageHeader* image_header) { - const PointerSize pointer_size = InstructionSetPointerSize(isa_); - PatchOatArtMethodVisitor visitor(this); - image_header->VisitPackedArtMethods(&visitor, heap_->Begin(), pointer_size); -} - -void PatchOat::PatchImTables(const ImageHeader* image_header) { - const PointerSize pointer_size = InstructionSetPointerSize(isa_); - // We can safely walk target image since the conflict tables are independent. - image_header->VisitPackedImTables( - [this](ArtMethod* method) { - return RelocatedAddressOfPointer(method); - }, - image_->Begin(), - pointer_size); -} - -void PatchOat::PatchImtConflictTables(const ImageHeader* image_header) { - const PointerSize pointer_size = InstructionSetPointerSize(isa_); - // We can safely walk target image since the conflict tables are independent. - image_header->VisitPackedImtConflictTables( - [this](ArtMethod* method) { - return RelocatedAddressOfPointer(method); - }, - image_->Begin(), - pointer_size); -} - -class PatchOat::FixupRootVisitor : public RootVisitor { - public: - explicit FixupRootVisitor(const PatchOat* patch_oat) : patch_oat_(patch_oat) { - } - - void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) - override REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < count; ++i) { - *roots[i] = patch_oat_->RelocatedAddressOfPointer(*roots[i]); - } - } - - void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) - override REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < count; ++i) { - roots[i]->Assign(patch_oat_->RelocatedAddressOfPointer(roots[i]->AsMirrorPtr())); - } - } - - private: - const PatchOat* const patch_oat_; -}; - -void PatchOat::PatchInternedStrings(const ImageHeader* image_header) { - const auto& section = image_header->GetInternedStringsSection(); - if (section.Size() == 0) { - return; - } - InternTable temp_table; - // Note that we require that ReadFromMemory does not make an internal copy of the elements. - // This also relies on visit roots not doing any verification which could fail after we update - // the roots to be the image addresses. - temp_table.AddTableFromMemory(image_->Begin() + section.Offset()); - FixupRootVisitor visitor(this); - temp_table.VisitRoots(&visitor, kVisitRootFlagAllRoots); -} - -void PatchOat::PatchClassTable(const ImageHeader* image_header) { - const auto& section = image_header->GetClassTableSection(); - if (section.Size() == 0) { - return; - } - // Note that we require that ReadFromMemory does not make an internal copy of the elements. - // This also relies on visit roots not doing any verification which could fail after we update - // the roots to be the image addresses. - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - ClassTable temp_table; - temp_table.ReadFromMemory(image_->Begin() + section.Offset()); - FixupRootVisitor visitor(this); - temp_table.VisitRoots(UnbufferedRootVisitor(&visitor, RootInfo(kRootUnknown))); -} - - -class PatchOat::RelocatedPointerVisitor { - public: - explicit RelocatedPointerVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {} - - template <typename T> - T* operator()(T* ptr, void** dest_addr ATTRIBUTE_UNUSED = nullptr) const { - return patch_oat_->RelocatedAddressOfPointer(ptr); - } - - private: - PatchOat* const patch_oat_; -}; - -void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots) { - auto* dex_caches = down_cast<mirror::ObjectArray<mirror::DexCache>*>( - img_roots->Get(ImageHeader::kDexCaches)); - const PointerSize pointer_size = InstructionSetPointerSize(isa_); - for (size_t i = 0, count = dex_caches->GetLength(); i < count; ++i) { - auto* orig_dex_cache = dex_caches->GetWithoutChecks(i); - auto* copy_dex_cache = RelocatedCopyOf(orig_dex_cache); - // Though the DexCache array fields are usually treated as native pointers, we set the full - // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is - // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e. - // static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))). - mirror::StringDexCacheType* orig_strings = orig_dex_cache->GetStrings(); - mirror::StringDexCacheType* relocated_strings = RelocatedAddressOfPointer(orig_strings); - copy_dex_cache->SetField64<false>( - mirror::DexCache::StringsOffset(), - static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_strings))); - if (orig_strings != nullptr) { - orig_dex_cache->FixupStrings(RelocatedCopyOf(orig_strings), RelocatedPointerVisitor(this)); - } - mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes(); - mirror::TypeDexCacheType* relocated_types = RelocatedAddressOfPointer(orig_types); - copy_dex_cache->SetField64<false>( - mirror::DexCache::ResolvedTypesOffset(), - static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types))); - if (orig_types != nullptr) { - orig_dex_cache->FixupResolvedTypes(RelocatedCopyOf(orig_types), - RelocatedPointerVisitor(this)); - } - mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods(); - mirror::MethodDexCacheType* relocated_methods = RelocatedAddressOfPointer(orig_methods); - copy_dex_cache->SetField64<false>( - mirror::DexCache::ResolvedMethodsOffset(), - static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_methods))); - if (orig_methods != nullptr) { - mirror::MethodDexCacheType* copy_methods = RelocatedCopyOf(orig_methods); - for (size_t j = 0, num = orig_dex_cache->NumResolvedMethods(); j != num; ++j) { - mirror::MethodDexCachePair orig = - mirror::DexCache::GetNativePairPtrSize(orig_methods, j, pointer_size); - mirror::MethodDexCachePair copy(RelocatedAddressOfPointer(orig.object), orig.index); - mirror::DexCache::SetNativePairPtrSize(copy_methods, j, copy, pointer_size); - } - } - mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields(); - mirror::FieldDexCacheType* relocated_fields = RelocatedAddressOfPointer(orig_fields); - copy_dex_cache->SetField64<false>( - mirror::DexCache::ResolvedFieldsOffset(), - static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_fields))); - if (orig_fields != nullptr) { - mirror::FieldDexCacheType* copy_fields = RelocatedCopyOf(orig_fields); - for (size_t j = 0, num = orig_dex_cache->NumResolvedFields(); j != num; ++j) { - mirror::FieldDexCachePair orig = - mirror::DexCache::GetNativePairPtrSize(orig_fields, j, pointer_size); - mirror::FieldDexCachePair copy(RelocatedAddressOfPointer(orig.object), orig.index); - mirror::DexCache::SetNativePairPtrSize(copy_fields, j, copy, pointer_size); - } - } - mirror::MethodTypeDexCacheType* orig_method_types = orig_dex_cache->GetResolvedMethodTypes(); - mirror::MethodTypeDexCacheType* relocated_method_types = - RelocatedAddressOfPointer(orig_method_types); - copy_dex_cache->SetField64<false>( - mirror::DexCache::ResolvedMethodTypesOffset(), - static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_method_types))); - if (orig_method_types != nullptr) { - orig_dex_cache->FixupResolvedMethodTypes(RelocatedCopyOf(orig_method_types), - RelocatedPointerVisitor(this)); - } - - GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites(); - GcRoot<mirror::CallSite>* relocated_call_sites = RelocatedAddressOfPointer(orig_call_sites); - copy_dex_cache->SetField64<false>( - mirror::DexCache::ResolvedCallSitesOffset(), - static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_call_sites))); - if (orig_call_sites != nullptr) { - orig_dex_cache->FixupResolvedCallSites(RelocatedCopyOf(orig_call_sites), - RelocatedPointerVisitor(this)); - } - } -} - -bool PatchOat::PatchImage(bool primary_image) { - ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); - CHECK_GT(image_->Size(), sizeof(ImageHeader)); - // These are the roots from the original file. - mirror::ObjectArray<mirror::Object>* img_roots = image_header->GetImageRoots().Ptr(); - image_header->RelocateImage(delta_); - - PatchArtFields(image_header); - PatchArtMethods(image_header); - PatchImTables(image_header); - PatchImtConflictTables(image_header); - PatchInternedStrings(image_header); - PatchClassTable(image_header); - // Patch dex file int/long arrays which point to ArtFields. - PatchDexFileArrays(img_roots); - - if (primary_image) { - VisitObject(img_roots); - } - - if (!image_header->IsValid()) { - LOG(ERROR) << "relocation renders image header invalid"; - return false; - } - - { - TimingLogger::ScopedTiming t("Walk Bitmap", timings_); - // Walk the bitmap. - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - VisitObject(obj); - }; - bitmap_->Walk(visitor); - } - return true; -} - - -void PatchOat::PatchVisitor::operator() (ObjPtr<mirror::Object> obj, - MemberOffset off, - bool is_static_unused ATTRIBUTE_UNUSED) const { - mirror::Object* referent = obj->GetFieldObject<mirror::Object, kVerifyNone>(off); - mirror::Object* moved_object = patcher_->RelocatedAddressOfPointer(referent); - copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object); -} - -void PatchOat::PatchVisitor::operator() (ObjPtr<mirror::Class> cls ATTRIBUTE_UNUSED, - ObjPtr<mirror::Reference> ref) const { - MemberOffset off = mirror::Reference::ReferentOffset(); - mirror::Object* referent = ref->GetReferent(); - DCHECK(referent == nullptr || - Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(referent)) << referent; - mirror::Object* moved_object = patcher_->RelocatedAddressOfPointer(referent); - copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object); -} - -// Called by PatchImage. -void PatchOat::VisitObject(mirror::Object* object) { - mirror::Object* copy = RelocatedCopyOf(object); - CHECK(copy != nullptr); - if (kUseBakerReadBarrier) { - object->AssertReadBarrierState(); - } - PatchOat::PatchVisitor visitor(this, copy); - object->VisitReferences<kVerifyNone>(visitor, visitor); - if (object->IsClass<kVerifyNone>()) { - const PointerSize pointer_size = InstructionSetPointerSize(isa_); - mirror::Class* klass = object->AsClass(); - mirror::Class* copy_klass = down_cast<mirror::Class*>(copy); - RelocatedPointerVisitor native_visitor(this); - klass->FixupNativePointers(copy_klass, pointer_size, native_visitor); - auto* vtable = klass->GetVTable(); - if (vtable != nullptr) { - vtable->Fixup(RelocatedCopyOfFollowImages(vtable), pointer_size, native_visitor); - } - mirror::IfTable* iftable = klass->GetIfTable(); - for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - if (iftable->GetMethodArrayCount(i) > 0) { - auto* method_array = iftable->GetMethodArray(i); - CHECK(method_array != nullptr); - method_array->Fixup(RelocatedCopyOfFollowImages(method_array), - pointer_size, - native_visitor); - } - } - } else if (object->GetClass() == GetClassRoot<mirror::Method>() || - object->GetClass() == GetClassRoot<mirror::Constructor>()) { - // Need to go update the ArtMethod. - auto* dest = down_cast<mirror::Executable*>(copy); - auto* src = down_cast<mirror::Executable*>(object); - dest->SetArtMethod(RelocatedAddressOfPointer(src->GetArtMethod())); - } -} - -void PatchOat::FixupMethod(ArtMethod* object, ArtMethod* copy) { - const PointerSize pointer_size = InstructionSetPointerSize(isa_); - copy->CopyFrom(object, pointer_size); - // Just update the entry points if it looks like we should. - // TODO: sanity check all the pointers' values - copy->SetDeclaringClass(RelocatedAddressOfPointer(object->GetDeclaringClass().Ptr())); - copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer( - object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size); - // No special handling for IMT conflict table since all pointers are moved by the same offset. - copy->SetDataPtrSize(RelocatedAddressOfPointer( - object->GetDataPtrSize(pointer_size)), pointer_size); -} - -static int orig_argc; -static char** orig_argv; - -static std::string CommandLine() { - std::vector<std::string> command; - for (int i = 0; i < orig_argc; ++i) { - command.push_back(orig_argv[i]); - } - return android::base::Join(command, ' '); -} - -static void UsageErrorV(const char* fmt, va_list ap) { - std::string error; - android::base::StringAppendV(&error, fmt, ap); - LOG(ERROR) << error; -} - -static void UsageError(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - UsageErrorV(fmt, ap); - va_end(ap); -} - -NO_RETURN static void Usage(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - UsageErrorV(fmt, ap); - va_end(ap); - - UsageError("Command: %s", CommandLine().c_str()); - UsageError("Usage: patchoat [options]..."); - UsageError(""); - UsageError(" --instruction-set=<isa>: Specifies the instruction set the patched code is"); - UsageError(" compiled for (required)."); - UsageError(""); - UsageError(" --input-image-location=<file.art>: Specifies the 'location' of the image file to"); - UsageError(" be patched."); - UsageError(""); - UsageError(" --output-image-directory=<dir>: Specifies the directory to write the patched"); - UsageError(" image file(s) to."); - UsageError(""); - UsageError(" --output-image-relocation-directory=<dir>: Specifies the directory 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"); - UsageError(""); - - exit(EXIT_FAILURE); -} - -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_directory, - off_t base_delta, - bool base_delta_set, - bool debug) { - CHECK(!input_image_location.empty()); - if ((output_image_directory.empty()) && (output_image_relocation_directory.empty())) { - Usage("Image patching requires --output-image-directory or --output-image-relocation-directory"); - } - - if (!base_delta_set) { - Usage("Must supply a desired new offset or delta."); - } - - if (!IsAligned<kPageSize>(base_delta)) { - Usage("Base offset/delta must be aligned to a pagesize (0x%08x) boundary.", kPageSize); - } - - if (debug) { - LOG(INFO) << "moving offset by " << base_delta - << " (0x" << std::hex << base_delta << ") bytes or " - << std::dec << (base_delta/kPageSize) << " pages."; - } - - TimingLogger::ScopedTiming pt("patch image and oat", &timings); - - 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; - } - return ret ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int patchoat(int argc, char **argv) { - Locks::Init(); - InitLogging(argv, Runtime::Abort); - MemMap::Init(); - const bool debug = kIsDebugBuild; - orig_argc = argc; - orig_argv = argv; - TimingLogger timings("patcher", false, false); - - // Skip over the command name. - argv++; - argc--; - - if (argc == 0) { - Usage("No arguments specified"); - } - - timings.StartTiming("Patchoat"); - - // cmd line args - bool isa_set = false; - InstructionSet isa = InstructionSet::kNone; - std::string input_image_location; - std::string output_image_directory; - std::string output_image_relocation_directory; - 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]); - const bool log_options = false; - if (log_options) { - LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i]; - } - if (option.starts_with("--instruction-set=")) { - isa_set = true; - const char* isa_str = option.substr(strlen("--instruction-set=")).data(); - isa = GetInstructionSetFromString(isa_str); - if (isa == InstructionSet::kNone) { - Usage("Unknown or invalid instruction set %s", isa_str); - } - } else if (option.starts_with("--input-image-location=")) { - input_image_location = option.substr(strlen("--input-image-location=")).data(); - } else if (option.starts_with("--output-image-directory=")) { - output_image_directory = option.substr(strlen("--output-image-directory=")).data(); - } else if (option.starts_with("--output-image-relocation-directory=")) { - output_image_relocation_directory = - option.substr(strlen("--output-image-relocation-directory=")).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; - if (!android::base::ParseInt(base_delta_str, &base_delta)) { - Usage("Failed to parse --base-offset-delta argument '%s' as an off_t", base_delta_str); - } - } else if (option == "--dump-timings") { - 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()); - } - } - - // The instruction set is mandatory. This simplifies things... - if (!isa_set) { - Usage("Instruction set must be set."); - } - - 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_directory, - base_delta, - base_delta_set, - debug); - } - - timings.EndTiming(); - if (dump_timings) { - LOG(INFO) << Dumpable<TimingLogger>(timings); - } - - return ret; -} - -} // namespace art - -int main(int argc, char **argv) { - return art::patchoat(argc, argv); -} diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h deleted file mode 100644 index 237ef5075c..0000000000 --- a/patchoat/patchoat.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_PATCHOAT_PATCHOAT_H_ -#define ART_PATCHOAT_PATCHOAT_H_ - -#include "arch/instruction_set.h" -#include "base/enums.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "base/os.h" -#include "elf_file.h" -#include "elf_utils.h" -#include "gc/accounting/space_bitmap.h" -#include "gc/heap.h" -#include "gc/space/image_space.h" -#include "runtime.h" - -namespace art { - -class ArtMethod; -class ImageHeader; -class OatHeader; - -namespace mirror { -class Object; -class PointerArray; -class Reference; -class Class; -} // namespace mirror - -class PatchOat { - public: - // Relocates the provided image by the specified offset. If output_image_directory is non-empty, - // outputs the relocated image into that directory. If output_image_relocation_directory is - // non-empty, outputs image relocation files (see GeneratePatch) into that directory. - static bool Patch(const std::string& image_location, - off_t delta, - const std::string& output_image_directory, - const std::string& output_image_relocation_directory, - InstructionSet isa, - TimingLogger* timings); - static bool Verify(const std::string& image_location, - const std::string& output_image_filename, - InstructionSet isa, - TimingLogger* timings); - - // Generates a patch which can be used to efficiently relocate the original file or to check that - // a relocated file matches the original. The patch is generated from the difference of the - // |original| and the already |relocated| image, and written to |output| in the form of unsigned - // LEB128 for each relocation position. - static bool GeneratePatch(const MemMap& original, - const MemMap& relocated, - std::vector<uint8_t>* output, - std::string* error_msg); - - ~PatchOat() {} - PatchOat(PatchOat&&) = default; - - private: - // All pointers are only borrowed. - PatchOat(InstructionSet isa, MemMap* image, - gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, - std::map<gc::space::ImageSpace*, MemMap>* map, TimingLogger* timings) - : image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), isa_(isa), space_map_(map), timings_(timings) {} - - // Was the .art image at image_path made with --compile-pic ? - static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path); - - static bool CreateVdexAndOatSymlinks(const std::string& input_image_filename, - const std::string& output_image_filename); - - - void VisitObject(mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_); - void FixupMethod(ArtMethod* object, ArtMethod* copy) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool PatchImage(bool primary_image) REQUIRES_SHARED(Locks::mutator_lock_); - void PatchArtFields(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); - void PatchArtMethods(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); - void PatchImTables(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); - void PatchImtConflictTables(const ImageHeader* image_header) - REQUIRES_SHARED(Locks::mutator_lock_); - void PatchInternedStrings(const ImageHeader* image_header) - REQUIRES_SHARED(Locks::mutator_lock_); - void PatchClassTable(const ImageHeader* image_header) - REQUIRES_SHARED(Locks::mutator_lock_); - void PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool WriteImage(File* out); - - template <typename T> - T* RelocatedCopyOf(T* obj) const { - if (obj == nullptr) { - return nullptr; - } - DCHECK_GT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->Begin())); - DCHECK_LT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->End())); - uintptr_t heap_off = - reinterpret_cast<uintptr_t>(obj) - reinterpret_cast<uintptr_t>(heap_->Begin()); - DCHECK_LT(heap_off, image_->Size()); - return reinterpret_cast<T*>(image_->Begin() + heap_off); - } - - template <typename T> - T* RelocatedCopyOfFollowImages(T* obj) const { - if (obj == nullptr) { - return nullptr; - } - // Find ImageSpace this belongs to. - auto image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); - for (gc::space::ImageSpace* image_space : image_spaces) { - if (image_space->Contains(obj)) { - uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) - - reinterpret_cast<uintptr_t>(image_space->GetMemMap()->Begin()); - return reinterpret_cast<T*>(space_map_->find(image_space)->second.Begin() + heap_off); - } - } - LOG(FATAL) << "Did not find object in boot image space " << obj; - UNREACHABLE(); - } - - template <typename T> - T* RelocatedAddressOfPointer(T* obj) const { - if (obj == nullptr) { - return obj; - } - auto ret = reinterpret_cast<uintptr_t>(obj) + delta_; - // Trim off high bits in case negative relocation with 64 bit patchoat. - if (Is32BitISA()) { - ret = static_cast<uintptr_t>(static_cast<uint32_t>(ret)); - } - return reinterpret_cast<T*>(ret); - } - - bool Is32BitISA() const { - return InstructionSetPointerSize(isa_) == PointerSize::k32; - } - - // 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 { - public: - PatchVisitor(PatchOat* patcher, mirror::Object* copy) : patcher_(patcher), copy_(copy) {} - ~PatchVisitor() {} - void operator() (ObjPtr<mirror::Object> obj, MemberOffset off, bool b) const - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // For reference classes. - void operator() (ObjPtr<mirror::Class> cls, ObjPtr<mirror::Reference> ref) const - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // TODO: Consider using these for updating native class roots? - void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) - const {} - void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} - - private: - PatchOat* const patcher_; - mirror::Object* const copy_; - }; - - // A mmap of the image we are patching. This is modified. - const MemMap* const image_; - // The bitmap over the image within the heap we are patching. This is not modified. - gc::accounting::ContinuousSpaceBitmap* const bitmap_; - // The heap we are patching. This is not modified. - const MemMap* const heap_; - // The amount we are changing the offset by. - const off_t delta_; - // Active instruction set, used to know the entrypoint size. - const InstructionSet isa_; - - const std::map<gc::space::ImageSpace*, MemMap>* space_map_; - - TimingLogger* timings_; - - class FixupRootVisitor; - class RelocatedPointerVisitor; - class PatchOatArtFieldVisitor; - class PatchOatArtMethodVisitor; - - DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat); -}; - -} // namespace art -#endif // ART_PATCHOAT_PATCHOAT_H_ diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc deleted file mode 100644 index 79ae9872ba..0000000000 --- a/patchoat/patchoat_test.cc +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <openssl/sha.h> -#include <dirent.h> -#include <sys/types.h> - -#include <string> -#include <vector> - -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "base/hex_dump.h" -#include "base/leb128.h" -#include "dexopt_test.h" -#include "runtime.h" - -#include <gtest/gtest.h> - -namespace art { - -using android::base::StringPrintf; - -class PatchoatTest : public DexoptTest { - public: - static bool ListDirFilesEndingWith( - const std::string& dir, - const std::string& suffix, - std::vector<std::string>* filenames, - std::string* error_msg) { - DIR* d = opendir(dir.c_str()); - if (d == nullptr) { - *error_msg = "Failed to open directory"; - return false; - } - dirent* e; - struct stat s; - size_t suffix_len = suffix.size(); - while ((e = readdir(d)) != nullptr) { - if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { - continue; - } - size_t name_len = strlen(e->d_name); - if ((name_len < suffix_len) || (strcmp(&e->d_name[name_len - suffix_len], suffix.c_str()))) { - continue; - } - std::string basename(e->d_name); - std::string filename = dir + "/" + basename; - int stat_result = lstat(filename.c_str(), &s); - if (stat_result != 0) { - *error_msg = - StringPrintf("Failed to stat %s: stat returned %d", filename.c_str(), stat_result); - return false; - } - if (S_ISDIR(s.st_mode)) { - continue; - } - filenames->push_back(basename); - } - closedir(d); - return true; - } - - static void AddRuntimeArg(std::vector<std::string>& args, const std::string& arg) { - args.push_back("--runtime-arg"); - args.push_back(arg); - } - - bool CompileBootImage(const std::vector<std::string>& extra_args, - const std::string& image_file_name_prefix, - uint32_t base_addr, - std::string* error_msg) { - Runtime* const runtime = Runtime::Current(); - std::vector<std::string> argv; - argv.push_back(runtime->GetCompilerExecutable()); - AddRuntimeArg(argv, "-Xms64m"); - AddRuntimeArg(argv, "-Xmx64m"); - std::vector<std::string> dex_files = GetLibCoreDexFileNames(); - for (const std::string& dex_file : dex_files) { - argv.push_back("--dex-file=" + dex_file); - argv.push_back("--dex-location=" + dex_file); - } - if (runtime->IsJavaDebuggable()) { - argv.push_back("--debuggable"); - } - runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - - AddRuntimeArg(argv, "-Xverify:softfail"); - - if (!kIsTargetBuild) { - argv.push_back("--host"); - } - - argv.push_back("--image=" + image_file_name_prefix + ".art"); - argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); - argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); - argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); - argv.push_back("--compile-pic"); - argv.push_back("--multi-image"); - argv.push_back("--no-generate-debug-info"); - - std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); - argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); - - // We must set --android-root. - const char* android_root = getenv("ANDROID_ROOT"); - CHECK(android_root != nullptr); - argv.push_back("--android-root=" + std::string(android_root)); - argv.insert(argv.end(), extra_args.begin(), extra_args.end()); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - static std::vector<std::string> BasePatchoatCommand(const std::string& input_image_location, - off_t base_offset_delta) { - Runtime* const runtime = Runtime::Current(); - std::vector<std::string> argv; - argv.push_back(runtime->GetPatchoatExecutable()); - argv.push_back("--input-image-location=" + input_image_location); - argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); - argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); - - return argv; - } - - bool RelocateBootImage(const std::string& input_image_location, - const std::string& output_image_directory, - off_t base_offset_delta, - std::string* error_msg) { - std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta); - argv.push_back("--output-image-directory=" + output_image_directory); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool VerifyBootImage(const std::string& input_image_location, - const std::string& output_image_directory, - off_t base_offset_delta, - std::string* error_msg) { - std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta); - argv.push_back("--output-image-directory=" + output_image_directory); - argv.push_back("--verify"); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool GenerateBootImageRelFile(const std::string& input_image_location, - const std::string& output_rel_directory, - off_t base_offset_delta, - std::string* error_msg) { - std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta); - argv.push_back("--output-image-relocation-directory=" + output_rel_directory); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool RunDex2OatOrPatchoat(const std::vector<std::string>& args, std::string* error_msg) { - int link[2]; - - if (pipe(link) == -1) { - return false; - } - - pid_t pid = fork(); - if (pid == -1) { - return false; - } - - if (pid == 0) { - // We need dex2oat to actually log things. - setenv("ANDROID_LOG_TAGS", "*:e", 1); - dup2(link[1], STDERR_FILENO); - close(link[0]); - close(link[1]); - std::vector<const char*> c_args; - for (const std::string& str : args) { - c_args.push_back(str.c_str()); - } - c_args.push_back(nullptr); - execv(c_args[0], const_cast<char* const*>(c_args.data())); - exit(1); - UNREACHABLE(); - } else { - close(link[1]); - char buffer[128]; - memset(buffer, 0, 128); - ssize_t bytes_read = 0; - - while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { - *error_msg += std::string(buffer, bytes_read); - } - close(link[0]); - int status = -1; - if (waitpid(pid, &status, 0) != -1) { - return (status == 0); - } - return false; - } - } - - bool CompileBootImageToDir( - const std::string& output_dir, - const std::vector<std::string>& dex2oat_extra_args, - uint32_t base_addr, - std::string* error_msg) { - return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); - } - - bool CopyImageChecksumAndSetPatchDelta( - const std::string& src_image_filename, - const std::string& dest_image_filename, - off_t dest_patch_delta, - std::string* error_msg) { - std::unique_ptr<File> src_file(OS::OpenFileForReading(src_image_filename.c_str())); - if (src_file.get() == nullptr) { - *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); - return false; - } - ImageHeader src_header; - if (!src_file->ReadFully(&src_header, sizeof(src_header))) { - *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); - return false; - } - - std::unique_ptr<File> dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); - if (dest_file.get() == nullptr) { - *error_msg = - StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); - return false; - } - ImageHeader dest_header; - if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { - *error_msg = - StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); - return false; - } - dest_header.SetOatChecksum(src_header.GetOatChecksum()); - dest_header.SetPatchDelta(dest_patch_delta); - if (!dest_file->ResetOffset()) { - *error_msg = - StringPrintf( - "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); - return false; - } - if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { - *error_msg = - StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); - dest_file->Erase(); - return false; - } - if (dest_file->FlushCloseOrErase() != 0) { - *error_msg = - StringPrintf( - "Failed to flush/close destination image file %s", dest_image_filename.c_str()); - return false; - } - - return true; - } - - bool ReadFully( - const std::string& filename, std::vector<uint8_t>* contents, std::string* error_msg) { - std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str())); - if (file.get() == nullptr) { - *error_msg = "Failed to open"; - return false; - } - int64_t size = file->GetLength(); - if (size < 0) { - *error_msg = "Failed to get size"; - return false; - } - contents->resize(size); - if (!file->ReadFully(&(*contents)[0], size)) { - *error_msg = "Failed to read"; - contents->clear(); - return false; - } - return true; - } - - bool BinaryDiff( - const std::string& filename1, const std::string& filename2, std::string* error_msg) { - std::string read_error_msg; - std::vector<uint8_t> image1; - if (!ReadFully(filename1, &image1, &read_error_msg)) { - *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); - return true; - } - std::vector<uint8_t> image2; - if (!ReadFully(filename2, &image2, &read_error_msg)) { - *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); - return true; - } - if (image1.size() != image1.size()) { - *error_msg = - StringPrintf( - "%s and %s are of different size: %zu vs %zu", - filename1.c_str(), - filename2.c_str(), - image1.size(), - image2.size()); - return true; - } - size_t size = image1.size(); - for (size_t i = 0; i < size; i++) { - if (image1[i] != image2[i]) { - *error_msg = - StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); - size_t hexdump_size = std::min<size_t>(16u, size - i); - HexDump dump1(&image1[i], hexdump_size, /* show_actual_addresses */ false, /* prefix */ ""); - HexDump dump2(&image2[i], hexdump_size, /* show_actual_addresses */ false, /* prefix */ ""); - std::ostringstream oss; - oss << "\n" << dump1 << "\n" << dump2; - *error_msg += oss.str(); - return true; - } - } - - return false; - } -}; - -TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { -#if defined(ART_USE_READ_BARRIER) - // This test checks that relocating a boot image using patchoat produces the same result as - // producing the boot image for that relocated base address using dex2oat. To be precise, these - // two files will have two small differences: the OAT checksum and base address. However, this - // test takes this into account. - - // Compile boot image into a random directory using dex2oat - ScratchFile dex2oat_orig_scratch; - dex2oat_orig_scratch.Unlink(); - std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700)); - const uint32_t orig_base_addr = 0x60000000; - // Force deterministic output. We want the boot images created by this dex2oat run and the run - // below to differ only in their base address. - std::vector<std::string> dex2oat_extra_args; - dex2oat_extra_args.push_back("--force-determinism"); - dex2oat_extra_args.push_back("-j1"); // Might not be needed. Causes a 3-5x slowdown. - std::string error_msg; - if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { - FAIL() << "CompileBootImage1 failed: " << error_msg; - } - - // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated - // in the sense that it uses a different base address. - ScratchFile dex2oat_reloc_scratch; - dex2oat_reloc_scratch.Unlink(); - std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); - const uint32_t reloc_base_addr = 0x70000000; - if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { - FAIL() << "CompileBootImage2 failed: " << error_msg; - } - const off_t base_addr_delta = reloc_base_addr - orig_base_addr; - - // Relocate the original boot image using patchoat. The image is relocated by the same amount - // as the second/relocated image produced by dex2oat. - ScratchFile patchoat_scratch; - patchoat_scratch.Unlink(); - std::string patchoat_dir = patchoat_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); - std::string dex2oat_orig_with_arch_dir = - dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA); - // The arch-including symlink is needed by patchoat - ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str())); - if (!RelocateBootImage( - dex2oat_orig_dir + "/boot.art", - patchoat_dir, - base_addr_delta, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; - } - - // Assert that patchoat created the same set of .art files as dex2oat - std::vector<std::string> dex2oat_image_basenames; - std::vector<std::string> patchoat_image_basenames; - if (!ListDirFilesEndingWith(dex2oat_reloc_dir, ".art", &dex2oat_image_basenames, &error_msg)) { - FAIL() << "Failed to list *.art files in " << dex2oat_reloc_dir << ": " << error_msg; - } - if (!ListDirFilesEndingWith(patchoat_dir, ".art", &patchoat_image_basenames, &error_msg)) { - FAIL() << "Failed to list *.art files in " << patchoat_dir << ": " << error_msg; - } - std::sort(dex2oat_image_basenames.begin(), dex2oat_image_basenames.end()); - std::sort(patchoat_image_basenames.begin(), patchoat_image_basenames.end()); - // .art file names output by patchoat look like tmp@art-data-<random>-<random>@boot*.art. To - // compare these with .art file names output by dex2oat we retain only the part of the file name - // after the last @. - std::vector<std::string> patchoat_image_shortened_basenames(patchoat_image_basenames.size()); - for (size_t i = 0; i < patchoat_image_basenames.size(); i++) { - patchoat_image_shortened_basenames[i] = - patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of('@') + 1); - } - ASSERT_EQ(dex2oat_image_basenames, patchoat_image_shortened_basenames); - - // Patch up the dex2oat-relocated image files so that it looks as though they were relocated by - // patchoat. patchoat preserves the OAT checksum header field and sets patch delta header field. - for (const std::string& image_basename : dex2oat_image_basenames) { - if (!CopyImageChecksumAndSetPatchDelta( - dex2oat_orig_dir + "/" + image_basename, - dex2oat_reloc_dir + "/" + image_basename, - base_addr_delta, - &error_msg)) { - FAIL() << "Unable to patch up " << image_basename << ": " << error_msg; - } - } - - // Assert that the patchoat-relocated images are identical to the dex2oat-relocated images - for (size_t i = 0; i < dex2oat_image_basenames.size(); i++) { - const std::string& dex2oat_image_basename = dex2oat_image_basenames[i]; - const std::string& dex2oat_image_filename = dex2oat_reloc_dir + "/" + dex2oat_image_basename; - const std::string& patchoat_image_filename = patchoat_dir + "/" + patchoat_image_basenames[i]; - if (BinaryDiff(dex2oat_image_filename, patchoat_image_filename, &error_msg)) { - FAIL() << "patchoat- and dex2oat-relocated variants of " << dex2oat_image_basename - << " differ: " << error_msg; - } - } - - ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); - ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); - ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); - rmdir(dex2oat_orig_dir.c_str()); - rmdir(dex2oat_reloc_dir.c_str()); - rmdir(patchoat_dir.c_str()); -#else - LOG(INFO) << "Skipping PatchoatRelocationSameAsDex2oatRelocation"; - // Force-print to std::cout so it's also outside the logcat. - std::cout << "Skipping PatchoatRelocationSameAsDex2oatRelocation" << std::endl; -#endif -} - -// These tests check that a boot image relocated using patchoat can be unrelocated -// using the .rel file created by patchoat. -// -// The tests don't work when heap poisoning is enabled because some of the -// references are negated. b/72117833 is tracking the effort to have patchoat -// and its tests support heap poisoning. -class PatchoatVerificationTest : public PatchoatTest { - protected: - void CreateRelocatedBootImage() { - // Compile boot image into a random directory using dex2oat - ScratchFile dex2oat_orig_scratch; - dex2oat_orig_scratch.Unlink(); - dex2oat_orig_dir_ = dex2oat_orig_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(dex2oat_orig_dir_.c_str(), 0700)); - const uint32_t orig_base_addr = 0x60000000; - std::vector<std::string> dex2oat_extra_args; - std::string error_msg; - if (!CompileBootImageToDir(dex2oat_orig_dir_, dex2oat_extra_args, orig_base_addr, &error_msg)) { - FAIL() << "CompileBootImage1 failed: " << error_msg; - } - - // Generate image relocation file for the original boot image - std::string dex2oat_orig_with_arch_dir = - dex2oat_orig_dir_ + "/" + GetInstructionSetString(kRuntimeISA); - // The arch-including symlink is needed by patchoat - ASSERT_EQ(0, symlink(dex2oat_orig_dir_.c_str(), dex2oat_orig_with_arch_dir.c_str())); - base_addr_delta_ = 0x100000; - if (!GenerateBootImageRelFile( - dex2oat_orig_dir_ + "/boot.art", - dex2oat_orig_dir_, - base_addr_delta_, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; - } - - // Relocate the original boot image using patchoat - ScratchFile relocated_scratch; - relocated_scratch.Unlink(); - relocated_dir_ = relocated_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(relocated_dir_.c_str(), 0700)); - // Use a different relocation delta from the one used when generating .rel files above. This is - // to make sure .rel files are not specific to a particular relocation delta. - base_addr_delta_ -= 0x10000; - if (!RelocateBootImage( - dex2oat_orig_dir_ + "/boot.art", - relocated_dir_, - base_addr_delta_, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; - } - - // Assert that patchoat created the same set of .art and .art.rel files - std::vector<std::string> rel_basenames; - std::vector<std::string> relocated_image_basenames; - if (!ListDirFilesEndingWith(dex2oat_orig_dir_, ".rel", &rel_basenames, &error_msg)) { - FAIL() << "Failed to list *.art.rel files in " << dex2oat_orig_dir_ << ": " << error_msg; - } - if (!ListDirFilesEndingWith(relocated_dir_, ".art", &relocated_image_basenames, &error_msg)) { - FAIL() << "Failed to list *.art files in " << relocated_dir_ << ": " << error_msg; - } - std::sort(rel_basenames.begin(), rel_basenames.end()); - std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end()); - - // .art and .art.rel file names output by patchoat look like - // tmp@art-data-<random>-<random>@boot*.art, encoding the name of the directory in their name. - // To compare these with each other, we retain only the part of the file name after the last @, - // and we also drop the extension. - std::vector<std::string> rel_shortened_basenames(rel_basenames.size()); - std::vector<std::string> relocated_image_shortened_basenames(relocated_image_basenames.size()); - for (size_t i = 0; i < rel_basenames.size(); i++) { - rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of('@') + 1); - rel_shortened_basenames[i] = - rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find('.')); - } - for (size_t i = 0; i < relocated_image_basenames.size(); i++) { - relocated_image_shortened_basenames[i] = - relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of('@') + 1); - relocated_image_shortened_basenames[i] = - relocated_image_shortened_basenames[i].substr( - 0, relocated_image_shortened_basenames[i].find('.')); - } - ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames); - } - - void TearDown() override { - if (!dex2oat_orig_dir_.empty()) { - ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true); - rmdir(dex2oat_orig_dir_.c_str()); - } - if (!relocated_dir_.empty()) { - ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true); - rmdir(relocated_dir_.c_str()); - } - PatchoatTest::TearDown(); - } - - std::string dex2oat_orig_dir_; - std::string relocated_dir_; - off_t base_addr_delta_; -}; - -// Assert that verification works with the .rel files. -TEST_F(PatchoatVerificationTest, Sucessful) { - TEST_DISABLED_FOR_HEAP_POISONING(); - CreateRelocatedBootImage(); - - std::string error_msg; - if (!VerifyBootImage( - dex2oat_orig_dir_ + "/boot.art", - relocated_dir_, - base_addr_delta_, - &error_msg)) { - FAIL() << "VerifyBootImage failed: " << error_msg; - } -} - -// Corrupt the image file and check that the verification fails gracefully. -TEST_F(PatchoatVerificationTest, CorruptedImage) { - TEST_DISABLED_FOR_HEAP_POISONING(); - CreateRelocatedBootImage(); - - std::string error_msg; - std::string relocated_image_filename; - if (!GetDalvikCacheFilename((dex2oat_orig_dir_ + "/boot.art").c_str(), - relocated_dir_.c_str(), - &relocated_image_filename, - &error_msg)) { - FAIL() << "Failed to find relocated image file name: " << error_msg; - } - ASSERT_EQ(truncate(relocated_image_filename.c_str(), sizeof(ImageHeader)), 0) - << relocated_image_filename; - - if (VerifyBootImage( - dex2oat_orig_dir_ + "/boot.art", - relocated_dir_, - base_addr_delta_, - &error_msg)) { - FAIL() << "VerifyBootImage should have failed since the image was intentionally corrupted"; - } -} - -// Corrupt the relocation file and check that the verification fails gracefully. -TEST_F(PatchoatVerificationTest, CorruptedRelFile) { - TEST_DISABLED_FOR_HEAP_POISONING(); - CreateRelocatedBootImage(); - - std::string error_msg; - std::string art_filename = dex2oat_orig_dir_ + "/boot.art"; - std::string rel_filename = dex2oat_orig_dir_ + "/boot.art.rel"; - std::unique_ptr<File> art_file(OS::OpenFileForReading(art_filename.c_str())); - std::unique_ptr<File> rel_file(OS::OpenFileReadWrite(rel_filename.c_str())); - rel_file->ClearContent(); - uint8_t buffer[64] = {}; - ASSERT_TRUE(rel_file->WriteFully(&buffer, SHA256_DIGEST_LENGTH)); - // Encode single relocation which is just past the end of the image file. - size_t leb_size = EncodeUnsignedLeb128(buffer, art_file->GetLength()) - buffer; - ASSERT_TRUE(rel_file->WriteFully(&buffer, leb_size)); - ASSERT_EQ(rel_file->FlushClose(), 0); - ASSERT_EQ(art_file->Close(), 0); - - if (VerifyBootImage( - dex2oat_orig_dir_ + "/boot.art", - relocated_dir_, - base_addr_delta_, - &error_msg)) { - FAIL() << "VerifyBootImage should have failed since the rel file was intentionally corrupted"; - } -} - -} // namespace art diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 556ff69a5b..462620f53a 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -39,8 +39,6 @@ void DexoptTest::SetUp() { void DexoptTest::PreRuntimeCreate() { std::string error_msg; - ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg; - ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg; UnreserveImageSpace(); } @@ -182,34 +180,6 @@ void DexoptTest::GenerateOatForTest(const char* dex_location, CompilerFilter::Fi GenerateOatForTest(dex_location, filter, /* with_alternate_image */ false); } -bool DexoptTest::PreRelocateImage(const std::string& image_location, std::string* error_msg) { - std::string dalvik_cache; - bool have_android_data; - bool dalvik_cache_exists; - bool is_global_cache; - GetDalvikCache(GetInstructionSetString(kRuntimeISA), - /* create_if_absent */ true, - &dalvik_cache, - &have_android_data, - &dalvik_cache_exists, - &is_global_cache); - if (!dalvik_cache_exists) { - *error_msg = "Failed to create dalvik cache"; - return false; - } - - std::string patchoat = GetAndroidRoot(); - patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"; - - std::vector<std::string> argv; - argv.push_back(patchoat); - argv.push_back("--input-image-location=" + image_location); - argv.push_back("--output-image-directory=" + dalvik_cache); - argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); - argv.push_back("--base-offset-delta=0x00008000"); - return Exec(argv, error_msg); -} - void DexoptTest::ReserveImageSpace() { MemMap::Init(); diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h index 067b67ae1e..efbdcbaa07 100644 --- a/runtime/dexopt_test.h +++ b/runtime/dexopt_test.h @@ -62,11 +62,6 @@ class DexoptTest : public Dex2oatEnvironmentTest { static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg); private: - // Pre-Relocate the image to a known non-zero offset so we don't have to - // deal with the runtime randomly relocating the image by 0 and messing up - // the expected results of the tests. - bool PreRelocateImage(const std::string& image_location, std::string* error_msg); - // Reserve memory around where the image will be loaded so other memory // won't conflict when it comes time to load the image. // This can be called with an already loaded image to reserve the space diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 4d16eb537d..2e495cc866 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -751,7 +751,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xcompiler:filename\n"); UsageMessage(stream, " -Xcompiler-option dex2oat-option\n"); UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); - UsageMessage(stream, " -Xpatchoat:filename\n"); + UsageMessage(stream, " -Xpatchoat:filename (obsolete, ignored)\n"); UsageMessage(stream, " -Xusejit:booleanvalue\n"); UsageMessage(stream, " -Xjitinitialsize:N\n"); UsageMessage(stream, " -Xjitmaxsize:N\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 48c172edab..a48f1feeb9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -695,15 +695,6 @@ static jobject CreateSystemClassLoader(Runtime* runtime) { return env->NewGlobalRef(system_class_loader.get()); } -std::string Runtime::GetPatchoatExecutable() const { - if (!patchoat_executable_.empty()) { - return patchoat_executable_; - } - std::string patchoat_executable(GetAndroidRoot()); - patchoat_executable += (kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"); - return patchoat_executable; -} - std::string Runtime::GetCompilerExecutable() const { if (!compiler_executable_.empty()) { return compiler_executable_; @@ -1190,7 +1181,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList); compiler_callbacks_ = runtime_options.GetOrDefault(Opt::CompilerCallbacksPtr); - patchoat_executable_ = runtime_options.ReleaseOrDefault(Opt::PatchOat); must_relocate_ = runtime_options.GetOrDefault(Opt::Relocate); is_zygote_ = runtime_options.Exists(Opt::Zygote); is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC); diff --git a/runtime/runtime.h b/runtime/runtime.h index f0bf7548af..ea2eb396ca 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -164,7 +164,6 @@ class Runtime { } std::string GetCompilerExecutable() const; - std::string GetPatchoatExecutable() const; const std::vector<std::string>& GetCompilerOptions() const { return compiler_options_; @@ -845,7 +844,6 @@ class Runtime { bool image_dex2oat_enabled_; std::string compiler_executable_; - std::string patchoat_executable_; std::vector<std::string> compiler_options_; std::vector<std::string> image_compiler_options_; std::string image_location_; @@ -963,7 +961,7 @@ class Runtime { bool implicit_suspend_checks_; // Thread suspension checks are implicit. // Whether or not the sig chain (and implicitly the fault handler) should be - // disabled. Tools like dex2oat or patchoat don't need them. This enables + // disabled. Tools like dex2oat don't need them. This enables // building a statically link version of dex2oat. bool no_sig_chain_; diff --git a/test/117-nopatchoat/expected.txt b/test/117-nopatchoat/expected.txt deleted file mode 100644 index 7a24e31775..0000000000 --- a/test/117-nopatchoat/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -JNI_OnLoad called -Has oat is true, has executable oat is expected. -This is a function call diff --git a/test/117-nopatchoat/info.txt b/test/117-nopatchoat/info.txt deleted file mode 100644 index aa9f57cb03..0000000000 --- a/test/117-nopatchoat/info.txt +++ /dev/null @@ -1 +0,0 @@ -Test that disables patchoat'ing the application. diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc deleted file mode 100644 index c673dd7eb1..0000000000 --- a/test/117-nopatchoat/nopatchoat.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "class_linker.h" -#include "dex/dex_file-inl.h" -#include "gc/heap.h" -#include "gc/space/image_space.h" -#include "mirror/class-inl.h" -#include "oat_file.h" -#include "runtime.h" -#include "scoped_thread_state_change-inl.h" -#include "thread.h" - -namespace art { - -class NoPatchoatTest { - public: - static const OatDexFile* getOatDexFile(jclass cls) { - ScopedObjectAccess soa(Thread::Current()); - ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); - const DexFile& dex_file = klass->GetDexFile(); - return dex_file.GetOatDexFile(); - } - - static bool isRelocationDeltaZero() { - std::vector<gc::space::ImageSpace*> spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - return !spaces.empty() && spaces[0]->GetImageHeader().GetPatchDelta() == 0; - } - - static bool hasExecutableOat(jclass cls) { - const OatDexFile* oat_dex_file = getOatDexFile(cls); - - return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable(); - } -}; - -extern "C" JNIEXPORT jboolean JNICALL Java_Main_isRelocationDeltaZero(JNIEnv*, jclass) { - return NoPatchoatTest::isRelocationDeltaZero(); -} - -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasExecutableOat(JNIEnv*, jclass cls) { - return NoPatchoatTest::hasExecutableOat(cls); -} - -} // namespace art diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run deleted file mode 100755 index 4c33f7a450..0000000000 --- a/test/117-nopatchoat/run +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ensure flags includes prebuild and relocate. It doesn't make sense unless we -# have a oat file we want to relocate. -flags="$@" - -# This test is supposed to test with oat files. Make sure that the no-prebuild flag isn't set, -# or complain. -# Note: prebuild is the default. -if [[ "${flags}" == *--no-prebuild* ]] ; then - echo "Test 117-nopatchoat is not intended to run in no-prebuild mode." - exit 1 -fi - -# This test is supposed to test relocation. Make sure that the no-relocate flag isn't set, -# or complain. -# Note: relocate is the default. -if [[ "${flags}" == *--no-relocate* ]] ; then - echo "Test 117-nopatchoat is not intended to run in no-relocate mode." - exit 1 -fi - -${RUN} ${flags} diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java deleted file mode 100644 index dfb98b0757..0000000000 --- a/test/117-nopatchoat/src/Main.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -public class Main { - public static void main(String[] args) { - System.loadLibrary(args[0]); - - boolean executable_correct = hasExecutableOat(); - - System.out.println( - "Has oat is " + hasOatFile() + ", has executable oat is " + ( - executable_correct ? "expected" : "not expected") + "."); - - System.out.println(functionCall()); - } - - public static String functionCall() { - String arr[] = {"This", "is", "a", "function", "call"}; - String ret = ""; - for (int i = 0; i < arr.length; i++) { - ret = ret + arr[i] + " "; - } - return ret.substring(0, ret.length() - 1); - } - - private native static boolean hasOatFile(); - - private native static boolean hasExecutableOat(); - - private native static boolean isRelocationDeltaZero(); -} diff --git a/test/Android.bp b/test/Android.bp index 4b61463585..8f23058cf7 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -454,7 +454,6 @@ cc_defaults { "004-UnsafeTest/unsafe_test.cc", "044-proxy/native_proxy.cc", "051-thread/thread_test.cc", - "117-nopatchoat/nopatchoat.cc", "1337-gc-coverage/gc_coverage.cc", "136-daemon-jni-shutdown/daemon_jni_shutdown.cc", "137-cfi/cfi.cc", diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index d5db76a82a..e9ed0511e1 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -40,7 +40,6 @@ LIBRARY_DIRECTORY="lib" TEST_DIRECTORY="nativetest" MAIN="" OPTIMIZE="y" -PATCHOAT="" PREBUILD="y" QUIET="n" RELOCATE="n" @@ -59,7 +58,6 @@ USE_JVMTI="n" VERIFY="y" # y=yes,n=no,s=softfail ZYGOTE="" DEX_VERIFY="" -USE_PATCHOAT="y" INSTRUCTION_SET_FEATURES="" ARGS="" EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS. @@ -166,10 +164,6 @@ while true; do shift BOOT_IMAGE="$1" shift - elif [ "x$1" = "x--no-patchoat" ]; then - PATCHOAT="-Xpatchoat:${FALSE_BIN}" - USE_PATCHOAT="n" - shift elif [ "x$1" = "x--relocate" ]; then RELOCATE="y" shift @@ -774,7 +768,6 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $FLAGS \ $DEX_VERIFY \ -XXlib:$LIB \ - $PATCHOAT \ $DEX2OAT \ $DALVIKVM_ISA_FEATURES_ARGS \ $ZYGOTE \ @@ -803,15 +796,11 @@ if [ "x$RUN_TEST_ASAN_OPTIONS" != "x" ] ; then fi RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}detect_leaks=0" -# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use +# For running, we must turn off logging when dex2oat is missing. Otherwise we use # the same defaults as for prebuilt: everything when --dev, otherwise errors and above only. if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then if [ "$DEV_MODE" = "y" ]; then export ANDROID_LOG_TAGS='*:d' - elif [ "$USE_PATCHOAT" = "n" ]; then - # All tests would log the error of failing dex2oat/patchoat. Be silent here and only - # log fatal events. - export ANDROID_LOG_TAGS='*:s' elif [ "$HAVE_IMAGE" = "n" ]; then # All tests would log the error of missing image. Be silent here and only log fatal # events. diff --git a/test/knownfailures.json b/test/knownfailures.json index fc4b25fa2b..6eb4707ea4 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -70,22 +70,20 @@ "doesn't (and isn't meant to) work with --prebuild."] }, { - "tests": ["117-nopatchoat", - "147-stripped-dex-fallback", + "tests": ["147-stripped-dex-fallback", "608-checker-unresolved-lse"], "variant": "no-prebuild" }, { - "tests": ["117-nopatchoat", - "118-noimage-dex2oat"], + "tests": ["118-noimage-dex2oat"], "variant": "no-relocate", - "description": ["117-nopatchoat is not broken per-se it just doesn't", - "work (and isn't meant to) without --prebuild", + "description": ["118-noimage-dex2oat is not broken per-se it just ", + "doesn't work (and isn't meant to) without --prebuild ", "--relocate"] }, { "tests" : "629-vdex-speed", - "variant": "interp-ac | interpreter | jit | relocate-npatchoat", + "variant": "interp-ac | interpreter | jit", "description": "629 requires compilation." }, { @@ -172,19 +170,18 @@ }, { "tests": "147-stripped-dex-fallback", - "variant": "no-image | relocate-npatchoat", + "variant": "no-image", "description": ["147-stripped-dex-fallback is disabled because it", "requires --prebuild."] }, { "tests": ["116-nodex2oat", - "117-nopatchoat", "118-noimage-dex2oat", "137-cfi", "138-duplicate-classes-check2"], - "variant": "no-image | relocate-npatchoat", + "variant": "no-image", "description": ["All these tests check that we have sane behavior if we", - "don't have a patchoat or dex2oat. Therefore we", + "don't have a dex2oat. Therefore we", "shouldn't run them in situations where we actually", "don't have these since they explicitly test for them.", "These all also assume we have an image."] @@ -339,7 +336,6 @@ { "tests": ["018-stack-overflow", "116-nodex2oat", - "117-nopatchoat", "118-noimage-dex2oat", "126-miranda-multidex", "137-cfi"], @@ -767,7 +763,6 @@ "111-unresolvable-exception", "115-native-bridge", "116-nodex2oat", - "117-nopatchoat", "118-noimage-dex2oat", "127-checker-secondarydex", "129-ThreadGetId", @@ -1045,7 +1040,7 @@ }, { "tests": "677-fsi", - "variant": "no-image | no-prebuild | relocate-npatchoat | jvm", + "variant": "no-image | no-prebuild | jvm", "description": ["Test requires a successful dex2oat invocation"] }, { diff --git a/test/run-test b/test/run-test index ef173026c1..c32fe3aa5f 100755 --- a/test/run-test +++ b/test/run-test @@ -148,7 +148,6 @@ jvmti_redefine_stress="false" strace="false" always_clean="no" never_clean="no" -have_patchoat="yes" have_image="yes" multi_image_suffix="" android_root="/system" @@ -194,9 +193,6 @@ while true; do lib="libdvm.so" runtime="dalvik" shift - elif [ "x$1" = "x--no-patchoat" ]; then - have_patchoat="no" - shift elif [ "x$1" = "x--no-image" ]; then have_image="no" shift @@ -572,10 +568,6 @@ if [ "$target_mode" = "no" ]; then fi fi -if [ "$have_patchoat" = "no" ]; then - run_args="${run_args} --no-patchoat" -fi - if [ ! "$runtime" = "jvm" ]; then run_args="${run_args} --lib $lib" fi @@ -631,11 +623,6 @@ if [ "$bisection_search" = "yes" -a "$prebuild_mode" = "yes" ]; then usage="yes" fi -if [ "$bisection_search" = "yes" -a "$have_patchoat" = "no" ]; then - err_echo "--bisection-search and --no-patchoat are mutually exclusive" - usage="yes" -fi - # TODO: Chroot-based bisection search is not supported yet (see below); implement it. if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then err_echo "--chroot with --bisection-search is unsupported" @@ -704,7 +691,6 @@ if [ "$usage" = "yes" ]; then "If used, then the" echo " other runtime options are ignored." echo " --no-dex2oat Run as though dex2oat was failing." - echo " --no-patchoat Run as though patchoat was failing." echo " --prebuild Run dex2oat on the files before starting test. (default)" echo " --no-prebuild Do not run dex2oat on the files before starting" echo " the test." diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 72dc165699..2fa7d2ae96 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -148,7 +148,7 @@ def gather_test_info(): VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'} VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'prebuild'} VARIANT_TYPE_DICT['cdex_level'] = {'cdex-none', 'cdex-fast'} - VARIANT_TYPE_DICT['relocate'] = {'relocate-npatchoat', 'relocate', 'no-relocate'} + VARIANT_TYPE_DICT['relocate'] = {'relocate', 'no-relocate'} VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'} VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'} VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress', 'redefine-stress', 'trace-stress', @@ -440,8 +440,6 @@ def run_tests(tests): options_test += ' --relocate' elif relocate == 'no-relocate': options_test += ' --no-relocate' - elif relocate == 'relocate-npatchoat': - options_test += ' --relocate --no-patchoat' if trace == 'trace': options_test += ' --trace' |