diff options
| -rw-r--r-- | runtime/class_linker.cc | 40 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 18 | ||||
| -rw-r--r-- | runtime/runtime_image.cc | 118 |
3 files changed, 138 insertions, 38 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 151a0ced75..b24cda7471 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1588,24 +1588,36 @@ static void VisitInternedStringReferences( uint32_t raw_member_offset = sro_base[offset_index].second; DCHECK_ALIGNED(base_offset, 2); - DCHECK_ALIGNED(raw_member_offset, 2); ObjPtr<mirror::Object> obj_ptr = reinterpret_cast<mirror::Object*>(space->Begin() + base_offset); - MemberOffset member_offset(raw_member_offset); - ObjPtr<mirror::String> referred_string = - obj_ptr->GetFieldObject<mirror::String, + if (obj_ptr->IsDexCache() && raw_member_offset >= sizeof(mirror::DexCache)) { + // Special case for strings referenced from dex cache array. + uint32_t offset = raw_member_offset - sizeof(mirror::DexCache); + ObjPtr<mirror::String> referred_string = + obj_ptr->AsDexCache()->GetStringsArray()->Get(offset); + DCHECK(referred_string != nullptr); + ObjPtr<mirror::String> visited = visitor(referred_string); + if (visited != referred_string) { + obj_ptr->AsDexCache()->GetStringsArray()->Set(offset, visited.Ptr()); + } + } else { + DCHECK_ALIGNED(raw_member_offset, 2); + MemberOffset member_offset(raw_member_offset); + ObjPtr<mirror::String> referred_string = + obj_ptr->GetFieldObject<mirror::String, + kVerifyNone, + kWithoutReadBarrier, + /* kIsVolatile= */ false>(member_offset); + DCHECK(referred_string != nullptr); + + ObjPtr<mirror::String> visited = visitor(referred_string); + if (visited != referred_string) { + obj_ptr->SetFieldObject</* kTransactionActive= */ false, + /* kCheckTransaction= */ false, kVerifyNone, - kWithoutReadBarrier, - /* kIsVolatile= */ false>(member_offset); - DCHECK(referred_string != nullptr); - - ObjPtr<mirror::String> visited = visitor(referred_string); - if (visited != referred_string) { - obj_ptr->SetFieldObject</* kTransactionActive= */ false, - /* kCheckTransaction= */ false, - kVerifyNone, - /* kIsVolatile= */ false>(member_offset, visited); + /* kIsVolatile= */ false>(member_offset, visited); + } } } } diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 02ad72b01d..2e8f6be641 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -391,6 +391,17 @@ class ImageSpace::PatchObjectVisitor final { } } + template <typename T> void VisitGcRootDexCacheArray(mirror::GcRootArray<T>* array) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (array == nullptr) { + return; + } + uint32_t size = reinterpret_cast<uint32_t*>(array)[-1]; + for (uint32_t i = 0; i < size; ++i) { + PatchGcRoot(&array->GetGcRoot(i)); + } + } + void VisitDexCacheArrays(ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::NativeArray<ArtMethod>* old_resolved_methods = dex_cache->GetResolvedMethodsArray(); @@ -406,6 +417,13 @@ class ImageSpace::PatchObjectVisitor final { dex_cache->SetResolvedFieldsArray(resolved_fields); VisitNativeDexCacheArray(resolved_fields); } + + mirror::GcRootArray<mirror::String>* old_strings = dex_cache->GetStringsArray(); + if (old_strings != nullptr) { + mirror::GcRootArray<mirror::String>* strings = native_visitor_(old_strings); + dex_cache->SetStringsArray(strings); + VisitGcRootDexCacheArray(strings); + } } template <bool kMayBeNull = true, typename T> diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc index b65e6349b0..6ad497548b 100644 --- a/runtime/runtime_image.cc +++ b/runtime/runtime_image.cc @@ -178,6 +178,10 @@ class RuntimeImageHelper { return dex_cache_arrays_; } + const std::vector<AppImageReferenceOffsetInfo>& GetStringReferenceOffsets() const { + return string_reference_offsets_; + } + const ImageHeader& GetHeader() const { return header_; } @@ -272,7 +276,8 @@ class RuntimeImageHelper { // 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); + sections_[ImageHeader::kSectionStringReferenceOffsets] = ImageSection( + cur_pos, string_reference_offsets_.size() * sizeof(string_reference_offsets_[0])); // Round up to the alignment dex caches arrays expects. cur_pos = @@ -377,24 +382,6 @@ class RuntimeImageHelper { 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 - // table with these strings. - for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) { - ObjPtr<mirror::String> str = dex_cache->GetResolvedString(dex::StringIndex(i)); - if (str != nullptr && !IsInBootImage(str.Ptr())) { - uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode()); - DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode())) - << "Dex cache strings should be interned"; - if (intern_table_.FindWithHash(str.Ptr(), hash) == intern_table_.end()) { - uint32_t offset = CopyObject(str); - intern_table_.InsertWithHash(image_begin_ + offset + sizeof(ImageHeader), hash); - } - } - } - } - // Helper class to collect classes that we will generate in the image. class ClassTableVisitor { public: @@ -523,14 +510,12 @@ class RuntimeImageHelper { ArenaVector<Handle<mirror::Class>>& classes_to_write_; }; - void EmitStringsAndClasses(Thread* self, - Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array) + void EmitClasses(Thread* self, 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); @@ -693,6 +678,11 @@ class RuntimeImageHelper { mirror::NativeArray<ArtField>* old_field_array = cache->GetResolvedFieldsArray(); cache->SetResolvedFieldsArray(visitor(old_field_array)); RelocateNativeDexCacheArray(old_field_array, dex_file.NumFieldIds(), visitor); + + mirror::GcRootArray<mirror::String>* old_strings_array = cache->GetStringsArray(); + cache->SetStringsArray(visitor(old_strings_array)); + // No need to relocate string entries in the array, we have already done this when + // creating the DexCache copy. } void RelocateNativePointers() { @@ -983,8 +973,9 @@ 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); + // Emit classes defined in the app class loader (which will also indrirectly + // emit dex caches and their arrays). + EmitClasses(soa.Self(), dex_cache_array); return true; } @@ -1052,6 +1043,30 @@ class RuntimeImageHelper { native_relocations_[array] = std::make_pair(relocation_kind, offset); } + template <typename T> + mirror::GcRootArray<T>* CreateGcRootDexCacheArray(uint32_t num_entries, + uint32_t max_entries, + mirror::GcRootArray<T>* array) { + if (array == nullptr) { + return nullptr; + } + size_t size = num_entries * sizeof(GcRoot<T>); + + bool only_startup = !mirror::DexCache::ShouldAllocateFullArray(num_entries, max_entries); + std::vector<uint8_t>& data = only_startup ? metadata_ : dex_cache_arrays_; + NativeRelocationKind relocation_kind = only_startup + ? NativeRelocationKind::kStartupNativeDexCacheArray + : NativeRelocationKind::kFullNativeDexCacheArray; + size_t offset = data.size() + sizeof(uint32_t); + data.resize(offset + size); + // We need to store `num_entries` because ImageSpace doesn't have + // access to the dex files when relocating dex caches. + reinterpret_cast<uint32_t*>(data.data() + offset)[-1] = num_entries; + native_relocations_[array] = std::make_pair(relocation_kind, offset); + + return reinterpret_cast<mirror::GcRootArray<T>*>(data.data() + 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()) { @@ -1064,6 +1079,7 @@ class RuntimeImageHelper { reinterpret_cast<mirror::DexCache*>(copy)->ResetNativeArrays(); reinterpret_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr); + // Copy the ArtMethod array. mirror::NativeArray<ArtMethod>* resolved_methods = cache->GetResolvedMethodsArray(); CopyNativeDexCacheArray(cache->GetDexFile()->NumMethodIds(), mirror::DexCache::kDexCacheMethodCacheSize, @@ -1071,6 +1087,7 @@ class RuntimeImageHelper { // Store the array pointer in the dex cache, which will be relocated at the end. reinterpret_cast<mirror::DexCache*>(copy)->SetResolvedMethodsArray(resolved_methods); + // Copy the ArtField array. mirror::NativeArray<ArtField>* resolved_fields = cache->GetResolvedFieldsArray(); CopyNativeDexCacheArray(cache->GetDexFile()->NumFieldIds(), mirror::DexCache::kDexCacheFieldCacheSize, @@ -1078,6 +1095,46 @@ class RuntimeImageHelper { // Store the array pointer in the dex cache, which will be relocated at the end. reinterpret_cast<mirror::DexCache*>(copy)->SetResolvedFieldsArray(resolved_fields); + // Copy the string array. + mirror::GcRootArray<mirror::String>* strings = cache->GetStringsArray(); + mirror::GcRootArray<mirror::String>* new_strings = + CreateGcRootDexCacheArray(cache->GetDexFile()->NumStringIds(), + mirror::DexCache::kDexCacheStringCacheSize, + strings); + // Store the array pointer in the dex cache, which will be relocated at the end. + reinterpret_cast<mirror::DexCache*>(copy)->SetStringsArray(strings); + + // The code below copies new objects, so invalidate the address we have for + // `copy`. + copy = nullptr; + if (strings != nullptr) { + for (uint32_t i = 0; i < cache->GetDexFile()->NumStringIds(); ++i) { + ObjPtr<mirror::String> str = strings->Get(i); + if (str == nullptr || IsInBootImage(str.Ptr())) { + new_strings->Set(i, str.Ptr()); + } else { + uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode()); + DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode())) + << "Dex cache strings should be interned"; + auto it2 = intern_table_.FindWithHash(str.Ptr(), hash); + if (it2 == intern_table_.end()) { + uint32_t string_offset = CopyObject(str); + uint32_t address = image_begin_ + string_offset + sizeof(ImageHeader); + intern_table_.InsertWithHash(address, hash); + new_strings->Set(i, reinterpret_cast<mirror::String*>(address)); + } else { + new_strings->Set(i, reinterpret_cast<mirror::String*>(*it2)); + } + // To not confuse string references from the dex cache object and + // string references from the array, we put an offset bigger than the + // size of a DexCache object. ClassLinker::VisitInternedStringReferences + // knows how to decode this offset. + string_reference_offsets_.emplace_back( + sizeof(ImageHeader) + offset, sizeof(mirror::DexCache) + i); + } + } + } + return offset; } @@ -1256,6 +1313,8 @@ class RuntimeImageHelper { std::vector<uint8_t> metadata_; std::vector<uint8_t> dex_cache_arrays_; + std::vector<AppImageReferenceOffsetInfo> string_reference_offsets_; + // Bitmap of live objects in `objects_`. Populated from `object_offsets_` // once we know `object_section_size`. gc::accounting::ContinuousSpaceBitmap image_bitmap_; @@ -1451,6 +1510,17 @@ bool RuntimeImage::WriteImageToDisk(std::string* error_msg) { return false; } + // Write string offset array section. + auto string_offsets_section = + image.GetHeader().GetImageSection(ImageHeader::kSectionStringReferenceOffsets); + if (out->Write(reinterpret_cast<const char*>(image.GetStringReferenceOffsets().data()), + string_offsets_section.Size(), + string_offsets_section.Offset()) != string_offsets_section.Size()) { + *error_msg = "Could not write string reference offsets " + temp_path; + out->Erase(/*unlink=*/true); + return false; + } + // Write dex cache array section. auto dex_cache_section = image.GetHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays); if (out->Write(reinterpret_cast<const char*>(image.GetDexCacheArrays().data()), |