OSDN Git Service

Runtime can now be set to require relocation
authorAlex Light <allight@google.com>
Wed, 2 Jul 2014 23:28:08 +0000 (16:28 -0700)
committerAndreas Gampe <agampe@google.com>
Tue, 22 Jul 2014 15:24:14 +0000 (08:24 -0700)
Add a pair of runtime flags -Xrelocate and -Xnorelocate that can force
the runtime to require that all files that are run are relocated, to
prevent attacks based on the known art base address.

Add support for running patchoat on oat files compiled without an image.

Change run-test to have new --prebuild and --relocate flags.

Bug: 15358152

Change-Id: I91166c62dd1ab80e5cbcb7883a2cd0d56afca32d

46 files changed:
Android.mk
build/Android.common_build.mk
build/Android.common_test.mk
compiler/Android.mk
compiler/dex/quick_compiler_callbacks.h
compiler/driver/compiler_driver.cc
compiler/elf_patcher.cc [new file with mode: 0644]
compiler/elf_patcher.h [new file with mode: 0644]
compiler/image_test.cc
compiler/image_writer.cc
compiler/image_writer.h
compiler/oat_test.cc
compiler/oat_writer.cc
compiler/oat_writer.h
dex2oat/dex2oat.cc
oatdump/oatdump.cc
patchoat/patchoat.cc
patchoat/patchoat.h
runtime/base/scoped_flock.cc
runtime/base/scoped_flock.h
runtime/class_linker.cc
runtime/class_linker.h
runtime/common_runtime_test.cc
runtime/common_runtime_test.h
runtime/compiler_callbacks.h
runtime/gc/space/image_space.cc
runtime/gc/space/image_space.h
runtime/indirect_reference_table.cc
runtime/noop_compiler_callbacks.h
runtime/oat.cc
runtime/oat.h
runtime/oat_file.cc
runtime/oat_file.h
runtime/parsed_options.cc
runtime/parsed_options.h
runtime/runtime.cc
runtime/runtime.h
runtime/utils.cc
runtime/utils.h
test/Android.oat.mk
test/Android.run-test.mk
test/etc/host-run-test-jar
test/etc/push-and-run-prebuilt-test-jar [new file with mode: 0755]
test/etc/push-and-run-test-jar
test/run-all-tests
test/run-test

index 7a95dfe..5a819f6 100644 (file)
@@ -329,7 +329,7 @@ $$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OA
                --dex-location=/$(1) --oat-file=$$@ \
                --instruction-set=$(DEX2OAT_TARGET_ARCH) \
                --instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
-               --android-root=$(PRODUCT_OUT)/system
+               --android-root=$(PRODUCT_OUT)/system --include-patch-information
 
 endif
 
index c39bc5d..353fab1 100644 (file)
@@ -207,6 +207,25 @@ ifndef LIBART_IMG_TARGET_BASE_ADDRESS
   $(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
 endif
 ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+
+ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
+  LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA=-0x1000000
+endif
+ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA
+  LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA=0x1000000
+endif
+ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+
+ifndef LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA
+  LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA=-0x1000000
+endif
+ifndef LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA
+  LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA=0x1000000
+endif
+ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA)
+ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA)
+
 ART_TARGET_LDFLAGS :=
 ifeq ($(TARGET_CPU_SMP),true)
   ART_TARGET_CFLAGS += -DANDROID_SMP=1
index 56be8ea..4987567 100644 (file)
@@ -45,6 +45,21 @@ ART_TEST_GC_VERIFY ?= $(ART_TEST_FULL)
 # Do you want tests with the GC stress mode enabled run?
 ART_TEST_GC_STRESS ?= $(ART_TEST_FULL)
 
+# Do you want oat tests with relocation enabled?
+ART_TEST_OAT_RELOCATE ?= true
+
+# Do you want oat tests with relocation disabled?
+ART_TEST_OAT_NO_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation enabled?
+ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation disabled?
+ART_TEST_RUN_TEST_NO_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with prebuild enabled?
+ART_TEST_RUN_TEST_PREBUILD ?= true
+
 # Define the command run on test failure. $(1) is the name of the test. Executed by the shell.
 define ART_TEST_FAILED
   ( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \
index ac2f9d6..61ff109 100644 (file)
@@ -118,6 +118,7 @@ LIBART_COMPILER_SRC_FILES := \
        compilers.cc \
        compiler.cc \
        elf_fixup.cc \
+       elf_patcher.cc \
        elf_stripper.cc \
        elf_writer.cc \
        elf_writer_quick.cc \
index 7c9614f..cdf71b6 100644 (file)
@@ -41,6 +41,11 @@ class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
 
     void ClassRejected(ClassReference ref) OVERRIDE;
 
+    // We are running in an environment where we can call patchoat safely so we should.
+    bool IsRelocationPossible() OVERRIDE {
+      return true;
+    }
+
   private:
     VerificationResults* const verification_results_;
     DexFileToMethodInlinerMap* const method_inliner_map_;
index 9e88c8d..f85bc65 100644 (file)
@@ -955,7 +955,8 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i
     if (class_in_image) {
       // boot -> app class pointers.
       *is_type_initialized = resolved_class->IsInitialized();
-      *use_direct_type_ptr = true;
+      // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+      *use_direct_type_ptr = !GetCompilerOptions().GetIncludePatchInformation();
       *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class);
       return true;
     } else {
@@ -1099,6 +1100,9 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
   *direct_method = 0;
   bool use_dex_cache = false;
   const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
+  // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+  const bool force_relocations = (compiling_boot ||
+                                  GetCompilerOptions().GetIncludePatchInformation());
   if (compiler_->IsPortable()) {
     if (sharp_type != kStatic && sharp_type != kDirect) {
       return;
@@ -1109,7 +1113,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
       return;
     }
     // TODO: support patching on all architectures.
-    use_dex_cache = compiling_boot && !support_boot_image_fixup_;
+    use_dex_cache = force_relocations && !support_boot_image_fixup_;
   }
   bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
   if (!use_dex_cache) {
@@ -1128,8 +1132,8 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
   if (method_code_in_boot) {
     *stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
   }
-  if (!use_dex_cache && compiling_boot) {
-    if (!IsImageClass(method->GetDeclaringClassDescriptor())) {
+  if (!use_dex_cache && force_relocations) {
+    if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
       // We can only branch directly to Methods that are resolved in the DexCache.
       // Otherwise we won't invoke the resolution trampoline.
       use_dex_cache = true;
@@ -1150,7 +1154,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
       if (dex_method_idx != DexFile::kDexNoIndex) {
         target_method->dex_method_index = dex_method_idx;
       } else {
-        if (compiling_boot && !use_dex_cache) {
+        if (force_relocations && !use_dex_cache) {
           target_method->dex_method_index = method->GetDexMethodIndex();
           target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
         }
@@ -1167,19 +1171,26 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
       *type = sharp_type;
     }
   } else {
-    bool method_in_image = compiling_boot ||
+    bool method_in_image =
         Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
-    if (method_in_image) {
+    if (method_in_image || compiling_boot) {
+      // We know we must be able to get to the method in the image, so use that pointer.
       CHECK(!method->IsAbstract());
       *type = sharp_type;
-      *direct_method = compiling_boot ? -1 : reinterpret_cast<uintptr_t>(method);
-      *direct_code = compiling_boot ? -1 : compiler_->GetEntryPointOf(method);
+      *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
+      *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
       target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
       target_method->dex_method_index = method->GetDexMethodIndex();
     } else if (!must_use_direct_pointers) {
       // Set the code and rely on the dex cache for the method.
       *type = sharp_type;
-      *direct_code = compiler_->GetEntryPointOf(method);
+      if (force_relocations) {
+        *direct_code = -1;
+        target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+        target_method->dex_method_index = method->GetDexMethodIndex();
+      } else {
+        *direct_code = compiler_->GetEntryPointOf(method);
+      }
     } else {
       // Direct pointers were required but none were available.
       VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
new file mode 100644 (file)
index 0000000..6112fbb
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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 "elf_patcher.h"
+
+#include <vector>
+#include <set>
+
+#include "elf_file.h"
+#include "elf_utils.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/string-inl.h"
+#include "oat.h"
+#include "os.h"
+#include "utils.h"
+
+namespace art {
+
+bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf_file,
+                       const std::string& oat_location,
+                       ImageAddressCallback cb, void* cb_data,
+                       std::string* error_msg) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+  if (oat_file == nullptr) {
+    CHECK(Runtime::Current()->IsCompiler());
+    oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+    if (oat_file == nullptr) {
+      *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
+                                error_msg->c_str());
+      return false;
+    }
+    CHECK_EQ(class_linker->RegisterOatFile(oat_file), oat_file);
+  }
+  return ElfPatcher::Patch(driver, elf_file, oat_file,
+                           reinterpret_cast<uintptr_t>(oat_file->Begin()), cb, cb_data, error_msg);
+}
+
+bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf, const OatFile* oat_file,
+                       uintptr_t oat_data_start, ImageAddressCallback cb, void* cb_data,
+                       std::string* error_msg) {
+  Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
+  if (data_sec == nullptr) {
+    *error_msg = "Unable to find .rodata section and oat header";
+    return false;
+  }
+  OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
+  if (!oat_header->IsValid()) {
+    *error_msg = "Oat header was not valid";
+    return false;
+  }
+
+  ElfPatcher p(driver, elf, oat_file, oat_header, oat_data_start, cb, cb_data, error_msg);
+  return p.PatchElf();
+}
+
+mirror::ArtMethod* ElfPatcher::GetTargetMethod(const CompilerDriver::CallPatchInformation* patch) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  StackHandleScope<1> hs(Thread::Current());
+  Handle<mirror::DexCache> dex_cache(
+      hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
+  mirror::ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
+                                                          patch->GetTargetMethodIdx(),
+                                                          dex_cache,
+                                                          NullHandle<mirror::ClassLoader>(),
+                                                          NullHandle<mirror::ArtMethod>(),
+                                                          patch->GetTargetInvokeType());
+  CHECK(method != NULL)
+    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
+  CHECK(!method->IsRuntimeMethod())
+    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
+  CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
+    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
+    << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
+    << PrettyMethod(method);
+  return method;
+}
+
+mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  StackHandleScope<2> hs(Thread::Current());
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
+  mirror::Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
+                                                   dex_cache, NullHandle<mirror::ClassLoader>());
+  CHECK(klass != NULL)
+    << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
+  CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
+    << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
+    << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
+    << PrettyClass(klass);
+  return klass;
+}
+
+void ElfPatcher::AddPatch(uintptr_t p) {
+  if (write_patches_ && patches_set_.find(p) == patches_set_.end()) {
+    patches_set_.insert(p);
+    patches_.push_back(p);
+  }
+}
+
+uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) {
+  CHECK_GE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()));
+  uintptr_t off = patch_ptr - reinterpret_cast<uintptr_t>(oat_file_->Begin());
+  uintptr_t ret = reinterpret_cast<uintptr_t>(oat_header_) + off;
+
+  CHECK_GE(ret, reinterpret_cast<uintptr_t>(elf_file_->Begin()));
+  CHECK_LT(ret, reinterpret_cast<uintptr_t>(elf_file_->End()));
+  return reinterpret_cast<uint32_t*>(ret);
+}
+
+void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
+                                                                patch->GetReferrerClassDefIdx(),
+                                                                patch->GetReferrerMethodIdx());
+  // TODO: make this Thumb2 specific
+  uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
+  uintptr_t patch_ptr = reinterpret_cast<uintptr_t>(base + patch->GetLiteralOffset());
+  uint32_t* patch_location = GetPatchLocation(patch_ptr);
+  if (kIsDebugBuild) {
+    if (patch->IsCall()) {
+      const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
+      const DexFile::MethodId& id =
+          cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
+      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
+      uint32_t actual = *patch_location;
+      CHECK(actual == expected || actual == value) << std::hex
+          << "actual=" << actual
+          << "expected=" << expected
+          << "value=" << value;
+    }
+    if (patch->IsType()) {
+      const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
+      const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
+      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
+      uint32_t actual = *patch_location;
+      CHECK(actual == expected || actual == value) << std::hex
+          << "actual=" << actual
+          << "expected=" << expected
+          << "value=" << value;
+    }
+  }
+  *patch_location = value;
+  oat_header_->UpdateChecksum(patch_location, sizeof(value));
+
+  if (patch->IsCall() && patch->AsCall()->IsRelative()) {
+    // We never record relative patches.
+    return;
+  }
+  uintptr_t loc = patch_ptr - (reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
+                               oat_header_->GetExecutableOffset());
+  CHECK_GT(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
+                      oat_header_->GetExecutableOffset());
+  CHECK_LT(loc, oat_file_->Size() - oat_header_->GetExecutableOffset());
+  AddPatch(loc);
+}
+
+bool ElfPatcher::PatchElf() {
+  // TODO if we are adding patches the resulting ELF file might have a
+  // potentially rather large amount of free space where patches might have been
+  // placed. We should adjust the ELF file to get rid of this excess space.
+  if (write_patches_) {
+    patches_.reserve(compiler_driver_->GetCodeToPatch().size() +
+                     compiler_driver_->GetMethodsToPatch().size() +
+                     compiler_driver_->GetClassesToPatch().size());
+  }
+  Thread* self = Thread::Current();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const char* old_cause = self->StartAssertNoThreadSuspension("ElfPatcher");
+
+  typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
+  const CallPatches& code_to_patch = compiler_driver_->GetCodeToPatch();
+  for (size_t i = 0; i < code_to_patch.size(); i++) {
+    const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
+
+    mirror::ArtMethod* target = GetTargetMethod(patch);
+    uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
+    DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
+    const OatFile* target_oat = class_linker->FindOpenedOatFileForDexFile(*patch->GetTargetDexFile());
+    // Get where the data actually starts. if target is this oat_file_ it is oat_data_start_,
+    // otherwise it is wherever target_oat is loaded.
+    uintptr_t oat_data_addr = GetBaseAddressFor(target_oat);
+    uintptr_t code_base = reinterpret_cast<uintptr_t>(target_oat->Begin());
+    uintptr_t code_offset = quick_code - code_base;
+    bool is_quick_offset = false;
+    if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
+      is_quick_offset = true;
+      code_offset = oat_header_->GetQuickToInterpreterBridgeOffset();
+    } else if (quick_code ==
+        reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
+      CHECK(target->IsNative());
+      is_quick_offset = true;
+      code_offset = oat_header_->GetQuickGenericJniTrampolineOffset();
+    }
+    uintptr_t value;
+    if (patch->IsRelative()) {
+      // value to patch is relative to the location being patched
+      const void* quick_oat_code =
+        class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
+                                         patch->GetReferrerClassDefIdx(),
+                                         patch->GetReferrerMethodIdx());
+      if (is_quick_offset) {
+        // If its a quick offset it means that we are doing a relative patch from the class linker
+        // oat_file to the elf_patcher oat_file so we need to adjust the quick oat code to be the
+        // one in the output oat_file (ie where it is actually going to be loaded).
+        quick_code = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
+        quick_oat_code =
+            reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
+                oat_data_addr - code_base);
+      }
+      uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
+      uintptr_t patch_location = base + patch->GetLiteralOffset();
+      value = quick_code - patch_location + patch->RelativeOffset();
+    } else if (code_offset != 0) {
+      value = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
+    } else {
+      value = 0;
+    }
+    SetPatchLocation(patch, value);
+  }
+
+  const CallPatches& methods_to_patch = compiler_driver_->GetMethodsToPatch();
+  for (size_t i = 0; i < methods_to_patch.size(); i++) {
+    const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
+    mirror::ArtMethod* target = GetTargetMethod(patch);
+    SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+  }
+
+  const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
+      compiler_driver_->GetClassesToPatch();
+  for (size_t i = 0; i < classes_to_patch.size(); i++) {
+    const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
+    mirror::Class* target = GetTargetType(patch);
+    SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+  }
+
+  self->EndAssertNoThreadSuspension(old_cause);
+
+  if (write_patches_) {
+    return WriteOutPatchData();
+  }
+  return true;
+}
+
+bool ElfPatcher::WriteOutPatchData() {
+  Elf32_Shdr* shdr = elf_file_->FindSectionByName(".oat_patches");
+  if (shdr != nullptr) {
+    CHECK_EQ(shdr, elf_file_->FindSectionByType(SHT_OAT_PATCH))
+        << "Incorrect type for .oat_patches section";
+    CHECK_LE(patches_.size() * sizeof(uintptr_t), shdr->sh_size)
+        << "We got more patches than anticipated";
+    CHECK_LE(reinterpret_cast<uintptr_t>(elf_file_->Begin()) + shdr->sh_offset + shdr->sh_size,
+              reinterpret_cast<uintptr_t>(elf_file_->End())) << "section is too large";
+    CHECK(shdr == &elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) ||
+          shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
+        << "Section overlaps onto next section";
+    // It's mmap'd so we can just memcpy.
+    memcpy(elf_file_->Begin() + shdr->sh_offset, patches_.data(),
+           patches_.size() * sizeof(uintptr_t));
+    // TODO We should fill in the newly empty space between the last patch and
+    // the start of the next section by moving the following sections down if
+    // possible.
+    shdr->sh_size = patches_.size() * sizeof(uintptr_t);
+    return true;
+  } else {
+    LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
+    *error_msg_ = "Unable to find section to write patch information to in ";
+    *error_msg_ += elf_file_->GetFile().GetPath();
+    return false;
+  }
+}
+
+}  // namespace art
diff --git a/compiler/elf_patcher.h b/compiler/elf_patcher.h
new file mode 100644 (file)
index 0000000..0a9f0a0
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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_COMPILER_ELF_PATCHER_H_
+#define ART_COMPILER_ELF_PATCHER_H_
+
+#include "base/mutex.h"
+#include "driver/compiler_driver.h"
+#include "elf_file.h"
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "mirror/object.h"
+#include "oat_file.h"
+#include "oat.h"
+#include "os.h"
+
+namespace art {
+
+class ElfPatcher {
+ public:
+  typedef void* (*ImageAddressCallback)(void* data, mirror::Object* obj);
+
+  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+                    const std::string& oat_location,
+                    ImageAddressCallback cb, void* cb_data,
+                    std::string* error_msg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+                    const OatFile* oat_file, uintptr_t oat_data_begin,
+                    ImageAddressCallback cb, void* cb_data,
+                    std::string* error_msg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+                    const std::string& oat_location,
+                    std::string* error_msg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return ElfPatcher::Patch(driver, elf_file, oat_location,
+                             DefaultImageAddressCallback, nullptr, error_msg);
+  }
+
+  static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+                    const OatFile* oat_file, uintptr_t oat_data_begin,
+                    std::string* error_msg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return ElfPatcher::Patch(driver, elf_file, oat_file, oat_data_begin,
+                             DefaultImageAddressCallback, nullptr, error_msg);
+  }
+
+ private:
+  ElfPatcher(const CompilerDriver* driver, ElfFile* elf_file, const OatFile* oat_file,
+             OatHeader* oat_header, uintptr_t oat_data_begin,
+             ImageAddressCallback cb, void* cb_data, std::string* error_msg)
+      : compiler_driver_(driver), elf_file_(elf_file), oat_file_(oat_file),
+        oat_header_(oat_header), oat_data_begin_(oat_data_begin), get_image_address_(cb),
+        cb_data_(cb_data), error_msg_(error_msg),
+        write_patches_(compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {}
+  ~ElfPatcher() {}
+
+  static void* DefaultImageAddressCallback(void* data_unused, mirror::Object* obj) {
+    return static_cast<void*>(obj);
+  }
+
+  bool PatchElf()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  mirror::ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  mirror::Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void AddPatch(uintptr_t off);
+
+  void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Takes the pointer into the oat_file_ and get the pointer in to the ElfFile.
+  uint32_t* GetPatchLocation(uintptr_t patch_ptr);
+
+  bool WriteOutPatchData();
+
+  uintptr_t GetBaseAddressFor(const OatFile* f) {
+    if (f == oat_file_) {
+      return oat_data_begin_;
+    } else {
+      return reinterpret_cast<uintptr_t>(f->Begin());
+    }
+  }
+
+  const CompilerDriver* compiler_driver_;
+
+  // The elf_file containing the oat_data we are patching up
+  ElfFile* elf_file_;
+
+  // The oat_file that is actually loaded.
+  const OatFile* oat_file_;
+
+  // The oat_header_ within the elf_file_
+  OatHeader* oat_header_;
+
+  // Where the elf_file will be loaded during normal runs.
+  uintptr_t oat_data_begin_;
+
+  // Callback to get image addresses.
+  ImageAddressCallback get_image_address_;
+  void* cb_data_;
+
+  std::string* error_msg_;
+  std::vector<uintptr_t> patches_;
+  std::set<uintptr_t> patches_set_;
+  bool write_patches_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElfPatcher);
+};
+
+}  // namespace art
+#endif  // ART_COMPILER_ELF_PATCHER_H_
index fe4fcd4..3005e56 100644 (file)
@@ -80,7 +80,7 @@ TEST_F(ImageTest, WriteRead) {
       t.NewTiming("WriteElf");
       ScopedObjectAccess soa(Thread::Current());
       SafeMap<std::string, std::string> key_value_store;
-      OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, compiler_driver_.get(), &timings,
+      OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), &timings,
                            &key_value_store);
       bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
                                                 !kIsTargetBuild,
