diff options
author | Andreas Gampe <agampe@google.com> | 2014-07-22 15:25:28 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-07-21 15:45:44 +0000 |
commit | 16fc9f617e395758eb95b5f2124c79a828186b55 (patch) | |
tree | 36958b15842205addaf6d2a13e40823eab47c8bc /compiler | |
parent | 84568fdf08f8f476292996ad653b4453d2894d23 (diff) | |
parent | a59dd80f9f48cb750d329d4d4af2d99d72b484d1 (diff) | |
download | art-16fc9f617e395758eb95b5f2124c79a828186b55.tar.gz art-16fc9f617e395758eb95b5f2124c79a828186b55.tar.bz2 art-16fc9f617e395758eb95b5f2124c79a828186b55.zip |
Merge "Runtime can now be set to require relocation"
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/Android.mk | 1 | ||||
-rw-r--r-- | compiler/dex/quick_compiler_callbacks.h | 5 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 31 | ||||
-rw-r--r-- | compiler/elf_patcher.cc | 293 | ||||
-rw-r--r-- | compiler/elf_patcher.h | 132 | ||||
-rw-r--r-- | compiler/image_test.cc | 2 | ||||
-rw-r--r-- | compiler/image_writer.cc | 226 | ||||
-rw-r--r-- | compiler/image_writer.h | 8 | ||||
-rw-r--r-- | compiler/oat_test.cc | 3 | ||||
-rw-r--r-- | compiler/oat_writer.cc | 5 | ||||
-rw-r--r-- | compiler/oat_writer.h | 2 |
11 files changed, 491 insertions, 217 deletions
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_; |