summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc153
1 files changed, 120 insertions, 33 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9030017b84..b7472250ad 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -104,6 +104,8 @@
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/object_reference.h"
+#include "mirror/object_reference-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
@@ -1171,18 +1173,31 @@ class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
gc::accounting::HeapBitmap* const live_bitmap_;
};
-class FixupInternVisitor {
+/*
+ * A class used to ensure that all strings in an AppImage have been properly
+ * interned.
+ */
+class VerifyStringInterningVisitor {
public:
- ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const
+ explicit VerifyStringInterningVisitor(const gc::space::ImageSpace& space) :
+ uninterned_string_found_(false),
+ space_(space),
+ intern_table_(*Runtime::Current()->GetInternTable()) {}
+
+ ALWAYS_INLINE
+ void TestObject(ObjPtr<mirror::Object> referred_obj) const
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (obj != nullptr && obj->IsString()) {
- const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString());
- return intern;
+ if (referred_obj != nullptr &&
+ space_.HasAddress(referred_obj.Ptr()) &&
+ referred_obj->IsString()) {
+ ObjPtr<mirror::String> referred_str = referred_obj->AsString();
+ uninterned_string_found_ = uninterned_string_found_ ||
+ (intern_table_.LookupStrong(Thread::Current(), referred_str) != referred_str);
}
- return obj;
}
- ALWAYS_INLINE void VisitRootIfNonNull(
+ ALWAYS_INLINE
+ void VisitRootIfNonNull(
mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (!root->IsNull()) {
@@ -1190,44 +1205,77 @@ class FixupInternVisitor {
}
}
- ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
- root->Assign(TryInsertIntern(root->AsMirrorPtr()));
+ TestObject(root->AsMirrorPtr());
}
// Visit Class Fields
- ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj,
- MemberOffset offset,
- bool is_static ATTRIBUTE_UNUSED) const
+ ALWAYS_INLINE
+ void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// There could be overlap between ranges, we must avoid visiting the same reference twice.
// Avoid the class field since we already fixed it up in FixupClassVisitor.
if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
// Updating images, don't do a read barrier.
- // Only string fields are fixed, don't do a verify.
- mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
- offset);
- obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref));
+ ObjPtr<mirror::Object> referred_obj =
+ obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
+
+ TestObject(referred_obj);
}
}
void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
ObjPtr<mirror::Reference> ref) const
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
- this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ operator()(ref, mirror::Reference::ReferentOffset(), false);
}
- void operator()(mirror::Object* obj) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (obj->IsDexCache()) {
- obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this);
- } else {
- // Don't visit native roots for non-dex-cache
- obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this);
- }
- }
+ mutable bool uninterned_string_found_;
+ const gc::space::ImageSpace& space_;
+ InternTable& intern_table_;
};
+/*
+ * This function verifies that string references in the AppImage have been
+ * properly interned. To be considered properly interned a reference must
+ * point to the same version of the string that the intern table does.
+ */
+bool VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks::mutator_lock_) {
+ const gc::accounting::ContinuousSpaceBitmap* bitmap = space.GetMarkBitmap();
+ const ImageHeader& image_header = space.GetImageHeader();
+ const uint8_t* target_base = space.GetMemMap()->Begin();
+ const ImageSection& objects_section = image_header.GetObjectsSection();
+ uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+ uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+ VerifyStringInterningVisitor visitor(space);
+ bitmap->VisitMarkedRange(objects_begin,
+ objects_end,
+ [&space, &visitor](mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (space.HasAddress(obj)) {
+ if (obj->IsDexCache()) {
+ obj->VisitReferences</* kVisitNativeRoots */ true,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
+ } else {
+ // Don't visit native roots for non-dex-cache as they can't contain
+ // native references to strings. This is verified during compilation
+ // by ImageWriter::VerifyNativeGCRootInvariants.
+ obj->VisitReferences</* kVisitNativeRoots */ false,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
+ }
+ }
+ });
+
+ return !visitor.uninterned_string_found_;
+}
+
// new_class_set is the set of classes that were read from the class table section in the image.
// If there was no class table section, it is null.
// Note: using a class here to avoid having to make ClassLinker internals public.
@@ -1268,6 +1316,7 @@ void AppImageClassLoadersAndDexCachesHelper::Update(
CHECK(!class_linker->FindDexCacheDataLocked(*dex_file).IsValid());
class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
}
+
if (kIsDebugBuild) {
CHECK(new_class_set != nullptr);
mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
@@ -1275,17 +1324,20 @@ void AppImageClassLoadersAndDexCachesHelper::Update(
for (size_t j = 0; j != num_types; ++j) {
// The image space is not yet added to the heap, avoid read barriers.
ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+
if (space->HasAddress(klass.Ptr())) {
DCHECK(!klass->IsErroneous()) << klass->GetStatus();
auto it = new_class_set->find(ClassTable::TableSlot(klass));
DCHECK(it != new_class_set->end());
DCHECK_EQ(it->Read(), klass);
ObjPtr<mirror::Class> super_class = klass->GetSuperClass();
+
if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
auto it2 = new_class_set->find(ClassTable::TableSlot(super_class));
DCHECK(it2 != new_class_set->end());
DCHECK_EQ(it2->Read(), super_class);
}
+
for (ArtMethod& m : klass->GetDirectMethods(kRuntimePointerSize)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
const void* oat_code = m.IsInvokable() ? class_linker->GetQuickOatCodeFor(&m) : code;
@@ -1296,6 +1348,7 @@ void AppImageClassLoadersAndDexCachesHelper::Update(
DCHECK_EQ(code, oat_code) << m.PrettyMethod();
}
}
+
for (ArtMethod& m : klass->GetVirtualMethods(kRuntimePointerSize)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
const void* oat_code = m.IsInvokable() ? class_linker->GetQuickOatCodeFor(&m) : code;
@@ -1311,20 +1364,54 @@ void AppImageClassLoadersAndDexCachesHelper::Update(
}
}
}
+
if (ClassLinker::kAppImageMayContainStrings) {
- // Fixup all the literal strings happens at app images which are supposed to be interned.
+ // Iterate over the string reference offsets stored in the image and intern
+ // the strings they point to.
+
ScopedTrace timing("AppImage:InternString");
const auto& image_header = space->GetImageHeader();
- const auto bitmap = space->GetMarkBitmap(); // bitmap of objects
const uint8_t* target_base = space->GetMemMap()->Begin();
- const ImageSection& objects_section = image_header.GetObjectsSection();
+ const ImageSection& sro_section = image_header.GetImageStringReferenceOffsetsSection();
+
+ size_t num_string_offsets = sro_section.Size() / sizeof(uint32_t);
+
+ if (kIsDebugBuild) {
+ LOG(INFO)
+ << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = "
+ << num_string_offsets;
+ }
- uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
- uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+ DCHECK(Runtime::Current()->GetInternTable() != nullptr);
- FixupInternVisitor fixup_intern_visitor;
- bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
+ InternTable& intern_table = *Runtime::Current()->GetInternTable();
+ const uint32_t* sro_base =
+ reinterpret_cast<const uint32_t*>(target_base + sro_section.Offset());
+
+ for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) {
+ if (HasNativeRefTag(sro_base[offset_index])) {
+ void* raw_field_addr = space->Begin() + ClearNativeRefTag(sro_base[offset_index]);
+ mirror::CompressedReference<mirror::Object>* objref_addr =
+ reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(raw_field_addr);
+ mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString();
+ DCHECK(referred_string != nullptr);
+
+ objref_addr->Assign(intern_table.InternStrong(referred_string));
+
+ } else {
+ void* raw_field_addr = space->Begin() + sro_base[offset_index];
+ mirror::HeapReference<mirror::Object>* objref_addr =
+ reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr);
+ mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString();
+ DCHECK(referred_string != nullptr);
+
+ objref_addr->Assign<false>(intern_table.InternStrong(referred_string));
+ }
+ }
+
+ DCHECK(VerifyStringInterning(*space));
}
+
if (kVerifyArtMethodDeclaringClasses) {
ScopedTrace timing("AppImage:VerifyDeclaringClasses");
ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);