index 8ef2964..d102bbc 100644 (file)
@@ -29,6 +29,7 @@
 #include "driver/compiler_driver.h"
 #include "elf_file.h"
 #include "elf_utils.h"
+#include "elf_patcher.h"
 #include "elf_writer.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap.h"
@@ -84,7 +85,7 @@ bool ImageWriter::Write(const std::string& image_filename,
     return false;
   }
   std::string error_msg;
-  oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location, &error_msg);
+  oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
   if (oat_file_ == nullptr) {
     LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
         << ": " << error_msg;
@@ -801,214 +802,35 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
   }
 }
 
-static ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  StackHandleScope<1> hs(Thread::Current());
-  Handle<mirror::DexCache> dex_cache(
-      hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
-  ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
-                                                  patch->GetTargetMethodIdx(),
-                                                  dex_cache,
-                                                  NullHandle<mirror::ClassLoader>(),
-                                                  NullHandle<mirror::ArtMethod>(),
-                                                  patch->GetTargetInvokeType());
-  CHECK(method != NULL)
-    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
-  CHECK(!method->IsRuntimeMethod())
-    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
-  CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
-    << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
-    << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
-    << PrettyMethod(method);
-  return method;
-}
-
-static Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  StackHandleScope<2> hs(Thread::Current());
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
-  Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
-                                           dex_cache, NullHandle<mirror::ClassLoader>());
-  CHECK(klass != NULL)
-    << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
-  CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
-    << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
-    << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
-    << PrettyClass(klass);
-  return klass;
+static OatHeader* GetOatHeaderFromElf(ElfFile* elf) {
+  Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
+  if (data_sec == nullptr) {
+    return nullptr;
+  }
+  return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
 }
 
 void ImageWriter::PatchOatCodeAndMethods(File* elf_file) {
-  std::vector<uintptr_t> patches;
-  std::set<uintptr_t> patches_set;
-  auto maybe_push = [&patches, &patches_set] (uintptr_t p) {
-    if (patches_set.find(p) == patches_set.end()) {
-      patches.push_back(p);
-      patches_set.insert(p);
-    }
-  };
-  const bool add_patches = compiler_driver_.GetCompilerOptions().GetIncludePatchInformation();
-  if (add_patches) {
-    // TODO if we are adding patches the resulting ELF file might have a potentially rather large
-    // amount of free space where patches might have been placed. We should adjust the ELF file to
-    // get rid of this excess space.
-    patches.reserve(compiler_driver_.GetCodeToPatch().size() +
-                    compiler_driver_.GetMethodsToPatch().size() +
-                    compiler_driver_.GetClassesToPatch().size());
-  }
-  uintptr_t loc = 0;
-  Thread* self = Thread::Current();
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
-
-  typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
-  const CallPatches& code_to_patch = compiler_driver_.GetCodeToPatch();
-  for (size_t i = 0; i < code_to_patch.size(); i++) {
-    const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
-    ArtMethod* target = GetTargetMethod(patch);
-    uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
-    DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
-    uintptr_t code_base = reinterpret_cast<uintptr_t>(&oat_file_->GetOatHeader());
-    uintptr_t code_offset = quick_code - code_base;
-    bool is_quick_offset = false;
-    if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
-      is_quick_offset = true;
-      code_offset = quick_to_interpreter_bridge_offset_;
-    } else if (quick_code ==
-        reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
-      CHECK(target->IsNative());
-      is_quick_offset = true;
-      code_offset = quick_generic_jni_trampoline_offset_;
-    }
-    uintptr_t value;
-    if (patch->IsRelative()) {
-      // value to patch is relative to the location being patched
-      const void* quick_oat_code =
-        class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
-                                         patch->GetReferrerClassDefIdx(),
-                                         patch->GetReferrerMethodIdx());
-      if (is_quick_offset) {
-        // If its a quick offset it means that we are doing a relative patch from the class linker
-        // oat_file to the image writer oat_file so we need to adjust the quick oat code to be the
-        // one in the image writer oat_file.
-        quick_code = PointerToLowMemUInt32(GetOatAddress(code_offset));
-        quick_oat_code =
-            reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
-                reinterpret_cast<uintptr_t>(oat_data_begin_) - code_base);
-      }
-      uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
-      uintptr_t patch_location = base + patch->GetLiteralOffset();
-      value = quick_code - patch_location + patch->RelativeOffset();
-    } else {
-      value = PointerToLowMemUInt32(GetOatAddress(code_offset));
-    }
-    SetPatchLocation(patch, value, &loc);
-    if (add_patches && !patch->AsCall()->IsRelative()) {
-      maybe_push(loc);
-    }
-  }
-
-  const CallPatches& methods_to_patch = compiler_driver_.GetMethodsToPatch();
-  for (size_t i = 0; i < methods_to_patch.size(); i++) {
-    const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
-    ArtMethod* target = GetTargetMethod(patch);
-    SetPatchLocation(patch, PointerToLowMemUInt32(GetImageAddress(target)), &loc);
-    if (add_patches && !patch->AsCall()->IsRelative()) {
-      maybe_push(loc);
-    }
+  std::string error_msg;
+  std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file, PROT_READ|PROT_WRITE,
+                                             MAP_SHARED, &error_msg));
+  if (elf.get() == nullptr) {
+    LOG(FATAL) << "Unable patch oat file: " << error_msg;
+    return;
   }
-
-  const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
-      compiler_driver_.GetClassesToPatch();
-  for (size_t i = 0; i < classes_to_patch.size(); i++) {
-    const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
-    Class* target = GetTargetType(patch);
-    SetPatchLocation(patch, PointerToLowMemUInt32(GetImageAddress(target)), &loc);
-    if (add_patches) {
-      maybe_push(loc);
-    }
+  if (!ElfPatcher::Patch(&compiler_driver_, elf.get(), oat_file_,
+                         reinterpret_cast<uintptr_t>(oat_data_begin_),
+                         GetImageAddressCallback, reinterpret_cast<void*>(this),
+                         &error_msg)) {
+    LOG(FATAL) << "unable to patch oat file: " << error_msg;
+    return;
   }
+  OatHeader* oat_header = GetOatHeaderFromElf(elf.get());
+  CHECK(oat_header != nullptr);
+  CHECK(oat_header->IsValid());
 
-  // Update the image header with the new checksum after patching
   ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
-  image_header->SetOatChecksum(oat_file_->GetOatHeader().GetChecksum());
-  self->EndAssertNoThreadSuspension(old_cause);
-
-  // Update the ElfFiles SHT_OAT_PATCH section to include the patches.
-  if (add_patches) {
-    std::string err;
-    // TODO we are mapping in the contents of this file twice. We should be able
-    // to do it only once, which would be better.
-    std::unique_ptr<ElfFile> file(ElfFile::Open(elf_file, true, false, &err));
-    if (file == nullptr) {
-      LOG(ERROR) << err;
-    }
-    Elf32_Shdr* shdr = file->FindSectionByName(".oat_patches");
-    if (shdr != nullptr) {
-      CHECK_EQ(shdr, file->FindSectionByType(SHT_OAT_PATCH))
-          << "Incorrect type for .oat_patches section";
-      CHECK_LE(patches.size() * sizeof(uintptr_t), shdr->sh_size)
-          << "We got more patches than anticipated";
-      CHECK_LE(reinterpret_cast<uintptr_t>(file->Begin()) + shdr->sh_offset + shdr->sh_size,
-               reinterpret_cast<uintptr_t>(file->End())) << "section is too large";
-      CHECK(shdr == &file->GetSectionHeader(file->GetSectionHeaderNum() - 1) ||
-            shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
-          << "Section overlaps onto next section";
-      // It's mmap'd so we can just memcpy.
-      memcpy(file->Begin() + shdr->sh_offset, patches.data(), patches.size()*sizeof(uintptr_t));
-      // TODO We should fill in the newly empty space between the last patch and the start of the
-      // next section by moving the following sections down if possible.
-      shdr->sh_size = patches.size() * sizeof(uintptr_t);
-    } else {
-      LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
-    }
-  }
-}
-
-void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value,
-                                   uintptr_t* patched_ptr) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
-                                                                patch->GetReferrerClassDefIdx(),
-                                                                patch->GetReferrerMethodIdx());
-  OatHeader& oat_header = const_cast<OatHeader&>(oat_file_->GetOatHeader());
-  // TODO: make this Thumb2 specific
-  uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
-  uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset());
-  if (kIsDebugBuild) {
-    if (patch->IsCall()) {
-      const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
-      const DexFile::MethodId& id = cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
-      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
-      uint32_t actual = *patch_location;
-      CHECK(actual == expected || actual == value) << std::hex
-          << "actual=" << actual
-          << "expected=" << expected
-          << "value=" << value;
-    }
-    if (patch->IsType()) {
-      const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
-      const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
-      uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
-      uint32_t actual = *patch_location;
-      CHECK(actual == expected || actual == value) << std::hex
-          << "actual=" << actual
-          << "expected=" << expected
-          << "value=" << value;
-    }
-  }
-  *patch_location = value;
-  oat_header.UpdateChecksum(patch_location, sizeof(value));
-
-  uintptr_t loc = reinterpret_cast<uintptr_t>(patch_location) -
-      (reinterpret_cast<uintptr_t>(oat_file_->Begin()) + oat_header.GetExecutableOffset());
-  CHECK_GT(reinterpret_cast<uintptr_t>(patch_location),
-            reinterpret_cast<uintptr_t>(oat_file_->Begin()) + oat_header.GetExecutableOffset());
-  CHECK_LT(loc, oat_file_->Size() - oat_header.GetExecutableOffset());
-
-  *patched_ptr = loc;
+  image_header->SetOatChecksum(oat_header->GetChecksum());
 }
 
 }  // namespace art
