summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/class_linker.cc40
-rw-r--r--runtime/gc/space/image_space.cc18
-rw-r--r--runtime/runtime_image.cc118
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()),