summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--build/Android.common_path.mk1
-rw-r--r--build/Android.gtest.mk16
-rw-r--r--patchoat/Android.bp64
-rw-r--r--patchoat/patchoat.cc1324
-rw-r--r--patchoat/patchoat.h201
-rw-r--r--patchoat/patchoat_test.cc617
-rw-r--r--runtime/dexopt_test.cc30
-rw-r--r--runtime/dexopt_test.h5
-rw-r--r--runtime/parsed_options.cc2
-rw-r--r--runtime/runtime.cc10
-rw-r--r--runtime/runtime.h4
-rw-r--r--test/117-nopatchoat/expected.txt3
-rw-r--r--test/117-nopatchoat/info.txt1
-rw-r--r--test/117-nopatchoat/nopatchoat.cc59
-rwxr-xr-xtest/117-nopatchoat/run37
-rw-r--r--test/117-nopatchoat/src/Main.java44
-rw-r--r--test/Android.bp1
-rwxr-xr-xtest/etc/run-test-jar13
-rw-r--r--test/knownfailures.json23
-rwxr-xr-xtest/run-test14
-rwxr-xr-xtest/testrunner/testrunner.py4
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'