index cf5bc93..e8bcf7f 100644 (file)
@@ -70,6 +70,11 @@ class ImageWriter {
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
+  }
+
   mirror::Object* GetImageAddress(mirror::Object* object) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (object == NULL) {
@@ -159,9 +164,6 @@ class ImageWriter {
   // Patches references in OatFile to expect runtime addresses.
   void PatchOatCodeAndMethods(File* elf_file)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value,
-                        uintptr_t* patched_location)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   const CompilerDriver& compiler_driver_;
 
index 84f0b3c..11d1728 100644 (file)
@@ -120,6 +120,7 @@ TEST_F(OatTest, WriteRead) {
   OatWriter oat_writer(class_linker->GetBootClassPath(),
                        42U,
                        4096U,
+                       0,
                        compiler_driver_.get(),
                        &timings,
                        &key_value_store);
@@ -183,7 +184,7 @@ TEST_F(OatTest, WriteRead) {
 TEST_F(OatTest, OatHeaderSizeCheck) {
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
-  EXPECT_EQ(80U, sizeof(OatHeader));
+  EXPECT_EQ(84U, sizeof(OatHeader));
   EXPECT_EQ(8U, sizeof(OatMethodOffsets));
   EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
   EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
index 63a3c8c..22f36f4 100644 (file)
@@ -50,6 +50,7 @@ namespace art {
 OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
                      uint32_t image_file_location_oat_checksum,
                      uintptr_t image_file_location_oat_begin,
+                     int32_t image_patch_delta,
                      const CompilerDriver* compiler,
                      TimingLogger* timings,
                      SafeMap<std::string, std::string>* key_value_store)
@@ -57,6 +58,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
     dex_files_(&dex_files),
     image_file_location_oat_checksum_(image_file_location_oat_checksum),
     image_file_location_oat_begin_(image_file_location_oat_begin),
+    image_patch_delta_(image_patch_delta),
     key_value_store_(key_value_store),
     oat_header_(NULL),
     size_dex_file_alignment_(0),
@@ -126,6 +128,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
   CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
   CHECK_EQ(compiler->IsImage(),
            key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end());
+  CHECK_ALIGNED(image_patch_delta_, kPageSize);
 }
 
 OatWriter::~OatWriter() {
@@ -808,6 +811,7 @@ size_t OatWriter::InitOatCode(size_t offset) {
   oat_header_->SetExecutableOffset(offset);
   size_executable_offset_alignment_ = offset - old_offset;
   if (compiler_driver_->IsImage()) {
+    CHECK_EQ(image_patch_delta_, 0);
     InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
 
     #define DO_TRAMPOLINE(field, fn_name) \
@@ -840,6 +844,7 @@ size_t OatWriter::InitOatCode(size_t offset) {
     oat_header_->SetQuickImtConflictTrampolineOffset(0);
     oat_header_->SetQuickResolutionTrampolineOffset(0);
     oat_header_->SetQuickToInterpreterBridgeOffset(0);
+    oat_header_->SetImagePatchDelta(image_patch_delta_);
   }
   return offset;
 }
index 3d34956..945048e 100644 (file)
@@ -79,6 +79,7 @@ class OatWriter {
   OatWriter(const std::vector<const DexFile*>& dex_files,
             uint32_t image_file_location_oat_checksum,
             uintptr_t image_file_location_oat_begin,
+            int32_t image_patch_delta,
             const CompilerDriver* compiler,
             TimingLogger* timings,
             SafeMap<std::string, std::string>* key_value_store);
@@ -253,6 +254,7 @@ class OatWriter {
   // dependencies on the image.
   uint32_t image_file_location_oat_checksum_;
   uintptr_t image_file_location_oat_begin_;
+  int32_t image_patch_delta_;
 
   // data to write
   SafeMap<std::string, std::string>* key_value_store_;
index 6d861d4..d6501a1 100644 (file)
@@ -45,6 +45,7 @@
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "elf_fixup.h"
+#include "elf_patcher.h"
 #include "elf_stripper.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space-inl.h"
@@ -324,11 +325,28 @@ class Dex2Oat {
     return ReadImageClasses(image_classes_stream);
   }
 
+  bool PatchOatCode(const CompilerDriver* compiler_driver, File* oat_file,
+                    const std::string& oat_location, std::string* error_msg) {
+    // We asked to include patch information but we are not making an image. We need to fix
+    // everything up manually.
+    std::unique_ptr<ElfFile> elf_file(ElfFile::Open(oat_file, PROT_READ|PROT_WRITE,
+                                                    MAP_SHARED, error_msg));
+    if (elf_file.get() == NULL) {
+      LOG(ERROR) << error_msg;
+      return false;
+    }
+    {
+      ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
+      return ElfPatcher::Patch(compiler_driver, elf_file.get(), oat_location, error_msg);
+    }
+  }
+
   const CompilerDriver* CreateOatFile(const std::string& boot_image_option,
                                       const std::string& android_root,
                                       bool is_host,
                                       const std::vector<const DexFile*>& dex_files,
                                       File* oat_file,
+                                      const std::string& oat_location,
                                       const std::string& bitcode_filename,
                                       bool image,
                                       std::unique_ptr<CompilerDriver::DescriptorSet>& image_classes,
@@ -380,6 +398,7 @@ class Dex2Oat {
     std::string image_file_location;
     uint32_t image_file_location_oat_checksum = 0;
     uintptr_t image_file_location_oat_data_begin = 0;
+    int32_t image_patch_delta = 0;
     if (!driver->IsImage()) {
       TimingLogger::ScopedTiming t3("Loading image checksum", &timings);
       gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
@@ -387,6 +406,7 @@ class Dex2Oat {
       image_file_location_oat_data_begin =
           reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
       image_file_location = image_space->GetImageFilename();
+      image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
     }
 
     if (!image_file_location.empty()) {
@@ -395,6 +415,7 @@ class Dex2Oat {
 
     OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
                          image_file_location_oat_data_begin,
+                         image_patch_delta,
                          driver.get(),
                          &timings,
                          key_value_store);
@@ -405,6 +426,16 @@ class Dex2Oat {
       return nullptr;
     }
 
+    if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
+      t2.NewTiming("Patching ELF");
+      std::string error_msg;
+      if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
+        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+        LOG(ERROR) << "Error was: " << error_msg;
+        return nullptr;
+      }
+    }
+
     return driver.release();
   }
 
@@ -1361,6 +1392,7 @@ static int dex2oat(int argc, char** argv) {
                                                                         is_host,
                                                                         dex_files,
                                                                         oat_file.get(),
+                                                                        oat_location,
                                                                         bitcode_filename,
                                                                         image,
                                                                         image_classes,
@@ -1370,7 +1402,6 @@ static int dex2oat(int argc, char** argv) {
                                                                         compiler_phases_timings,
                                                                         profile_file,
                                                                         key_value_store.get()));
-
   if (compiler.get() == nullptr) {
     LOG(ERROR) << "Failed to create oat file: " << oat_location;
     return EXIT_FAILURE;
@@ -1420,9 +1451,9 @@ static int dex2oat(int argc, char** argv) {
   // memory mapped so we could predict where its contents were based
   // on the file size. Now that it is an ELF file, we need to inspect
   // the ELF file to understand the in memory segment layout including
-  // where the oat header is located within. ImageWriter's
-  // PatchOatCodeAndMethods uses the PatchInformation from the
-  // Compiler to touch up absolute references in the oat file.
+  // where the oat header is located within. ElfPatcher's Patch method
+  // uses the PatchInformation from the Compiler to touch up absolute
+  // references in the oat file.
   //
   // 3. We fixup the ELF program headers so that dlopen will try to
   // load the .so at the desired location at runtime by offsetting the
index b8f20f3..068a450 100644 (file)
@@ -165,6 +165,8 @@ class OatDumper {
                            GetQuickToInterpreterBridgeOffset);
 #undef DUMP_OAT_HEADER_OFFSET
 
+    os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta();
+
     os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
     os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
 
@@ -771,6 +773,8 @@ class ImageDumper {
 
     os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
 
+    os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
+
     {
       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
       Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
@@ -819,10 +823,13 @@ class ImageDumper {
     os << "OAT LOCATION: " << oat_location;
     os << "\n";
     std::string error_msg;
-    const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location, &error_msg);
-    if (oat_file == NULL) {
-      os << "NOT FOUND: " << error_msg << "\n";
-      return;
+    const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+    if (oat_file == nullptr) {
+      oat_file = OatFile::Open(oat_location, oat_location, NULL, false, &error_msg);
+      if (oat_file == nullptr) {
+        os << "NOT FOUND: " << error_msg << "\n";
+        return;
+      }
     }
     os << "\n";
 
index 85b4e6d..55d582f 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/file.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include <string>
 #include <vector>
 
+#include "base/scoped_flock.h"
 #include "base/stringpiece.h"
 #include "base/stringprintf.h"
 #include "elf_utils.h"
@@ -125,7 +128,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta,
              delta, timings);
   t.NewTiming("Patching files");
   if (!p.PatchImage()) {
-    LOG(INFO) << "Failed to patch image file " << input_image->GetPath();
+    LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
     return false;
   }
 
@@ -216,11 +219,11 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o
              delta, timings);
   t.NewTiming("Patching files");
   if (!p.PatchElf()) {
-    LOG(INFO) << "Failed to patch oat file " << input_oat->GetPath();
+    LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
     return false;
   }
   if (!p.PatchImage()) {
-    LOG(INFO) << "Failed to patch image file " << input_image->GetPath();
+    LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
     return false;
   }
 
@@ -236,6 +239,12 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o
 
 bool PatchOat::WriteElf(File* out) {
   TimingLogger::ScopedTiming t("Writing Elf File", timings_);
+  std::string error_msg;
+
+  // Lock the output file.
+  ScopedFlock flock;
+  flock.Init(out, &error_msg);
+
   CHECK(oat_file_.get() != nullptr);
   CHECK(out != nullptr);
   size_t expect = oat_file_->Size();
@@ -250,6 +259,12 @@ bool PatchOat::WriteElf(File* out) {
 
 bool PatchOat::WriteImage(File* out) {
   TimingLogger::ScopedTiming t("Writing image File", timings_);
+  std::string error_msg;
+
+  // Lock the output file.
+  ScopedFlock flock;
+  flock.Init(out, &error_msg);
+
   CHECK(image_ != nullptr);
   CHECK(out != nullptr);
   size_t expect = image_->Size();
@@ -437,19 +452,50 @@ bool PatchOat::CheckOatFile() {
   return true;
 }
 
+bool PatchOat::PatchOatHeader() {
+  Elf32_Shdr *rodata_sec = oat_file_->FindSectionByName(".rodata");
+  if (rodata_sec == nullptr) {
+    return false;
+  }
+  OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file_->Begin() + rodata_sec->sh_offset);
+  if (!oat_header->IsValid()) {
+    LOG(ERROR) << "Elf file " << oat_file_->GetFile().GetPath() << " has an invalid oat header";
+    return false;
+  }
+  oat_header->RelocateOat(delta_);
+  return true;
+}
+
 bool PatchOat::PatchElf() {
-  TimingLogger::ScopedTiming t("Fixup Elf Headers", timings_);
+  TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
+  if (!PatchTextSection()) {
+    return false;
+  }
+
+  if (!PatchOatHeader()) {
+    return false;
+  }
+
+  bool need_fixup = false;
+  t.NewTiming("Fixup Elf Headers");
   // Fixup Phdr's
   for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) {
     Elf32_Phdr& hdr = oat_file_->GetProgramHeader(i);
-    if (hdr.p_vaddr != 0) {
+    if (hdr.p_vaddr != 0 && hdr.p_vaddr != hdr.p_offset) {
+      need_fixup = true;
       hdr.p_vaddr += delta_;
     }
-    if (hdr.p_paddr != 0) {
+    if (hdr.p_paddr != 0 && hdr.p_paddr != hdr.p_offset) {
+      need_fixup = true;
       hdr.p_paddr += delta_;
     }
   }
-  // Fixup Shdr's
+  if (!need_fixup) {
+    // This was never passed through ElfFixup so all headers/symbols just have their offset as
+    // their addr. Therefore we do not need to update these parts.
+    return true;
+  }
+  t.NewTiming("Fixup Section Headers");
   for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) {
     Elf32_Shdr& hdr = oat_file_->GetSectionHeader(i);
     if (hdr.sh_addr != 0) {
@@ -457,7 +503,7 @@ bool PatchOat::PatchElf() {
     }
   }
 
-  // Fixup Dynamics.
+  t.NewTiming("Fixup Dynamics");
   for (Elf32_Word i = 0; i < oat_file_->GetDynamicNum(); i++) {
     Elf32_Dyn& dyn = oat_file_->GetDynamic(i);
     if (IsDynamicSectionPointer(dyn.d_tag, oat_file_->GetHeader().e_machine)) {
@@ -481,12 +527,6 @@ bool PatchOat::PatchElf() {
     }
   }
 
-  t.NewTiming("Fixup Elf Text Section");
-  // Fixup text
-  if (!PatchTextSection()) {
-    return false;
-  }
-
   return true;
 }
 
@@ -511,7 +551,7 @@ bool PatchOat::PatchSymbols(Elf32_Shdr* section) {
 bool PatchOat::PatchTextSection() {
   Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
   if (patches_sec == nullptr) {
-    LOG(INFO) << ".oat_patches section not found. Aborting patch";
+    LOG(ERROR) << ".oat_patches section not found. Aborting patch";
     return false;
   }
   DCHECK(CheckOatFile()) << "Oat file invalid";
@@ -614,7 +654,8 @@ static void Usage(const char *fmt, ...) {
   UsageError("");
   UsageError("  --patched-image-location=<file.art>: Use the same patch delta as was used to");
   UsageError("      patch the given image location. If used one must also specify the");
-  UsageError("      --instruction-set flag.");
+  UsageError("      --instruction-set flag. It will search for this image in the same way that");
+  UsageError("      is done when loading one.");
   UsageError("");
   UsageError("  --dump-timings: dump out patch timing information");
   UsageError("");
@@ -909,7 +950,25 @@ static int patchoat(int argc, char **argv) {
     if (!isa_set) {
       Usage("specifying a location requires specifying an instruction set");
     }
-    patched_image_filename = GetSystemImageFilename(patched_image_location.c_str(), isa);
+    std::string system_filename;
+    bool has_system = false;
+    std::string cache_filename;
+    bool has_cache = false;
+    bool has_android_data_unused = false;
+    if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
+                                                  &system_filename, &has_system, &cache_filename,
+                                                  &has_android_data_unused, &has_cache)) {
+      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+    }
+    if (has_cache) {
+      patched_image_filename = cache_filename;
+    } else if (has_system) {
+      LOG(WARNING) << "Only image file found was in /system for image location "
+                   << patched_image_location;
+      patched_image_filename = system_filename;
+    } else {
+      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+    }
     if (debug) {
       LOG(INFO) << "Using patched-image-file " << patched_image_filename;
     }
@@ -969,6 +1028,14 @@ static int patchoat(int argc, char **argv) {
 
     if (output_oat_fd != -1) {
       output_oat.reset(new File(output_oat_fd, output_oat_filename));
+    } else if (output_oat_filename == input_oat_filename) {
+      // This could be a wierd situation, since we'd be writting from an mmap'd copy of this file.
+      // Lets just unlink it.
+      if (0 != unlink(input_oat_filename.c_str())) {
+        PLOG(ERROR) << "Could not unlink " << input_oat_filename << " to make room for output";
+        return false;
+      }
+      output_oat.reset(OS::CreateEmptyFile(output_oat_filename.c_str()));
     } else {
       CHECK(!output_oat_filename.empty());
       output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
@@ -994,7 +1061,9 @@ static int patchoat(int argc, char **argv) {
   };
 
   if (debug) {
-    LOG(INFO) << "moving offset by " << base_delta << " (0x" << std::hex << base_delta << ") bytes";
+    LOG(INFO) << "moving offset by " << base_delta
+              << " (0x" << std::hex << base_delta << ") bytes or "
+              << std::dec << (base_delta/kPageSize) << " pages.";
   }
 
   bool ret;
@@ -1011,6 +1080,7 @@ static int patchoat(int argc, char **argv) {
     ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
   }
   cleanup(ret);
+  sync();
   return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
index a63e6f4..6960d3b 100644 (file)
@@ -79,6 +79,7 @@ class PatchOat {
   // Patches oat in place, modifying the oat_file given to the constructor.
   bool PatchElf();
   bool PatchTextSection();
+  bool PatchOatHeader();
   bool PatchSymbols(Elf32_Shdr* section);
 
   bool PatchImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
index 351de3d..bf091d0 100644 (file)
@@ -58,6 +58,22 @@ bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
   }
 }
 
+bool ScopedFlock::Init(File* file, std::string* error_msg) {
+  file_.reset(new File(dup(file->Fd())));
+  if (file_->Fd() == -1) {
+    file_.reset();
+    *error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
+                              file->GetPath().c_str(), strerror(errno));
+    return false;
+  }
+  if (0 != TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX))) {
+    file_.reset();
+    *error_msg = StringPrintf("Failed to lock file '%s': %s", file->GetPath().c_str(), strerror(errno));
+    return false;
+  }
+  return true;
+}
+
 File* ScopedFlock::GetFile() {
   CHECK(file_.get() != NULL);
   return file_.get();
index f8ed805..08612e3 100644 (file)
@@ -37,6 +37,10 @@ class ScopedFlock {
   // changed (usually due to a new file being created at the same path)
   // between attempts to lock it.
   bool Init(const char* filename, std::string* error_msg);
+  // Attempt to acquire an exclusive file lock (see flock(2)) on 'file'.
+  // Returns true if the lock could be acquired or false if an error
+  // occured.
+  bool Init(File* file, std::string* error_msg);
 
   // Returns the (locked) file associated with this instance.
   File* GetFile();
@@ -45,6 +49,7 @@ class ScopedFlock {
   bool HasFile();
 
   ~ScopedFlock();
+
  private:
   std::unique_ptr<File> file_;
   DISALLOW_COPY_AND_ASSIGN(ScopedFlock);
index 3364491..753f2f0 100644 (file)
@@ -814,6 +814,7 @@ bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_
         return false;
       }
 
+      // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
       open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
                                                              oat_location, &error_msg));
 
@@ -938,6 +939,13 @@ const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_l
                               actual_image_oat_offset);
     return nullptr;
   }
+  int32_t expected_patch_delta = image_header.GetPatchDelta();
+  int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+  if (expected_patch_delta != actual_patch_delta) {
+    *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
+                              " found %d", oat_location, expected_patch_delta, actual_patch_delta);
+    return nullptr;
+  }
   const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
                                                                     &dex_location_checksum);
   if (oat_dex_file == nullptr) {
@@ -998,19 +1006,23 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
   // image header from the image for the right instruction set.
   uint32_t image_oat_checksum = 0;
   uintptr_t image_oat_data_begin = 0;
-  if (instruction_set == kRuntimeISA) {
+  int32_t image_patch_delta = 0;
+  if (instruction_set == Runtime::Current()->GetInstructionSet()) {
     const ImageHeader& image_header = image_space->GetImageHeader();
     image_oat_checksum = image_header.GetOatChecksum();
     image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
+    image_patch_delta = image_header.GetPatchDelta();
   } else {
     std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
         image_space->GetImageLocation().c_str(), instruction_set));
     image_oat_checksum = image_header->GetOatChecksum();
     image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
+    image_patch_delta = image_header->GetPatchDelta();
   }
   const OatHeader& oat_header = oat_file->GetOatHeader();
   bool image_check = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
-                      && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+                      && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin)
+                      && (oat_header.GetImagePatchDelta() == image_patch_delta));
 
   const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
                                                                     &dex_location_checksum);
@@ -1049,16 +1061,11 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
   return false;
 }
 
-const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_file_location,
-                                                        const char* dex_location,
-                                                        std::string* error_msg,
-                                                        bool* open_failed) {
-  std::unique_ptr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg));
-  if (oat_file.get() == nullptr) {
-    *open_failed = true;
-    return nullptr;
-  }
-  *open_failed = false;
+bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
+                                       const char* dex_location,
+                                       std::string* error_msg) {
+  CHECK(oat_file != nullptr);
+  CHECK(dex_location != nullptr);
   std::unique_ptr<const DexFile> dex_file;
   uint32_t dex_location_checksum;
   if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
@@ -1068,26 +1075,21 @@ const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_f
     const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, NULL);
     if (oat_dex_file == nullptr) {
       *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
-                                "dex file '%s': %s", oat_file_location.c_str(), dex_location,
+                                "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
                                 error_msg->c_str());
-      return nullptr;
+      return false;
     }
     dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
   } else {
-    bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum,
+    bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum,
                                            kRuntimeISA, error_msg);
     if (!verified) {
-      return nullptr;
+      return false;
     }
     dex_file.reset(oat_file->GetOatDexFile(dex_location,
                                            &dex_location_checksum)->OpenDexFile(error_msg));
   }
-
-  if (dex_file.get() != nullptr) {
-    return oat_file.release();
-  } else {
-    return nullptr;
-  }
+  return dex_file.get() != nullptr;
 }
 
 const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
