diff options
author | 2022-12-20 16:25:58 +0000 | |
---|---|---|
committer | 2023-01-17 10:15:09 +0000 | |
commit | d80bbba155fd18a38075cf6f626bee382c2b492c (patch) | |
tree | 90a0282dca2c05f3b3484d7e21de5d4b6213e5b7 /runtime/class_linker.cc | |
parent | 9e3761d6b98e090cff2e30e0f5e9714f434dd0f2 (diff) |
Keep classes alive for stack traces with copied methods.
Test: Additional test in 141-class-unload.
Test: testrunner.py --host --optimizing
Bug: 263254495
Change-Id: Iea925ab96fb7c8d1b825c0b100e689ab722d3159
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r-- | runtime/class_linker.cc | 68 |
1 files changed, 66 insertions, 2 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 8082d46e25..eaad1f017a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2056,7 +2056,7 @@ bool ClassLinker::AddImageSpace( } else if (special_root->IsIntArray()) { size_t count = special_root->AsIntArray()->GetLength(); if (oat_file->GetVdexFile()->GetNumberOfDexFiles() != count) { - *error_msg = "Cheksums count does not match"; + *error_msg = "Checksums count does not match"; return false; } static_assert(sizeof(VdexFile::VdexChecksum) == sizeof(int32_t)); @@ -2065,7 +2065,7 @@ bool ClassLinker::AddImageSpace( const VdexFile::VdexChecksum* vdex_checksums = oat_file->GetVdexFile()->GetDexChecksumsArray(); if (memcmp(art_checksums, vdex_checksums, sizeof(VdexFile::VdexChecksum) * count) != 0) { - *error_msg = "Image and vdex cheksums did not match"; + *error_msg = "Image and vdex checksums did not match"; return false; } } else if (IsBootClassLoader(special_root.Get())) { @@ -10399,9 +10399,73 @@ ObjPtr<mirror::Class> ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* meth CHECK(method->IsCopied()); FindVirtualMethodHolderVisitor visitor(method, image_pointer_size_); VisitClasses(&visitor); + DCHECK(visitor.holder_ != nullptr); return visitor.holder_; } +ObjPtr<mirror::ClassLoader> ClassLinker::GetHoldingClassLoaderOfCopiedMethod(Thread* self, + ArtMethod* method) { + // Note: `GetHoldingClassOfCopiedMethod(method)` is a lot more expensive than finding + // the class loader, so we're using it only to verify the result in debug mode. + CHECK(method->IsCopied()); + gc::Heap* heap = Runtime::Current()->GetHeap(); + // Check if the copied method is in the boot class path. + if (heap->IsBootImageAddress(method) || GetAllocatorForClassLoader(nullptr)->Contains(method)) { + DCHECK(GetHoldingClassOfCopiedMethod(method)->GetClassLoader() == nullptr); + return nullptr; + } + // Check if the copied method is in an app image. + // Note: Continuous spaces contain boot image spaces and app image spaces. + // However, they are sorted by address, so boot images are not trivial to skip. + ArrayRef<gc::space::ContinuousSpace* const> spaces(heap->GetContinuousSpaces()); + DCHECK_GE(spaces.size(), heap->GetBootImageSpaces().size()); + for (gc::space::ContinuousSpace* space : spaces) { + if (space->IsImageSpace()) { + gc::space::ImageSpace* image_space = space->AsImageSpace(); + size_t offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin(); + const ImageSection& methods_section = image_space->GetImageHeader().GetMethodsSection(); + if (offset - methods_section.Offset() < methods_section.Size()) { + // Grab the class loader from the first non-BCP class in the app image class table. + // Note: If we allow classes from arbitrary parent or library class loaders in app + // images, this shall need to be updated to actually search for the exact class. + const ImageSection& class_table_section = + image_space->GetImageHeader().GetClassTableSection(); + CHECK_NE(class_table_section.Size(), 0u); + const uint8_t* ptr = image_space->Begin() + class_table_section.Offset(); + size_t read_count = 0; + ClassTable::ClassSet class_set(ptr, /*make_copy_of_data=*/ false, &read_count); + CHECK(!class_set.empty()); + auto it = class_set.begin(); + // No read barrier needed for references to non-movable image classes. + while ((*it).Read<kWithoutReadBarrier>()->IsBootStrapClassLoaded()) { + ++it; + CHECK(it != class_set.end()); + } + ObjPtr<mirror::ClassLoader> class_loader = + (*it).Read<kWithoutReadBarrier>()->GetClassLoader(); + DCHECK(GetHoldingClassOfCopiedMethod(method)->GetClassLoader() == class_loader); + return class_loader; + } + } + } + // Otherwise, the method must be in one of the `LinearAlloc` memory areas. + jweak result = nullptr; + { + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + for (const ClassLoaderData& data : class_loaders_) { + if (data.allocator->Contains(method)) { + result = data.weak_root; + break; + } + } + } + CHECK(result != nullptr) << "Did not find allocator holding the copied method: " << method + << " " << method->PrettyMethod(); + // The `method` is alive, so the class loader must also be alive. + return ObjPtr<mirror::ClassLoader>::DownCast( + Runtime::Current()->GetJavaVM()->DecodeWeakGlobalAsStrong(result)); +} + bool ClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { // Should not be called on ClassLinker, only on AotClassLinker that overrides this. |