diff options
author | Brian Carlstrom <bdc@google.com> | 2013-07-12 13:46:57 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2013-07-12 17:49:01 -0700 |
commit | 7940e44f4517de5e2634a7e07d58d0fb26160513 (patch) | |
tree | ac90242d96229a6942f6e24ab137bc1f8f2e0025 /compiler/image_writer.cc | |
parent | 5cd9e3b122f276f610980cbaf0d2ad6ed4cd9088 (diff) | |
download | art-7940e44f4517de5e2634a7e07d58d0fb26160513.zip art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.gz art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.bz2 |
Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees
to prevent dependency creep. They can now be individually built
without rebuilding the rest of the art projects. dalvikvm and jdwpspy
were already this way. Builds in the art directory should behave as
before, building everything including tests.
Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
Diffstat (limited to 'compiler/image_writer.cc')
-rw-r--r-- | compiler/image_writer.cc | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc new file mode 100644 index 0000000..8d32a91 --- /dev/null +++ b/compiler/image_writer.cc @@ -0,0 +1,680 @@ +/* + * Copyright (C) 2011 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 "image_writer.h" + +#include <sys/stat.h> + +#include <vector> + +#include "base/logging.h" +#include "base/unix_file/fd_file.h" +#include "class_linker.h" +#include "compiled_method.h" +#include "dex_file-inl.h" +#include "driver/compiler_driver.h" +#include "elf_writer.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/heap.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" +#include "globals.h" +#include "image.h" +#include "intern_table.h" +#include "mirror/array-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache-inl.h" +#include "mirror/field-inl.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "oat.h" +#include "oat_file.h" +#include "object_utils.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "sirt_ref.h" +#include "UniquePtr.h" +#include "utils.h" + +using namespace art::mirror; + +namespace art { + +bool ImageWriter::Write(const std::string& image_filename, + uintptr_t image_begin, + const std::string& oat_filename, + const std::string& oat_location) { + CHECK(!image_filename.empty()); + + CHECK_NE(image_begin, 0U); + image_begin_ = reinterpret_cast<byte*>(image_begin); + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const std::vector<DexCache*>& all_dex_caches = class_linker->GetDexCaches(); + for (size_t i = 0; i < all_dex_caches.size(); i++) { + DexCache* dex_cache = all_dex_caches[i]; + dex_caches_.insert(dex_cache); + } + + UniquePtr<File> oat_file(OS::OpenFile(oat_filename.c_str(), true, false)); + if (oat_file.get() == NULL) { + LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location; + return false; + } + oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location); + class_linker->RegisterOatFile(*oat_file_); + interpreter_to_interpreter_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterEntryOffset(); + interpreter_to_quick_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToQuickEntryOffset(); + portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset(); + quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset(); + + { + Thread::Current()->TransitionFromSuspendedToRunnable(); + PruneNonImageClasses(); // Remove junk + ComputeLazyFieldsForImageClasses(); // Add useful information + ComputeEagerResolvedStrings(); + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + heap->CollectGarbage(false); // Remove garbage. + // Trim size of alloc spaces. + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + space->AsDlMallocSpace()->Trim(); + } + } + + if (!AllocMemory()) { + return false; + } +#ifndef NDEBUG + { + ScopedObjectAccess soa(Thread::Current()); + CheckNonImageClassesRemoved(); + } +#endif + Thread::Current()->TransitionFromSuspendedToRunnable(); + size_t oat_loaded_size = 0; + size_t oat_data_offset = 0; + ElfWriter::GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); + CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); + CopyAndFixupObjects(); + PatchOatCodeAndMethods(); + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + + UniquePtr<File> image_file(OS::OpenFile(image_filename.c_str(), true)); + if (image_file.get() == NULL) { + LOG(ERROR) << "Failed to open image file " << image_filename; + return false; + } + if (fchmod(image_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; + return EXIT_FAILURE; + } + bool success = image_file->WriteFully(image_->Begin(), image_end_); + if (!success) { + PLOG(ERROR) << "Failed to write image file " << image_filename; + return false; + } + return true; +} + +bool ImageWriter::AllocMemory() { + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + size_t size = 0; + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + size += space->Size(); + } + } + + int prot = PROT_READ | PROT_WRITE; + size_t length = RoundUp(size, kPageSize); + image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot)); + if (image_.get() == NULL) { + LOG(ERROR) << "Failed to allocate memory for image file generation"; + return false; + } + return true; +} + +void ImageWriter::ComputeLazyFieldsForImageClasses() { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, NULL); +} + +bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) { + c->ComputeName(); + return true; +} + +void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { + if (!obj->GetClass()->IsStringClass()) { + return; + } + String* string = obj->AsString(); + const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); + ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); + typedef Set::const_iterator CacheIt; // TODO: C++0x auto + for (CacheIt it = writer->dex_caches_.begin(), end = writer->dex_caches_.end(); it != end; ++it) { + DexCache* dex_cache = *it; + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); + if (string_id != NULL) { + // This string occurs in this dex file, assign the dex cache entry. + uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); + if (dex_cache->GetResolvedString(string_idx) == NULL) { + dex_cache->SetResolvedString(string_idx, string); + } + } + } +} + +void ImageWriter::ComputeEagerResolvedStrings() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // TODO: Check image spaces only? + gc::Heap* heap = Runtime::Current()->GetHeap(); + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + heap->GetLiveBitmap()->Walk(ComputeEagerResolvedStringsCallback, this); +} + +bool ImageWriter::IsImageClass(const Class* klass) { + return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptor()); +} + +struct NonImageClasses { + ImageWriter* image_writer; + std::set<std::string>* non_image_classes; +}; + +void ImageWriter::PruneNonImageClasses() { + if (compiler_driver_.GetImageClasses() == NULL) { + return; + } + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + + // Make a list of classes we would like to prune. + std::set<std::string> non_image_classes; + NonImageClasses context; + context.image_writer = this; + context.non_image_classes = &non_image_classes; + class_linker->VisitClasses(NonImageClassesVisitor, &context); + + // Remove the undesired classes from the class roots. + typedef std::set<std::string>::const_iterator ClassIt; // TODO: C++0x auto + for (ClassIt it = non_image_classes.begin(), end = non_image_classes.end(); it != end; ++it) { + class_linker->RemoveClass((*it).c_str(), NULL); + } + + // Clear references to removed classes from the DexCaches. + AbstractMethod* resolution_method = runtime->GetResolutionMethod(); + typedef Set::const_iterator CacheIt; // TODO: C++0x auto + for (CacheIt it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ++it) { + DexCache* dex_cache = *it; + for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { + Class* klass = dex_cache->GetResolvedType(i); + if (klass != NULL && !IsImageClass(klass)) { + dex_cache->SetResolvedType(i, NULL); + dex_cache->GetInitializedStaticStorage()->Set(i, NULL); + } + } + for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { + AbstractMethod* method = dex_cache->GetResolvedMethod(i); + if (method != NULL && !IsImageClass(method->GetDeclaringClass())) { + dex_cache->SetResolvedMethod(i, resolution_method); + } + } + for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { + Field* field = dex_cache->GetResolvedField(i); + if (field != NULL && !IsImageClass(field->GetDeclaringClass())) { + dex_cache->SetResolvedField(i, NULL); + } + } + } +} + +bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { + NonImageClasses* context = reinterpret_cast<NonImageClasses*>(arg); + if (!context->image_writer->IsImageClass(klass)) { + context->non_image_classes->insert(ClassHelper(klass).GetDescriptor()); + } + return true; +} + +void ImageWriter::CheckNonImageClassesRemoved() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (compiler_driver_.GetImageClasses() == NULL) { + return; + } + + gc::Heap* heap = Runtime::Current()->GetHeap(); + Thread* self = Thread::Current(); + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + } + + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->GetLiveBitmap()->Walk(CheckNonImageClassesRemovedCallback, this); +} + +void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { + ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); + if (!obj->IsClass()) { + return; + } + Class* klass = obj->AsClass(); + if (!image_writer->IsImageClass(klass)) { + image_writer->DumpImageClasses(); + CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() + << " " << PrettyDescriptor(klass); + } +} + +void ImageWriter::DumpImageClasses() { + CompilerDriver::DescriptorSet* image_classes = compiler_driver_.GetImageClasses(); + CHECK(image_classes != NULL); + typedef std::set<std::string>::const_iterator It; // TODO: C++0x auto + for (It it = image_classes->begin(), end = image_classes->end(); it != end; ++it) { + LOG(INFO) << " " << *it; + } +} + +void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) { + DCHECK(obj != NULL); + DCHECK(arg != NULL); + ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); + + // if it is a string, we want to intern it if its not interned. + if (obj->GetClass()->IsStringClass()) { + // we must be an interned string that was forward referenced and already assigned + if (image_writer->IsImageOffsetAssigned(obj)) { + DCHECK_EQ(obj, obj->AsString()->Intern()); + return; + } + SirtRef<String> interned(Thread::Current(), obj->AsString()->Intern()); + if (obj != interned.get()) { + if (!image_writer->IsImageOffsetAssigned(interned.get())) { + // interned obj is after us, allocate its location early + image_writer->AssignImageOffset(interned.get()); + } + // point those looking for this object to the interned version. + image_writer->SetImageOffset(obj, image_writer->GetImageOffset(interned.get())); + return; + } + // else (obj == interned), nothing to do but fall through to the normal case + } + + image_writer->AssignImageOffset(obj); +} + +ObjectArray<Object>* ImageWriter::CreateImageRoots() const { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;"); + Thread* self = Thread::Current(); + + // build an Object[] of all the DexCaches used in the source_space_ + ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class, + dex_caches_.size()); + int i = 0; + typedef Set::const_iterator It; // TODO: C++0x auto + for (It it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ++it, ++i) { + dex_caches->Set(i, *it); + } + + // build an Object[] of the roots needed to restore the runtime + SirtRef<ObjectArray<Object> > + image_roots(self, + ObjectArray<Object>::Alloc(self, object_array_class, + ImageHeader::kImageRootsMax)); + image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); + image_roots->Set(ImageHeader::kCalleeSaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kSaveAll)); + image_roots->Set(ImageHeader::kRefsOnlySaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)); + image_roots->Set(ImageHeader::kRefsAndArgsSaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); + image_roots->Set(ImageHeader::kOatLocation, + String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); + image_roots->Set(ImageHeader::kDexCaches, + dex_caches); + image_roots->Set(ImageHeader::kClassRoots, + class_linker->GetClassRoots()); + for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { + CHECK(image_roots->Get(i) != NULL); + } + return image_roots.get(); +} + +void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) { + CHECK_NE(0U, oat_loaded_size); + Thread* self = Thread::Current(); + SirtRef<ObjectArray<Object> > image_roots(self, CreateImageRoots()); + + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + DCHECK(!spaces.empty()); + DCHECK_EQ(0U, image_end_); + + // leave space for the header, but do not write it yet, we need to + // know where image_roots is going to end up + image_end_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment + + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + // TODO: Image spaces only? + // TODO: Add InOrderWalk to heap bitmap. + const char* old = self->StartAssertNoThreadSuspension("ImageWriter"); + DCHECK(heap->GetLargeObjectsSpace()->GetLiveObjects()->IsEmpty()); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + space->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); + DCHECK_LT(image_end_, image_->Size()); + } + self->EndAssertNoThreadSuspension(old); + } + + const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize); + const byte* oat_file_end = oat_file_begin + oat_loaded_size; + oat_data_begin_ = oat_file_begin + oat_data_offset; + const byte* oat_data_end = oat_data_begin_ + oat_file_->Size(); + + // return to write header at start of image with future location of image_roots + ImageHeader image_header(reinterpret_cast<uint32_t>(image_begin_), + reinterpret_cast<uint32_t>(GetImageAddress(image_roots.get())), + oat_file_->GetOatHeader().GetChecksum(), + reinterpret_cast<uint32_t>(oat_file_begin), + reinterpret_cast<uint32_t>(oat_data_begin_), + reinterpret_cast<uint32_t>(oat_data_end), + reinterpret_cast<uint32_t>(oat_file_end)); + memcpy(image_->Begin(), &image_header, sizeof(image_header)); + + // Note that image_end_ is left at end of used space +} + +void ImageWriter::CopyAndFixupObjects() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); + gc::Heap* heap = Runtime::Current()->GetHeap(); + // TODO: heap validation can't handle this fix up pass + heap->DisableObjectValidation(); + // TODO: Image spaces only? + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + heap->GetLiveBitmap()->Walk(CopyAndFixupObjectsCallback, this); + self->EndAssertNoThreadSuspension(old_cause); +} + +void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { + DCHECK(object != NULL); + DCHECK(arg != NULL); + const Object* obj = object; + ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); + + // see GetLocalAddress for similar computation + size_t offset = image_writer->GetImageOffset(obj); + byte* dst = image_writer->image_->Begin() + offset; + const byte* src = reinterpret_cast<const byte*>(obj); + size_t n = obj->SizeOf(); + DCHECK_LT(offset + n, image_writer->image_->Size()); + memcpy(dst, src, n); + Object* copy = reinterpret_cast<Object*>(dst); + copy->SetField32(Object::MonitorOffset(), 0, false); // We may have inflated the lock during compilation. + image_writer->FixupObject(obj, copy); +} + +void ImageWriter::FixupObject(const Object* orig, Object* copy) { + DCHECK(orig != NULL); + DCHECK(copy != NULL); + copy->SetClass(down_cast<Class*>(GetImageAddress(orig->GetClass()))); + // TODO: special case init of pointers to malloc data (or removal of these pointers) + if (orig->IsClass()) { + FixupClass(orig->AsClass(), down_cast<Class*>(copy)); + } else if (orig->IsObjectArray()) { + FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy)); + } else if (orig->IsMethod()) { + FixupMethod(orig->AsMethod(), down_cast<AbstractMethod*>(copy)); + } else { + FixupInstanceFields(orig, copy); + } +} + +void ImageWriter::FixupClass(const Class* orig, Class* copy) { + FixupInstanceFields(orig, copy); + FixupStaticFields(orig, copy); +} + +void ImageWriter::FixupMethod(const AbstractMethod* orig, AbstractMethod* copy) { + FixupInstanceFields(orig, copy); + + // OatWriter replaces the code_ with an offset value. + // Here we readjust to a pointer relative to oat_begin_ + if (orig->IsAbstract()) { + // Code for abstract methods is set to the abstract method error stub when we load the image. + copy->SetEntryPointFromCompiledCode(NULL); + copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*> + (GetOatAddress(interpreter_to_interpreter_entry_offset_))); + return; + } else { + copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*> + (GetOatAddress(interpreter_to_quick_entry_offset_))); + } + + if (orig == Runtime::Current()->GetResolutionMethod()) { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + return; + } + + // Use original code if it exists. Otherwise, set the code pointer to the resolution trampoline. + const byte* code = GetOatAddress(orig->GetOatCodeOffset()); + if (code != NULL) { + copy->SetEntryPointFromCompiledCode(code); + } else { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + } + + if (orig->IsNative()) { + // The native method's pointer is set to a stub to lookup via dlsym when we load the image. + // Note this is not the code_ pointer, that is handled above. + copy->SetNativeMethod(NULL); + } else { + // normal (non-abstract non-native) methods have mapping tables to relocate + uint32_t mapping_table_off = orig->GetOatMappingTableOffset(); + const byte* mapping_table = GetOatAddress(mapping_table_off); + copy->SetMappingTable(reinterpret_cast<const uint32_t*>(mapping_table)); + + uint32_t vmap_table_offset = orig->GetOatVmapTableOffset(); + const byte* vmap_table = GetOatAddress(vmap_table_offset); + copy->SetVmapTable(reinterpret_cast<const uint16_t*>(vmap_table)); + + uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset(); + const byte* native_gc_map = GetOatAddress(native_gc_map_offset); + copy->SetNativeGcMap(reinterpret_cast<const uint8_t*>(native_gc_map)); + } +} + +void ImageWriter::FixupObjectArray(const ObjectArray<Object>* orig, ObjectArray<Object>* copy) { + for (int32_t i = 0; i < orig->GetLength(); ++i) { + const Object* element = orig->Get(i); + copy->SetPtrWithoutChecks(i, GetImageAddress(element)); + } +} + +void ImageWriter::FixupInstanceFields(const Object* orig, Object* copy) { + DCHECK(orig != NULL); + DCHECK(copy != NULL); + Class* klass = orig->GetClass(); + DCHECK(klass != NULL); + FixupFields(orig, + copy, + klass->GetReferenceInstanceOffsets(), + false); +} + +void ImageWriter::FixupStaticFields(const Class* orig, Class* copy) { + DCHECK(orig != NULL); + DCHECK(copy != NULL); + FixupFields(orig, + copy, + orig->GetReferenceStaticOffsets(), + true); +} + +void ImageWriter::FixupFields(const Object* orig, + Object* copy, + uint32_t ref_offsets, + bool is_static) { + if (ref_offsets != CLASS_WALK_SUPER) { + // Found a reference offset bitmap. Fixup the specified offsets. + while (ref_offsets != 0) { + size_t right_shift = CLZ(ref_offsets); + MemberOffset byte_offset = CLASS_OFFSET_FROM_CLZ(right_shift); + const Object* ref = orig->GetFieldObject<const Object*>(byte_offset, false); + // Use SetFieldPtr to avoid card marking since we are writing to the image. + copy->SetFieldPtr(byte_offset, GetImageAddress(ref), false); + ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift); + } + } else { + // There is no reference offset bitmap. In the non-static case, + // walk up the class inheritance hierarchy and find reference + // offsets the hard way. In the static case, just consider this + // class. + for (const Class *klass = is_static ? orig->AsClass() : orig->GetClass(); + klass != NULL; + klass = is_static ? NULL : klass->GetSuperClass()) { + size_t num_reference_fields = (is_static + ? klass->NumReferenceStaticFields() + : klass->NumReferenceInstanceFields()); + for (size_t i = 0; i < num_reference_fields; ++i) { + Field* field = (is_static + ? klass->GetStaticField(i) + : klass->GetInstanceField(i)); + MemberOffset field_offset = field->GetOffset(); + const Object* ref = orig->GetFieldObject<const Object*>(field_offset, false); + // Use SetFieldPtr to avoid card marking since we are writing to the image. + copy->SetFieldPtr(field_offset, GetImageAddress(ref), false); + } + } + } + if (!is_static && orig->IsReferenceInstance()) { + // Fix-up referent, that isn't marked as an object field, for References. + Field* field = orig->GetClass()->FindInstanceField("referent", "Ljava/lang/Object;"); + MemberOffset field_offset = field->GetOffset(); + const Object* ref = orig->GetFieldObject<const Object*>(field_offset, false); + // Use SetFieldPtr to avoid card marking since we are writing to the image. + copy->SetFieldPtr(field_offset, GetImageAddress(ref), false); + } +} + +static AbstractMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + DexCache* dex_cache = class_linker->FindDexCache(patch->GetDexFile()); + AbstractMethod* method = class_linker->ResolveMethod(patch->GetDexFile(), + patch->GetTargetMethodIdx(), + dex_cache, + NULL, + NULL, + patch->GetTargetInvokeType()); + CHECK(method != NULL) + << patch->GetDexFile().GetLocation() << " " << patch->GetTargetMethodIdx(); + CHECK(!method->IsRuntimeMethod()) + << patch->GetDexFile().GetLocation() << " " << patch->GetTargetMethodIdx(); + CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method) + << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " " + << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " " + << PrettyMethod(method); + return method; +} + +void ImageWriter::PatchOatCodeAndMethods() { + Thread* self = Thread::Current(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); + + typedef std::vector<const CompilerDriver::PatchInformation*> Patches; + const Patches& code_to_patch = compiler_driver_.GetCodeToPatch(); + for (size_t i = 0; i < code_to_patch.size(); i++) { + const CompilerDriver::PatchInformation* patch = code_to_patch[i]; + AbstractMethod* target = GetTargetMethod(patch); + uint32_t code = reinterpret_cast<uint32_t>(class_linker->GetOatCodeFor(target)); + uint32_t code_base = reinterpret_cast<uint32_t>(&oat_file_->GetOatHeader()); + uint32_t code_offset = code - code_base; + SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetOatAddress(code_offset))); + } + + const Patches& methods_to_patch = compiler_driver_.GetMethodsToPatch(); + for (size_t i = 0; i < methods_to_patch.size(); i++) { + const CompilerDriver::PatchInformation* patch = methods_to_patch[i]; + AbstractMethod* target = GetTargetMethod(patch); + SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetImageAddress(target))); + } + + // 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); +} + +void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const void* oat_code = class_linker->GetOatCodeFor(patch->GetDexFile(), + 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<uint32_t>(oat_code) & ~0x1); + uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset()); +#ifndef NDEBUG + const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); + uint32_t expected = reinterpret_cast<uint32_t>(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; +#endif + *patch_location = value; + oat_header.UpdateChecksum(patch_location, sizeof(value)); +} + +} // namespace art |