@@ -1097,51 +1099,25 @@ const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
     std::vector<std::string>* error_msgs,
     bool* obsolete_file_cleanup_failed) {
   *obsolete_file_cleanup_failed = false;
-  // Look for an existing file next to dex. for example, for
-  // /foo/bar/baz.jar, look for /foo/bar/<isa>/baz.odex.
-  std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
-  bool open_failed;
+  bool already_opened = false;
+  std::string dex_location_str(dex_location);
+  std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
+                                                                     &already_opened,
+                                                                     obsolete_file_cleanup_failed,
+                                                                     error_msgs));
   std::string error_msg;
-  const OatFile* oat_file = LoadOatFileAndVerifyDexFile(odex_filename, dex_location, &error_msg,
-                                                        &open_failed);
-  if (oat_file != nullptr) {
-    return oat_file;
-  }
-  if (dex_location_checksum == nullptr) {
-    error_msgs->push_back(StringPrintf("Failed to open oat file from %s and no classes.dex found in"
-                                      "%s: %s", odex_filename.c_str(), dex_location,
+  if (oat_file.get() == nullptr) {
+    error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
+                                       dex_location));
+    return nullptr;
+  } else if (!VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+    error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
+                                       "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
                                        error_msg.c_str()));
     return nullptr;
+  } else {
+    return oat_file.release();
   }
-
-  std::string cache_error_msg;
-  const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
-  std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location,
-                                                         dalvik_cache.c_str()));
-  oat_file = LoadOatFileAndVerifyDexFile(cache_location, dex_location, &cache_error_msg,
-                                         &open_failed);
-  if (oat_file != nullptr) {
-    return oat_file;
-  }
-
-  if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
-    std::string error_msg = StringPrintf("Failed to remove obsolete file from %s when searching"
-                                         "for dex file %s: %s",
-                                         cache_location.c_str(), dex_location, strerror(errno));
-    error_msgs->push_back(error_msg);
-    VLOG(class_linker) << error_msg;
-    // Let the caller know that we couldn't remove the obsolete file.
-    // This is a good indication that further writes may fail as well.
-    *obsolete_file_cleanup_failed = true;
-  }
-
-  std::string compound_msg = StringPrintf("Failed to open oat file from %s (error '%s') or %s "
-                                          "(error '%s').", odex_filename.c_str(), error_msg.c_str(),
-                                          cache_location.c_str(), cache_error_msg.c_str());
-  VLOG(class_linker) << compound_msg;
-  error_msgs->push_back(compound_msg);
-
-  return nullptr;
 }
 
 const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
@@ -1156,6 +1132,277 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string&
   return nullptr;
 }
 
+const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
+                                                       InstructionSet isa,
+                                                       bool *already_opened,
+                                                       bool *obsolete_file_cleanup_failed,
+                                                       std::vector<std::string>* error_msgs) {
+  // Find out if we've already opened the file
+  const OatFile* ret = nullptr;
+  std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
+  ret = FindOpenedOatFileFromOatLocation(odex_filename);
+  if (ret != nullptr) {
+    *already_opened = true;
+    return ret;
+  }
+
+  std::string dalvik_cache;
+  bool have_android_data = false;
+  bool have_dalvik_cache = false;
+  GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
+                 &have_android_data, &have_dalvik_cache);
+  std::string cache_filename;
+  if (have_dalvik_cache) {
+    cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
+    ret = FindOpenedOatFileFromOatLocation(cache_filename);
+    if (ret != nullptr) {
+      *already_opened = true;
+      return ret;
+    }
+  } else {
+    // If we need to relocate we should just place odex back where it started.
+    cache_filename = odex_filename;
+  }
+
+  ret = nullptr;
+
+  // We know that neither the odex nor the cache'd version is already in use, if it even exists.
+  //
+  // Now we do the following:
+  // 1) Try and open the odex version
+  // 2) If present, checksum-verified & relocated correctly return it
+  // 3) Close the odex version to free up its address space.
+  // 4) Try and open the cache version
+  // 5) If present, checksum-verified & relocated correctly return it
+  // 6) Close the cache version to free up its address space.
+  // 7) If we should relocate:
+  //   a) If we have opened and checksum-verified the odex version relocate it to
+  //      'cache_filename' and return it
+  //   b) If we have opened and checksum-verified the cache version relocate it in place and return
+  //      it. This should not happen often (I think only the run-test's will hit this case).
+  // 8) If the cache-version was present we should delete it since it must be obsolete if we get to
+  //    this point.
+  // 9) Return nullptr
+
+  *already_opened = false;
+  const Runtime* runtime = Runtime::Current();
+  CHECK(runtime != nullptr);
+  bool executable = !runtime->IsCompiler();
+
+  std::string odex_error_msg;
+  bool should_patch_system = false;
+  bool odex_checksum_verified = false;
+  {
+    // There is a high probability that these both these oat files map similar/the same address
+    // spaces so we must scope them like this so they each gets its turn.
+    std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, NULL,
+                                                         executable, &odex_error_msg));
+    if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
+                                                       &odex_checksum_verified,
+                                                       &odex_error_msg)) {
+      error_msgs->push_back(odex_error_msg);
+      return odex_oat_file.release();
+    } else if (odex_checksum_verified) {
+      // We can just relocate
+      should_patch_system = true;
+      odex_error_msg = "Image Patches are incorrect";
+    }
+  }
+
+
+  std::string cache_error_msg;
+  bool should_patch_cache = false;
+  bool cache_checksum_verified = false;
+  if (have_dalvik_cache) {
+    std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, NULL,
+                                                          executable, &cache_error_msg));
+    if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
+                                                        &cache_checksum_verified,
+                                                        &cache_error_msg)) {
+      error_msgs->push_back(cache_error_msg);
+      return cache_oat_file.release();
+    } else if (cache_checksum_verified) {
+      // We can just relocate
+      should_patch_cache = true;
+      cache_error_msg = "Image Patches are incorrect";
+    }
+  } else if (have_android_data) {
+    // dalvik_cache does not exist but android data does. This means we should be able to create
+    // it, so we should try.
+    GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
+  }
+
+  ret = nullptr;
+  std::string error_msg;
+  if (runtime->CanRelocate()) {
+    // Run relocation
+    const std::string& image_location =
+        Runtime::Current()->GetHeap()->GetImageSpace()->GetImageLocation();
+    if (odex_checksum_verified && should_patch_system) {
+      ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
+    } else if (cache_checksum_verified && should_patch_cache) {
+      CHECK(have_dalvik_cache);
+      ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
+    }
+  }
+  if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
+    // implicitly: were able to fine where the cached version is but we were unable to use it,
+    // either as a destination for relocation or to open a file. We should delete it if it is
+    // there.
+    if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
+      std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
+                                              "searching for dex file %s: %s",
+                                              cache_filename.c_str(), dex_location.c_str(),
+                                              strerror(errno));
+      error_msgs->push_back(rm_error_msg);
+      VLOG(class_linker) << rm_error_msg;
+      // Let the caller know that we couldn't remove the obsolete file.
+      // This is a good indication that further writes may fail as well.
+      *obsolete_file_cleanup_failed = true;
+    }
+  }
+  if (ret == nullptr) {
+    VLOG(class_linker) << error_msg;
+    error_msgs->push_back(error_msg);
+    std::string relocation_msg;
+    if (runtime->CanRelocate()) {
+      relocation_msg = StringPrintf(" and relocation failed");
+    }
+    if (have_dalvik_cache && cache_checksum_verified) {
+      error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
+                                "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
+                                cache_filename.c_str(), cache_error_msg.c_str(),
+                                relocation_msg.c_str());
+    } else {
+      error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
+                               "dalvik_cache availible)%s.", odex_filename.c_str(),
+                               odex_error_msg.c_str(), relocation_msg.c_str());
+    }
+    VLOG(class_linker) << error_msg;
+    error_msgs->push_back(error_msg);
+  }
+  return ret;
+}
+
+const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
+                                                const std::string& output_oat,
+                                                const std::string& image_location,
+                                                InstructionSet isa,
+                                                std::string* error_msg) {
+  Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
+  std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+
+  std::string isa_arg("--instruction-set=");
+  isa_arg += GetInstructionSetString(isa);
+  std::string input_oat_filename_arg("--input-oat-file=");
+  input_oat_filename_arg += input_oat;
+  std::string output_oat_filename_arg("--output-oat-file=");
+  output_oat_filename_arg += output_oat;
+  std::string patched_image_arg("--patched-image-location=");
+  patched_image_arg += image_location;
+
+  std::vector<std::string> argv;
+  argv.push_back(patchoat);
+  argv.push_back(isa_arg);
+  argv.push_back(input_oat_filename_arg);
+  argv.push_back(output_oat_filename_arg);
+  argv.push_back(patched_image_arg);
+
+  std::string command_line(Join(argv, ' '));
+  LOG(INFO) << "Relocate Oat File: " << command_line;
+  bool success = Exec(argv, error_msg);
+  if (success) {
+    std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, NULL,
+                                                  !Runtime::Current()->IsCompiler(), error_msg));
+    bool checksum_verified = false;
+    if (output.get() != nullptr && CheckOatFile(output.get(), isa, &checksum_verified, error_msg)) {
+      return output.release();
+    } else if (output.get() != nullptr) {
+      *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
+                                "but output file '%s' failed verifcation: %s",
+                                input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
+    } else {
+      *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
+                                "but was unable to open output file '%s': %s",
+                                input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
+    }
+  } else {
+    *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
+                              "failed: %s", input_oat.c_str(), output_oat.c_str(),
+                              error_msg->c_str());
+  }
+  return nullptr;
+}
+
+int32_t ClassLinker::GetRequiredDelta(const OatFile* oat_file, InstructionSet isa) {
+  Runtime* runtime = Runtime::Current();
+  int32_t real_patch_delta;
+  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+  if (isa == Runtime::Current()->GetInstructionSet()) {
+    const ImageHeader& image_header = image_space->GetImageHeader();
+    real_patch_delta = image_header.GetPatchDelta();
+  } else {
+    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+        image_space->GetImageLocation().c_str(), isa));
+    real_patch_delta = image_header->GetPatchDelta();
+  }
+  const OatHeader& oat_header = oat_file->GetOatHeader();
+  return real_patch_delta - oat_header.GetImagePatchDelta();
+}
+
+bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+                               bool* checksum_verified,
+                               std::string* error_msg) {
+  std::string compound_msg("Oat file failed to verify: ");
+  Runtime* runtime = Runtime::Current();
+  uint32_t real_image_checksum;
+  void* real_image_oat_offset;
+  int32_t real_patch_delta;
+  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+  if (isa == Runtime::Current()->GetInstructionSet()) {
+    const ImageHeader& image_header = image_space->GetImageHeader();
+    real_image_checksum = image_header.GetOatChecksum();
+    real_image_oat_offset = image_header.GetOatDataBegin();
+    real_patch_delta = image_header.GetPatchDelta();
+  } else {
+    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+        image_space->GetImageLocation().c_str(), isa));
+    real_image_checksum = image_header->GetOatChecksum();
+    real_image_oat_offset = image_header->GetOatDataBegin();
+    real_patch_delta = image_header->GetPatchDelta();
+  }
+
+  const OatHeader& oat_header = oat_file->GetOatHeader();
+
+  uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
+  *checksum_verified = oat_image_checksum == real_image_checksum;
+  if (!*checksum_verified) {
+    compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)",
+                                 real_image_checksum, oat_image_checksum);
+  }
+
+  void* oat_image_oat_offset =
+      reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
+  bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
+  if (!offset_verified) {
+    compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
+                                 real_image_oat_offset, oat_image_oat_offset);
+  }
+
+  int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+  bool patch_delta_verified = oat_patch_delta == real_patch_delta;
+  if (!patch_delta_verified) {
+    compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
+                                 real_patch_delta, oat_patch_delta);
+  }
+
+  bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
+  if (ret) {
+    *error_msg = compound_msg;
+  }
+  return ret;
+}
+
 const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
                                                        std::string* error_msg) {
   const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
index c17f88d..b108f61 100644 (file)
@@ -265,10 +265,6 @@ class ClassLinker {
                        std::string* error_msg)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
-  const OatFile* FindOatFileFromOatLocation(const std::string& location,
-                                            std::string* error_msg)
-      LOCKS_EXCLUDED(dex_lock_);
-
   // Find or create the oat file holding dex_location. Then load all corresponding dex files
   // (if multidex) into the given vector.
   bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
@@ -546,9 +542,31 @@ class ClassLinker {
   const OatFile* FindOpenedOatFile(const char* oat_location, const char* dex_location,
                                    const uint32_t* const dex_location_checksum)
       LOCKS_EXCLUDED(dex_lock_);
+
+  // Will open the oat file directly without relocating, even if we could/should do relocation.
+  const OatFile* FindOatFileFromOatLocation(const std::string& oat_location,
+                                            std::string* error_msg)
+      LOCKS_EXCLUDED(dex_lock_);
+
   const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
       LOCKS_EXCLUDED(dex_lock_);
 
+  const OatFile* OpenOatFileFromDexLocation(const std::string& dex_location,
+                                            InstructionSet isa,
+                                            bool* already_opened,
+                                            bool* obsolete_file_cleanup_failed,
+                                            std::vector<std::string>* error_msg)
+      LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
+
+  const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
+                                     const std::string& image_location, InstructionSet isa,
+                                     std::string* error_msg)
+      LOCKS_EXCLUDED(Locks::mutator_lock_);
+
+  bool CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+                    bool* checksum_verified, std::string* error_msg);
+  int32_t GetRequiredDelta(const OatFile* oat_file, InstructionSet isa);
+
   // Note: will not register the oat file.
   const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
                                                     uint32_t dex_location_checksum,
