--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
$(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
# 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) ] || \
compilers.cc \
compiler.cc \
elf_fixup.cc \
+ elf_patcher.cc \
elf_stripper.cc \
elf_writer.cc \
elf_writer_quick.cc \
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_;
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 {
*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;
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) {
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;
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();
}
*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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_
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,
#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"
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;
}
}
-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
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) {
// 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_;
OatWriter oat_writer(class_linker->GetBootClassPath(),
42U,
4096U,
+ 0,
compiler_driver_.get(),
&timings,
&key_value_store);
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));
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)
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),
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() {
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) \
oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
oat_header_->SetQuickToInterpreterBridgeOffset(0);
+ oat_header_->SetImagePatchDelta(image_patch_delta_);
}
return offset;
}
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);
// 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_;
#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"
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,
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();
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()) {
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);
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();
}
is_host,
dex_files,
oat_file.get(),
+ oat_location,
bitcode_filename,
image,
image_classes,
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;
// 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
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());
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);
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";
#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"
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;
}
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;
}
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();
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();
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) {
}
}
- // 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)) {
}
}
- t.NewTiming("Fixup Elf Text Section");
- // Fixup text
- if (!PatchTextSection()) {
- return false;
- }
-
return true;
}
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";
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("");
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;
}
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));
};
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;
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
}
cleanup(ret);
+ sync();
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
}
// 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_);
}
}
+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();
// 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();
bool HasFile();
~ScopedFlock();
+
private:
std::unique_ptr<File> file_;
DISALLOW_COPY_AND_ASSIGN(ScopedFlock);
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));
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) {
// 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);
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)) {
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(
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) {
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);
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,
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,
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)
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);
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;
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());
// 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();
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() { }
};
#include "image_space.h"
+#include <random>
+
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "base/scoped_flock.h"
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;
}
" 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;
}
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.
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_);
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);
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);
};
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;
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);
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;
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;
}
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_;
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;
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_;
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,
// 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,
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.
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") {
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:")) {
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");
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_;
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),
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_;
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_;
#include <utility>
#include <vector>
+#include "compiler_callbacks.h"
#include "instrumentation.h"
#include "instruction_set.h"
#include "jobject_comparator.h"
class MethodVerifier;
}
class ClassLinker;
-class CompilerCallbacks;
class DexFile;
class InternTable;
class JavaVMExt;
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_;
}
}
std::string GetCompilerExecutable() const;
+ std::string GetPatchoatExecutable() const;
const std::vector<std::string>& GetCompilerOptions() const {
return compiler_options_;
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_;
}
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();
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")) {
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) {
// 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,
# 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):
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,$$@)
$(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,$$@)
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))
# 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.
# 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.
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.
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.
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)
# $(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
$$(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
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
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
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
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
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.
run_test_rule_name :=
uc_host_or_target :=
prereq_rule :=
+ uc_reloc_type :=
uc_compiler :=
endef # define-test-art-run-test
# 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
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)
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), \
$(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, \
$(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), \
$(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)))
$(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, \
$(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), \
$(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)))
$(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 :=
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 :=
}
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
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"
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
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
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 "$@"
--- /dev/null
+#!/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
fi
}
+RELOCATE="y"
GDB="n"
DEBUGGER="n"
INTERPRETER="n"
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
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
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"
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
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"
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
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"
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
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
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)."
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