diff options
author | 2023-01-29 18:16:24 +0000 | |
---|---|---|
committer | 2023-01-29 18:16:24 +0000 | |
commit | 24b3d648ff6c2c200003f55ac63fc910d7bfd40f (patch) | |
tree | 1c1324dc63cca9a7b3ea7a0a432535fa25c47d88 | |
parent | df68c0a6f0d36728fa728049e5bcec20de2d0d5e (diff) |
Revert "Write classes in runtime-generated app image."
This reverts commit df68c0a6f0d36728fa728049e5bcec20de2d0d5e.
Reason for revert: b/267084527
Change-Id: I2eacf89c23db00c46d56e78009efa11bed31af60
-rw-r--r-- | dex2oat/linker/image_writer.cc | 20 | ||||
-rw-r--r-- | dex2oat/linker/image_writer.h | 14 | ||||
-rw-r--r-- | libartbase/base/safe_map.h | 3 | ||||
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/class_linker.cc | 6 | ||||
-rw-r--r-- | runtime/class_table-inl.h | 10 | ||||
-rw-r--r-- | runtime/class_table.h | 18 | ||||
-rw-r--r-- | runtime/handle_scope-inl.h | 27 | ||||
-rw-r--r-- | runtime/handle_scope.h | 9 | ||||
-rw-r--r-- | runtime/mirror/array-inl.h | 16 | ||||
-rw-r--r-- | runtime/mirror/array.h | 2 | ||||
-rw-r--r-- | runtime/mirror/class.h | 2 | ||||
-rw-r--r-- | runtime/oat.cc | 23 | ||||
-rw-r--r-- | runtime/oat.h | 14 | ||||
-rw-r--r-- | runtime/runtime_image.cc | 770 | ||||
-rw-r--r-- | test/845-data-image/src-art/Main.java | 180 |
16 files changed, 113 insertions, 1002 deletions
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index db843e44a6..2c3c2206bf 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -3294,7 +3294,25 @@ const uint8_t* ImageWriter::GetOatAddress(StubType type) const { const OatFile* oat_file = image_spaces[0]->GetOatFile(); CHECK(oat_file != nullptr); const OatHeader& header = oat_file->GetOatHeader(); - return header.GetOatAddress(type); + switch (type) { + // TODO: We could maybe clean this up if we stored them in an array in the oat header. + case StubType::kQuickGenericJNITrampoline: + return static_cast<const uint8_t*>(header.GetQuickGenericJniTrampoline()); + case StubType::kJNIDlsymLookupTrampoline: + return static_cast<const uint8_t*>(header.GetJniDlsymLookupTrampoline()); + case StubType::kJNIDlsymLookupCriticalTrampoline: + return static_cast<const uint8_t*>(header.GetJniDlsymLookupCriticalTrampoline()); + case StubType::kQuickIMTConflictTrampoline: + return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline()); + case StubType::kQuickResolutionTrampoline: + return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline()); + case StubType::kQuickToInterpreterBridge: + return static_cast<const uint8_t*>(header.GetQuickToInterpreterBridge()); + case StubType::kNterpTrampoline: + return static_cast<const uint8_t*>(header.GetNterpTrampoline()); + default: + UNREACHABLE(); + } } const ImageInfo& primary_image_info = GetImageInfo(0); return GetOatAddressForOffset(primary_image_info.GetStubOffset(type), primary_image_info); diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index f81df4e805..e5eeacc02d 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -45,7 +45,6 @@ #include "intern_table.h" #include "lock_word.h" #include "mirror/dex_cache.h" -#include "oat.h" #include "oat_file.h" #include "obj_ptr.h" @@ -230,6 +229,18 @@ class ImageWriter final { }; friend std::ostream& operator<<(std::ostream& stream, NativeObjectRelocationType type); + enum class StubType { + kJNIDlsymLookupTrampoline, + kJNIDlsymLookupCriticalTrampoline, + kQuickGenericJNITrampoline, + kQuickIMTConflictTrampoline, + kQuickResolutionTrampoline, + kQuickToInterpreterBridge, + kNterpTrampoline, + kLast = kNterpTrampoline, + }; + friend std::ostream& operator<<(std::ostream& stream, StubType stub_type); + static constexpr size_t kBinBits = MinimumBitsToStore<uint32_t>(static_cast<size_t>(Bin::kMirrorCount) - 1); // uint32 = typeof(lockword_) @@ -701,6 +712,7 @@ class ImageWriter final { std::ostream& operator<<(std::ostream& stream, ImageWriter::Bin bin); std::ostream& operator<<(std::ostream& stream, ImageWriter::NativeObjectRelocationType type); +std::ostream& operator<<(std::ostream& stream, ImageWriter::StubType stub_type); } // namespace linker } // namespace art diff --git a/libartbase/base/safe_map.h b/libartbase/base/safe_map.h index fa13fe0f68..c6d4353038 100644 --- a/libartbase/base/safe_map.h +++ b/libartbase/base/safe_map.h @@ -49,9 +49,8 @@ class SafeMap { SafeMap() = default; SafeMap(const SafeMap&) = default; SafeMap(SafeMap&&) noexcept = default; - explicit SafeMap(const allocator_type& allocator) : map_(allocator) {} explicit SafeMap(const key_compare& cmp, const allocator_type& allocator = allocator_type()) - : map_(cmp, allocator) { + : map_(cmp, allocator) { } Self& operator=(const Self& rhs) { diff --git a/runtime/Android.bp b/runtime/Android.bp index a1a0b4873e..bfe04f3aff 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -592,7 +592,6 @@ gensrcs { "jni_id_type.h", "linear_alloc.h", "lock_word.h", - "oat.h", "oat_file.h", "process_state.h", "reflective_value_visitor.h", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b829f5916c..a2f451347a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2180,11 +2180,9 @@ bool ClassLinker::AddImageSpace( ObjPtr<mirror::Class> klass(root.Read()); // Do not update class loader for boot image classes where the app image // class loader is only the initiating loader but not the defining loader. - if (space->HasAddress(klass.Ptr())) { + // Avoid read barrier since we are comparing against null. + if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) { klass->SetClassLoader(loader); - } else { - DCHECK(klass->IsBootStrapClassLoaded()); - DCHECK(Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass.Ptr())); } } } diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index ecc8a0a620..67eeb553a4 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -213,11 +213,6 @@ inline ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass, uint32_t de DCHECK_EQ(descriptor_hash, klass->DescriptorHash()); } -inline ClassTable::TableSlot::TableSlot(uint32_t ptr, uint32_t descriptor_hash) - : data_(ptr | MaskHash(descriptor_hash)) { - DCHECK_ALIGNED(ptr, kObjectAlignment); -} - template <typename Filter> inline void ClassTable::RemoveStrongRoots(const Filter& filter) { WriterMutexLock mu(Thread::Current(), lock_); @@ -232,11 +227,6 @@ inline ObjPtr<mirror::Class> ClassTable::LookupByDescriptor(ObjPtr<mirror::Class return Lookup(descriptor, hash); } -inline size_t ClassTable::Size() const { - ReaderMutexLock mu(Thread::Current(), lock_); - return classes_.size(); -} - } // namespace art #endif // ART_RUNTIME_CLASS_TABLE_INL_H_ diff --git a/runtime/class_table.h b/runtime/class_table.h index 7e263737c3..123c069f0e 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -58,31 +58,18 @@ class ClassTable { explicit TableSlot(ObjPtr<mirror::Class> klass); TableSlot(ObjPtr<mirror::Class> klass, uint32_t descriptor_hash); - TableSlot(uint32_t ptr, uint32_t descriptor_hash); TableSlot& operator=(const TableSlot& copy) { data_.store(copy.data_.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } - uint32_t Data() const { - return data_.load(std::memory_order_relaxed); - } - bool IsNull() const REQUIRES_SHARED(Locks::mutator_lock_); uint32_t Hash() const { return MaskHash(data_.load(std::memory_order_relaxed)); } - uint32_t NonHashData() const { - return RemoveHash(Data()); - } - - static uint32_t RemoveHash(uint32_t hash) { - return hash & ~kHashMask; - } - static uint32_t MaskHash(uint32_t hash) { return hash & kHashMask; } @@ -181,11 +168,6 @@ class ClassTable { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the number of classes in the class table. - size_t Size() const - REQUIRES(!lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - // Update a class in the table with the new class. Returns the existing class which was replaced. ObjPtr<mirror::Class> UpdateClass(const char* descriptor, ObjPtr<mirror::Class> new_klass, diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h index 60a82a29ae..3aa9e5221d 100644 --- a/runtime/handle_scope-inl.h +++ b/runtime/handle_scope-inl.h @@ -121,15 +121,6 @@ inline void HandleScope::VisitRoots(Visitor& visitor) { } } -template <typename Visitor> -inline void HandleScope::VisitHandles(Visitor& visitor) { - for (size_t i = 0, count = NumberOfReferences(); i < count; ++i) { - if (GetHandle(i) != nullptr) { - visitor.Visit(GetHandle(i)); - } - } -} - template<size_t kNumReferences> template<class T> inline MutableHandle<T> FixedSizeHandleScope<kNumReferences>::NewHandle(T* object) { return NewHandle(ObjPtr<T>(object)); @@ -188,15 +179,6 @@ inline void BaseHandleScope::VisitRoots(Visitor& visitor) { } } -template <typename Visitor> -inline void BaseHandleScope::VisitHandles(Visitor& visitor) { - if (LIKELY(!IsVariableSized())) { - AsHandleScope()->VisitHandles(visitor); - } else { - AsVariableSized()->VisitHandles(visitor); - } -} - inline VariableSizedHandleScope* BaseHandleScope::AsVariableSized() { DCHECK(IsVariableSized()); return down_cast<VariableSizedHandleScope*>(this); @@ -287,15 +269,6 @@ inline void VariableSizedHandleScope::VisitRoots(Visitor& visitor) { } } -template <typename Visitor> -inline void VariableSizedHandleScope::VisitHandles(Visitor& visitor) { - LocalScopeType* cur = current_scope_; - while (cur != nullptr) { - cur->VisitHandles(visitor); - cur = reinterpret_cast<LocalScopeType*>(cur->GetLink()); - } -} - } // namespace art #endif // ART_RUNTIME_HANDLE_SCOPE_INL_H_ diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index a43e889111..89127e4cf3 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -56,9 +56,6 @@ class PACKED(4) BaseHandleScope { template <typename Visitor> ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); - template <typename Visitor> - ALWAYS_INLINE void VisitHandles(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Link to previous BaseHandleScope or null. BaseHandleScope* GetLink() const { return link_; @@ -151,9 +148,6 @@ class PACKED(4) HandleScope : public BaseHandleScope { template <typename Visitor> ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); - template <typename Visitor> - ALWAYS_INLINE void VisitHandles(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); - protected: // Return backing storage used for references. ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const { @@ -267,9 +261,6 @@ class VariableSizedHandleScope : public BaseHandleScope { template <typename Visitor> void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); - template <typename Visitor> - ALWAYS_INLINE void VisitHandles(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static constexpr size_t kLocalScopeSize = 64u; static constexpr size_t kSizeOfReferencesPerScope = diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 2bdf8277cd..a7faa376cd 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -240,16 +240,13 @@ inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) { // C style casts here since we sometimes have T be a pointer, or sometimes an integer // (for stack traces). using ConversionType = typename std::conditional_t<std::is_pointer_v<T>, uintptr_t, T>; - // Note: we cast the array directly when unchecked as this code gets called by - // runtime_image, which can pass a 64bit pointer and therefore cannot be held - // by an ObjPtr. if (kPointerSize == PointerSize::k64) { uint64_t value = - static_cast<uint64_t>(reinterpret_cast<LongArray*>(this)->GetWithoutChecks(idx)); + static_cast<uint64_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx)); return (T) dchecked_integral_cast<ConversionType>(value); } else { uint32_t value = - static_cast<uint32_t>(reinterpret_cast<IntArray*>(this)->GetWithoutChecks(idx)); + static_cast<uint32_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx)); return (T) dchecked_integral_cast<ConversionType>(value); } } @@ -264,15 +261,12 @@ inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) { template<bool kTransactionActive, bool kCheckTransaction, bool kUnchecked> inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) { - // Note: we cast the array directly when unchecked as this code gets called by - // runtime_image, which can pass a 64bit pointer and therefore cannot be held - // by an ObjPtr. if (ptr_size == PointerSize::k64) { - (kUnchecked ? reinterpret_cast<LongArray*>(this) : AsLongArray().Ptr())-> + (kUnchecked ? ObjPtr<LongArray>::DownCast(ObjPtr<Object>(this)) : AsLongArray())-> SetWithoutChecks<kTransactionActive, kCheckTransaction>(idx, element); } else { uint32_t element32 = dchecked_integral_cast<uint32_t>(element); - (kUnchecked ? reinterpret_cast<IntArray*>(this) : AsIntArray().Ptr()) + (kUnchecked ? ObjPtr<IntArray>::DownCast(ObjPtr<Object>(this)) : AsIntArray()) ->SetWithoutChecks<kTransactionActive, kCheckTransaction>(idx, element32); } } @@ -284,7 +278,7 @@ inline void PointerArray::SetElementPtrSize(uint32_t idx, T* element, PointerSiz } template <VerifyObjectFlags kVerifyFlags, typename Visitor> -inline void PointerArray::Fixup(mirror::PointerArray* dest, +inline void PointerArray::Fixup(ObjPtr<mirror::PointerArray> dest, PointerSize pointer_size, const Visitor& visitor) { for (size_t i = 0, count = GetLength(); i < count; ++i) { diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 0116fdee6f..dfe7d475c1 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -264,7 +264,7 @@ class PointerArray : public Array { // Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies // to dest if visitor(source_ptr) != source_ptr. template <VerifyObjectFlags kVerifyFlags = kVerifyNone, typename Visitor> - void Fixup(mirror::PointerArray* dest, PointerSize pointer_size, const Visitor& visitor) + void Fixup(ObjPtr<mirror::PointerArray> dest, PointerSize pointer_size, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); // Works like memcpy(), except we guarantee not to allow tearing of array values (ie using smaller diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index b9eb9d05b4..bd37534b6b 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -65,7 +65,6 @@ template<typename T> class StrideIterator; template<size_t kNumReferences> class PACKED(4) StackHandleScope; class Thread; class DexCacheVisitor; -class RuntimeImageHelper; namespace mirror { @@ -1580,7 +1579,6 @@ class MANAGED Class final : public Object { friend struct art::ClassOffsets; // for verifying offset information friend class Object; // For VisitReferences friend class linker::ImageWriter; // For SetStatusInternal - friend class art::RuntimeImageHelper; // For SetStatusInternal DISALLOW_IMPLICIT_CONSTRUCTORS(Class); }; diff --git a/runtime/oat.cc b/runtime/oat.cc index 2c7a73f964..42bb7d4894 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -467,27 +467,4 @@ void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_); } -const uint8_t* OatHeader::GetOatAddress(StubType type) const { - DCHECK_LE(type, StubType::kLast); - switch (type) { - // TODO: We could maybe clean this up if we stored them in an array in the oat header. - case StubType::kQuickGenericJNITrampoline: - return static_cast<const uint8_t*>(GetQuickGenericJniTrampoline()); - case StubType::kJNIDlsymLookupTrampoline: - return static_cast<const uint8_t*>(GetJniDlsymLookupTrampoline()); - case StubType::kJNIDlsymLookupCriticalTrampoline: - return static_cast<const uint8_t*>(GetJniDlsymLookupCriticalTrampoline()); - case StubType::kQuickIMTConflictTrampoline: - return static_cast<const uint8_t*>(GetQuickImtConflictTrampoline()); - case StubType::kQuickResolutionTrampoline: - return static_cast<const uint8_t*>(GetQuickResolutionTrampoline()); - case StubType::kQuickToInterpreterBridge: - return static_cast<const uint8_t*>(GetQuickToInterpreterBridge()); - case StubType::kNterpTrampoline: - return static_cast<const uint8_t*>(GetNterpTrampoline()); - default: - UNREACHABLE(); - } -} - } // namespace art diff --git a/runtime/oat.h b/runtime/oat.h index e062baaee9..3b32e11daa 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -29,18 +29,6 @@ namespace art { enum class InstructionSet; class InstructionSetFeatures; -enum class StubType { - kJNIDlsymLookupTrampoline, - kJNIDlsymLookupCriticalTrampoline, - kQuickGenericJNITrampoline, - kQuickIMTConflictTrampoline, - kQuickResolutionTrampoline, - kQuickToInterpreterBridge, - kNterpTrampoline, - kLast = kNterpTrampoline, -}; -std::ostream& operator<<(std::ostream& stream, StubType stub_type); - class PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; @@ -123,8 +111,6 @@ class PACKED(4) OatHeader { bool IsConcurrentCopying() const; bool RequiresImage() const; - const uint8_t* GetOatAddress(StubType type) const; - private: bool KeyHasValue(const char* key, const char* value, size_t value_size) const; diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc index 9d59c4d363..7137991dcc 100644 --- a/runtime/runtime_image.cc +++ b/runtime/runtime_image.cc @@ -22,12 +22,9 @@ #include "android-base/stringprintf.h" -#include "base/arena_allocator.h" -#include "base/arena_containers.h" #include "base/bit_utils.h" #include "base/file_utils.h" #include "base/length_prefixed_array.h" -#include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "class_loader_utils.h" @@ -40,36 +37,23 @@ #include "mirror/object_array-inl.h" #include "mirror/object_array.h" #include "mirror/string-inl.h" -#include "oat.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" namespace art { /** - * The native data structures that we store in the image. - */ -enum class NativeRelocationKind { - kArtFieldArray, - kArtMethodArray, - kArtMethod, - kImTable, -}; - -/** * Helper class to generate an app image at runtime. */ class RuntimeImageHelper { public: explicit RuntimeImageHelper(gc::Heap* heap) : - sections_(ImageHeader::kSectionCount), boot_image_begin_(heap->GetBootImagesStartAddress()), boot_image_size_(heap->GetBootImagesSize()), image_begin_(boot_image_begin_ + boot_image_size_), // Note: image relocation considers the image header in the bitmap. object_section_size_(sizeof(ImageHeader)), - intern_table_(InternStringHash(this), InternStringEquals(this)), - class_table_(ClassDescriptorHash(this), ClassDescriptorEquals()) {} + intern_table_(InternStringHash(this), InternStringEquals(this)) {} bool Generate(std::string* error_msg) { @@ -78,15 +62,12 @@ class RuntimeImageHelper { } // Generate the sections information stored in the header. - CreateImageSections(); - - // Now that all sections have been created and we know their offset and - // size, relocate native pointers inside classes and ImTables. - RelocateNativePointers(); + dchecked_vector<ImageSection> sections(ImageHeader::kSectionCount); + CreateImageSections(sections); // Generate the bitmap section, stored page aligned after the sections data // and of size `object_section_size_` page aligned. - size_t sections_end = sections_[ImageHeader::kSectionMetadata].End(); + size_t sections_end = sections[ImageHeader::kSectionMetadata].End(); image_bitmap_ = gc::accounting::ContinuousSpaceBitmap::Create( "image bitmap", reinterpret_cast<uint8_t*>(image_begin_), @@ -97,7 +78,7 @@ class RuntimeImageHelper { reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset)); } const size_t bitmap_bytes = image_bitmap_.Size(); - auto* bitmap_section = §ions_[ImageHeader::kSectionImageBitmap]; + auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap]; *bitmap_section = ImageSection(RoundUp(sections_end, kPageSize), RoundUp(bitmap_bytes, kPageSize)); @@ -120,7 +101,7 @@ class RuntimeImageHelper { /* component_count= */ 1, image_begin_, sections_end, - sections_.data(), + sections.data(), /* image_roots= */ image_begin_ + sizeof(ImageHeader), /* oat_checksum= */ 0, /* oat_file_begin= */ 0, @@ -142,20 +123,8 @@ class RuntimeImageHelper { return true; } - const std::vector<uint8_t>& GetObjects() const { - return objects_; - } - - const std::vector<uint8_t>& GetArtMethods() const { - return art_methods_; - } - - const std::vector<uint8_t>& GetArtFields() const { - return art_fields_; - } - - const std::vector<uint8_t>& GetImTables() const { - return im_tables_; + const std::vector<uint8_t>& GetData() const { + return image_data_; } const ImageHeader& GetHeader() const { @@ -174,16 +143,12 @@ class RuntimeImageHelper { intern_table_.WriteToMemory(data.data()); } - void GenerateClassTableData(std::vector<uint8_t>& data) const { - class_table_.WriteToMemory(data.data()); - } - private: bool IsInBootImage(const void* obj) const { return reinterpret_cast<uintptr_t>(obj) - boot_image_begin_ < boot_image_size_; } - // Returns a pointer that can be stored in `objects_`: + // Returns a pointer that can be stored in `image_data_`: // - The pointer itself for boot image objects, // - The offset in the image for all other objects. mirror::Object* GetOrComputeImageAddress(ObjPtr<mirror::Object> object) @@ -191,90 +156,57 @@ class RuntimeImageHelper { if (object == nullptr || IsInBootImage(object.Ptr())) { DCHECK(object == nullptr || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(object)); return object.Ptr(); - } - - if (object->IsClassLoader()) { + } else if (object->IsClassLoader()) { // DexCache and Class point to class loaders. For runtime-generated app // images, we don't encode the class loader. It will be set when the // runtime is loading the image. return nullptr; - } - - if (object->GetClass() == GetClassRoot<mirror::ClassExt>()) { - // No need to encode `ClassExt`. If needed, it will be reconstructed at - // runtime. - return nullptr; - } - - uint32_t offset = 0u; - if (object->IsClass()) { - offset = CopyClass(object->AsClass()); - } else if (object->IsDexCache()) { - offset = CopyDexCache(object->AsDexCache()); } else { - offset = CopyObject(object); + uint32_t offset = CopyObject(object); + return reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset); } - return reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset); } - void CreateImageSections() { - sections_[ImageHeader::kSectionObjects] = ImageSection(0u, object_section_size_); - sections_[ImageHeader::kSectionArtFields] = - ImageSection(sections_[ImageHeader::kSectionObjects].End(), art_fields_.size()); - - // Round up to the alignment for ArtMethod. - static_assert(IsAligned<sizeof(void*)>(ArtMethod::Size(kRuntimePointerSize))); - size_t cur_pos = RoundUp(sections_[ImageHeader::kSectionArtFields].End(), sizeof(void*)); - sections_[ImageHeader::kSectionArtMethods] = ImageSection(cur_pos, art_methods_.size()); - - // Round up to the alignment for ImTables. - cur_pos = RoundUp(sections_[ImageHeader::kSectionArtMethods].End(), sizeof(void*)); - sections_[ImageHeader::kSectionImTables] = ImageSection(cur_pos, im_tables_.size()); - - // Round up to the alignment for conflict tables. - cur_pos = RoundUp(sections_[ImageHeader::kSectionImTables].End(), sizeof(void*)); - sections_[ImageHeader::kSectionIMTConflictTables] = ImageSection(cur_pos, 0u); - - sections_[ImageHeader::kSectionRuntimeMethods] = - ImageSection(sections_[ImageHeader::kSectionIMTConflictTables].End(), 0u); + void CreateImageSections(dchecked_vector<ImageSection>& sections) const { + sections[ImageHeader::kSectionObjects] = + ImageSection(0u, object_section_size_); + sections[ImageHeader::kSectionArtFields] = + ImageSection(sections[ImageHeader::kSectionObjects].End(), 0u); + sections[ImageHeader::kSectionArtMethods] = + ImageSection(sections[ImageHeader::kSectionArtFields].End(), 0u); + sections[ImageHeader::kSectionImTables] = + ImageSection(sections[ImageHeader::kSectionArtMethods].End(), 0u); + sections[ImageHeader::kSectionIMTConflictTables] = + ImageSection(sections[ImageHeader::kSectionImTables].End(), 0u); + sections[ImageHeader::kSectionRuntimeMethods] = + ImageSection(sections[ImageHeader::kSectionIMTConflictTables].End(), 0u); // Round up to the alignment the string table expects. See HashSet::WriteToMemory. - cur_pos = RoundUp(sections_[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t)); + size_t cur_pos = RoundUp(sections[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t)); size_t intern_table_bytes = intern_table_.WriteToMemory(nullptr); - sections_[ImageHeader::kSectionInternedStrings] = ImageSection(cur_pos, intern_table_bytes); + sections[ImageHeader::kSectionInternedStrings] = ImageSection(cur_pos, intern_table_bytes); // Obtain the new position and round it up to the appropriate alignment. - cur_pos = RoundUp(sections_[ImageHeader::kSectionInternedStrings].End(), sizeof(uint64_t)); - - size_t class_table_bytes = class_table_.WriteToMemory(nullptr); - sections_[ImageHeader::kSectionClassTable] = ImageSection(cur_pos, class_table_bytes); + cur_pos = RoundUp(sections[ImageHeader::kSectionInternedStrings].End(), sizeof(uint64_t)); + sections[ImageHeader::kSectionClassTable] = ImageSection(cur_pos, 0u); // Round up to the alignment of the offsets we are going to store. - cur_pos = RoundUp(sections_[ImageHeader::kSectionClassTable].End(), sizeof(uint32_t)); - sections_[ImageHeader::kSectionStringReferenceOffsets] = ImageSection(cur_pos, 0u); + cur_pos = RoundUp(sections[ImageHeader::kSectionClassTable].End(), sizeof(uint32_t)); + sections[ImageHeader::kSectionStringReferenceOffsets] = ImageSection(cur_pos, 0u); // Round up to the alignment of the offsets we are going to store. cur_pos = - RoundUp(sections_[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(uint32_t)); + RoundUp(sections[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(uint32_t)); - sections_[ImageHeader::kSectionMetadata] = ImageSection(cur_pos, 0u); + sections[ImageHeader::kSectionMetadata] = ImageSection(cur_pos, 0u); } - // Returns the copied mirror Object if in the image, or the object directly if - // in the boot image. For the copy, this is really its content, it should not + // Returns the copied mirror Object. This is really its content, it should not // be returned as an `ObjPtr` (as it's not a GC object), nor stored anywhere. template<typename T> T* FromImageOffsetToRuntimeContent(uint32_t offset) { - if (offset == 0u || IsInBootImage(reinterpret_cast<const void*>(offset))) { - return reinterpret_cast<T*>(offset); - } - uint32_t vector_data_offset = FromImageOffsetToVectorOffset(offset); - return reinterpret_cast<T*>(objects_.data() + vector_data_offset); - } - - uint32_t FromImageOffsetToVectorOffset(uint32_t offset) const { - DCHECK(!IsInBootImage(reinterpret_cast<const void*>(offset))); - return offset - sizeof(ImageHeader) - image_begin_; + uint32_t vector_data_offset = offset - sizeof(ImageHeader) - image_begin_; + return reinterpret_cast<T*>(image_data_.data() + vector_data_offset); } class InternStringHash { @@ -320,39 +252,6 @@ class RuntimeImageHelper { using InternTableSet = HashSet<uint32_t, DefaultEmptyFn<uint32_t>, InternStringHash, InternStringEquals>; - class ClassDescriptorHash { - public: - explicit ClassDescriptorHash(RuntimeImageHelper* helper) : helper_(helper) {} - - uint32_t operator()(const ClassTable::TableSlot& slot) const NO_THREAD_SAFETY_ANALYSIS { - uint32_t ptr = slot.NonHashData(); - if (helper_->IsInBootImage(reinterpret_cast32<const void*>(ptr))) { - return reinterpret_cast32<mirror::Class*>(ptr)->DescriptorHash(); - } - return helper_->class_hashes_[helper_->FromImageOffsetToVectorOffset(ptr)]; - } - - private: - RuntimeImageHelper* helper_; - }; - - class ClassDescriptorEquals { - public: - ClassDescriptorEquals() {} - - bool operator()(const ClassTable::TableSlot& a, const ClassTable::TableSlot& b) - const NO_THREAD_SAFETY_ANALYSIS { - // No need to fetch the descriptor: we know the classes we are inserting - // in the ClassTable are unique. - return a.Data() == b.Data(); - } - }; - - using ClassTableSet = HashSet<ClassTable::TableSlot, - ClassTable::TableSlotEmptyFn, - ClassDescriptorHash, - ClassDescriptorEquals>; - void VisitDexCache(ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = *dex_cache->GetDexFile(); // Currently only copy string objects into the image. Populate the intern @@ -371,410 +270,11 @@ class RuntimeImageHelper { } } - // Helper class to collect classes that we will generate in the image. - class ClassTableVisitor { - public: - ClassTableVisitor(Handle<mirror::ClassLoader> loader, VariableSizedHandleScope& handles) - : loader_(loader), handles_(handles) {} - - bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { - // Record app classes and boot classpath classes: app classes will be - // generated in the image and put in the class table, boot classpath - // classes will be put in the class table. - ObjPtr<mirror::ClassLoader> class_loader = klass->GetClassLoader(); - if (class_loader == loader_.Get() || class_loader == nullptr) { - handles_.NewHandle(klass); - } - return true; - } - - private: - Handle<mirror::ClassLoader> loader_; - VariableSizedHandleScope& handles_; - }; - - // Helper class visitor to filter out classes we cannot emit. - class PruneVisitor { - public: - PruneVisitor(Thread* self, - RuntimeImageHelper* helper, - const ArenaSet<const DexFile*>& dex_files, - ArenaVector<Handle<mirror::Class>>& classes, - ArenaAllocator& allocator) - : self_(self), - helper_(helper), - dex_files_(dex_files), - visited_(allocator.Adapter()), - classes_to_write_(classes) {} - - bool CanEmitHelper(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - // Only emit classes that are resolved and not erroneous. - if (!cls->IsResolved() || cls->IsErroneous()) { - return false; - } - - // Classes in the boot image can be trivially encoded directly. - if (helper_->IsInBootImage(cls.Get())) { - return true; - } - - // If the class comes from a dex file which is not part of the primary - // APK, don't encode it. - if (!ContainsElement(dex_files_, &cls->GetDexFile())) { - return false; - } - - // Ensure pointers to classes in `cls` can also be emitted. - StackHandleScope<1> hs(self_); - MutableHandle<mirror::Class> other_class = hs.NewHandle(cls->GetSuperClass()); - if (!CanEmit(other_class)) { - return false; - } - - other_class.Assign(cls->GetComponentType()); - if (!CanEmit(other_class)) { - return false; - } - - for (size_t i = 0, num_interfaces = cls->NumDirectInterfaces(); i < num_interfaces; ++i) { - other_class.Assign(cls->GetDirectInterface(i)); - if (!CanEmit(other_class)) { - return false; - } - } - return true; - } - - bool CanEmit(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls == nullptr) { - return true; - } - const dex::ClassDef* class_def = cls->GetClassDef(); - if (class_def == nullptr) { - // Covers array classes and proxy classes. - // TODO: Handle these differently. - return false; - } - auto existing = visited_.find(class_def); - if (existing != visited_.end()) { - // Already processed; - return existing->second == VisitState::kCanEmit; - } - - visited_.Put(class_def, VisitState::kVisiting); - if (CanEmitHelper(cls)) { - visited_.Overwrite(class_def, VisitState::kCanEmit); - return true; - } else { - visited_.Overwrite(class_def, VisitState::kCannotEmit); - return false; - } - } - - void Visit(Handle<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) { - MutableHandle<mirror::Class> cls(obj.GetReference()); - if (CanEmit(cls)) { - if (cls->IsBootStrapClassLoaded()) { - DCHECK(helper_->IsInBootImage(cls.Get())); - // Insert the bootclasspath class in the class table. - uint32_t hash = cls->DescriptorHash(); - helper_->class_table_.InsertWithHash(ClassTable::TableSlot(cls.Get(), hash), hash); - } else { - classes_to_write_.push_back(cls); - } - } - } - - private: - enum class VisitState { - kVisiting, - kCanEmit, - kCannotEmit, - }; - - Thread* const self_; - RuntimeImageHelper* const helper_; - const ArenaSet<const DexFile*>& dex_files_; - ArenaSafeMap<const dex::ClassDef*, VisitState> visited_; - ArenaVector<Handle<mirror::Class>>& classes_to_write_; - }; - - void EmitStringsAndClasses(Thread* self, - Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array) + void VisitDexCaches(Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array) REQUIRES_SHARED(Locks::mutator_lock_) { - ArenaAllocator allocator(Runtime::Current()->GetArenaPool()); - ArenaSet<const DexFile*> dex_files(allocator.Adapter()); for (int32_t i = 0; i < dex_cache_array->GetLength(); ++i) { - dex_files.insert(dex_cache_array->Get(i)->AsDexCache()->GetDexFile()); VisitDexCache(ObjPtr<mirror::DexCache>::DownCast((dex_cache_array->Get(i)))); } - - StackHandleScope<1> hs(self); - Handle<mirror::ClassLoader> loader = hs.NewHandle( - dex_cache_array->Get(0)->AsDexCache()->GetClassLoader()); - ClassTable* const class_table = loader->GetClassTable(); - if (class_table == nullptr) { - return; - } - - VariableSizedHandleScope handles(self); - { - ClassTableVisitor class_table_visitor(loader, handles); - class_table->Visit(class_table_visitor); - } - - ArenaVector<Handle<mirror::Class>> classes_to_write(allocator.Adapter()); - classes_to_write.reserve(class_table->Size()); - { - PruneVisitor prune_visitor(self, this, dex_files, classes_to_write, allocator); - handles.VisitHandles(prune_visitor); - } - - for (Handle<mirror::Class> cls : classes_to_write) { - ScopedAssertNoThreadSuspension sants("Writing class"); - CopyClass(cls.Get()); - } - } - - // Helper visitor returning the location of a native pointer in the image. - class NativePointerVisitor { - public: - explicit NativePointerVisitor(RuntimeImageHelper* helper) : helper_(helper) {} - - template <typename T> - T* operator()(T* ptr, void** dest_addr ATTRIBUTE_UNUSED) const { - return helper_->NativeLocationInImage(ptr); - } - - template <typename T> T* operator()(T* ptr) const { - return helper_->NativeLocationInImage(ptr); - } - - private: - RuntimeImageHelper* helper_; - }; - - template <typename T> T* NativeLocationInImage(T* ptr) const { - if (ptr == nullptr || IsInBootImage(ptr)) { - return ptr; - } - - auto it = native_relocations_.find(ptr); - DCHECK(it != native_relocations_.end()); - switch (it->second.first) { - case NativeRelocationKind::kArtMethod: - case NativeRelocationKind::kArtMethodArray: { - uint32_t offset = sections_[ImageHeader::kSectionArtMethods].Offset(); - return reinterpret_cast<T*>(image_begin_ + offset + it->second.second); - } - case NativeRelocationKind::kArtFieldArray: { - uint32_t offset = sections_[ImageHeader::kSectionArtFields].Offset(); - return reinterpret_cast<T*>(image_begin_ + offset + it->second.second); - } - case NativeRelocationKind::kImTable: { - uint32_t offset = sections_[ImageHeader::kSectionImTables].Offset(); - return reinterpret_cast<T*>(image_begin_ + offset + it->second.second); - } - } - } - - template <typename Visitor> - void RelocateMethodPointerArrays(mirror::Class* klass, const Visitor& visitor) - REQUIRES_SHARED(Locks::mutator_lock_) { - // A bit of magic here: we cast contents from our buffer to mirror::Class, - // and do pointer comparison between 1) these classes, and 2) boot image objects. - // Both kinds do not move. - - // See if we need to fixup the vtable field. - mirror::Class* super = FromImageOffsetToRuntimeContent<mirror::Class>( - reinterpret_cast32<uint32_t>( - klass->GetSuperClass<kVerifyNone, kWithoutReadBarrier>().Ptr())); - DCHECK(super != nullptr) << "j.l.Object should never be in an app runtime image"; - mirror::PointerArray* vtable = FromImageOffsetToRuntimeContent<mirror::PointerArray>( - reinterpret_cast32<uint32_t>(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>().Ptr())); - mirror::PointerArray* super_vtable = FromImageOffsetToRuntimeContent<mirror::PointerArray>( - reinterpret_cast32<uint32_t>(super->GetVTable<kVerifyNone, kWithoutReadBarrier>().Ptr())); - if (vtable != nullptr && vtable != super_vtable) { - DCHECK(!IsInBootImage(vtable)); - vtable->Fixup(vtable, kRuntimePointerSize, visitor); - } - - // See if we need to fixup entries in the IfTable. - mirror::IfTable* iftable = FromImageOffsetToRuntimeContent<mirror::IfTable>( - reinterpret_cast32<uint32_t>( - klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>().Ptr())); - mirror::IfTable* super_iftable = FromImageOffsetToRuntimeContent<mirror::IfTable>( - reinterpret_cast32<uint32_t>( - super->GetIfTable<kVerifyNone, kWithoutReadBarrier>().Ptr())); - int32_t iftable_count = iftable->Count(); - int32_t super_iftable_count = super_iftable->Count(); - for (int32_t i = 0; i < iftable_count; ++i) { - mirror::PointerArray* methods = FromImageOffsetToRuntimeContent<mirror::PointerArray>( - reinterpret_cast32<uint32_t>( - iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i).Ptr())); - mirror::PointerArray* super_methods = (i < super_iftable_count) - ? FromImageOffsetToRuntimeContent<mirror::PointerArray>( - reinterpret_cast32<uint32_t>( - super_iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i).Ptr())) - : nullptr; - if (methods != super_methods) { - DCHECK(!IsInBootImage(methods)); - methods->Fixup(methods, kRuntimePointerSize, visitor); - } - } - } - - void RelocateNativePointers() { - ScopedObjectAccess soa(Thread::Current()); - NativePointerVisitor visitor(this); - for (auto it : classes_) { - mirror::Class* cls = reinterpret_cast<mirror::Class*>(&objects_[it.second]); - cls->FixupNativePointers(cls, kRuntimePointerSize, visitor); - RelocateMethodPointerArrays(cls, visitor); - } - for (auto it : native_relocations_) { - if (it.second.first == NativeRelocationKind::kImTable) { - ImTable* im_table = reinterpret_cast<ImTable*>(im_tables_.data() + it.second.second); - RelocateImTable(im_table, visitor); - } - } - } - - void RelocateImTable(ImTable* im_table, const NativePointerVisitor& visitor) { - for (size_t i = 0; i < ImTable::kSize; ++i) { - ArtMethod* method = im_table->Get(i, kRuntimePointerSize); - ArtMethod* new_method = nullptr; - if (method->IsRuntimeMethod() && !IsInBootImage(method)) { - // New IMT conflict method: just use the boot image version. - // TODO: Consider copying the new IMT conflict method. - new_method = Runtime::Current()->GetImtConflictMethod(); - DCHECK(IsInBootImage(new_method)); - } else { - new_method = visitor(method); - } - if (method != new_method) { - im_table->Set(i, new_method, kRuntimePointerSize); - } - } - } - - void CopyFieldArrays(ObjPtr<mirror::Class> cls, uint32_t class_image_address) - REQUIRES_SHARED(Locks::mutator_lock_) { - LengthPrefixedArray<ArtField>* fields[] = { - cls->GetSFieldsPtr(), cls->GetIFieldsPtr(), - }; - for (LengthPrefixedArray<ArtField>* cur_fields : fields) { - if (cur_fields != nullptr) { - // Copy the array. - size_t number_of_fields = cur_fields->size(); - size_t size = LengthPrefixedArray<ArtField>::ComputeSize(number_of_fields); - size_t offset = art_fields_.size(); - art_fields_.resize(offset + size); - auto* dest_array = - reinterpret_cast<LengthPrefixedArray<ArtField>*>(art_fields_.data() + offset); - memcpy(dest_array, cur_fields, size); - native_relocations_[cur_fields] = - std::make_pair(NativeRelocationKind::kArtFieldArray, offset); - - // Update the class pointer of individual fields. - for (size_t i = 0; i != number_of_fields; ++i) { - dest_array->At(i).GetDeclaringClassAddressWithoutBarrier()->Assign( - reinterpret_cast<mirror::Class*>(class_image_address)); - } - } - } - } - - void CopyMethodArrays(ObjPtr<mirror::Class> cls, uint32_t class_image_address) - REQUIRES_SHARED(Locks::mutator_lock_) { - size_t number_of_methods = cls->NumMethods(); - if (number_of_methods == 0) { - return; - } - - size_t size = LengthPrefixedArray<ArtMethod>::ComputeSize(number_of_methods); - size_t offset = art_methods_.size(); - art_methods_.resize(offset + size); - auto* dest_array = - reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(art_methods_.data() + offset); - memcpy(dest_array, cls->GetMethodsPtr(), size); - native_relocations_[cls->GetMethodsPtr()] = - std::make_pair(NativeRelocationKind::kArtMethodArray, offset); - - for (size_t i = 0; i != number_of_methods; ++i) { - ArtMethod* method = &cls->GetMethodsPtr()->At(i); - ArtMethod* copy = &dest_array->At(i); - - // Update the class pointer. - ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass(); - if (declaring_class == cls) { - copy->GetDeclaringClassAddressWithoutBarrier()->Assign( - reinterpret_cast<mirror::Class*>(class_image_address)); - } else { - DCHECK(method->IsCopied()); - if (!IsInBootImage(declaring_class.Ptr())) { - DCHECK(classes_.find(declaring_class->GetClassDef()) != classes_.end()); - copy->GetDeclaringClassAddressWithoutBarrier()->Assign( - reinterpret_cast<mirror::Class*>( - image_begin_ + sizeof(ImageHeader) + classes_[declaring_class->GetClassDef()])); - } - } - - // Record the native relocation of the method. - uintptr_t copy_offset = - reinterpret_cast<uintptr_t>(copy) - reinterpret_cast<uintptr_t>(art_methods_.data()); - native_relocations_[method] = std::make_pair(NativeRelocationKind::kArtMethod, copy_offset); - - // Ignore the single-implementation info for abstract method. - if (method->IsAbstract()) { - copy->SetHasSingleImplementation(false); - copy->SetSingleImplementation(nullptr, kRuntimePointerSize); - } - - // Set the entrypoint and data pointer of the method. - const std::vector<gc::space::ImageSpace*>& image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - DCHECK(!image_spaces.empty()); - const OatFile* oat_file = image_spaces[0]->GetOatFile(); - DCHECK(oat_file != nullptr); - const OatHeader& header = oat_file->GetOatHeader(); - const uint8_t* address = header.GetOatAddress(method->IsNative() - ? StubType::kQuickGenericJNITrampoline - : StubType::kQuickToInterpreterBridge); - copy->SetEntryPointFromQuickCompiledCode(address); - - if (method->IsNative()) { - StubType stub_type = method->IsCriticalNative() - ? StubType::kJNIDlsymLookupCriticalTrampoline - : StubType::kJNIDlsymLookupTrampoline; - copy->SetEntryPointFromJni(header.GetOatAddress(stub_type)); - } else if (method->IsInvokable()) { - DCHECK(method->HasCodeItem()) << method->PrettyMethod(); - ptrdiff_t code_item_offset = reinterpret_cast<const uint8_t*>(method->GetCodeItem()) - - method->GetDexFile()->DataBegin(); - copy->SetDataPtrSize( - reinterpret_cast<const void*>(code_item_offset), kRuntimePointerSize); - } - } - } - - void CopyImTable(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - ImTable* table = cls->GetImt(kRuntimePointerSize); - - // If the table is null or shared and/or already emitted, we can skip. - if (table == nullptr || IsInBootImage(table) || HasNativeRelocation(table)) { - return; - } - const size_t size = ImTable::SizeInBytes(kRuntimePointerSize); - size_t offset = im_tables_.size(); - im_tables_.resize(offset + size); - uint8_t* dest = im_tables_.data() + offset; - memcpy(dest, table, size); - native_relocations_[table] = std::make_pair(NativeRelocationKind::kImTable, offset); - } - - bool HasNativeRelocation(void* ptr) const { - return native_relocations_.find(ptr) != native_relocations_.end(); } bool WriteObjects(std::string* error_msg) { @@ -873,9 +373,8 @@ class RuntimeImageHelper { CopyObject(image_roots.Get()); } - // Emit string referenced in dex caches, and classes defined in the app class loader. - EmitStringsAndClasses(soa.Self(), dex_cache_array); - + // Copy objects stored in the dex caches. + VisitDexCaches(dex_cache_array); return true; } @@ -895,15 +394,12 @@ class RuntimeImageHelper { void operator()(ObjPtr<mirror::Object> obj, MemberOffset offset, - bool is_static) const + bool is_static ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { - // We don't copy static fields, instead classes will be marked as resolved - // and initialized at runtime. - ObjPtr<mirror::Object> ref = - is_static ? nullptr : obj->GetFieldObject<mirror::Object>(offset); + ObjPtr<mirror::Object> ref = obj->GetFieldObject<mirror::Object>(offset); mirror::Object* address = image_->GetOrComputeImageAddress(ref.Ptr()); mirror::Object* copy = - reinterpret_cast<mirror::Object*>(image_->objects_.data() + copy_offset_); + reinterpret_cast<mirror::Object*>(image_->image_data_.data() + copy_offset_); copy->GetFieldObjectReferenceAddr<kVerifyNone>(offset)->Assign(address); } @@ -919,85 +415,31 @@ class RuntimeImageHelper { size_t copy_offset_; }; - uint32_t CopyDexCache(ObjPtr<mirror::DexCache> cache) REQUIRES_SHARED(Locks::mutator_lock_) { - auto it = dex_caches_.find(cache->GetDexFile()); - if (it != dex_caches_.end()) { - return it->second; - } - uint32_t offset = CopyObject(cache); - dex_caches_[cache->GetDexFile()] = offset; - // For dex caches, clear pointers to data that will be set at runtime. - mirror::Object* copy = reinterpret_cast<mirror::Object*>(objects_.data() + offset); - reinterpret_cast<mirror::DexCache*>(copy)->ResetNativeArrays(); - reinterpret_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr); - return offset; - } - - uint32_t CopyClass(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - const dex::ClassDef* class_def = cls->GetClassDef(); - auto it = classes_.find(class_def); - if (it != classes_.end()) { - return it->second; - } - uint32_t offset = CopyObject(cls); - classes_[class_def] = offset; - - uint32_t hash = cls->DescriptorHash(); - // Save the hash, the `HashSet` implementation requires to find it. - class_hashes_[offset] = hash; - uint32_t class_image_address = image_begin_ + sizeof(ImageHeader) + offset; - bool inserted = - class_table_.InsertWithHash(ClassTable::TableSlot(class_image_address, hash), hash).second; - DCHECK(inserted) << "Class " << cls->PrettyDescriptor() - << " (" << cls.Ptr() << ") already inserted"; - - // Clear internal state. - mirror::Class* copy = reinterpret_cast<mirror::Class*>(objects_.data() + offset); - copy->SetClinitThreadId(static_cast<pid_t>(0u)); - copy->SetStatusInternal(cls->IsVerified() ? ClassStatus::kVerified : ClassStatus::kResolved); - copy->SetObjectSizeAllocFastPath(std::numeric_limits<uint32_t>::max()); - copy->SetAccessFlags(copy->GetAccessFlags() & ~kAccRecursivelyInitialized); - - // Clear static field values. - MemberOffset static_offset = cls->GetFirstReferenceStaticFieldOffset(kRuntimePointerSize); - memset(objects_.data() + offset + static_offset.Uint32Value(), - 0, - cls->GetClassSize() - static_offset.Uint32Value()); - - CopyFieldArrays(cls, class_image_address); - CopyMethodArrays(cls, class_image_address); - if (cls->ShouldHaveImt()) { - CopyImTable(cls); - } - - return offset; - } - - // Copy `obj` in `objects_` and relocate references. Returns the offset + // Copy `obj` in `image_data_` and relocate references. Returns the offset // within our buffer. uint32_t CopyObject(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) { - // Copy the object in `objects_`. + // Copy the object in `image_data_`. size_t object_size = obj->SizeOf(); - size_t offset = objects_.size(); + size_t offset = image_data_.size(); DCHECK(IsAligned<kObjectAlignment>(offset)); object_offsets_.push_back(offset); - objects_.resize(RoundUp(offset + object_size, kObjectAlignment)); - memcpy(objects_.data() + offset, obj.Ptr(), object_size); + image_data_.resize(RoundUp(image_data_.size() + object_size, kObjectAlignment)); + memcpy(image_data_.data() + offset, obj.Ptr(), object_size); object_section_size_ += RoundUp(object_size, kObjectAlignment); // Fixup reference pointers. FixupVisitor visitor(this, offset); obj->VisitReferences</*kVisitNativeRoots=*/ false>(visitor, visitor); - mirror::Object* copy = reinterpret_cast<mirror::Object*>(objects_.data() + offset); + mirror::Object* copy = reinterpret_cast<mirror::Object*>(image_data_.data() + offset); // Clear any lockword data. copy->SetLockWord(LockWord::Default(), /* as_volatile= */ false); - if (obj->IsString()) { - // Ensure a string always has a hashcode stored. This is checked at - // runtime because boot images don't want strings dirtied due to hashcode. - reinterpret_cast<mirror::String*>(copy)->GetHashCode(); + // For dex caches, clear pointers to data that will be set at runtime. + if (obj->IsDexCache()) { + reinterpret_cast<mirror::DexCache*>(copy)->ResetNativeArrays(); + reinterpret_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr); } return offset; } @@ -1096,28 +538,16 @@ class RuntimeImageHelper { // sections. ImageHeader header_; - // Contents of the various sections. - std::vector<uint8_t> objects_; - std::vector<uint8_t> art_fields_; - std::vector<uint8_t> art_methods_; - std::vector<uint8_t> im_tables_; + // Contents of the image sections. + std::vector<uint8_t> image_data_; - // Bitmap of live objects in `objects_`. Populated from `object_offsets_` + // Bitmap of live objects in `image_data_`. Populated from `object_offsets_` // once we know `object_section_size`. gc::accounting::ContinuousSpaceBitmap image_bitmap_; - // Sections stored in the header. - dchecked_vector<ImageSection> sections_; - - // A list of offsets in `objects_` where objects begin. + // A list of offsets in `image_data_` where objects begin. std::vector<uint32_t> object_offsets_; - std::map<const dex::ClassDef*, uint32_t> classes_; - std::map<const DexFile*, uint32_t> dex_caches_; - std::map<uint32_t, uint32_t> class_hashes_; - - std::map<void*, std::pair<NativeRelocationKind, uint32_t>> native_relocations_; - // Cached values of boot image information. const uint32_t boot_image_begin_; const uint32_t boot_image_size_; @@ -1133,13 +563,6 @@ class RuntimeImageHelper { // The intern table for strings that we will write to disk. InternTableSet intern_table_; - - // The class table holding classes that we will write to disk. - ClassTableSet class_table_; - - friend class ClassDescriptorHash; - friend class PruneVisitor; - friend class NativePointerVisitor; }; std::string RuntimeImage::GetRuntimeImagePath(const std::string& dex_location) { @@ -1178,94 +601,43 @@ bool RuntimeImage::WriteImageToDisk(std::string* error_msg) { return false; } - // Write objects. The header is written at the end in case we get killed. - if (out->Write(reinterpret_cast<const char*>(image.GetObjects().data()), - image.GetObjects().size(), - sizeof(ImageHeader)) != static_cast<int64_t>(image.GetObjects().size())) { + // Write section infos. The header is written at the end in case we get killed. + if (!out->Write(reinterpret_cast<const char*>(image.GetData().data()), + image.GetData().size(), + sizeof(ImageHeader))) { *error_msg = "Could not write image data to " + temp_path; - out->Erase(/*unlink=*/true); + out->Unlink(); return false; } { - // Write fields. - auto fields_section = image.GetHeader().GetImageSection(ImageHeader::kSectionArtFields); - if (out->Write(reinterpret_cast<const char*>(image.GetArtFields().data()), - fields_section.Size(), - fields_section.Offset()) != fields_section.Size()) { - *error_msg = "Could not write fields section " + temp_path; - out->Erase(/*unlink=*/true); - return false; - } - } - - { - // Write methods. - auto methods_section = image.GetHeader().GetImageSection(ImageHeader::kSectionArtMethods); - if (out->Write(reinterpret_cast<const char*>(image.GetArtMethods().data()), - methods_section.Size(), - methods_section.Offset()) != methods_section.Size()) { - *error_msg = "Could not write methods section " + temp_path; - out->Erase(/*unlink=*/true); - return false; - } - } - - { - // Write im tables. - auto im_tables_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImTables); - if (out->Write(reinterpret_cast<const char*>(image.GetImTables().data()), - im_tables_section.Size(), - im_tables_section.Offset()) != im_tables_section.Size()) { - *error_msg = "Could not write ImTable section " + temp_path; - out->Erase(/*unlink=*/true); - return false; - } - } - - { // Write intern string set. auto intern_section = image.GetHeader().GetImageSection(ImageHeader::kSectionInternedStrings); std::vector<uint8_t> intern_data(intern_section.Size()); image.GenerateInternData(intern_data); - if (out->Write(reinterpret_cast<const char*>(intern_data.data()), - intern_section.Size(), - intern_section.Offset()) != intern_section.Size()) { + if (!out->Write(reinterpret_cast<const char*>(intern_data.data()), + intern_section.Size(), + intern_section.Offset())) { *error_msg = "Could not write intern section " + temp_path; - out->Erase(/*unlink=*/true); - return false; - } - } - - { - // Write class table. - auto class_table_section = image.GetHeader().GetImageSection(ImageHeader::kSectionClassTable); - std::vector<uint8_t> class_table_data(class_table_section.Size()); - image.GenerateClassTableData(class_table_data); - if (out->Write(reinterpret_cast<const char*>(class_table_data.data()), - class_table_section.Size(), - class_table_section.Offset()) != class_table_section.Size()) { - *error_msg = "Could not write class table section " + temp_path; - out->Erase(/*unlink=*/true); + out->Unlink(); return false; } } // Write bitmap. auto bitmap_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImageBitmap); - if (out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()), - bitmap_section.Size(), - bitmap_section.Offset()) != bitmap_section.Size()) { + if (!out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()), + bitmap_section.Size(), + bitmap_section.Offset())) { *error_msg = "Could not write image bitmap " + temp_path; - out->Erase(/*unlink=*/true); + out->Unlink(); return false; } // Now write header. - if (out->Write(reinterpret_cast<const char*>(&image.GetHeader()), sizeof(ImageHeader), 0u) != - sizeof(ImageHeader)) { + if (!out->Write(reinterpret_cast<const char*>(&image.GetHeader()), sizeof(ImageHeader), 0u)) { *error_msg = "Could not write image header to " + temp_path; - out->Erase(/*unlink=*/true); + out->Unlink(); return false; } diff --git a/test/845-data-image/src-art/Main.java b/test/845-data-image/src-art/Main.java index 71dd770b8a..c8d3e621a2 100644 --- a/test/845-data-image/src-art/Main.java +++ b/test/845-data-image/src-art/Main.java @@ -18,76 +18,10 @@ import dalvik.system.DexFile; import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.util.concurrent.CyclicBarrier; -// Add an interface for testing generating classes and interfaces. -interface Itf { - public int someMethod(); - public default int someDefaultMethod() { return 42; } -} - -// Add a second interface with many methods to force a conflict in the IMT. We want a second -// interface to make sure `Itf` gets entries with the imt_unimplemented_method runtime method. -interface Itf2 { - default int defaultMethod1() { return 1; } - default int defaultMethod2() { return 2; } - default int defaultMethod3() { return 3; } - default int defaultMethod4() { return 4; } - default int defaultMethod5() { return 5; } - default int defaultMethod6() { return 6; } - default int defaultMethod7() { return 7; } - default int defaultMethod8() { return 8; } - default int defaultMethod9() { return 9; } - default int defaultMethod10() { return 10; } - default int defaultMethod11() { return 11; } - default int defaultMethod12() { return 12; } - default int defaultMethod13() { return 13; } - default int defaultMethod14() { return 14; } - default int defaultMethod15() { return 15; } - default int defaultMethod16() { return 16; } - default int defaultMethod17() { return 17; } - default int defaultMethod18() { return 18; } - default int defaultMethod19() { return 19; } - default int defaultMethod20() { return 20; } - default int defaultMethod21() { return 21; } - default int defaultMethod22() { return 22; } - default int defaultMethod23() { return 23; } - default int defaultMethod24() { return 24; } - default int defaultMethod25() { return 25; } - default int defaultMethod26() { return 26; } - default int defaultMethod27() { return 27; } - default int defaultMethod28() { return 28; } - default int defaultMethod29() { return 29; } - default int defaultMethod30() { return 30; } - default int defaultMethod31() { return 31; } - default int defaultMethod32() { return 32; } - default int defaultMethod33() { return 33; } - default int defaultMethod34() { return 34; } - default int defaultMethod35() { return 35; } - default int defaultMethod36() { return 36; } - default int defaultMethod37() { return 37; } - default int defaultMethod38() { return 38; } - default int defaultMethod39() { return 39; } - default int defaultMethod40() { return 40; } - default int defaultMethod41() { return 41; } - default int defaultMethod42() { return 42; } - default int defaultMethod43() { return 43; } - default int defaultMethod44() { return 44; } - default int defaultMethod45() { return 45; } - default int defaultMethod46() { return 46; } - default int defaultMethod47() { return 47; } - default int defaultMethod48() { return 48; } - default int defaultMethod49() { return 49; } - default int defaultMethod50() { return 50; } - default int defaultMethod51() { return 51; } -} - -class Itf2Impl implements Itf2 { -} +public class Main { -public class Main implements Itf { static String myString = "MyString"; static class MyThread extends Thread { @@ -109,7 +43,6 @@ public class Main implements Itf { } } } - public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); @@ -143,8 +76,6 @@ public class Main implements Itf { } } - runClassTests(); - // Test that we emit an empty lock word. If we are not, then this synchronized call here would // block on a run with the runtime image. synchronized (myString) { @@ -175,115 +106,6 @@ public class Main implements Itf { } } - static class MyProxy implements InvocationHandler { - - private Object obj; - - public static Object newInstance(Object obj) { - return java.lang.reflect.Proxy.newProxyInstance( - obj.getClass().getClassLoader(), - obj.getClass().getInterfaces(), - new MyProxy(obj)); - } - - private MyProxy(Object obj) { - this.obj = obj; - } - - public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { - return m.invoke(obj, args); - } - } - - public static Itf itf = new Main(); - public static Itf2 itf2 = new Itf2Impl(); - - public static void runClassTests() { - // Test Class.getName, app images expect all strings to have hash codes. - assertEquals("Main", Main.class.getName()); - - // Basic tests for invokes with a copied method. - assertEquals(3, new Main().someMethod()); - assertEquals(42, new Main().someDefaultMethod()); - - assertEquals(3, itf.someMethod()); - assertEquals(42, itf.someDefaultMethod()); - - // Test with a proxy class. - Itf foo = (Itf) MyProxy.newInstance(new Main()); - assertEquals(3, foo.someMethod()); - assertEquals(42, foo.someDefaultMethod()); - - // Call all interface methods to trigger the creation of a imt conflict method. - itf2.defaultMethod1(); - itf2.defaultMethod2(); - itf2.defaultMethod3(); - itf2.defaultMethod4(); - itf2.defaultMethod5(); - itf2.defaultMethod6(); - itf2.defaultMethod7(); - itf2.defaultMethod8(); - itf2.defaultMethod9(); - itf2.defaultMethod10(); - itf2.defaultMethod11(); - itf2.defaultMethod12(); - itf2.defaultMethod13(); - itf2.defaultMethod14(); - itf2.defaultMethod15(); - itf2.defaultMethod16(); - itf2.defaultMethod17(); - itf2.defaultMethod18(); - itf2.defaultMethod19(); - itf2.defaultMethod20(); - itf2.defaultMethod21(); - itf2.defaultMethod22(); - itf2.defaultMethod23(); - itf2.defaultMethod24(); - itf2.defaultMethod25(); - itf2.defaultMethod26(); - itf2.defaultMethod27(); - itf2.defaultMethod28(); - itf2.defaultMethod29(); - itf2.defaultMethod30(); - itf2.defaultMethod31(); - itf2.defaultMethod32(); - itf2.defaultMethod33(); - itf2.defaultMethod34(); - itf2.defaultMethod35(); - itf2.defaultMethod36(); - itf2.defaultMethod37(); - itf2.defaultMethod38(); - itf2.defaultMethod39(); - itf2.defaultMethod40(); - itf2.defaultMethod41(); - itf2.defaultMethod42(); - itf2.defaultMethod43(); - itf2.defaultMethod44(); - itf2.defaultMethod45(); - itf2.defaultMethod46(); - itf2.defaultMethod47(); - itf2.defaultMethod48(); - itf2.defaultMethod49(); - itf2.defaultMethod50(); - itf2.defaultMethod51(); - } - - private static void assertEquals(int expected, int actual) { - if (expected != actual) { - throw new Error("Expected " + expected + ", got " + actual); - } - } - - private static void assertEquals(String expected, String actual) { - if (!expected.equals(actual)) { - throw new Error("Expected \"" + expected + "\", got \"" + actual + "\""); - } - } - - public int someMethod() { - return 3; - } - private static native boolean hasOatFile(); private static native boolean hasImage(); private static native String getCompilerFilter(Class<?> cls); |