@@ -575,14 +593,10 @@ class ClassLinker {
                                                              bool* obsolete_file_cleanup_failed)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
-  // Find a verify an oat file with the given dex file. Will return nullptr when the oat file
-  // was not found or the dex file could not be verified.
-  // Note: Does not register the oat file.
-  const OatFile* LoadOatFileAndVerifyDexFile(const std::string& oat_file_location,
-                                             const char* dex_location,
-                                             std::string* error_msg,
-                                             bool* open_failed)
-      LOCKS_EXCLUDED(dex_lock_);
+  // verify an oat file with the given dex file. Will return false when the dex file could not be
+  // verified. Will return true otherwise.
+  bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
+                            std::string* error_msg);
 
   mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass,
                                             mirror::Class* proxy_class)
@@ -720,6 +734,8 @@ class ClassLinker {
   const void* quick_to_interpreter_bridge_trampoline_;
 
   friend class ImageWriter;  // for GetClassRoots
+  friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
+  friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
   FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
   FRIEND_TEST(mirror::DexCacheTest, Open);
   FRIEND_TEST(ExceptionTest, FindExceptionHandler);
index f47f13d..2826f89 100644 (file)
@@ -199,12 +199,13 @@ void CommonRuntimeTest::SetUp() {
   runtime_->GetHeap()->VerifyHeap();  // Check for heap corruption before the test
 }
 
-void CommonRuntimeTest::TearDown() {
-  const char* android_data = getenv("ANDROID_DATA");
-  ASSERT_TRUE(android_data != nullptr);
-  DIR* dir = opendir(dalvik_cache_.c_str());
+
+void CommonRuntimeTest::ClearDirectory(const char* dirpath) {
+  ASSERT_TRUE(dirpath != nullptr);
+  DIR* dir = opendir(dirpath);
   ASSERT_TRUE(dir != nullptr);
   dirent* e;
+  struct stat s;
   while ((e = readdir(dir)) != nullptr) {
     if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
       continue;
@@ -212,10 +213,24 @@ void CommonRuntimeTest::TearDown() {
     std::string filename(dalvik_cache_);
     filename.push_back('/');
     filename.append(e->d_name);
-    int unlink_result = unlink(filename.c_str());
-    ASSERT_EQ(0, unlink_result);
+    int stat_result = lstat(filename.c_str(), &s);
+    ASSERT_EQ(0, stat_result) << "unable to stat " << filename;
+    if (S_ISDIR(s.st_mode)) {
+      ClearDirectory(filename.c_str());
+      int rmdir_result = rmdir(filename.c_str());
+      ASSERT_EQ(0, rmdir_result) << filename;
+    } else {
+      int unlink_result = unlink(filename.c_str());
+      ASSERT_EQ(0, unlink_result) << filename;
+    }
   }
   closedir(dir);
+}
+
+void CommonRuntimeTest::TearDown() {
+  const char* android_data = getenv("ANDROID_DATA");
+  ASSERT_TRUE(android_data != nullptr);
+  ClearDirectory(dalvik_cache_.c_str());
   int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
   ASSERT_EQ(0, rmdir_cache_result);
   int rmdir_data_result = rmdir(android_data_.c_str());
index d045031..eb96352 100644 (file)
@@ -81,6 +81,8 @@ class CommonRuntimeTest : public testing::Test {
   // Allow subclases such as CommonCompilerTest to add extra options.
   virtual void SetUpRuntimeOptions(RuntimeOptions* options) {}
 
+  void ClearDirectory(const char* dirpath);
+
   virtual void TearDown();
 
   std::string GetLibCoreDexFileName();
index b07043f..d1a6861 100644 (file)
@@ -36,6 +36,10 @@ class CompilerCallbacks {
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
     virtual void ClassRejected(ClassReference ref) = 0;
 
+    // Return true if we should attempt to relocate to a random base address if we have not already
+    // done so. Return false if relocating in this way would be problematic.
+    virtual bool IsRelocationPossible() = 0;
+
   protected:
     CompilerCallbacks() { }
 };
index a87aa89..3eaa64c 100644 (file)
@@ -16,6 +16,8 @@
 
 #include "image_space.h"
 
+#include <random>
+
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "base/scoped_flock.h"
@@ -94,86 +96,304 @@ static bool GenerateImage(const std::string& image_filename, std::string* error_
 
 bool ImageSpace::FindImageFilename(const char* image_location,
                                    const InstructionSet image_isa,
-                                   std::string* image_filename,
-                                   bool *is_system) {
+                                   std::string* system_filename,
+                                   bool* has_system,
+                                   std::string* cache_filename,
+                                   bool* dalvik_cache_exists,
+                                   bool* has_cache) {
+  *has_system = false;
+  *has_cache = false;
   // image_location = /system/framework/boot.art
   // system_image_location = /system/framework/<image_isa>/boot.art
   std::string system_image_filename(GetSystemImageFilename(image_location, image_isa));
   if (OS::FileExists(system_image_filename.c_str())) {
-    *image_filename = system_image_filename;
-    *is_system = true;
+    *system_filename = system_image_filename;
+    *has_system = true;
+  }
+
+  bool have_android_data = false;
+  *dalvik_cache_exists = false;
+  std::string dalvik_cache;
+  GetDalvikCache(GetInstructionSetString(image_isa), true, &dalvik_cache,
+                 &have_android_data, dalvik_cache_exists);
+
+  if (have_android_data && *dalvik_cache_exists) {
+    // Always set output location even if it does not exist,
+    // so that the caller knows where to create the image.
+    //
+    // image_location = /system/framework/boot.art
+    // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
+    std::string error_msg;
+    if (!GetDalvikCacheFilename(image_location, dalvik_cache.c_str(), cache_filename, &error_msg)) {
+      LOG(WARNING) << error_msg;
+      return *has_system;
+    }
+    *has_cache = OS::FileExists(cache_filename->c_str());
+  }
+  return *has_system || *has_cache;
+}
+
+static bool ReadSpecificImageHeader(const char* filename, ImageHeader* image_header) {
+    std::unique_ptr<File> image_file(OS::OpenFileForReading(filename));
+    if (image_file.get() == nullptr) {
+      return false;
+    }
+    const bool success = image_file->ReadFully(image_header, sizeof(ImageHeader));
+    if (!success || !image_header->IsValid()) {
+      return false;
+    }
     return true;
+}
+
+static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
+  CHECK_ALIGNED(min_delta, kPageSize);
+  CHECK_ALIGNED(max_delta, kPageSize);
+  CHECK_LT(min_delta, max_delta);
+
+  std::default_random_engine generator;
+  generator.seed(NanoTime() * getpid());
+  std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
+  int32_t r = distribution(generator);
+  if (r % 2 == 0) {
+    r = RoundUp(r, kPageSize);
+  } else {
+    r = RoundDown(r, kPageSize);
   }
+  CHECK_LT(min_delta, r);
+  CHECK_GT(max_delta, r);
+  CHECK_ALIGNED(r, kPageSize);
+  return r;
+}
 
-  const std::string dalvik_cache = GetDalvikCacheOrDie(GetInstructionSetString(image_isa));
+bool ImageSpace::RelocateImage(const char* image_location, const char* dest_filename,
+                               InstructionSet isa, std::string* error_msg) {
+  std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
 
-  // Always set output location even if it does not exist,
-  // so that the caller knows where to create the image.
-  //
-  // image_location = /system/framework/boot.art
-  // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
-  *image_filename = GetDalvikCacheFilenameOrDie(image_location, dalvik_cache.c_str());
-  *is_system = false;
-  return OS::FileExists(image_filename->c_str());
+  std::string input_image_location_arg("--input-image-location=");
+  input_image_location_arg += image_location;
+
+  std::string output_image_filename_arg("--output-image-file=");
+  output_image_filename_arg += dest_filename;
+
+  std::string input_oat_location_arg("--input-oat-location=");
+  input_oat_location_arg += ImageHeader::GetOatLocationFromImageLocation(image_location);
+
+  std::string output_oat_filename_arg("--output-oat-file=");
+  output_oat_filename_arg += ImageHeader::GetOatLocationFromImageLocation(dest_filename);
+
+  std::string instruction_set_arg("--instruction-set=");
+  instruction_set_arg += GetInstructionSetString(isa);
+
+  std::string base_offset_arg("--base-offset-delta=");
+  StringAppendF(&base_offset_arg, "%d", ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
+                                                                    ART_BASE_ADDRESS_MAX_DELTA));
+
+  std::vector<std::string> argv;
+  argv.push_back(patchoat);
+
+  argv.push_back(input_image_location_arg);
+  argv.push_back(output_image_filename_arg);
+
+  argv.push_back(input_oat_location_arg);
+  argv.push_back(output_oat_filename_arg);
+
+  argv.push_back(instruction_set_arg);
+  argv.push_back(base_offset_arg);
+
+  std::string command_line(Join(argv, ' '));
+  LOG(INFO) << "RelocateImage: " << command_line;
+  return Exec(argv, error_msg);
+}
+
+static ImageHeader* ReadSpecificImageHeaderOrDie(const char* filename) {
+  std::unique_ptr<ImageHeader> hdr(new ImageHeader);
+  if (!ReadSpecificImageHeader(filename, hdr.get())) {
+    LOG(FATAL) << "Unable to read image header for " << filename;
+    return nullptr;
+  }
+  return hdr.release();
 }
 
 ImageHeader* ImageSpace::ReadImageHeaderOrDie(const char* image_location,
                                               const InstructionSet image_isa) {
-  std::string image_filename;
-  bool is_system = false;
-  if (FindImageFilename(image_location, image_isa, &image_filename, &is_system)) {
-    std::unique_ptr<File> image_file(OS::OpenFileForReading(image_filename.c_str()));
-    std::unique_ptr<ImageHeader> image_header(new ImageHeader);
-    const bool success = image_file->ReadFully(image_header.get(), sizeof(ImageHeader));
-    if (!success || !image_header->IsValid()) {
-      LOG(FATAL) << "Invalid Image header for: " << image_filename;
-      return nullptr;
+  std::string system_filename;
+  bool has_system = false;
+  std::string cache_filename;
+  bool has_cache = false;
+  bool dalvik_cache_exists = false;
+  if (FindImageFilename(image_location, image_isa, &system_filename, &has_system,
+                        &cache_filename, &dalvik_cache_exists, &has_cache)) {
+    if (Runtime::Current()->ShouldRelocate()) {
+      if (has_system && has_cache) {
+        std::unique_ptr<ImageHeader> sys_hdr(new ImageHeader);
+        std::unique_ptr<ImageHeader> cache_hdr(new ImageHeader);
+        if (!ReadSpecificImageHeader(system_filename.c_str(), sys_hdr.get())) {
+          LOG(FATAL) << "Unable to read image header for " << image_location << " at "
+                     << system_filename;
+          return nullptr;
+        }
+        if (!ReadSpecificImageHeader(cache_filename.c_str(), cache_hdr.get())) {
+          LOG(FATAL) << "Unable to read image header for " << image_location << " at "
+                     << cache_filename;
+          return nullptr;
+        }
+        if (sys_hdr->GetOatChecksum() != cache_hdr->GetOatChecksum()) {
+          LOG(FATAL) << "Unable to find a relocated version of image file " << image_location;
+          return nullptr;
+        }
+        return cache_hdr.release();
+      } else if (!has_cache) {
+        LOG(FATAL) << "Unable to find a relocated version of image file " << image_location;
+        return nullptr;
+      } else if (!has_system && has_cache) {
+        // This can probably just use the cache one.
+        return ReadSpecificImageHeaderOrDie(cache_filename.c_str());
+      }
+    } else {
+      // We don't want to relocate, Just pick the appropriate one if we have it and return.
+      if (has_system && has_cache) {
+        // We want the cache if the checksum matches, otherwise the system.
+        std::unique_ptr<ImageHeader> system(ReadSpecificImageHeaderOrDie(system_filename.c_str()));
+        std::unique_ptr<ImageHeader> cache(ReadSpecificImageHeaderOrDie(cache_filename.c_str()));
+        if (system.get() == nullptr ||
+            (cache.get() != nullptr && cache->GetOatChecksum() == system->GetOatChecksum())) {
+          return cache.release();
+        } else {
+          return system.release();
+        }
+      } else if (has_system) {
+        return ReadSpecificImageHeaderOrDie(system_filename.c_str());
+      } else if (has_cache) {
+        return ReadSpecificImageHeaderOrDie(cache_filename.c_str());
+      }
     }
-
-    return image_header.release();
   }
 
   LOG(FATAL) << "Unable to find image file for: " << image_location;
   return nullptr;
 }
 
+static bool ChecksumsMatch(const char* image_a, const char* image_b) {
+  ImageHeader hdr_a;
+  ImageHeader hdr_b;
+  return ReadSpecificImageHeader(image_a, &hdr_a) && ReadSpecificImageHeader(image_b, &hdr_b)
+      && hdr_a.GetOatChecksum() == hdr_b.GetOatChecksum();
+}
+
 ImageSpace* ImageSpace::Create(const char* image_location,
                                const InstructionSet image_isa) {
-  std::string image_filename;
   std::string error_msg;
-  bool is_system = false;
-  const bool found_image = FindImageFilename(image_location, image_isa, &image_filename,
-                                             &is_system);
-
-  // Note that we must not use the file descriptor associated with
-  // ScopedFlock::GetFile to Init the image file. We want the file
-  // descriptor (and the associated exclusive lock) to be released when
-  // we leave Create.
-  ScopedFlock image_lock;
-  image_lock.Init(image_filename.c_str(), &error_msg);
-
+  std::string system_filename;
+  bool has_system = false;
+  std::string cache_filename;
+  bool has_cache = false;
+  bool dalvik_cache_exists = false;
+  const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
+                                             &has_system, &cache_filename, &dalvik_cache_exists,
+                                             &has_cache);
+
+  ImageSpace* space;
+  bool relocate = Runtime::Current()->ShouldRelocate();
   if (found_image) {
-    ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, !is_system,
-                                         &error_msg);
+    const std::string* image_filename;
+    bool is_system = false;
+    bool relocated_version_used = false;
+    if (relocate) {
+      CHECK(dalvik_cache_exists) << "Requiring relocation for image " << image_location << " "
+                                 << "at " << system_filename << " but we do not have any "
+                                 << "dalvik_cache to find/place it in.";
+      if (has_system) {
+        if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
+          // We already have a relocated version
+          image_filename = &cache_filename;
+          relocated_version_used = true;
+        } else {
+          // We cannot have a relocated version, Relocate the system one and use it.
+          if (RelocateImage(image_location, cache_filename.c_str(), image_isa,
+                            &error_msg)) {
+            relocated_version_used = true;
+            image_filename = &cache_filename;
+          } else {
+            LOG(FATAL) << "Unable to relocate image " << image_location << " "
+                       << "from " << system_filename << " to " << cache_filename << ": "
+                       << error_msg;
+            return nullptr;
+          }
+        }
+      } else {
+        CHECK(has_cache);
+        // We can just use cache's since it should be fine. This might or might not be relocated.
+        image_filename = &cache_filename;
+      }
+    } else {
+      if (has_system && has_cache) {
+        // Check they have the same cksum. If they do use the cache. Otherwise system.
+        if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
+          image_filename = &cache_filename;
+          relocated_version_used = true;
+        } else {
+          image_filename = &system_filename;
+        }
+      } else if (has_system) {
+        image_filename = &system_filename;
+      } else {
+        CHECK(has_cache);
+        image_filename = &cache_filename;
+      }
+    }
+    {
+      // Note that we must not use the file descriptor associated with
+      // ScopedFlock::GetFile to Init the image file. We want the file
+      // descriptor (and the associated exclusive lock) to be released when
+      // we leave Create.
+      ScopedFlock image_lock;
+      image_lock.Init(image_filename->c_str(), &error_msg);
+      LOG(INFO) << "Using image file " << image_filename->c_str() << " for image location "
+                << image_location;
+      space = ImageSpace::Init(image_filename->c_str(), image_location,
+                               false, &error_msg);
+    }
     if (space != nullptr) {
       return space;
     }
 
-    // If the /system file exists, it should be up-to-date, don't try to generate it.
-    // If it's not the /system file, log a warning and fall through to GenerateImage.
-    if (is_system) {
-      LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+    // If the /system file exists, it should be up-to-date, don't try to generate it. Same if it is
+    // a relocated copy from something in /system (i.e. checksum's match).
+    // Otherwise, log a warning and fall through to GenerateImage.
+    if (relocated_version_used) {
+      LOG(FATAL) << "Attempted to use relocated version of " << image_location << " "
+                 << "at " << cache_filename << " generated from " << system_filename << " "
+                 << "but image failed to load: " << error_msg;
+      return nullptr;
+    } else if (is_system) {
+      LOG(FATAL) << "Failed to load /system image '" << *image_filename << "': " << error_msg;
       return nullptr;
     } else {
       LOG(WARNING) << error_msg;
     }
   }
 
-  CHECK(GenerateImage(image_filename, &error_msg))
-      << "Failed to generate image '" << image_filename << "': " << error_msg;
-  ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, true, &error_msg);
+  CHECK(dalvik_cache_exists) << "No place to put generated image.";
+  CHECK(GenerateImage(cache_filename, &error_msg))
+      << "Failed to generate image '" << cache_filename << "': " << error_msg;
+  // TODO Should I relocate this image? Sure
+  if (relocate) {
+    if (!RelocateImage(cache_filename.c_str(), cache_filename.c_str(), image_isa, &error_msg)) {
+      LOG(FATAL) << "Failed to relocate newly created image " << cache_filename.c_str();
+      return nullptr;
+    }
+  }
+  {
+    // Note that we must not use the file descriptor associated with
+    // ScopedFlock::GetFile to Init the image file. We want the file
+    // descriptor (and the associated exclusive lock) to be released when
+    // we leave Create.
+    ScopedFlock image_lock;
+    image_lock.Init(cache_filename.c_str(), &error_msg);
+    space = ImageSpace::Init(cache_filename.c_str(), image_location, true, &error_msg);
+  }
   if (space == nullptr) {
-    LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+    LOG(FATAL) << "Failed to load generated image '" << cache_filename << "': " << error_msg;
   }
   return space;
 }
@@ -316,6 +536,15 @@ OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg)
                               " in image %s", oat_checksum, image_oat_checksum, GetName());
     return nullptr;
   }
