1 /*
2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "cds/aotClassInitializer.hpp"
26 #include "cds/archiveBuilder.hpp"
27 #include "cds/cdsHeapVerifier.hpp"
28 #include "classfile/classLoaderDataGraph.hpp"
29 #include "classfile/javaClasses.inline.hpp"
30 #include "classfile/moduleEntry.hpp"
31 #include "classfile/stringTable.hpp"
32 #include "classfile/symbolTable.hpp"
33 #include "classfile/systemDictionary.hpp"
34 #include "classfile/systemDictionaryShared.hpp"
35 #include "classfile/vmSymbols.hpp"
36 #include "logging/log.hpp"
37 #include "logging/logStream.hpp"
38 #include "memory/resourceArea.hpp"
39 #include "oops/fieldStreams.inline.hpp"
40 #include "oops/klass.inline.hpp"
41 #include "oops/oop.inline.hpp"
42 #include "oops/oopHandle.inline.hpp"
43 #include "runtime/fieldDescriptor.inline.hpp"
44
45 #if INCLUDE_CDS_JAVA_HEAP
46
47 // CDSHeapVerifier is used to check for problems where an archived object references a
48 // static field that may be get a different value at runtime.
49 //
50 // *Please see comments in aotClassInitializer.cpp for how to avoid such problems*,
51 //
52 // In the following example,
53 // Foo.get.test()
54 // correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled,
55 // because the archived archivedFoo.bar value is different than Bar.bar.
56 //
57 // class Foo {
58 // static final Foo archivedFoo; // this field is archived by CDS
59 // Bar bar;
60 // static {
61 // CDS.initializeFromArchive(Foo.class);
76 // }
77 //
78 // The check itself is simple:
79 // [1] CDSHeapVerifier::do_klass() collects all static fields
80 // [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them
81 // should be in [1]
82 //
83 // However, it's legal for *some* static fields to be referenced. The reasons are explained
84 // in the table of ADD_EXCL below.
85 //
86 // [A] In most of the cases, the module bootstrap code will update the static field
87 // to point to part of the archived module graph. E.g.,
88 // - java/lang/System::bootLayer
89 // - jdk/internal/loader/ClassLoaders::BOOT_LOADER
90 // [B] A final static String that's explicitly initialized inside <clinit>, but
91 // its value is deterministic and is always the same string literal.
92 // [C] A non-final static string that is assigned a string literal during class
93 // initialization; this string is never changed during -Xshare:dump.
94 // [D] Simple caches whose value doesn't matter.
95 // [E] Other cases (see comments in-line below).
96
97 CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
98 {
99 # define ADD_EXCL(...) { static const char* e[] = {__VA_ARGS__, nullptr}; add_exclusion(e); }
100
101 // Unfortunately this needs to be manually maintained. If
102 // test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedEnumTest.java fails,
103 // you might need to fix the core library code, or fix the ADD_EXCL entries below.
104 //
105 // class field type
106 ADD_EXCL("java/lang/ClassLoader$Holder", "scl"); // A
107 ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A
108 "ALL_UNNAMED_MODULE_SET", // A
109 "EVERYONE_MODULE", // A
110 "EVERYONE_SET"); // A
111
112 // This is the same as java/util/ImmutableCollections::EMPTY_SET, which is archived
113 ADD_EXCL("java/lang/reflect/AccessFlag$Location", "EMPTY_SET"); // E
114
115 ADD_EXCL("java/lang/System", "bootLayer"); // A
276 void do_field(fieldDescriptor* fd) {
277 if (fd->field_type() != T_OBJECT) {
278 return;
279 }
280
281 oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
282 if (static_obj_field != nullptr) {
283 if (_verifier->is_shared_secret_accessor(static_obj_field)) {
284 return;
285 }
286
287 Klass* field_type = static_obj_field->klass();
288 if (_exclusions != nullptr) {
289 for (const char** p = _exclusions; *p != nullptr; p++) {
290 if (fd->name()->equals(*p)) {
291 return;
292 }
293 }
294 }
295
296 if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) {
297 // This field looks like like this in the Java source:
298 // static final SOME_STRING = "a string literal";
299 // This string literal has been stored in the shared string table, so it's OK
300 // for the archived objects to refer to it.
301 return;
302 }
303 if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) {
304 // This field points to an archived mirror.
305 return;
306 }
307
308 if (field_type->is_instance_klass()) {
309 InstanceKlass* field_ik = InstanceKlass::cast(field_type);
310 if (field_ik->is_enum_subclass()) {
311 if (field_ik->has_archived_enum_objs() || ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
312 // This field is an Enum. If any instance of this Enum has been archived, we will archive
313 // all static fields of this Enum as well.
314 return;
315 }
467 if (info->orig_referrer() != nullptr) {
468 HeapShared::CachedOopInfo* ref = HeapShared::get_cached_oop_info(info->orig_referrer());
469 assert(ref != nullptr, "sanity");
470 level = trace_to_root(st, info->orig_referrer(), orig_obj, ref) + 1;
471 } else if (java_lang_String::is_instance(orig_obj)) {
472 st->print_cr("[%2d] (shared string table)", level++);
473 }
474 Klass* k = orig_obj->klass();
475 ResourceMark rm;
476 st->print("[%2d] ", level);
477 orig_obj->print_address_on(st);
478 st->print(" %s", k->internal_name());
479 if (java_lang_Class::is_instance(orig_obj)) {
480 st->print(" (%s::%s)", java_lang_Class::as_Klass(orig_obj)->external_name(), static_field_name(orig_obj, orig_field));
481 }
482 if (orig_field != nullptr) {
483 if (k->is_instance_klass()) {
484 TraceFields clo(orig_obj, orig_field, st);
485 InstanceKlass::cast(k)->do_nonstatic_fields(&clo);
486 } else {
487 assert(orig_obj->is_objArray(), "must be");
488 objArrayOop array = (objArrayOop)orig_obj;
489 for (int i = 0; i < array->length(); i++) {
490 if (array->obj_at(i) == orig_field) {
491 st->print(" @[%d]", i);
492 break;
493 }
494 }
495 }
496 }
497 st->cr();
498
499 return level;
500 }
501
502 void CDSHeapVerifier::verify() {
503 CDSHeapVerifier verf;
504 HeapShared::archived_object_cache()->iterate(&verf);
505 }
506
507 #endif // INCLUDE_CDS_JAVA_HEAP
|
1 /*
2 * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "cds/aotClassInitializer.hpp"
26 #include "cds/archiveBuilder.hpp"
27 #include "cds/cdsHeapVerifier.hpp"
28 #include "classfile/classLoaderDataGraph.hpp"
29 #include "classfile/javaClasses.inline.hpp"
30 #include "classfile/moduleEntry.hpp"
31 #include "classfile/stringTable.hpp"
32 #include "classfile/symbolTable.hpp"
33 #include "classfile/systemDictionary.hpp"
34 #include "classfile/systemDictionaryShared.hpp"
35 #include "classfile/vmSymbols.hpp"
36 #include "logging/log.hpp"
37 #include "logging/logStream.hpp"
38 #include "memory/resourceArea.hpp"
39 #include "oops/fieldStreams.inline.hpp"
40 #include "oops/klass.inline.hpp"
41 #include "oops/oop.inline.hpp"
42 #include "oops/oopCast.inline.hpp"
43 #include "oops/oopHandle.inline.hpp"
44 #include "runtime/fieldDescriptor.inline.hpp"
45
46 #if INCLUDE_CDS_JAVA_HEAP
47
48 // CDSHeapVerifier is used to check for problems where an archived object references a
49 // static field that may be get a different value at runtime.
50 //
51 // *Please see comments in aotClassInitializer.cpp for how to avoid such problems*,
52 //
53 // In the following example,
54 // Foo.get.test()
55 // correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled,
56 // because the archived archivedFoo.bar value is different than Bar.bar.
57 //
58 // class Foo {
59 // static final Foo archivedFoo; // this field is archived by CDS
60 // Bar bar;
61 // static {
62 // CDS.initializeFromArchive(Foo.class);
77 // }
78 //
79 // The check itself is simple:
80 // [1] CDSHeapVerifier::do_klass() collects all static fields
81 // [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them
82 // should be in [1]
83 //
84 // However, it's legal for *some* static fields to be referenced. The reasons are explained
85 // in the table of ADD_EXCL below.
86 //
87 // [A] In most of the cases, the module bootstrap code will update the static field
88 // to point to part of the archived module graph. E.g.,
89 // - java/lang/System::bootLayer
90 // - jdk/internal/loader/ClassLoaders::BOOT_LOADER
91 // [B] A final static String that's explicitly initialized inside <clinit>, but
92 // its value is deterministic and is always the same string literal.
93 // [C] A non-final static string that is assigned a string literal during class
94 // initialization; this string is never changed during -Xshare:dump.
95 // [D] Simple caches whose value doesn't matter.
96 // [E] Other cases (see comments in-line below).
97 //
98 // LIMITATION:
99 //
100 // CDSHeapVerifier can only check for problems with object identity. In the example above,
101 // if the Bar type has identity, the program's correctness requires that the identity
102 // of Foo.bar and Bar.bar to be equal. This requirement can be checked by CDSHeapVerifier.
103 //
104 // However, if Bar does not have identity (e.g., it's a value class, or is a primitive type),
105 // the program's correctness no longer requires that the identity of Foo.bar and Bar.bar
106 // to be equal (since they don't have an identity anymore). While the program's
107 // correctness may still have certain assumptions about Foo.bar and Bar.bar (such as the
108 // internal fields of these two values), such assumptions cannot be checked by CDSHeapVerifier.
109
110 CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
111 {
112 # define ADD_EXCL(...) { static const char* e[] = {__VA_ARGS__, nullptr}; add_exclusion(e); }
113
114 // Unfortunately this needs to be manually maintained. If
115 // test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedEnumTest.java fails,
116 // you might need to fix the core library code, or fix the ADD_EXCL entries below.
117 //
118 // class field type
119 ADD_EXCL("java/lang/ClassLoader$Holder", "scl"); // A
120 ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A
121 "ALL_UNNAMED_MODULE_SET", // A
122 "EVERYONE_MODULE", // A
123 "EVERYONE_SET"); // A
124
125 // This is the same as java/util/ImmutableCollections::EMPTY_SET, which is archived
126 ADD_EXCL("java/lang/reflect/AccessFlag$Location", "EMPTY_SET"); // E
127
128 ADD_EXCL("java/lang/System", "bootLayer"); // A
289 void do_field(fieldDescriptor* fd) {
290 if (fd->field_type() != T_OBJECT) {
291 return;
292 }
293
294 oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
295 if (static_obj_field != nullptr) {
296 if (_verifier->is_shared_secret_accessor(static_obj_field)) {
297 return;
298 }
299
300 Klass* field_type = static_obj_field->klass();
301 if (_exclusions != nullptr) {
302 for (const char** p = _exclusions; *p != nullptr; p++) {
303 if (fd->name()->equals(*p)) {
304 return;
305 }
306 }
307 }
308
309 if (!field_type->is_identity_class()) {
310 // See comment of LIMITATION above
311 // Any concrete value class will have a field ".null_reset" which holds an
312 // all-zero instance of the value class so it will not change between
313 // dump time and runtime.
314 return;
315 }
316
317 if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) {
318 // This field looks like like this in the Java source:
319 // static final SOME_STRING = "a string literal";
320 // This string literal has been stored in the shared string table, so it's OK
321 // for the archived objects to refer to it.
322 return;
323 }
324 if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) {
325 // This field points to an archived mirror.
326 return;
327 }
328
329 if (field_type->is_instance_klass()) {
330 InstanceKlass* field_ik = InstanceKlass::cast(field_type);
331 if (field_ik->is_enum_subclass()) {
332 if (field_ik->has_archived_enum_objs() || ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
333 // This field is an Enum. If any instance of this Enum has been archived, we will archive
334 // all static fields of this Enum as well.
335 return;
336 }
488 if (info->orig_referrer() != nullptr) {
489 HeapShared::CachedOopInfo* ref = HeapShared::get_cached_oop_info(info->orig_referrer());
490 assert(ref != nullptr, "sanity");
491 level = trace_to_root(st, info->orig_referrer(), orig_obj, ref) + 1;
492 } else if (java_lang_String::is_instance(orig_obj)) {
493 st->print_cr("[%2d] (shared string table)", level++);
494 }
495 Klass* k = orig_obj->klass();
496 ResourceMark rm;
497 st->print("[%2d] ", level);
498 orig_obj->print_address_on(st);
499 st->print(" %s", k->internal_name());
500 if (java_lang_Class::is_instance(orig_obj)) {
501 st->print(" (%s::%s)", java_lang_Class::as_Klass(orig_obj)->external_name(), static_field_name(orig_obj, orig_field));
502 }
503 if (orig_field != nullptr) {
504 if (k->is_instance_klass()) {
505 TraceFields clo(orig_obj, orig_field, st);
506 InstanceKlass::cast(k)->do_nonstatic_fields(&clo);
507 } else {
508 assert(orig_obj->is_refArray(), "must be");
509 refArrayOop array = oop_cast<refArrayOop>(orig_obj);
510 for (int i = 0; i < array->length(); i++) {
511 if (array->obj_at(i) == orig_field) {
512 st->print(" @[%d]", i);
513 break;
514 }
515 }
516 }
517 }
518 st->cr();
519
520 return level;
521 }
522
523 void CDSHeapVerifier::verify() {
524 CDSHeapVerifier verf;
525 HeapShared::archived_object_cache()->iterate(&verf);
526 }
527
528 #endif // INCLUDE_CDS_JAVA_HEAP
|