From 7aa11a492b44ac21c345b4beec5c9befd6f602bb Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 25 Mar 2026 13:48:24 +0000 Subject: [PATCH 1/4] 8377512: AOT cache creation fails with invalid native pointer Backport-of: 00c1f4b6248e116c8839b1fb19ce20182711667a --- .../share/cds/aotConstantPoolResolver.cpp | 55 ++++++++++++++++++- .../share/cds/aotConstantPoolResolver.hpp | 7 ++- src/hotspot/share/cds/archiveHeapWriter.cpp | 15 ++++- src/hotspot/share/oops/cpCache.cpp | 27 ++++++++- .../cds/appcds/aotCache/ExcludedClasses.java | 48 +++++++++++++++- 5 files changed, 141 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index 6ab63418e09..3ea0c76f870 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -81,6 +81,7 @@ bool AOTConstantPoolResolver::is_resolution_deterministic(ConstantPool* cp, int bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) { assert(!is_in_archivebuilder_buffer(cp_holder), "sanity"); assert(!is_in_archivebuilder_buffer(resolved_class), "sanity"); + assert_at_safepoint(); // try_add_candidate() is called below and requires to be at safepoint. if (resolved_class->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(resolved_class); @@ -285,7 +286,15 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m break; case Bytecodes::_invokehandle: - InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK); + if (CDSConfig::is_dumping_method_handles()) { + ResolvedMethodEntry* method_entry = cp->resolved_method_entry_at(raw_index); + int cp_index = method_entry->constant_pool_index(); + Symbol* sig = cp->uncached_signature_ref_at(cp_index); + Klass* k; + if (check_methodtype_signature(cp(), sig, &k, true)) { + InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK); + } + } break; default: @@ -339,7 +348,7 @@ void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, In // Check the MethodType signatures used by parameters to the indy BSMs. Make sure we don't // use types that have been excluded, or else we might end up creating MethodTypes that cannot be stored // in the AOT cache. -bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret) { +bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret, bool is_invokehandle) { ResourceMark rm; for (SignatureStream ss(sig); !ss.is_done(); ss.next()) { if (ss.is_reference()) { @@ -352,11 +361,18 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo if (SystemDictionaryShared::should_be_excluded(k)) { if (log_is_enabled(Warning, aot, resolve)) { ResourceMark rm; - log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); + log_warning(aot, resolve)("Cannot aot-resolve %s because %s is excluded", + is_invokehandle ? "invokehandle" : "Lambda proxy", + k->external_name()); } return false; } + // cp->pool_holder() must be able to resolve k in production run + precond(CDSConfig::is_dumping_aot_linked_classes()); + precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + if (ss.at_return_type() && return_type_ret != nullptr) { *return_type_ret = k; } @@ -414,11 +430,44 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant return false; } + // klass and sigature of the method (no need to check the method name) Symbol* sig = cp->method_handle_signature_ref_at(mh_index); + Symbol* klass_name = cp->klass_name_at(cp->method_handle_klass_index_at(mh_index)); + if (log_is_enabled(Debug, aot, resolve)) { ResourceMark rm; log_debug(aot, resolve)("Checking MethodType of MethodHandle for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string()); } + + { + Klass* k = find_loaded_class(Thread::current(), cp->pool_holder()->class_loader(), klass_name); + if (k == nullptr) { + // Dumping AOT cache: all classes should have been loaded by FinalImageRecipes::load_all_classes(). k must have + // been a class that was excluded when FinalImageRecipes recorded all classes at the end of the training run. + // + // Dumping static CDS archive: all classes in the classlist have already been loaded, before we resolve + // constants. k must have been a class that was excluded when the classlist was written + // at the end of the training run. + if (log_is_enabled(Warning, aot, resolve)) { + ResourceMark rm; + log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is not loaded", klass_name->as_C_string()); + } + return false; + } + if (SystemDictionaryShared::should_be_excluded(k)) { + if (log_is_enabled(Warning, aot, resolve)) { + ResourceMark rm; + log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name()); + } + return false; + } + + // cp->pool_holder() must be able to resolve k in production run + precond(CDSConfig::is_dumping_aot_linked_classes()); + precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data())); + precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data())); + } + return check_methodtype_signature(cp, sig); } diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp index 89d9d5c2476..6ad762b4121 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,10 @@ class AOTConstantPoolResolver : AllStatic { static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index, GrowableArray* resolve_fmi_list, TRAPS); - static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr); +public: + static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr, bool is_invokehandle = false); + +private: static bool check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig); static bool check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i); static bool check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i); diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index ee1e334e84b..39c23dab193 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -754,8 +754,19 @@ void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) { Metadata** buffered_field_addr = requested_addr_to_buffered_addr(requested_field_addr); Metadata* native_ptr = *buffered_field_addr; guarantee(native_ptr != nullptr, "sanity"); - guarantee(ArchiveBuilder::current()->has_been_buffered((address)native_ptr), - "Metadata %p should have been archived", native_ptr); + + if (RegeneratedClasses::has_been_regenerated(native_ptr)) { + native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr); + } + + if (!ArchiveBuilder::current()->has_been_archived((address)native_ptr)) { + ResourceMark rm; + LogStreamHandle(Error, aot) log; + log.print("Marking native pointer for oop %p (type = %s, offset = %d)", + cast_from_oop(src_obj), src_obj->klass()->external_name(), field_offset); + src_obj->print_on(&log); + fatal("Metadata %p should have been archived", native_ptr); + } address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr); address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr); diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index a855639e74b..fd8b9f1fff2 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -550,6 +550,8 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv return false; } + int cp_index = method_entry->constant_pool_index(); + if (!method_entry->is_resolved(Bytecodes::_invokevirtual)) { if (method_entry->method() == nullptr) { return false; @@ -557,9 +559,30 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv if (method_entry->method()->is_continuation_native_intrinsic()) { return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } + if (method_entry->is_resolved(Bytecodes::_invokehandle)) { + if (!CDSConfig::is_dumping_method_handles()) { + // invokehandle depends on archived MethodType and LambdaForms. + return false; + } + + Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index); + Klass* k; + if (!AOTConstantPoolResolver::check_methodtype_signature(constant_pool(), sig, &k, true)) { + // invokehandles that were resolved in the training run should have been filtered in + // AOTConstantPoolResolver::maybe_resolve_fmi_ref so we shouldn't come to here. + // + // If we come here it's because the AOT assembly phase has executed an invokehandle + // that uses an excluded type like jdk.jfr.Event. This should not happen because the + // AOT assembly phase should execute only a very limited set of Java code. + ResourceMark rm; + fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type"); + } + } + if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) { + return false; + } } - int cp_index = method_entry->constant_pool_index(); assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity"); if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java index f50a2d1f905..dcd98282d69 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar * TestApp * TestApp$Foo + * TestApp$Foo$A * TestApp$Foo$Bar * TestApp$Foo$ShouldBeExcluded * TestApp$Foo$ShouldBeExcludedChild @@ -43,6 +44,8 @@ */ import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -166,6 +169,8 @@ static int hotSpot() { long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 1000) { lambdaHotSpot(); + lambdaHotSpot2(); + invokeHandleHotSpot(); s.hotSpot2(); b.hotSpot3(); Taz.hotSpot4(); @@ -208,12 +213,52 @@ static void lambdaHotSpot() { } } + interface A { + Object get(); + } + + // Lambdas that refer to excluded classes should not be AOT-resolved + static void lambdaHotSpot2() { + long start = System.currentTimeMillis(); + A a = ShouldBeExcluded::new; + while (System.currentTimeMillis() - start < 20) { + Object obj = (ShouldBeExcluded)a.get(); + } + } + + static void invokeHandleHotSpot() { + try { + invokeHandleHotSpotImpl(); + } catch (Throwable t) { + throw new RuntimeException("Unexpected", t); + } + } + + static void invokeHandleHotSpotImpl() throws Throwable { + MethodHandle constructorHandle = + MethodHandles.lookup().unreflectConstructor(ShouldBeExcluded.class.getConstructor()); + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 20) { + // The JVM rewrites this to: + // invokehandle + // + // The AOT cache must not contain a java.lang.invoke.MethodType that refers to the + // ShouldBeExcluded class. + ShouldBeExcluded o = (ShouldBeExcluded)constructorHandle.invoke(); + if (o.getClass() != ShouldBeExcluded.class) { + throw new RuntimeException("Unexpected object: " + o); + } + } + } + static void doit(Runnable r) { r.run(); } // All subclasses of jdk.jfr.Event are excluded from the CDS archive. static class ShouldBeExcluded extends jdk.jfr.Event { + public ShouldBeExcluded() {} + int f = (int)(System.currentTimeMillis()) + 123; int m() { return f + 456; @@ -281,4 +326,3 @@ static void hotSpot4() { } } } - From 3a7fb8f19594fe143a23a6471639f2c9de0314b2 Mon Sep 17 00:00:00 2001 From: Anton Voznia Date: Fri, 27 Mar 2026 12:11:43 +0100 Subject: [PATCH 2/4] Rmove get_regenerated_object --- src/hotspot/share/cds/archiveHeapWriter.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index 39c23dab193..6c6ab901267 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -755,10 +755,6 @@ void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) { Metadata* native_ptr = *buffered_field_addr; guarantee(native_ptr != nullptr, "sanity"); - if (RegeneratedClasses::has_been_regenerated(native_ptr)) { - native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr); - } - if (!ArchiveBuilder::current()->has_been_archived((address)native_ptr)) { ResourceMark rm; LogStreamHandle(Error, aot) log; From 41ead06b56acdedf6d8aacc459d9b3e5b64a04d4 Mon Sep 17 00:00:00 2001 From: Anton Voznia Date: Fri, 27 Mar 2026 16:30:26 +0100 Subject: [PATCH 3/4] Remove code from JDK-8334898 --- src/hotspot/share/oops/cpCache.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index fd8b9f1fff2..70fb7cc8e53 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -560,10 +560,6 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } if (method_entry->is_resolved(Bytecodes::_invokehandle)) { - if (!CDSConfig::is_dumping_method_handles()) { - // invokehandle depends on archived MethodType and LambdaForms. - return false; - } Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index); Klass* k; @@ -578,9 +574,6 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type"); } } - if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) { - return false; - } } assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity"); From 3646fe3fde980e4bd02874d82f5050f7381f332a Mon Sep 17 00:00:00 2001 From: Anton Voznia Date: Fri, 27 Mar 2026 18:15:06 +0100 Subject: [PATCH 4/4] Fix --- src/hotspot/share/oops/cpCache.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 70fb7cc8e53..b0f629bf572 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -560,6 +560,9 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } if (method_entry->is_resolved(Bytecodes::_invokehandle)) { + if (!CDSConfig::is_dumping_method_handles()) { + return false; + } Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index); Klass* k; @@ -573,6 +576,7 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv ResourceMark rm; fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type"); } + return true; } } @@ -586,13 +590,6 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv method_entry->is_resolved(Bytecodes::_invokevirtual) || method_entry->is_resolved(Bytecodes::_invokespecial)) { return true; - } else if (method_entry->is_resolved(Bytecodes::_invokehandle)) { - if (CDSConfig::is_dumping_method_handles()) { - // invokehandle depends on archived MethodType and LambdaForms. - return true; - } else { - return false; - } } else { return false; }