+  int32_t image_patch_delta = image_header.GetPatchDelta();
+  int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+  if (oat_patch_delta != image_patch_delta) {
+    // We should have already relocated by this point. Bail out.
+    *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
+                              "in image %s", oat_patch_delta, image_patch_delta, GetName());
+    return nullptr;
+  }
+
   return oat_file;
 }
 
index dd9b580..debca52 100644 (file)
@@ -98,6 +98,20 @@ class ImageSpace : public MemMapSpace {
     return false;
   }
 
+  // Returns the filename of the image corresponding to
+  // requested image_location, or the filename where a new image
+  // should be written if one doesn't exist. Looks for a generated
+  // image in the specified location and then in the dalvik-cache.
+  //
+  // Returns true if an image was found, false otherwise.
+  static bool FindImageFilename(const char* image_location,
+                                InstructionSet image_isa,
+                                std::string* system_location,
+                                bool* has_system,
+                                std::string* data_location,
+                                bool* dalvik_cache_exists,
+                                bool* has_data);
+
  private:
   // Tries to initialize an ImageSpace from the given image path,
   // returning NULL on error.
@@ -110,16 +124,8 @@ class ImageSpace : public MemMapSpace {
                           bool validate_oat_file, std::string* error_msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Returns the filename of the image corresponding to
-  // requested image_location, or the filename where a new image
-  // should be written if one doesn't exist. Looks for a generated
-  // image in the specified location and then in the dalvik-cache.
-  //
-  // Returns true if an image was found, false otherwise.
-  static bool FindImageFilename(const char* image_location,
-                                InstructionSet image_isa,
-                                std::string* location,
-                                bool* is_system);
+  static bool RelocateImage(const char* image_location, const char* dest_filename,
+                            InstructionSet isa, std::string* error_msg);
 
   OatFile* OpenOatFile(const char* image, std::string* error_msg) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
index ad798ed..26ddba2 100644 (file)
@@ -74,6 +74,7 @@ IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
   table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
                                             PROT_READ | PROT_WRITE, false, &error_str));
   CHECK(table_mem_map_.get() != nullptr) << error_str;
+  CHECK_EQ(table_mem_map_->Size(), table_bytes);
 
   table_ = reinterpret_cast<mirror::Object**>(table_mem_map_->Begin());
   CHECK(table_ != nullptr);
index 65498de..e9ad353 100644 (file)
@@ -32,6 +32,11 @@ class NoopCompilerCallbacks FINAL : public CompilerCallbacks {
 
   void ClassRejected(ClassReference ref) OVERRIDE {}
 
+  // This is only used by compilers which need to be able to run without relocation even when it
+  // would normally be enabled. For example the patchoat executable, and dex2oat --image, both need
+  // to disable the relocation since both deal with writing out the images directly.
+  bool IsRelocationPossible() OVERRIDE { return false; }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(NoopCompilerCallbacks);
 };
index 1421baf..0a8c35b 100644 (file)
@@ -23,7 +23,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '7', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' };
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -67,6 +67,8 @@ OatHeader::OatHeader(InstructionSet instruction_set,
                      const SafeMap<std::string, std::string>* variable_data) {
   memcpy(magic_, kOatMagic, sizeof(kOatMagic));
   memcpy(version_, kOatVersion, sizeof(kOatVersion));
+  executable_offset_ = 0;
+  image_patch_delta_ = 0;
 
   adler32_checksum_ = adler32(0L, Z_NULL, 0);
 
@@ -98,7 +100,6 @@ OatHeader::OatHeader(InstructionSet instruction_set,
     UpdateChecksum(&key_value_store_, key_value_store_size_);
   }
 
-  executable_offset_ = 0;
   interpreter_to_interpreter_bridge_offset_ = 0;
   interpreter_to_compiled_code_bridge_offset_ = 0;
   jni_dlsym_lookup_offset_ = 0;
@@ -118,6 +119,12 @@ bool OatHeader::IsValid() const {
   if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
     return false;
   }
+  if (!IsAligned<kPageSize>(executable_offset_)) {
+    return false;
+  }
+  if (!IsAligned<kPageSize>(image_patch_delta_)) {
+    return false;
+  }
   return true;
 }
 
@@ -355,6 +362,26 @@ void OatHeader::SetQuickToInterpreterBridgeOffset(uint32_t offset) {
   UpdateChecksum(&quick_to_interpreter_bridge_offset_, sizeof(offset));
 }
 
+int32_t OatHeader::GetImagePatchDelta() const {
+  CHECK(IsValid());
+  return image_patch_delta_;
+}
+
+void OatHeader::RelocateOat(off_t delta) {
+  CHECK(IsValid());
+  CHECK_ALIGNED(delta, kPageSize);
+  image_patch_delta_ += delta;
+  if (image_file_location_oat_data_begin_ != 0) {
+    image_file_location_oat_data_begin_ += delta;
+  }
+}
+
+void OatHeader::SetImagePatchDelta(int32_t off) {
+  CHECK(IsValid());
+  CHECK_ALIGNED(off, kPageSize);
+  image_patch_delta_ = off;
+}
+
 uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
   CHECK(IsValid());
   return image_file_location_oat_checksum_;
index fbed596..6d5fefe 100644 (file)
@@ -88,6 +88,10 @@ class PACKED(4) OatHeader {
   uint32_t GetQuickToInterpreterBridgeOffset() const;
   void SetQuickToInterpreterBridgeOffset(uint32_t offset);
 
+  int32_t GetImagePatchDelta() const;
+  void RelocateOat(off_t delta);
+  void SetImagePatchDelta(int32_t off);
+
   InstructionSet GetInstructionSet() const;
   const InstructionSetFeatures& GetInstructionSetFeatures() const;
   uint32_t GetImageFileLocationOatChecksum() const;
@@ -129,6 +133,9 @@ class PACKED(4) OatHeader {
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
 
+  // The amount that the image this oat is associated with has been patched.
+  int32_t image_patch_delta_;
+
   uint32_t image_file_location_oat_checksum_;
   uint32_t image_file_location_oat_data_begin_;
 
index 86c1bae..f9cc36a 100644 (file)
@@ -87,6 +87,11 @@ OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::str
   return OpenElfFile(file, location, NULL, true, false, error_msg);
 }
 
+OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
+  CheckLocation(location);
+  return OpenElfFile(file, location, NULL, false, false, error_msg);
+}
+
 OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
                              const std::string& location,
                              byte* requested_base,
index 44f4466..3ec2e84 100644 (file)
@@ -52,6 +52,8 @@ class OatFile {
   // ImageWriter which wants to open a writable version from an existing
   // file descriptor for patching.
   static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
+  // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
+  static OatFile* OpenReadable(File* file, const std::string& location, std::string* error_msg);
 
   // Open an oat file backed by a std::vector with the given location.
   static OatFile* OpenMemory(std::vector<uint8_t>& oat_contents,
index 9a1d0f7..8d0aff8 100644 (file)
@@ -221,6 +221,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
 
   compiler_callbacks_ = nullptr;
   is_zygote_ = false;
+  must_relocate_ = kDefaultMustRelocate;
   if (kPoisonHeapReferences) {
     // kPoisonHeapReferences currently works only with the interpreter only.
     // TODO: make it work with the compiler.
@@ -390,6 +391,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
       ignore_max_footprint_ = true;
     } else if (option == "-XX:LowMemoryMode") {
       low_memory_mode_ = true;
+      // TODO Might want to turn off must_relocate here.
     } else if (option == "-XX:UseTLAB") {
       use_tlab_ = true;
     } else if (option == "-XX:EnableHSpaceCompactForOOM") {
@@ -408,6 +410,14 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
           reinterpret_cast<const char*>(options[i].second));
     } else if (option == "-Xzygote") {
       is_zygote_ = true;
+    } else if (StartsWith(option, "-Xpatchoat:")) {
+      if (!ParseStringAfterChar(option, ':', &patchoat_executable_)) {
+        return false;
+      }
+    } else if (option == "-Xrelocate") {
+      must_relocate_ = true;
+    } else if (option == "-Xnorelocate") {
+      must_relocate_ = false;
     } else if (option == "-Xint") {
       interpreter_only_ = true;
     } else if (StartsWith(option, "-Xgc:")) {
@@ -758,6 +768,8 @@ 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, "  -X[no]relocate\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
index 23f2bcf..29d5494 100644 (file)
@@ -47,6 +47,10 @@ class ParsedOptions {
   std::string jni_trace_;
   CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
+  // TODO Change this to true when we want it on by default.
+  static constexpr bool kDefaultMustRelocate = false;
+  bool must_relocate_;
+  std::string patchoat_executable_;
   bool interpreter_only_;
   bool is_explicit_gc_disabled_;
   bool use_tlab_;
index 0398365..b7eae85 100644 (file)
@@ -101,6 +101,7 @@ Runtime::Runtime()
       instruction_set_(kNone),
       compiler_callbacks_(nullptr),
       is_zygote_(false),
+      must_relocate_(false),
       is_concurrent_gc_enabled_(true),
       is_explicit_gc_disabled_(false),
       default_stack_size_(0),
@@ -385,6 +386,15 @@ jobject CreateSystemClassLoader() {
   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_;
@@ -557,6 +567,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
   properties_ = options->properties_;
 
   compiler_callbacks_ = options->compiler_callbacks_;
+  patchoat_executable_ = options->patchoat_executable_;
+  must_relocate_ = options->must_relocate_;
   is_zygote_ = options->is_zygote_;
   is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
 
index 6a5fe75..c81e36a 100644 (file)
@@ -26,6 +26,7 @@
 #include <utility>
 #include <vector>
 
+#include "compiler_callbacks.h"
 #include "instrumentation.h"
 #include "instruction_set.h"
 #include "jobject_comparator.h"
@@ -55,7 +56,6 @@ namespace verifier {
 class MethodVerifier;
 }
 class ClassLinker;
-class CompilerCallbacks;
 class DexFile;
 class InternTable;
 class JavaVMExt;
@@ -92,6 +92,18 @@ class Runtime {
     return compiler_callbacks_ != nullptr;
   }
 
+  bool CanRelocate() const {
+    return !IsCompiler() || compiler_callbacks_->IsRelocationPossible();
+  }
+
+  bool ShouldRelocate() const {
+    return must_relocate_ && CanRelocate();
+  }
+
+  bool MustRelocateIfPossible() const {
+    return must_relocate_;
+  }
+
   CompilerCallbacks* GetCompilerCallbacks() {
     return compiler_callbacks_;
   }
@@ -105,6 +117,7 @@ class Runtime {
   }
 
   std::string GetCompilerExecutable() const;
+  std::string GetPatchoatExecutable() const;
 
   const std::vector<std::string>& GetCompilerOptions() const {
     return compiler_options_;
@@ -486,10 +499,12 @@ class Runtime {
 
   CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
+  bool must_relocate_;
   bool is_concurrent_gc_enabled_;
   bool is_explicit_gc_disabled_;
 
   std::string compiler_executable_;
+  std::string patchoat_executable_;
   std::vector<std::string> compiler_options_;
   std::vector<std::string> image_compiler_options_;
 
index 8b1ad39..52cdcc1 100644 (file)
@@ -1154,22 +1154,55 @@ const char* GetAndroidRoot() {
 }
 
 const char* GetAndroidData() {
+  std::string error_msg;
+  const char* dir = GetAndroidDataSafe(&error_msg);
+  if (dir != nullptr) {
+    return dir;
+  } else {
+    LOG(FATAL) << error_msg;
+    return "";
+  }
+}
+
+const char* GetAndroidDataSafe(std::string* error_msg) {
   const char* android_data = getenv("ANDROID_DATA");
   if (android_data == NULL) {
     if (OS::DirectoryExists("/data")) {
       android_data = "/data";
     } else {
-      LOG(FATAL) << "ANDROID_DATA not set and /data does not exist";
-      return "";
+      *error_msg = "ANDROID_DATA not set and /data does not exist";
+      return nullptr;
     }
   }
   if (!OS::DirectoryExists(android_data)) {
-    LOG(FATAL) << "Failed to find ANDROID_DATA directory " << android_data;
-    return "";
+    *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data);
+    return nullptr;
   }
   return android_data;
 }
 
+void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
+                    bool* have_android_data, bool* dalvik_cache_exists) {
+  CHECK(subdir != nullptr);
+  std::string error_msg;
+  const char* android_data = GetAndroidDataSafe(&error_msg);
+  if (android_data == nullptr) {
+    *have_android_data = false;
+    *dalvik_cache_exists = false;
+    return;
+  } else {
+    *have_android_data = true;
+  }
+  const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+  *dalvik_cache = dalvik_cache_root + subdir;
+  *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str());
+  if (create_if_absent && !*dalvik_cache_exists && strcmp(android_data, "/data") != 0) {
+    // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
+    *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) &&
+                            (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST));
+  }
+}
+
 std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) {
   CHECK(subdir != nullptr);
   const char* android_data = GetAndroidData();
@@ -1196,9 +1229,11 @@ std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent)
   return dalvik_cache;
 }
 
