1 /* 2 * Copyright (c) 2024, 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/aotConstantPoolResolver.hpp" 26 #include "cds/archiveBuilder.hpp" 27 #include "cds/archiveUtils.inline.hpp" 28 #include "cds/cdsConfig.hpp" 29 #include "cds/finalImageRecipes.hpp" 30 #include "classfile/classLoader.hpp" 31 #include "classfile/javaClasses.hpp" 32 #include "classfile/systemDictionary.hpp" 33 #include "classfile/systemDictionaryShared.hpp" 34 #include "classfile/vmClasses.hpp" 35 #include "memory/oopFactory.hpp" 36 #include "memory/resourceArea.hpp" 37 #include "oops/constantPool.inline.hpp" 38 #include "runtime/handles.inline.hpp" 39 40 static FinalImageRecipes* _final_image_recipes = nullptr; 41 42 void* FinalImageRecipes::operator new(size_t size) throw() { 43 return ArchiveBuilder::current()->ro_region_alloc(size); 44 } 45 46 void FinalImageRecipes::record_recipes_impl() { 47 assert(CDSConfig::is_dumping_preimage_static_archive(), "must be"); 48 ResourceMark rm; 49 GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses(); 50 51 // Record the indys that have been resolved in the training run. These indys will be 52 // resolved during the final image assembly. 53 54 GrowableArray<InstanceKlass*> tmp_indy_klasses; 55 GrowableArray<Array<int>*> tmp_indy_cp_indices; 56 int total_indys_to_resolve = 0; 57 for (int i = 0; i < klasses->length(); i++) { 58 Klass* k = klasses->at(i); 59 if (k->is_instance_klass()) { 60 InstanceKlass* ik = InstanceKlass::cast(k); 61 GrowableArray<int> indices; 62 63 if (ik->constants()->cache() != nullptr) { 64 Array<ResolvedIndyEntry>* tmp_indy_entries = ik->constants()->cache()->resolved_indy_entries(); 65 if (tmp_indy_entries != nullptr) { 66 for (int i = 0; i < tmp_indy_entries->length(); i++) { 67 ResolvedIndyEntry* rie = tmp_indy_entries->adr_at(i); 68 int cp_index = rie->constant_pool_index(); 69 if (rie->is_resolved()) { 70 indices.append(cp_index); 71 } 72 } 73 } 74 } 75 76 if (indices.length() > 0) { 77 tmp_indy_klasses.append(ArchiveBuilder::current()->get_buffered_addr(ik)); 78 tmp_indy_cp_indices.append(ArchiveUtils::archive_array(&indices)); 79 total_indys_to_resolve += indices.length(); 80 } 81 } 82 } 83 84 _all_klasses = ArchiveUtils::archive_array(klasses); 85 ArchivePtrMarker::mark_pointer(&_all_klasses); 86 87 assert(tmp_indy_klasses.length() == tmp_indy_cp_indices.length(), "must be"); 88 if (tmp_indy_klasses.length() > 0) { 89 _indy_klasses = ArchiveUtils::archive_array(&tmp_indy_klasses); 90 _indy_cp_indices = ArchiveUtils::archive_array(&tmp_indy_cp_indices); 91 92 ArchivePtrMarker::mark_pointer(&_indy_klasses); 93 ArchivePtrMarker::mark_pointer(&_indy_cp_indices); 94 } 95 log_info(cds)("%d indies in %d classes will be resolved in final CDS image", total_indys_to_resolve, tmp_indy_klasses.length()); 96 } 97 98 void FinalImageRecipes::load_all_classes(TRAPS) { 99 assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); 100 Handle class_loader(THREAD, SystemDictionary::java_system_loader()); 101 for (int i = 0; i < _all_klasses->length(); i++) { 102 Klass* k = _all_klasses->at(i); 103 if (k->is_instance_klass()) { 104 InstanceKlass* ik = InstanceKlass::cast(k); 105 if (!ik->is_shared_unregistered_class() && !ik->is_hidden()) { 106 Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); 107 if (actual != ik) { 108 ResourceMark rm(THREAD); 109 log_error(cds)("Unable to resolve class from CDS archive: %s", ik->external_name()); 110 log_error(cds)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual)); 111 log_error(cds)("Please check if your VM command-line is the same as in the training run"); 112 MetaspaceShared::unrecoverable_writing_error(); 113 } 114 assert(ik->is_loaded(), "must be"); 115 ik->link_class(CHECK); 116 } 117 } 118 } 119 } 120 121 void FinalImageRecipes::apply_recipes_for_invokedynamic(TRAPS) { 122 assert(CDSConfig::is_dumping_final_static_archive(), "must be"); 123 124 if (CDSConfig::is_dumping_invokedynamic() && _indy_klasses != nullptr) { 125 assert(_indy_cp_indices != nullptr, "must be"); 126 for (int i = 0; i < _indy_klasses->length(); i++) { 127 InstanceKlass* ik = _indy_klasses->at(i); 128 ConstantPool* cp = ik->constants(); 129 Array<int>* cp_indices = _indy_cp_indices->at(i); 130 GrowableArray<bool> preresolve_list(cp->length(), cp->length(), false); 131 for (int j = 0; j < cp_indices->length(); j++) { 132 preresolve_list.at_put(cp_indices->at(j), true); 133 } 134 AOTConstantPoolResolver::preresolve_indy_cp_entries(THREAD, ik, &preresolve_list); 135 } 136 } 137 } 138 139 void FinalImageRecipes::record_recipes() { 140 _final_image_recipes = new FinalImageRecipes(); 141 _final_image_recipes->record_recipes_impl(); 142 } 143 144 void FinalImageRecipes::apply_recipes(TRAPS) { 145 assert(CDSConfig::is_dumping_final_static_archive(), "must be"); 146 if (_final_image_recipes != nullptr) { 147 _final_image_recipes->apply_recipes_impl(THREAD); 148 if (HAS_PENDING_EXCEPTION) { 149 log_error(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(), 150 java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION))); 151 log_error(cds)("Please check if your VM command-line is the same as in the training run"); 152 MetaspaceShared::unrecoverable_writing_error("Unexpected exception, use -Xlog:cds,exceptions=trace for detail"); 153 } 154 } 155 156 // Set it to null as we don't need to write this table into the final image. 157 _final_image_recipes = nullptr; 158 } 159 160 void FinalImageRecipes::apply_recipes_impl(TRAPS) { 161 load_all_classes(CHECK); 162 apply_recipes_for_invokedynamic(CHECK); 163 } 164 165 void FinalImageRecipes::serialize(SerializeClosure* soc) { 166 soc->do_ptr((void**)&_final_image_recipes); 167 }