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