-std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) {
+bool GetDalvikCacheFilename(const char* location, const char* cache_location,
+                            std::string* filename, std::string* error_msg) {
   if (location[0] != '/') {
-    LOG(FATAL) << "Expected path in location to be absolute: "<< location;
+    *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
+    return false;
   }
   std::string cache_file(&location[1]);  // skip leading slash
   if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) {
@@ -1206,7 +1241,17 @@ std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_
     cache_file += DexFile::kClassesDex;
   }
   std::replace(cache_file.begin(), cache_file.end(), '/', '@');
-  return StringPrintf("%s/%s", cache_location, cache_file.c_str());
+  *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
+  return true;
+}
+
+std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) {
+  std::string ret;
+  std::string error_msg;
+  if (!GetDalvikCacheFilename(location, cache_location, &ret, &error_msg)) {
+    LOG(FATAL) << error_msg;
+  }
+  return ret;
 }
 
 static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) {
index c920050..73872d3 100644 (file)
@@ -439,11 +439,22 @@ const char* GetAndroidRoot();
 
 // Find $ANDROID_DATA, /data, or abort.
 const char* GetAndroidData();
+// Find $ANDROID_DATA, /data, or return nullptr.
+const char* GetAndroidDataSafe(std::string* error_msg);
 
 // Returns the dalvik-cache location, or dies trying. subdir will be
 // appended to the cache location.
 std::string GetDalvikCacheOrDie(const char* subdir, bool create_if_absent = true);
-
+// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
+// have_android_data will be set to true if we have an ANDROID_DATA that exists,
+// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
+void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache,
+                    bool* have_android_data, bool* dalvik_cache_exists);
+
+// Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be
+// rooted at cache_location.
+bool GetDalvikCacheFilename(const char* file_location, const char* cache_location,
+                            std::string* filename, std::string* error_msg);
 // Returns the absolute dalvik-cache path for a DexFile or OatFile, or
 // dies trying. The path returned will be rooted at cache_location.
 std::string GetDalvikCacheFilenameOrDie(const char* file_location,
index 8d31f8d..111680a 100644 (file)
@@ -106,19 +106,21 @@ endef  # define-test-art-oat-rule-target
 # Define rules to run oat tests on the target.
 # $(1): directory - the name of the test we're building such as HelloWorld.
 # $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
+# $(3): additional options
+# $(4): name-addition
 define define-test-art-oat-rules-target
   # Define a phony rule to run a target oat test using the default compiler.
-  default_test_rule := test-art-target-oat-default-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
-  $(call define-test-art-oat-rule-target,$(1),$(2),$$(default_test_rule),)
+  default_test_rule := test-art-target-oat-default$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+  $(call define-test-art-oat-rule-target,$(1),$(2),$$(default_test_rule),$(3))
 
   ART_TEST_TARGET_OAT_DEFAULT$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(default_test_rule)
   ART_TEST_TARGET_OAT_DEFAULT_RULES += $$(default_test_rule)
   ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES += $$(default_test_rule)
 
-  optimizing_test_rule := test-art-target-oat-optimizing-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+  optimizing_test_rule := test-art-target-oat-optimizing$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
   ifeq ($$(ART_TEST_OPTIMIZING),true)
     $(call define-test-art-oat-rule-target,$(1),$(2),$$(optimizing_test_rule), \
-      -Xcompiler-option --compiler-backend=Optimizing)
+      -Xcompiler-option --compiler-backend=Optimizing $(3))
   else
     .PHONY: $$(optimizing_test_rule)
 $$(optimizing_test_rule):
@@ -130,15 +132,15 @@ $$(optimizing_test_rule):
   ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES += $$(optimizing_test_rule)
 
   # Define a phony rule to run a target oat test using the interpeter.
-  interpreter_test_rule := test-art-target-oat-interpreter-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
-  $(call define-test-art-oat-rule-target,$(1),$(2),$$(interpreter_test_rule),-Xint)
+  interpreter_test_rule := test-art-target-oat-interpreter$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+  $(call define-test-art-oat-rule-target,$(1),$(2),$$(interpreter_test_rule),-Xint $(3))
 
   ART_TEST_TARGET_OAT_INTERPRETER$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(interpreter_test_rule)
   ART_TEST_TARGET_OAT_INTERPRETER_RULES += $$(interpreter_test_rule)
   ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES += $$(interpreter_test_rule)
 
   # Define a phony rule to run both the default and interpreter variants.
-  all_test_rule :=  test-art-target-oat-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+  all_test_rule :=  test-art-target-oat$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
 .PHONY: $$(all_test_rule)
 $$(all_test_rule): $$(default_test_rule) $$(optimizing_test_rule) $$(interpreter_test_rule)
        $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
@@ -200,7 +202,7 @@ $(3): $$(ART_TEST_HOST_OAT_$(1)_DEX) $(ART_TEST_HOST_OAT_DEPENDENCIES)
        $(hide) mkdir -p $(ART_HOST_TEST_DIR)/android-data-$$@/dalvik-cache/$$($(2)HOST_ARCH)
        $(hide) cp $$(realpath $$<) $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar
        $(hide) $(DEX2OATD) $(DEX2OAT_FLAGS) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) $(4) \
-         --boot-image=$$(HOST_CORE_IMG_LOCATION) \
+         --boot-image=$$(HOST_CORE_IMG_LOCATION) --include-patch-information \
          --dex-file=$$(PRIVATE_DEX_FILE) --oat-file=$$(PRIVATE_OAT_FILE) \
          --instruction-set=$($(2)ART_HOST_ARCH) --host --android-root=$(HOST_OUT) \
          || $$(call ART_TEST_FAILED,$$@)
@@ -351,9 +353,21 @@ define define-test-art-oat-rules
   ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES :=
   ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES :=
   ART_TEST_TARGET_OAT_$(1)_RULES :=
-  $(call define-test-art-oat-rules-target,$(1),)
-  ifdef TARGET_2ND_ARCH
-    $(call define-test-art-oat-rules-target,$(1),2ND_)
+  ifeq ($(ART_TEST_OAT_NO_RELOCATE),true)
+    $(call define-test-art-oat-rules-target,$(1),, \
+          -Xnorelocate -Xcompiler-option --no-include-patch-information,-norelocate)
+    ifdef TARGET_2ND_ARCH
+      $(call define-test-art-oat-rules-target,$(1),2ND_, \
+          -Xnorelocate -Xcompiler-option --no-include-patch-information,-norelocate)
+    endif
+  endif
+  ifeq ($(ART_TEST_OAT_RELOCATE),true)
+    $(call define-test-art-oat-rules-target,$(1),, \
+          -Xrelocate -Xcompiler-option --include-patch-information,-relocate)
+    ifdef TARGET_2ND_ARCH
+      $(call define-test-art-oat-rules-target,$(1),2ND_, \
+          -Xrelocate -Xcompiler-option --include-patch-information,-relocate)
+    endif
   endif
   $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,,))
   $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-default,_DEFAULT))
index b218c24..a663b8b 100644 (file)
@@ -24,19 +24,20 @@ TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS))
 # List all the test names for host and target and compiler variants.
 # $(1): test name, e.g. 003-omnibus-opcodes
 # $(2): undefined, -trace, -gcverify or -gcstress
+# $(3): -relocate, -norelocate, -prebuild, or undefined.
 define all-run-test-names
-  test-art-host-run-test$(2)-default-$(1)32 \
-  test-art-host-run-test$(2)-optimizing-$(1)32 \
-  test-art-host-run-test$(2)-interpreter-$(1)32 \
-  test-art-host-run-test$(2)-default-$(1)64 \
-  test-art-host-run-test$(2)-optimizing-$(1)64 \
-  test-art-host-run-test$(2)-interpreter-$(1)64 \
-  test-art-target-run-test$(2)-default-$(1)32 \
-  test-art-target-run-test$(2)-optimizing-$(1)32 \
-  test-art-target-run-test$(2)-interpreter-$(1)32 \
-  test-art-target-run-test$(2)-default-$(1)64 \
-  test-art-target-run-test$(2)-optimizing-$(1)64 \
-  test-art-target-run-test$(2)-interpreter-$(1)64
+  test-art-host-run-test$(2)-default$(3)-$(1)32 \
+  test-art-host-run-test$(2)-optimizing$(3)-$(1)32 \
+  test-art-host-run-test$(2)-interpreter$(3)-$(1)32 \
+  test-art-host-run-test$(2)-default$(3)-$(1)64 \
+  test-art-host-run-test$(2)-optimizing$(3)-$(1)64 \
+  test-art-host-run-test$(2)-interpreter$(3)-$(1)64 \
+  test-art-target-run-test$(2)-default$(3)-$(1)32 \
+  test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
+  test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
+  test-art-target-run-test$(2)-default$(3)-$(1)64 \
+  test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
+  test-art-target-run-test$(2)-interpreter$(3)-$(1)64
 endef  # all-run-test-names
 
 # Tests that are timing sensitive and flaky on heavily loaded systems.
@@ -46,10 +47,22 @@ TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
 
  # disable timing sensitive tests on "dist" builds.
 ifdef dist_goal
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify))
-  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-relocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-norelocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-prebuild))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-prebuild))
+  ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
 endif
 
 # Tests that are broken in --trace mode.
@@ -83,7 +96,10 @@ TEST_ART_BROKEN_TRACE_RUN_TESTS := \
   700-LoadArgRegs \
   701-easy-div-rem
 
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
 
 # Tests that need more than 2MB of RAM or are running into other corner cases in GC stress related
 # to OOMEs.
@@ -92,7 +108,10 @@ TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
   080-oom-throw \
   096-array-copy-concurrent-gc
 
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
 
 
 # The path where build only targets will be output, e.g.
@@ -142,26 +161,96 @@ ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
 ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
 ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
 ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
 ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_ALL_RULES :=
 ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
 ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
 ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
 ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 
 # We need dex2oat and dalvikvm on the target as well as the core image.
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
@@ -185,12 +274,14 @@ endif
 # $(3): default, optimizing or interpreter
 # $(4): 32 or 64
 # $(5): run tests with tracing or GC verification enabled or not: trace, gcverify or undefined
+# $(6): relocate, norelocate, prebuild
 define define-test-art-run-test
   run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS))
-  run_test_rule_name := test-art-$(2)-run-test-$(3)-$(1)$(4)
+  run_test_rule_name :=
   uc_host_or_target :=
   prereq_rule :=
   skip_test := false
+  uc_reloc_type :=
   ifeq ($(2),host)
     uc_host_or_target := HOST
     run_test_options += --host
@@ -203,6 +294,31 @@ define define-test-art-run-test
       $$(error found $(2) expected host or target)
     endif
   endif
+  ifeq ($(6),relocate)
+    uc_reloc_type := RELOCATE
+    run_test_options += --relocate
+    ifneq ($(ART_TEST_RUN_TEST_RELOCATE),true)
+      skip_test := true
+    endif
+  else
+    ifeq ($(6),prebuild)
+      uc_reloc_type := PREBUILD
+      run_test_options += --relocate --prebuild
+      ifneq ($(ART_TEST_RUN_TEST_PREBUILD),true)
+        skip_test := true
+      endif
+    else
+      ifeq ($(6),norelocate)
+        uc_reloc_type := NORELOCATE
+        run_test_options += --no-relocate
+        ifneq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
+          skip_test := true
+        endif
+      else
+        $$(error found $(6) expected relocate, norelocate or prebuild)
+      endif
+    endif
+  endif
   uc_compiler :=
   ifeq ($(3),optimizing)
     uc_compiler := OPTIMIZING
@@ -231,7 +347,7 @@ define define-test-art-run-test
   endif
   ifeq ($(5),trace)
     run_test_options += --trace
-    run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(1)$(4)
+    run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(6)-$(1)$(4)
     ifneq ($$(ART_TEST_TRACE),true)
       skip_test := true
     endif
@@ -239,7 +355,7 @@ define define-test-art-run-test
     ifeq ($(5),gcverify)
       run_test_options += --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify \
         --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc
-      run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(1)$(4)
+      run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(6)-$(1)$(4)
       ifneq ($$(ART_TEST_GC_VERIFY),true)
         skip_test := true
       endif
@@ -247,7 +363,7 @@ define define-test-art-run-test
       ifeq ($(5),gcstress)
         run_test_options += --runtime-option -Xgc:SS --runtime-option -Xms2m \
           --runtime-option -Xmx2m --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify
-        run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(1)$(4)
+        run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(6)-$(1)$(4)
         ifneq ($$(ART_TEST_GC_STRESS),true)
           skip_test := true
         endif
@@ -255,6 +371,7 @@ define define-test-art-run-test
         ifneq (,$(5))
           $$(error found $(5) expected undefined or gcverify, gcstress or trace)
         endif
+        run_test_rule_name := test-art-$(2)-run-test-$(3)-$(6)-$(1)$(4)
       endif
     endif
   endif
@@ -279,9 +396,10 @@ $$(run_test_rule_name):
   ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)$(4)_RULES += $$(run_test_rule_name)
   ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
   ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$(1)_RULES += $$(run_test_rule_name)
-  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
+  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
   ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)_RULES += $$(run_test_rule_name)
   ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL_RULES += $$(run_test_rule_name)
+  ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
   ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL$(4)_RULES += $$(run_test_rule_name)
 
   # Clear locally defined variables.
@@ -290,6 +408,7 @@ $$(run_test_rule_name):
   run_test_rule_name :=
   uc_host_or_target :=
   prereq_rule :=
+  uc_reloc_type :=
   uc_compiler :=
 endef  # define-test-art-run-test
 
@@ -306,7 +425,8 @@ endef  # define-test-art-run-test-group-rule
 # Create rules for a group of run tests.
 # $(1): test name, e.g. 003-omnibus-opcodes
 # $(2): host or target
-define define-test-art-run-test-group
+# $(3): relocate, norelocate or prebuild
+define define-test-art-run-test-group-type
   group_uc_host_or_target :=
   ifeq ($(2),host)
     group_uc_host_or_target := HOST
@@ -322,18 +442,18 @@ define define-test-art-run-test-group
   ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
   ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
   ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
-  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+  $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
   do_second := false
   ifeq ($(2),host)
     ifneq ($$(HOST_PREFER_32_BIT),true)
@@ -345,20 +465,42 @@ define define-test-art-run-test-group
     endif
   endif
   ifeq (true,$$(do_second))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
-    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+    $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
   endif
+endef  # define-test-art-run-test-group-type
 
+# Create rules for a group of run tests.
+# $(1): test name, e.g. 003-omnibus-opcodes
+# $(2): host or target
+define define-test-art-run-test-group
+  group_uc_host_or_target :=
+  ifeq ($(2),host)
+    group_uc_host_or_target := HOST
+  else
+    ifeq ($(2),target)
+      group_uc_host_or_target := TARGET
+    else
+      $$(error found $(2) expected host or target)
+    endif
+  endif
+  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
+  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
+  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
+  ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
+  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),norelocate))
+  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),relocate))
+  $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),prebuild))
   $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \
     $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES)))
   $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-interpreter-$(1), \
@@ -380,6 +522,12 @@ endef  # define-test-art-run-test-group
 $(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),target)))
 $(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),host)))
 
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild, \
+  $(ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate, \
+  $(ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate, \
+  $(ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test, \
   $(ART_TEST_TARGET_RUN_TEST_ALL_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default, \
@@ -388,6 +536,24 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-inter
   $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing, \
   $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild, \
+  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild, \
+  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild, \
+  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate, \
+  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate, \
+  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate, \
+  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate, \
+  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate, \
+  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate, \
+  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX), \
   $(ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX), \
@@ -396,6 +562,30 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-inter
   $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX), \
   $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+  $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
 ifdef TARGET_2ND_ARCH
   $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
     $(ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
@@ -405,8 +595,38 @@ ifdef TARGET_2ND_ARCH
     $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
   $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
     $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+    $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
 endif
 
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild, \
+  $(ART_TEST_HOST_RUN_TEST_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate, \
+  $(ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate, \
+  $(ART_TEST_HOST_RUN_TEST_RELOCATE_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test, \
   $(ART_TEST_HOST_RUN_TEST_ALL_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default, \
@@ -415,6 +635,24 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpr
   $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing, \
   $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild, \
+  $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild, \
+  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild, \
+  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate, \
+  $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate, \
+  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate, \
+  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate, \
+  $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate, \
+  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate, \
+  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX), \
   $(ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX), \
@@ -423,6 +661,30 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpr
   $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
 $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX), \
   $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+  $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
 ifneq ($(HOST_PREFER_32_BIT),true)
   $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
     $(ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
@@ -432,6 +694,30 @@ ifneq ($(HOST_PREFER_32_BIT),true)
     $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
   $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
     $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+  $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+    $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
 endif
 
 define-test-art-run-test :=
@@ -442,23 +728,93 @@ ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
 ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
 ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
 ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
 ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_ALL_RULES :=
 ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
 ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
 ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
 ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
index 4265f1c..4d83c87 100755 (executable)
@@ -10,20 +10,28 @@ msg() {
 }
 
 DEBUGGER="n"
+PREBUILD="n"
 GDB="n"
+ISA="x86"
 INTERPRETER="n"
 VERIFY="y"
+RELOCATE="y"
 OPTIMIZE="y"
 INVOKE_WITH=""
 DEV_MODE="n"
 QUIET="n"
 FLAGS=""
+COMPILER_FLAGS=""
+BUILD_BOOT_OPT=""
 exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
         QUIET="y"
         shift
+    elif [ "x$1" = "x--prebuild" ]; then
+        PREBUILD="y"
+        shift
     elif [ "x$1" = "x--lib" ]; then
         shift
         if [ "x$1" = "x" ]; then
@@ -37,7 +45,9 @@ while true; do
         shift
     elif [ "x$1" = "x--boot" ]; then
         shift
-        BOOT_OPT="$1"
+        option="$1"
+        BOOT_OPT="$option"
+        BUILD_BOOT_OPT="--boot-image=${option#-Ximage:}"
         shift
     elif [ "x$1" = "x--debug" ]; then
         DEBUGGER="y"
@@ -65,6 +75,7 @@ while true; do
         INTERPRETER="y"
         shift
     elif [ "x$1" = "x--64" ]; then
+        ISA="x64"
         exe="${ANDROID_HOST_OUT}/bin/dalvikvm64"
         shift
     elif [ "x$1" = "x--no-verify" ]; then
@@ -73,15 +84,23 @@ while true; do
     elif [ "x$1" = "x--no-optimize" ]; then
         OPTIMIZE="n"
         shift
+    elif [ "x$1" = "x--no-relocate" ]; then
+        RELOCATE="n"
+        shift
+    elif [ "x$1" = "x--relocate" ]; then
+        RELOCATE="y"
+        shift
     elif [ "x$1" = "x-Xcompiler-option" ]; then
         shift
         option="$1"
         FLAGS="${FLAGS} -Xcompiler-option $option"
+        COMPILER_FLAGS="${COMPILER_FLAGS} $option"
         shift
     elif [ "x$1" = "x--runtime-option" ]; then
         shift
         option="$1"
         FLAGS="${FLAGS} $option"
+        COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg $option"
         shift
     elif [ "x$1" = "x--" ]; then
         shift
@@ -129,13 +148,40 @@ fi
 
 if [ "$INTERPRETER" = "y" ]; then
     INT_OPTS="-Xint"
+    COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
 fi
 
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+if [ "$RELOCATE" = "y" ]; then
+  FLAGS="${FLAGS} -Xrelocate"
+  COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --include-patch-information"
+  # Run test sets a fairly draconian ulimit that we will likely blow right over
+  # since we are relocating. Get the total size of the /system/framework directory
+  # in 512 byte blocks and set it as the ulimit. This should be more than enough
+  # room.
+  ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+else
+  FLAGS="${FLAGS} -Xnorelocate"
+  COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --no-include-patch-information"
+fi
 
+mkdir_cmd="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
+if [ "$PREBUILD" = "y" ]; then
+  prebuild_cmd="${ANDROID_HOST_OUT}/bin/dex2oatd $COMPILER_FLAGS --instruction-set=$ISA $BUILD_BOOT_OPT --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g")"
+else
+  prebuild_cmd="true"
+fi
+
+JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
 if [ "$DEV_MODE" = "y" ]; then
-  echo $cmdline "$@"
+  if [ "$PREBUILD" = "y" ]; then
+    echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
+  elif [ "$RELOCATE" = "y" ]; then
+    echo "$mkdir_cmd && $cmdline"
+  else
+    echo $cmdline
+  fi
 fi
 
 cd $ANDROID_BUILD_TOP
-$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@"
+$mkdir_cmd && $prebuild_cmd && $cmdline "$@"
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
new file mode 100755 (executable)
index 0000000..dd2ceeb
--- /dev/null
@@ -0,0 +1,212 @@
+#!/bin/sh
+#
+# Run the code in test.jar on the device. The jar should contain a top-level
+# class named Main to run.
+
+msg() {
+    if [ "$QUIET" = "n" ]; then
+        echo "$@"
+    fi
+}
+
+ARCHITECTURES_32="(arm|x86|mips|none)"
+ARCHITECTURES_64="(arm64|x86_64|none)"
+ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
+RELOCATE="y"
+GDB="n"
+DEBUGGER="n"
+INTERPRETER="n"
+VERIFY="y"
+OPTIMIZE="y"
+ZYGOTE=""
+QUIET="n"
+DEV_MODE="n"
+INVOKE_WITH=""
+FLAGS=""
+TARGET_SUFFIX=""
+COMPILE_FLAGS=""
+
+while true; do
+    if [ "x$1" = "x--quiet" ]; then
+        QUIET="y"
+        shift
+    elif [ "x$1" = "x--lib" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --lib" 1>&2
+            exit 1
+        fi
+        LIB="$1"
+        shift
+    elif [ "x$1" = "x-Xcompiler-option" ]; then
+        shift
+        option="$1"
+        FLAGS="${FLAGS} -Xcompiler-option $option"
+        COMPILE_FLAGS="${COMPILE_FLAGS} $option"
+        shift
+    elif [ "x$1" = "x--runtime-option" ]; then
+        shift
+        option="$1"
+        FLAGS="${FLAGS} $option"
+        COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg $option"
+        shift
+    elif [ "x$1" = "x--boot" ]; then
+        shift
+        BOOT_OPT="$1"
+        BUILD_BOOT_OPT="--boot-image=${1#-Ximage:}"
+        shift
+    elif [ "x$1" = "x--relocate" ]; then
+        RELOCATE="y"
+        shift
+    elif [ "x$1" = "x--no-relocate" ]; then
+        RELOCATE="n"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        DEBUGGER="y"
+        shift
+    elif [ "x$1" = "x--gdb" ]; then
+        GDB="y"
+        DEV_MODE="y"
+        shift
+    elif [ "x$1" = "x--zygote" ]; then
+        ZYGOTE="--zygote"
+        msg "Spawning from zygote"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        DEV_MODE="y"
+        shift
+    elif [ "x$1" = "x--interpreter" ]; then
+        INTERPRETER="y"
+        shift
+    elif [ "x$1" = "x--invoke-with" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --invoke-with" 1>&2
+            exit 1
+        fi
+        if [ "x$INVOKE_WITH" = "x" ]; then
+            INVOKE_WITH="$1"
+        else
+            INVOKE_WITH="$INVOKE_WITH $1"
+        fi
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        VERIFY="n"
+        shift
+    elif [ "x$1" = "x--no-optimize" ]; then
+        OPTIMIZE="n"
+        shift
+    elif [ "x$1" = "x--" ]; then
+        shift
+        break
+    elif [ "x$1" = "x--64" ]; then
+        TARGET_SUFFIX="64"
+        ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
+        shift
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown $0 option: $1" 1>&2
+        exit 1
+    else
+        break
+    fi
+done
+
+if [ "$ZYGOTE" = "" ]; then
+    if [ "$OPTIMIZE" = "y" ]; then
+        if [ "$VERIFY" = "y" ]; then
+            DEX_OPTIMIZE="-Xdexopt:verified"
+        else
+            DEX_OPTIMIZE="-Xdexopt:all"
+        fi
+        msg "Performing optimizations"
+    else
+        DEX_OPTIMIZE="-Xdexopt:none"
+        msg "Skipping optimizations"
+    fi
+
+    if [ "$VERIFY" = "y" ]; then
+        DEX_VERIFY=""
+        msg "Performing verification"
+    else
+        DEX_VERIFY="-Xverify:none"
+        msg "Skipping verification"
+    fi
+fi
+
+msg "------------------------------"
+
+ARCH=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
+if [ x"$ARCH" = "x" ]; then
+  echo "Unable to determine architecture"
+  exit 1
+fi
+
+if [ "$QUIET" = "n" ]; then
+  adb shell rm -r $DEX_LOCATION
+  adb shell mkdir -p $DEX_LOCATION
+  adb push $TEST_NAME.jar $DEX_LOCATION
+  adb push $TEST_NAME-ex.jar $DEX_LOCATION
+else
+  adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
+  adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
+  adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
+  adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
+fi
+
+if [ "$DEBUGGER" = "y" ]; then
+  # Use this instead for ddms and connect by running 'ddms':
+  # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+  # TODO: add a separate --ddms option?
+
+  PORT=12345
+  msg "Waiting for jdb to connect:"
+  msg "    adb forward tcp:$PORT tcp:$PORT"
+  msg "    jdb -attach localhost:$PORT"
+  DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+fi
+
+if [ "$GDB" = "y" ]; then
+    gdb="gdbserver$TARGET_SUFFIX :5039"
+    gdbargs="$exe"
+fi
+
+if [ "$INTERPRETER" = "y" ]; then
+    INT_OPTS="-Xint"
+    COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+fi
+
+JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+
+if [ "$RELOCATE" = "y" ]; then
+    RELOCATE_OPT="-Xrelocate"
+    BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
+    COMPILE_FLAGS="${COMPILE_FLAGS} --include-patch-information"
+    FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
+else
+    RELOCATE_OPT="-Xnorelocate"
+    BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
+fi
+
+# This is due to the fact this cmdline can get longer than the longest allowed
+# adb command and there is no way to get the exit status from a adb shell
+# command.
+cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
+    mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
+    $INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT  --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
+    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
+echo "$cmdline" > $cmdfile
+
+if [ "$DEV_MODE" = "y" ]; then
+  echo $cmdline
+fi
+
+if [ "$QUIET" = "n" ]; then
+  adb push $cmdfile $DEX_LOCATION/cmdline.sh
+else
+  adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
+fi
+
+adb shell sh $DEX_LOCATION/cmdline.sh
+
+rm -f $cmdfile
index b090c33..776a011 100755 (executable)
@@ -9,6 +9,7 @@ msg() {
     fi
 }
 
+RELOCATE="y"
 GDB="n"
 DEBUGGER="n"
 INTERPRETER="n"
@@ -61,6 +62,12 @@ while true; do
     elif [ "x$1" = "x--dev" ]; then
         DEV_MODE="y"
         shift
+    elif [ "x$1" = "x--relocate" ]; then
+        RELOCATE="y"
+        shift
+    elif [ "x$1" = "x--no-relocate" ]; then
+        RELOCATE="n"
+        shift
     elif [ "x$1" = "x--interpreter" ]; then
         INTERPRETER="y"
         shift
@@ -155,8 +162,15 @@ fi
 
 JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
 
+if [ "$RELOCATE" = "y" ]; then
+  RELOCATE_OPT="-Xrelocate"
+  FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
+else
+  RELOCATE_OPT="-Xnorelocate"
+fi
+
 cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
-    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+    $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
 if [ "$DEV_MODE" = "y" ]; then
   echo $cmdline "$@"
 fi
index 25d5c5f..c31fefe 100755 (executable)
@@ -83,6 +83,15 @@ while true; do
     elif [ "x$1" = "x--trace" ]; then
         run_args="${run_args} --trace"
         shift
+    elif [ "x$1" = "x--relocate" ]; then
+        run_args="${run_args} --relocate"
+        shift
+    elif [ "x$1" = "x--no-relocate" ]; then
+        run_args="${run_args} --no-relocate"
+        shift
+    elif [ "x$1" = "x--prebuild" ]; then
+        run_args="${run_args} --prebuild"
+        shift;
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -101,7 +110,8 @@ if [ "$usage" = "yes" ]; then
         echo "  Options are all passed to run-test; refer to that for " \
              "further documentation:"
         echo "    --debug --dev --host --interpreter --jvm --no-optimize"
-        echo "    --no-verify -O --update --valgrind --zygote --64"
+        echo "    --no-verify -O --update --valgrind --zygote --64 --relocate"
+        echo "    --prebuild"
         echo "  Specific Runtime Options:"
         echo "    --seq                Run tests one-by-one, avoiding failures caused by busy CPU"
     ) 1>&2
index 1df0ec2..5c38be3 100755 (executable)
@@ -61,9 +61,12 @@ build_output="build-output.txt"
 lib="libartd.so"
 run_args="--quiet"
 
+prebuild_mode="no"
 target_mode="yes"
 dev_mode="no"
 update_mode="no"
+debug_mode="no"
+relocate="yes"
 runtime="art"
 usage="no"
 build_only="no"
@@ -72,13 +75,11 @@ suffix64=""
 while true; do
     if [ "x$1" = "x--host" ]; then
         target_mode="no"
-        RUN="${progdir}/etc/host-run-test-jar"
         DEX_LOCATION=$tmp_dir
         shift
     elif [ "x$1" = "x--jvm" ]; then
         target_mode="no"
         runtime="jvm"
-        RUN="${progdir}/etc/reference-run-test-classes"
         NEED_DEX="false"
         shift
     elif [ "x$1" = "x-O" ]; then
@@ -88,6 +89,18 @@ while true; do
         lib="libdvm.so"
         runtime="dalvik"
         shift
+    elif [ "x$1" = "x--relocate" ]; then
+        relocate="yes"
+        shift
+    elif [ "x$1" = "x--no-relocate" ]; then
+        relocate="no"
+        shift
+    elif [ "x$1" = "x--prebuild" ]; then
+        prebuild_mode="yes"
+        shift;
+    elif [ "x$1" = "x--no-prebuild" ]; then
+        prebuild_mode="no"
+        shift;
     elif [ "x$1" = "x--image" ]; then
         shift
         image="$1"
@@ -174,6 +187,25 @@ while true; do
 done
 mkdir -p $tmp_dir
 
+if [ "$target_mode" = "no" ]; then
+    if [ "$runtime" = "jvm" ]; then
+        RUN="${progdir}/etc/reference-run-test-classes"
+        if [ "$prebuild_mode" = "yes" ]; then
+            echo "--prebuild with --jvm is unsupported";
+            exit 1;
+        fi
+    else
+        RUN="${progdir}/etc/host-run-test-jar"
+        if [ "$prebuild_mode" = "yes" ]; then
+            run_args="${run_args} --prebuild"
+        fi
+    fi
+else
+    if [ "$prebuild_mode" = "yes" ]; then
+        RUN="${progdir}/etc/push-and-run-prebuilt-test-jar"
+    fi
+fi
+
 if [ ! "$runtime" = "jvm" ]; then
   run_args="${run_args} --lib $lib"
 fi
@@ -199,6 +231,11 @@ elif [ "$runtime" = "art" ]; then
     else
         run_args="${run_args} --boot -Ximage:/data/art-test/core.art"
     fi
+    if [ "$relocate" = "yes" ]; then
+      run_args="${run_args} --relocate"
+    else
+      run_args="${run_args} --no-relocate"
+    fi
 fi
 
 if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
@@ -252,6 +289,13 @@ if [ "$usage" = "yes" ]; then
         echo "    --zygote             Spawn the process from the Zygote." \
              "If used, then the"
         echo "                         other runtime options are ignored."
+        echo "    --prebuild           Run dex2oat on the files before starting test."
+        echo "    --no-prebuild        Do not run dex2oat on the files before starting"
+        echo "                         the test. (default)"
+        echo "    --relocate           Force the use of relocating in the test, making"
+        echo "                         the image and oat files be relocated to a random"
+        echo "                         address before running. (default)"
+        echo "    --no-relocate        Force the use of no relocating in the test"
         echo "    --host               Use the host-mode virtual machine."
         echo "    --invoke-with        Pass --invoke-with option to runtime."
         echo "    --dalvik             Use Dalvik (off by default)."
@@ -308,7 +352,7 @@ if echo "$test_dir" | grep 089; then
 elif echo "$test_dir" | grep 083; then
   file_size_limit=5120
 fi
-if ! ulimit "$file_size_limit"; then
+if ! ulimit -S "$file_size_limit"; then
    echo "ulimit file size setting failed"
 fi