From ddb256911032cd7e6fae17c342261276066d8d25 Mon Sep 17 00:00:00 2001 From: Calvin Cheung Date: Mon, 24 Feb 2025 19:54:48 +0000 Subject: [PATCH] 8280682: Refactor AOT code source validation checks Co-authored-by: Ioi Lam Reviewed-by: iklam, asmehra, dholmes, kvn --- src/hotspot/share/cds/aotClassLocation.cpp | 999 ++++++++++++++++++ src/hotspot/share/cds/aotClassLocation.hpp | 269 +++++ src/hotspot/share/cds/archiveHeapLoader.hpp | 3 +- src/hotspot/share/cds/cdsConfig.cpp | 1 + src/hotspot/share/cds/cdsConstants.cpp | 1 - src/hotspot/share/cds/cdsProtectionDomain.cpp | 26 +- src/hotspot/share/cds/cppVtables.cpp | 1 - src/hotspot/share/cds/dynamicArchive.cpp | 15 +- src/hotspot/share/cds/filemap.cpp | 828 +-------------- src/hotspot/share/cds/filemap.hpp | 196 +--- src/hotspot/share/cds/heapShared.cpp | 6 +- src/hotspot/share/cds/metaspaceShared.cpp | 33 +- src/hotspot/share/cds/unregisteredClasses.cpp | 1 + src/hotspot/share/classfile/classLoader.cpp | 246 +---- src/hotspot/share/classfile/classLoader.hpp | 58 +- .../share/classfile/classLoader.inline.hpp | 43 +- .../share/classfile/classLoaderDataShared.cpp | 17 + .../share/classfile/classLoaderDataShared.hpp | 4 +- .../share/classfile/classLoaderExt.cpp | 254 +---- .../share/classfile/classLoaderExt.hpp | 90 +- src/hotspot/share/classfile/moduleEntry.cpp | 6 +- src/hotspot/share/classfile/stringTable.cpp | 1 - .../share/classfile/systemDictionary.cpp | 14 +- src/hotspot/share/memory/allocation.hpp | 3 +- src/hotspot/share/oops/klass.hpp | 6 +- src/hotspot/share/prims/jvmtiEnv.cpp | 4 +- src/hotspot/share/runtime/arguments.cpp | 1 - src/hotspot/share/runtime/threads.cpp | 9 - .../cds/appcds/BootClassPathMismatch.java | 23 +- .../runtime/cds/appcds/ClassPathAttr.java | 38 +- .../cds/appcds/CommonAppClasspath.java | 4 +- .../runtime/cds/appcds/NonExistClasspath.java | 4 +- .../cds/appcds/PrintSharedArchiveAndExit.java | 18 +- .../cds/appcds/SharedArchiveConsistency.java | 12 +- .../cds/appcds/TraceLongClasspath.java | 14 +- .../runtime/cds/appcds/WrongClasspath.java | 4 +- .../dynamicArchive/ArchiveConsistency.java | 18 +- .../dynamicArchive/WrongTopClasspath.java | 4 +- .../jigsaw/modulepath/ModulePathAndFMG.java | 47 +- .../lib/jdk/test/lib/cds/CDSArchiveUtils.java | 9 +- 40 files changed, 1537 insertions(+), 1793 deletions(-) create mode 100644 src/hotspot/share/cds/aotClassLocation.cpp create mode 100644 src/hotspot/share/cds/aotClassLocation.hpp diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp new file mode 100644 index 00000000000..8d453fe1773 --- /dev/null +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -0,0 +1,999 @@ +/* + * Copyright (c) 2003, 2025, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "cds/aotClassLocation.hpp" +#include "cds/archiveBuilder.hpp" +#include "cds/cdsConfig.hpp" +#include "cds/dynamicArchive.hpp" +#include "cds/filemap.hpp" +#include "cds/metaspaceShared.hpp" +#include "cds/serializeClosure.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/classLoaderData.hpp" +#include "classfile/javaClasses.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/metaspaceClosure.hpp" +#include "memory/resourceArea.hpp" +#include "oops/array.hpp" +#include "oops/objArrayKlass.hpp" +#include "runtime/arguments.hpp" +#include "utilities/classpathStream.hpp" +#include "utilities/formatBuffer.hpp" +#include "utilities/stringUtils.hpp" + +#include +#include + +AOTClassLocationConfig* AOTClassLocationConfig::_dumptime_instance = nullptr; +const AOTClassLocationConfig* AOTClassLocationConfig::_runtime_instance = nullptr; + +// A ClassLocationStream represents a list of code locations, which can be iterated using +// start() and has_next(). +class ClassLocationStream { +protected: + GrowableArray _array; + int _current; + + // Add one path to this stream. + void add_one_path(const char* path) { + _array.append(path); + } + + // Add all paths specified in cp; cp must be from -classpath or -Xbootclasspath/a. + void add_paths_in_classpath(const char* cp) { + ClasspathStream cp_stream(cp); + while (cp_stream.has_next()) { + add_one_path(cp_stream.get_next()); + } + } + +public: + ClassLocationStream() : _array(), _current(0) {} + + void print(outputStream* st) const { + const char* sep = ""; + for (int i = 0; i < _array.length(); i++) { + st->print("%s%s", sep, _array.at(i)); + sep = os::path_separator(); + } + } + + void add(ClassLocationStream& css) { + for (css.start(); css.has_next();) { + add_one_path(css.get_next()); + } + } + + // Iteration + void start() { _current = 0; } + bool has_next() const { return _current < _array.length(); } + const char* get_next() { + return _array.at(_current++); + } + + int current() const { return _current; } + bool is_empty() const { return _array.length() == 0; } +}; + +class BootCpClassLocationStream : public ClassLocationStream { +public: + BootCpClassLocationStream() : ClassLocationStream() { + // Arguments::get_boot_class_path() contains $JAVA_HOME/lib/modules, but we treat that separately + for (const char* bootcp = Arguments::get_boot_class_path(); *bootcp != '\0'; ++bootcp) { + if (*bootcp == *os::path_separator()) { + ++bootcp; + add_paths_in_classpath(bootcp); + break; + } + } + } +}; + +class AppCpClassLocationStream : public ClassLocationStream { +public: + AppCpClassLocationStream() : ClassLocationStream() { + const char* appcp = Arguments::get_appclasspath(); + if (strcmp(appcp, ".") == 0) { + appcp = ""; + } + add_paths_in_classpath(appcp); + } +}; + +class ModulePathClassLocationStream : public ClassLocationStream { + bool _has_non_jar_modules; +public: + ModulePathClassLocationStream(); + bool has_non_jar_modules() { return _has_non_jar_modules; } +}; + +// AllClassLocationStreams is used to iterate over all the code locations that +// are available to the application from -Xbootclasspath, -classpath and --module-path. +// When creating an AOT cache, we store the contents from AllClassLocationStreams +// into an array of AOTClassLocations. See AOTClassLocationConfig::dumptime_init_helper(). +// When loading the AOT cache in a production run, we compare the contents of the +// stored AOTClassLocations against the current AllClassLocationStreams to determine whether +// the AOT cache is compatible with the current JVM. See AOTClassLocationConfig::validate(). +class AllClassLocationStreams { + BootCpClassLocationStream _boot_cp; // Specified by -Xbootclasspath/a + AppCpClassLocationStream _app_cp; // Specified by -classpath + ModulePathClassLocationStream _module_path; // Specified by --module-path + ClassLocationStream _boot_and_app_cp; // Convenience for iterating over both _boot and _app +public: + BootCpClassLocationStream& boot_cp() { return _boot_cp; } + AppCpClassLocationStream& app_cp() { return _app_cp; } + ModulePathClassLocationStream& module_path() { return _module_path; } + ClassLocationStream& boot_and_app_cp() { return _boot_and_app_cp; } + + AllClassLocationStreams() : _boot_cp(), _app_cp(), _module_path(), _boot_and_app_cp() { + _boot_and_app_cp.add(_boot_cp); + _boot_and_app_cp.add(_app_cp); + } +}; + +static bool has_jar_suffix(const char* filename) { + // In jdk.internal.module.ModulePath.readModule(), it checks for the ".jar" suffix. + // Performing the same check here. + const char* dot = strrchr(filename, '.'); + if (dot != nullptr && strcmp(dot + 1, "jar") == 0) { + return true; + } + return false; +} + +static int compare_module_path_by_name(const char** p1, const char** p2) { + return strcmp(*p1, *p2); +} + +ModulePathClassLocationStream::ModulePathClassLocationStream() : ClassLocationStream(), _has_non_jar_modules(false) { + // Note: for handling of --module-path, see + // https://openjdk.org/jeps/261#Module-paths + // https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/module/ModuleFinder.html#of(java.nio.file.Path...) + + const char* jdk_module_path = Arguments::get_property("jdk.module.path"); + if (jdk_module_path == nullptr) { + return; + } + + ClasspathStream cp_stream(jdk_module_path); + while (cp_stream.has_next()) { + const char* path = cp_stream.get_next(); + DIR* dirp = os::opendir(path); + if (dirp == nullptr && errno == ENOTDIR && has_jar_suffix(path)) { + add_one_path(path); + } else if (dirp != nullptr) { + struct dirent* dentry; + bool found_jar = false; + while ((dentry = os::readdir(dirp)) != nullptr) { + const char* file_name = dentry->d_name; + if (has_jar_suffix(file_name)) { + size_t full_name_len = strlen(path) + strlen(file_name) + strlen(os::file_separator()) + 1; + char* full_name = NEW_RESOURCE_ARRAY(char, full_name_len); + int n = os::snprintf(full_name, full_name_len, "%s%s%s", path, os::file_separator(), file_name); + assert((size_t)n == full_name_len - 1, "Unexpected number of characters in string"); + add_one_path(full_name); + found_jar = true; + } else if (strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0) { + // Found some non jar entries + _has_non_jar_modules = true; + log_info(class, path)("Found non-jar path: '%s%s%s'", path, os::file_separator(), file_name); + } + } + if (!found_jar) { + log_info(class, path)("Found exploded module path: '%s'", path); + _has_non_jar_modules = true; + } + os::closedir(dirp); + } else { + _has_non_jar_modules = true; + } + } + + _array.sort(compare_module_path_by_name); +} + +AOTClassLocation* AOTClassLocation::allocate(JavaThread* current, const char* path, int index, + Group group, bool from_cpattr, bool is_jrt) { + size_t path_length = 0; + size_t manifest_length = 0; + bool check_time = false; + time_t timestamp = 0; + int64_t filesize = 0; + FileType type = FileType::NORMAL; + // Do not record the actual path of the jrt, as the entire JDK can be moved to a different + // directory. + const char* recorded_path = is_jrt ? "" : path; + path_length = strlen(recorded_path); + + struct stat st; + if (os::stat(path, &st) == 0) { + if ((st.st_mode & S_IFMT) == S_IFDIR) { + type = FileType::DIR; + } else { + timestamp = st.st_mtime; + filesize = st.st_size; + + // The timestamp of $JAVA_HOME/lib/modules is not checked at runtime. + check_time = !is_jrt; + } +#ifdef _WINDOWS + } else if (errno == ERROR_FILE_NOT_FOUND || errno == ERROR_PATH_NOT_FOUND) { + // On Windows, the errno could be ERROR_PATH_NOT_FOUND (3) in case the directory + // path doesn't exist. + type = FileType::NOT_EXIST; +#endif + } else if (errno == ENOENT) { + // We allow the file to not exist, as long as it also doesn't exist during runtime. + type = FileType::NOT_EXIST; + } else { + log_error(cds)("Unable to open file %s.", path); + MetaspaceShared::unrecoverable_loading_error(); + } + + ResourceMark rm(current); + char* manifest = nullptr; + + if (!is_jrt && type == FileType::NORMAL) { + manifest = read_manifest(current, path, manifest_length); // resource allocated + } + + size_t cs_size = header_size() + + + path_length + 1 /* nul-terminated */ + + manifest_length + 1; /* nul-terminated */ + + AOTClassLocation* cs = (AOTClassLocation*)os::malloc(cs_size, mtClassShared); + memset(cs, 0, cs_size); + cs->_path_length = path_length; + cs->_manifest_length = manifest_length; + cs->_check_time = check_time; + cs->_from_cpattr = from_cpattr; + cs->_timestamp = timestamp; + cs->_filesize = filesize; + cs->_file_type = type; + cs->_group = group; + cs->_index = index; + + strcpy(((char*)cs) + cs->path_offset(), recorded_path); + if (manifest_length > 0) { + memcpy(((char*)cs) + cs->manifest_offset(), manifest, manifest_length); + } + assert(*(cs->manifest() + cs->manifest_length()) == '\0', "should be nul-terminated"); + + if (strstr(cs->manifest(), "Multi-Release: true") != nullptr) { + cs->_is_multi_release_jar = true; + } + + if (strstr(cs->manifest(), "Extension-List:") != nullptr) { + vm_exit_during_cds_dumping(err_msg("-Xshare:dump does not support Extension-List in JAR manifest: %s", path)); + } + + return cs; +} + +char* AOTClassLocation::read_manifest(JavaThread* current, const char* path, size_t& manifest_length) { + manifest_length = 0; + + struct stat st; + if (os::stat(path, &st) != 0) { + return nullptr; + } + + ClassPathEntry* cpe = ClassLoader::create_class_path_entry(current, path, &st); + if (cpe == nullptr) { + // is a file, but not a JAR file + return nullptr; + } + assert(cpe->is_jar_file(), "should not be called with a directory"); + + const char* name = "META-INF/MANIFEST.MF"; + char* manifest; + jint size; + manifest = (char*) ((ClassPathZipEntry*)cpe)->open_entry(current, name, &size, true); + + if (manifest == nullptr || size <= 0) { // No Manifest + manifest_length = 0; + } else { + manifest_length = (size_t)size; + } + + delete cpe; + return manifest; +} + +// The result is resource allocated. +char* AOTClassLocation::get_cpattr() const { + if (_manifest_length == 0) { + return nullptr; + } + + size_t buf_size = _manifest_length + 1; + char* buf = NEW_RESOURCE_ARRAY(char, buf_size); + memcpy(buf, manifest(), _manifest_length); + buf[_manifest_length] = 0; // make sure it's 0-terminated + + // See http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest + // Replace all CR/LF and CR with LF + StringUtils::replace_no_expand(buf, "\r\n", "\n"); + // Remove all new-line continuation (remove all "\n " substrings) + StringUtils::replace_no_expand(buf, "\n ", ""); + + const char* tag = "Class-Path: "; + size_t tag_len = strlen(tag); + char* found = nullptr; + char* line_start = buf; + char* end = buf + _manifest_length; + + assert(*end == 0, "must be nul-terminated"); + + while (line_start < end) { + char* line_end = strchr(line_start, '\n'); + if (line_end == nullptr) { + // JAR spec require the manifest file to be terminated by a new line. + break; + } + if (strncmp(tag, line_start, tag_len) == 0) { + if (found != nullptr) { + // Same behavior as jdk/src/share/classes/java/util/jar/Attributes.java + // If duplicated entries are found, the last one is used. + log_warning(cds)("Warning: Duplicate name in Manifest: %s.\n" + "Ensure that the manifest does not have duplicate entries, and\n" + "that blank lines separate individual sections in both your\n" + "manifest and in the META-INF/MANIFEST.MF entry in the jar file:\n%s\n", tag, path()); + } + found = line_start + tag_len; + assert(found <= line_end, "sanity"); + *line_end = '\0'; + } + line_start = line_end + 1; + } + + return found; +} + +AOTClassLocation* AOTClassLocation::write_to_archive() const { + AOTClassLocation* archived_copy = (AOTClassLocation*)ArchiveBuilder::ro_region_alloc(total_size()); + memcpy((char*)archived_copy, (char*)this, total_size()); + return archived_copy; +} + +const char* AOTClassLocation::file_type_string() const { + switch (_file_type) { + case FileType::NORMAL: return "file"; + case FileType::DIR: return "dir"; + case FileType::NOT_EXIST: default: return "not-exist"; + } +} + +bool AOTClassLocation::check(const char* runtime_path, bool has_aot_linked_classes) const { + struct stat st; + if (os::stat(runtime_path, &st) != 0) { + if (_file_type != FileType::NOT_EXIST) { + log_warning(cds)("Required classpath entry does not exist: %s", runtime_path); + return false; + } + } else if ((st.st_mode & S_IFMT) == S_IFDIR) { + if (_file_type == FileType::NOT_EXIST) { + log_warning(cds)("'%s' must not exist", runtime_path); + return false; + } + if (_file_type == FileType::NORMAL) { + log_warning(cds)("'%s' must be a file", runtime_path); + return false; + } + if (!os::dir_is_empty(runtime_path)) { + log_warning(cds)("directory is not empty: '%s'", runtime_path); + return false; + } + } else { + if (_file_type == FileType::NOT_EXIST) { + log_warning(cds)("'%s' must not exist", runtime_path); + if (has_aot_linked_classes) { + log_error(cds)("CDS archive has aot-linked classes. It cannot be used because the " + "file %s exists", runtime_path); + return false; + } else { + log_warning(cds)("Archived non-system classes are disabled because the " + "file %s exists", runtime_path); + FileMapInfo::current_info()->set_has_platform_or_app_classes(false); + if (DynamicArchive::is_mapped()) { + FileMapInfo::dynamic_info()->set_has_platform_or_app_classes(false); + } + } + } + if (_file_type == FileType::DIR) { + log_warning(cds)("'%s' must be a directory", runtime_path); + return false; + } + bool size_differs = _filesize != st.st_size; + bool time_differs = _check_time && (_timestamp != st.st_mtime); + if (size_differs || time_differs) { + log_warning(cds)("This file is not the one used while building the shared archive file: '%s'%s%s", + runtime_path, + time_differs ? ", timestamp has changed" : "", + size_differs ? ", size has changed" : ""); + return false; + } + } + + log_info(class, path)("ok"); + return true; +} + +void AOTClassLocationConfig::dumptime_init(JavaThread* current) { + assert(CDSConfig::is_dumping_archive(), ""); + _dumptime_instance = NEW_C_HEAP_OBJ(AOTClassLocationConfig, mtClassShared); + _dumptime_instance->dumptime_init_helper(current); + if (current->has_pending_exception()) { + // we can get an exception only when we run out of metaspace, but that + // shouldn't happen this early in bootstrap. + java_lang_Throwable::print(current->pending_exception(), tty); + vm_exit_during_initialization("AOTClassLocationConfig::dumptime_init_helper() failed unexpectedly"); + } +} + +void AOTClassLocationConfig::dumptime_init_helper(TRAPS) { + ResourceMark rm; + GrowableClassLocationArray tmp_array; + AllClassLocationStreams all_css; + + AOTClassLocation* jrt = AOTClassLocation::allocate(THREAD, ClassLoader::get_jrt_entry()->name(), + 0, Group::MODULES_IMAGE, + /*from_cpattr*/false, /*is_jrt*/true); + tmp_array.append(jrt); + + parse(THREAD, tmp_array, all_css.boot_cp(), Group::BOOT_CLASSPATH, /*parse_manifest*/true); + _boot_classpath_end = tmp_array.length(); + + parse(THREAD, tmp_array, all_css.app_cp(), Group::APP_CLASSPATH, /*parse_manifest*/true); + _app_classpath_end = tmp_array.length(); + + parse(THREAD, tmp_array, all_css.module_path(), Group::MODULE_PATH, /*parse_manifest*/false); + _module_end = tmp_array.length(); + + _class_locations = MetadataFactory::new_array(ClassLoaderData::the_null_class_loader_data(), + tmp_array.length(), CHECK); + for (int i = 0; i < tmp_array.length(); i++) { + _class_locations->at_put(i, tmp_array.at(i)); + } + + const char* lcp = find_lcp(all_css.boot_and_app_cp(), _dumptime_lcp_len); + if (_dumptime_lcp_len > 0) { + os::free((void*)lcp); + log_info(class, path)("Longest common prefix = %s (%zu chars)", lcp, _dumptime_lcp_len); + } else { + assert(_dumptime_lcp_len == 0, "sanity"); + log_info(class, path)("Longest common prefix = (0 chars)"); + } + + _has_non_jar_modules = all_css.module_path().has_non_jar_modules(); + _has_platform_classes = false; + _has_app_classes = false; + _max_used_index = 0; +} + +// Find the longest common prefix of two paths, up to max_lcp_len. +// E.g. p1 = "/a/b/foo" +// p2 = "/a/b/bar" +// max_lcp_len = 3 +// -> returns 3 +static size_t find_lcp_of_two_paths(const char* p1, const char* p2, size_t max_lcp_len) { + size_t lcp_len = 0; + char sep = os::file_separator()[0]; + for (size_t i = 0; ; i++) { + char c1 = *p1++; + char c2 = *p2++; + if (c1 == 0 || c2 == 0 || c1 != c2) { + break; + } + if (c1 == sep) { + lcp_len = i + 1; + assert(lcp_len <= max_lcp_len, "sanity"); + if (lcp_len == max_lcp_len) { + break; + } + } + } + return lcp_len; +} + +// cheap-allocated if lcp_len > 0 +const char* AOTClassLocationConfig::find_lcp(ClassLocationStream& css, size_t& lcp_len) { + const char* first_path = nullptr; + char sep = os::file_separator()[0]; + + for (css.start(); css.has_next(); ) { + const char* path = css.get_next(); + if (first_path == nullptr) { + first_path = path; + const char* p = strrchr(first_path, sep); + if (p == nullptr) { + lcp_len = 0; + return ""; + } else { + lcp_len = p - first_path + 1; + } + } else { + lcp_len = find_lcp_of_two_paths(first_path, path, lcp_len); + if (lcp_len == 0) { + return ""; + } + } + } + + if (first_path != nullptr && lcp_len > 0) { + char* lcp = NEW_C_HEAP_ARRAY(char, lcp_len + 1, mtClassShared); + lcp[0] = 0; + strncat(lcp, first_path, lcp_len); + return lcp; + } else { + lcp_len = 0; + return ""; + } +} + +void AOTClassLocationConfig::parse(JavaThread* current, GrowableClassLocationArray& tmp_array, + ClassLocationStream& css, Group group, bool parse_manifest) { + for (css.start(); css.has_next(); ) { + add_class_location(current, tmp_array, css.get_next(), group, parse_manifest, /*from_cpattr*/false); + } +} + +void AOTClassLocationConfig::add_class_location(JavaThread* current, GrowableClassLocationArray& tmp_array, + const char* path, Group group, bool parse_manifest, bool from_cpattr) { + AOTClassLocation* cs = AOTClassLocation::allocate(current, path, tmp_array.length(), group, from_cpattr); + tmp_array.append(cs); + + if (!parse_manifest) { + // parse_manifest is true for -classpath and -Xbootclasspath/a, and false for --module-path. + return; + } + + ResourceMark rm; + char* cp_attr = cs->get_cpattr(); // resource allocated + if (cp_attr != nullptr && strlen(cp_attr) > 0) { + //trace_class_path("found Class-Path: ", cp_attr); FIXME + + char sep = os::file_separator()[0]; + const char* dir_name = cs->path(); + const char* dir_tail = strrchr(dir_name, sep); +#ifdef _WINDOWS + // On Windows, we also support forward slash as the file separator when locating entries in the classpath entry. + const char* dir_tail2 = strrchr(dir_name, '/'); + if (dir_tail == nullptr) { + dir_tail = dir_tail2; + } else if (dir_tail2 != nullptr && dir_tail2 > dir_tail) { + dir_tail = dir_tail2; + } +#endif + int dir_len; + if (dir_tail == nullptr) { + dir_len = 0; + } else { + dir_len = pointer_delta_as_int(dir_tail, dir_name) + 1; + } + + // Split the cp_attr by spaces, and add each file + char* file_start = cp_attr; + char* end = file_start + strlen(file_start); + + while (file_start < end) { + char* file_end = strchr(file_start, ' '); + if (file_end != nullptr) { + *file_end = 0; + file_end += 1; + } else { + file_end = end; + } + + size_t name_len = strlen(file_start); + if (name_len > 0) { + ResourceMark rm(current); + size_t libname_len = dir_len + name_len; + char* libname = NEW_RESOURCE_ARRAY(char, libname_len + 1); + int n = os::snprintf(libname, libname_len + 1, "%.*s%s", dir_len, dir_name, file_start); + assert((size_t)n == libname_len, "Unexpected number of characters in string"); + + // Avoid infinite recursion when two JAR files refer to each + // other via cpattr. + bool found_duplicate = false; + for (int i = boot_cp_start_index(); i < tmp_array.length(); i++) { + if (strcmp(tmp_array.at(i)->path(), libname) == 0) { + found_duplicate = true; + break; + } + } + if (!found_duplicate) { + add_class_location(current, tmp_array, libname, group, parse_manifest, /*from_cpattr*/true); + } + } + + file_start = file_end; + } + } +} + +AOTClassLocation const* AOTClassLocationConfig::class_location_at(int index) const { + return _class_locations->at(index); +} + +int AOTClassLocationConfig::get_module_shared_path_index(Symbol* location) const { + if (location->starts_with("jrt:", 4)) { + assert(class_location_at(0)->is_modules_image(), "sanity"); + return 0; + } + + if (num_module_paths() == 0) { + // The archive(s) were created without --module-path option + return -1; + } + + if (!location->starts_with("file:", 5)) { + return -1; + } + + // skip_uri_protocol was also called during dump time -- see ClassLoaderExt::process_module_table() + ResourceMark rm; + const char* file = ClassLoader::uri_to_path(location->as_C_string()); + for (int i = module_path_start_index(); i < module_path_end_index(); i++) { + const AOTClassLocation* cs = class_location_at(i); + assert(!cs->has_unnamed_module(), "must be"); + bool same = os::same_files(file, cs->path()); + log_debug(class, path)("get_module_shared_path_index (%d) %s : %s = %s", i, + location->as_C_string(), cs->path(), same ? "same" : "different"); + if (same) { + return i; + } + } + return -1; +} + +// We allow non-empty dirs as long as no classes have been loaded from them. +void AOTClassLocationConfig::check_nonempty_dirs() const { + assert(CDSConfig::is_dumping_archive(), "sanity"); + + bool has_nonempty_dir = false; + dumptime_iterate([&](AOTClassLocation* cs) { + if (cs->index() > _max_used_index) { + return false; // stop iterating + } + if (cs->is_dir()) { + if (!os::dir_is_empty(cs->path())) { + log_error(cds)("Error: non-empty directory '%s'", cs->path()); + has_nonempty_dir = true; + } + } + return true; // keep iterating + }); + + if (has_nonempty_dir) { + vm_exit_during_cds_dumping("Cannot have non-empty directory in paths", nullptr); + } +} + +AOTClassLocationConfig* AOTClassLocationConfig::write_to_archive() const { + Array* archived_copy = ArchiveBuilder::new_ro_array(_class_locations->length()); + for (int i = 0; i < _class_locations->length(); i++) { + archived_copy->at_put(i, _class_locations->at(i)->write_to_archive()); + ArchivePtrMarker::mark_pointer((address*)archived_copy->adr_at(i)); + } + + AOTClassLocationConfig* dumped = (AOTClassLocationConfig*)ArchiveBuilder::ro_region_alloc(sizeof(AOTClassLocationConfig)); + memcpy(dumped, this, sizeof(AOTClassLocationConfig)); + dumped->_class_locations = archived_copy; + ArchivePtrMarker::mark_pointer(&dumped->_class_locations); + + return dumped; +} + +bool AOTClassLocationConfig::check_classpaths(bool is_boot_classpath, bool has_aot_linked_classes, + int index_start, int index_end, + ClassLocationStream& runtime_css, + bool use_lcp_match, const char* runtime_lcp, + size_t runtime_lcp_len) const { + if (index_start >= index_end && runtime_css.is_empty()) { // nothing to check + return true; + } + + ResourceMark rm; + const char* which = is_boot_classpath ? "boot" : "app"; + LogTarget(Info, class, path) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print("Checking %s classpath", which); + ls.print_cr("%s", use_lcp_match ? " (with longest common prefix substitution)" : ""); + ls.print("- expected : '"); + print_dumptime_classpath(ls, index_start, index_end, use_lcp_match, _dumptime_lcp_len, runtime_lcp, runtime_lcp_len); + ls.print_cr("'"); + ls.print("- actual : '"); + runtime_css.print(&ls); + ls.print_cr("'"); + } + + runtime_css.start(); + for (int i = index_start; i < index_end; i++) { + ResourceMark rm; + const AOTClassLocation* cs = class_location_at(i); + const char* effective_dumptime_path = cs->path(); + if (use_lcp_match && _dumptime_lcp_len > 0) { + effective_dumptime_path = substitute(effective_dumptime_path, _dumptime_lcp_len, runtime_lcp, runtime_lcp_len); + } + + log_info(class, path)("Checking '%s' %s%s", effective_dumptime_path, cs->file_type_string(), + cs->from_cpattr() ? " (from JAR manifest ClassPath attribute)" : ""); + if (!cs->from_cpattr() && file_exists(effective_dumptime_path)) { + if (!runtime_css.has_next()) { + log_warning(cds)("%s classpath has fewer elements than expected", which); + return false; + } + const char* runtime_path = runtime_css.get_next(); + while (!file_exists(runtime_path) && runtime_css.has_next()) { + runtime_path = runtime_css.get_next(); + } + if (!os::same_files(effective_dumptime_path, runtime_path)) { + log_warning(cds)("The name of %s classpath [%d] does not match: expected '%s', got '%s'", + which, runtime_css.current(), effective_dumptime_path, runtime_path); + return false; + } + } + + if (!cs->check(effective_dumptime_path, has_aot_linked_classes)) { + return false; + } + } + + // Check if the runtime boot classpath has more entries than the one stored in the archive and if the app classpath + // or the module path requires validation. + if (is_boot_classpath && runtime_css.has_next() && (need_to_check_app_classpath() || num_module_paths() > 0)) { + // the check passes if all the extra runtime boot classpath entries are non-existent + if (check_paths_existence(runtime_css)) { + log_warning(cds)("boot classpath is longer than expected"); + return false; + } + } + + return true; +} + +bool AOTClassLocationConfig::file_exists(const char* filename) const{ + struct stat st; + return (os::stat(filename, &st) == 0 && st.st_size > 0); +} + +bool AOTClassLocationConfig::check_paths_existence(ClassLocationStream& runtime_css) const { + bool exist = false; + while (runtime_css.has_next()) { + const char* path = runtime_css.get_next(); + if (file_exists(path)) { + exist = true; + break; + } + } + return exist; +} + +bool AOTClassLocationConfig::check_module_paths(bool has_aot_linked_classes, int index_start, int index_end, + ClassLocationStream& runtime_css, + bool* has_extra_module_paths) const { + if (index_start >= index_end && runtime_css.is_empty()) { // nothing to check + return true; + } + + ResourceMark rm; + + LogTarget(Info, class, path) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Checking module paths"); + ls.print("- expected : '"); + print_dumptime_classpath(ls, index_start, index_end, false, 0, nullptr, 0); + ls.print_cr("'"); + ls.print("- actual : '"); + runtime_css.print(&ls); + ls.print_cr("'"); + } + + // Make sure all the dumptime module paths exist and are unchanged + for (int i = index_start; i < index_end; i++) { + const AOTClassLocation* cs = class_location_at(i); + const char* dumptime_path = cs->path(); + + assert(!cs->from_cpattr(), "not applicable for module path"); + log_info(class, path)("Checking '%s' %s", dumptime_path, cs->file_type_string()); + + if (!cs->check(dumptime_path, has_aot_linked_classes)) { + return false; + } + } + + // We allow runtime_css to be a superset of the module paths specified in dumptime. E.g., + // Dumptime: A:C + // Runtime: A:B:C + runtime_css.start(); + for (int i = index_start; i < index_end; i++) { + const AOTClassLocation* cs = class_location_at(i); + const char* dumptime_path = cs->path(); + + while (true) { + if (!runtime_css.has_next()) { + log_warning(cds)("module path has fewer elements than expected"); + *has_extra_module_paths = true; + return true; + } + // Both this->class_locations() and runtime_css are alphabetically sorted. Skip + // items in runtime_css until we see dumptime_path. + const char* runtime_path = runtime_css.get_next(); + if (!os::same_files(dumptime_path, runtime_path)) { + *has_extra_module_paths = true; + return true; + } else { + break; + } + } + } + + if (runtime_css.has_next()) { + *has_extra_module_paths = true; + } + + return true; +} + +void AOTClassLocationConfig::print_dumptime_classpath(LogStream& ls, int index_start, int index_end, + bool do_substitute, size_t remove_prefix_len, + const char* prepend, size_t prepend_len) const { + const char* sep = ""; + for (int i = index_start; i < index_end; i++) { + ResourceMark rm; + const AOTClassLocation* cs = class_location_at(i); + const char* path = cs->path(); + if (!cs->from_cpattr()) { + ls.print("%s", sep); + if (do_substitute) { + path = substitute(path, remove_prefix_len, prepend, prepend_len); + } + ls.print("%s", path); + sep = os::path_separator(); + } + } +} + +// Returned path is resource-allocated +const char* AOTClassLocationConfig::substitute(const char* path, // start with this path (which was recorded from dump time) + size_t remove_prefix_len, // remove this number of chars from the beginning + const char* prepend, // prepend this string + size_t prepend_len) { // length of the prepended string + size_t len = strlen(path); + assert(len > remove_prefix_len, "sanity"); + assert(prepend_len == strlen(prepend), "sanity"); + len -= remove_prefix_len; + len += prepend_len; + + char* buf = NEW_RESOURCE_ARRAY(char, len + 1); + int n = os::snprintf(buf, len + 1, "%s%s", prepend, path + remove_prefix_len); + assert(size_t(n) == len, "sanity"); + + return buf; +} + +// For performance, we avoid using LCP match if there's at least one +// AOTClassLocation can be matched exactly: this means all other AOTClassLocations must be +// matched exactly. +bool AOTClassLocationConfig::need_lcp_match(AllClassLocationStreams& all_css) const { + if (app_cp_end_index() == boot_cp_start_index()) { + // No need to use lcp-match when there are no boot/app paths. + // TODO: LCP-match not yet supported for modules. + return false; + } + + if (need_lcp_match_helper(boot_cp_start_index(), boot_cp_end_index(), all_css.boot_cp()) && + need_lcp_match_helper(app_cp_start_index(), app_cp_end_index(), all_css.app_cp())) { + return true; + } else { + return false; + } +} + +bool AOTClassLocationConfig::need_lcp_match_helper(int start, int end, ClassLocationStream& css) const { + int i = start; + for (css.start(); i < end && css.has_next(); ) { + const AOTClassLocation* cs = class_location_at(i++); + const char* runtime_path = css.get_next(); + if (cs->must_exist() && os::same_files(cs->path(), runtime_path)) { + // Most likely, we will come to here at the first iteration. + return false; + } + } + return true; +} + +bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_extra_module_paths) const { + ResourceMark rm; + AllClassLocationStreams all_css; + + const char* jrt = ClassLoader::get_jrt_entry()->name(); + bool success = class_location_at(0)->check(jrt, has_aot_linked_classes); + log_info(class, path)("Modules image %s validation: %s", jrt, success ? "passed" : "failed"); + if (!success) { + return false; + } + if (class_locations()->length() == 1) { + if ((module_path_start_index() >= module_path_end_index()) && Arguments::get_property("jdk.module.path") != nullptr) { + *has_extra_module_paths = true; + } else { + *has_extra_module_paths = false; + } + } else { + bool use_lcp_match = need_lcp_match(all_css); + const char* runtime_lcp; + size_t runtime_lcp_len; + + log_info(class, path)("Longest common prefix substitution in boot/app classpath matching: %s", + use_lcp_match ? "yes" : "no"); + if (use_lcp_match) { + runtime_lcp = find_lcp(all_css.boot_and_app_cp(), runtime_lcp_len); + log_info(class, path)("Longest common prefix: %s (%zu chars)", runtime_lcp, runtime_lcp_len); + } else { + runtime_lcp = nullptr; + runtime_lcp_len = 0; + } + + success = check_classpaths(true, has_aot_linked_classes, boot_cp_start_index(), boot_cp_end_index(), all_css.boot_cp(), + use_lcp_match, runtime_lcp, runtime_lcp_len); + log_info(class, path)("Archived boot classpath validation: %s", success ? "passed" : "failed"); + + if (success && need_to_check_app_classpath()) { + success = check_classpaths(false, has_aot_linked_classes, app_cp_start_index(), app_cp_end_index(), all_css.app_cp(), + use_lcp_match, runtime_lcp, runtime_lcp_len); + log_info(class, path)("Archived app classpath validation: %s", success ? "passed" : "failed"); + } + + if (success) { + success = check_module_paths(has_aot_linked_classes, module_path_start_index(), module_path_end_index(), + all_css.module_path(), has_extra_module_paths); + log_info(class, path)("Archived module path validation: %s%s", success ? "passed" : "failed", + (*has_extra_module_paths) ? " (extra module paths found)" : ""); + } + + if (runtime_lcp_len > 0) { + os::free((void*)runtime_lcp); + } + } + + if (success) { + _runtime_instance = this; + } else { + const char* mismatch_msg = "shared class paths mismatch"; + const char* hint_msg = log_is_enabled(Info, class, path) ? + "" : " (hint: enable -Xlog:class+path=info to diagnose the failure)"; + if (RequireSharedSpaces && !PrintSharedArchiveAndExit) { + log_error(cds)("%s%s", mismatch_msg, hint_msg); + MetaspaceShared::unrecoverable_loading_error(); + } else { + log_warning(cds)("%s%s", mismatch_msg, hint_msg); + } + } + return success; +} diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp new file mode 100644 index 00000000000..cb53e9c96e9 --- /dev/null +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2003, 2025, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CDS_AOTCLASSLOCATION_HPP +#define SHARE_CDS_AOTCLASSLOCATION_HPP + +#include "memory/allocation.hpp" +#include "oops/array.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" + +class AllClassLocationStreams; +class ClassLocationStream; +class LogStream; + +// An AOTClassLocation is a location where the application is configured to load Java classes +// from. It can be: +// - the location of $JAVA_HOME/lib/modules +// - an entry in -Xbootclasspath/a +// - an entry in -classpath +// - a JAR file specified using --module-path. +// +// AOTClassLocation is similar to java.security.CodeSource, except: +// - Only local files/dirs are allowed. Directories must be empty. Network locations are not allowed. +// - No code signing information is recorded. +// +// We avoid using pointers in AOTClassLocation to avoid runtime pointer relocation. Each AOTClassLocation +// is a variable-size structure: +// [ all fields specified below (sizeof(AOTClassLocation) bytes) ] +// [ path (_path_length bytes, including the terminating zero) ] +// [ manifest (_manifest_length bytes, including the terminating zero) ] +class AOTClassLocation { +public: + enum class Group : int { + MODULES_IMAGE, + BOOT_CLASSPATH, + APP_CLASSPATH, + MODULE_PATH + }; +private: + enum class FileType : int { + NORMAL, + DIR, + NOT_EXIST + }; + size_t _path_length; // does NOT include terminating zero + size_t _manifest_length; // does NOT include terminating zero + bool _check_time; + bool _from_cpattr; + bool _is_multi_release_jar; // is this a JAR file that has multi-release classes? + FileType _file_type; + Group _group; + int _index; // index of this AOTClassLocation inside AOTClassLocationConfig::_class_locations + time_t _timestamp; + int64_t _filesize; + + static size_t header_size() { return sizeof(AOTClassLocation); } // bytes + size_t path_offset() const { return header_size(); } + size_t manifest_offset() const { return path_offset() + _path_length + 1; } + static char* read_manifest(JavaThread* current, const char* path, size_t& manifest_length); + +public: + static AOTClassLocation* allocate(JavaThread* current, const char* path, int index, Group group, + bool from_cpattr = false, bool is_jrt = false); + + size_t total_size() const { return manifest_offset() + _manifest_length + 1; } + const char* path() const { return ((const char*)this) + path_offset(); } + size_t manifest_length() const { return _manifest_length; } + const char* manifest() const { return ((const char*)this) + manifest_offset(); } + bool must_exist() const { return _file_type != FileType::NOT_EXIST; } + bool must_not_exist() const { return _file_type == FileType::NOT_EXIST; } + bool is_dir() const { return _file_type == FileType::DIR; } + int index() const { return _index; } + bool is_modules_image() const { return _group == Group::MODULES_IMAGE; } + bool from_boot_classpath() const { return _group == Group::BOOT_CLASSPATH; } + bool from_app_classpath() const { return _group == Group::APP_CLASSPATH; } + bool from_module_path() const { return _group == Group::MODULE_PATH; } + bool is_multi_release_jar() const { return _is_multi_release_jar; } + + // Only boot/app classpaths can contain unnamed module + bool has_unnamed_module() const { return from_boot_classpath() || from_app_classpath(); } + + char* get_cpattr() const; + AOTClassLocation* write_to_archive() const; + + // Returns true IFF this AOTClassLocation is discovered from the -classpath or -Xbootclasspath/a by parsing the + // "Class-Path" attribute of a JAR file. + bool from_cpattr() const { return _from_cpattr; } + const char* file_type_string() const; + bool check(const char* runtime_path, bool has_aot_linked_classes) const; +}; + +// AOTClassLocationConfig +// +// Keep track of the set of AOTClassLocations used when an AOTCache is created. +// To load the AOTCache in a production run, the JVM must be using a compatible set of +// AOTClassLocations (subjected to AOTClassLocationConfig::validate()). +// +// In general, validation is performed on the AOTClassLocations to ensure the code locations used +// during AOTCache creation are the same as when the AOTCache is used during runtime. +// Non-existent entries are recorded during AOTCache creation. Those non-existent entries, +// if they are specified at runtime, must not exist. +// +// Some details on validation: +// - the boot classpath can be appended to at runtime if there's no app classpath and no +// module path specified when an AOTCache is created; +// - the app classpath can be appended to at runtime; +// - the module path at runtime can be a superset of the one specified during AOTCache creation. + +class AOTClassLocationConfig : public CHeapObj { + using Group = AOTClassLocation::Group; + using GrowableClassLocationArray = GrowableArrayCHeap; + + // Note: both of the following are non-null if we are dumping a dynamic archive. + static AOTClassLocationConfig* _dumptime_instance; + static const AOTClassLocationConfig* _runtime_instance; + + Array* _class_locations; // jrt -> -Xbootclasspath/a -> -classpath -> --module_path + int _boot_classpath_end; + int _app_classpath_end; + int _module_end; + bool _has_non_jar_modules; + bool _has_platform_classes; + bool _has_app_classes; + int _max_used_index; + size_t _dumptime_lcp_len; + + // accessors + Array* class_locations() const { return _class_locations; } + + void parse(JavaThread* current, GrowableClassLocationArray& tmp_array, ClassLocationStream& css, + Group group, bool parse_manifest); + void add_class_location(JavaThread* current, GrowableClassLocationArray& tmp_array, const char* path, + Group group, bool parse_manifest, bool from_cpattr); + void dumptime_init_helper(TRAPS); + + bool check_classpaths(bool is_boot_classpath, bool has_aot_linked_classes, + int index_start, int index_end, ClassLocationStream& runtime_css, + bool use_lcp_match, const char* runtime_lcp, size_t runtime_lcp_len) const; + bool check_module_paths(bool has_aot_linked_classes, int index_start, int index_end, ClassLocationStream& runtime_css, + bool* has_extra_module_paths) const; + bool file_exists(const char* filename) const; + bool check_paths_existence(ClassLocationStream& runtime_css) const; + + static const char* substitute(const char* path, size_t remove_prefix_len, + const char* prepend, size_t prepend_len); + static const char* find_lcp(ClassLocationStream& css, size_t& lcp_len); + bool need_lcp_match(AllClassLocationStreams& all_css) const; + bool need_lcp_match_helper(int start, int end, ClassLocationStream& css) const; + + template void dumptime_iterate_helper(FUNC func) const { + assert(_class_locations != nullptr, "sanity"); + int n = _class_locations->length(); + for (int i = 0; i < n; i++) { + if (!func(_class_locations->at(i))) { + break; + } + } + } + + template void iterate(FUNC func) const { + int n = class_locations()->length(); + for (int i = 0; i < n; i++) { + if (!func(class_locations()->at(i))) { + break; + } + } + } + + void check_nonempty_dirs() const; + bool need_to_check_app_classpath() const { + return (num_app_classpaths() > 0) && (_max_used_index >= app_cp_start_index()) && has_platform_or_app_classes(); + } + + void print_dumptime_classpath(LogStream& ls, int index_start, int index_limit, + bool do_substitute, size_t remove_prefix_len, + const char* prepend, size_t prepend_len) const; +public: + static AOTClassLocationConfig* dumptime() { + assert(_dumptime_instance != nullptr, "can only be called when dumping an AOT cache"); + return _dumptime_instance; + } + + static const AOTClassLocationConfig* runtime() { + assert(_runtime_instance != nullptr, "can only be called when using an AOT cache"); + return _runtime_instance; + } + + // Common accessors + int boot_cp_start_index() const { return 1; } + int boot_cp_end_index() const { return _boot_classpath_end; } + int app_cp_start_index() const { return boot_cp_end_index(); } + int app_cp_end_index() const { return _app_classpath_end; } + int module_path_start_index() const { return app_cp_end_index(); } + int module_path_end_index() const { return _module_end; } + bool has_platform_or_app_classes() const { return _has_app_classes || _has_platform_classes; } + bool has_non_jar_modules() const { return _has_non_jar_modules; } + int num_boot_classpaths() const { return boot_cp_end_index() - boot_cp_start_index(); } + int num_app_classpaths() const { return app_cp_end_index() - app_cp_start_index(); } + int num_module_paths() const { return module_path_end_index() - module_path_start_index(); } + + int length() const { + return _class_locations->length(); + } + + const AOTClassLocation* class_location_at(int index) const; + int get_module_shared_path_index(Symbol* location) const; + + // Functions used only during dumptime + static void dumptime_init(JavaThread* current); + + static void dumptime_set_has_app_classes() { + _dumptime_instance->_has_app_classes = true; + } + + static void dumptime_set_has_platform_classes() { + _dumptime_instance->_has_platform_classes = true; + } + + static void dumptime_update_max_used_index(int index) { + if (_dumptime_instance == nullptr) { + assert(index == 0, "sanity"); + } else if (_dumptime_instance->_max_used_index < index) { + _dumptime_instance->_max_used_index = index; + } + } + + static void dumptime_check_nonempty_dirs() { + _dumptime_instance->check_nonempty_dirs(); + } + + static bool dumptime_is_ready() { + return _dumptime_instance != nullptr; + } + template static void dumptime_iterate(FUNC func) { + _dumptime_instance->dumptime_iterate_helper(func); + } + + AOTClassLocationConfig* write_to_archive() const; + + // Functions used only during runtime + bool validate(bool has_aot_linked_classes, bool* has_extra_module_paths) const; +}; + + +#endif // SHARE_CDS_AOTCLASSLOCATION_HPP diff --git a/src/hotspot/share/cds/archiveHeapLoader.hpp b/src/hotspot/share/cds/archiveHeapLoader.hpp index 8b9fab91aa3..e559b447ebf 100644 --- a/src/hotspot/share/cds/archiveHeapLoader.hpp +++ b/src/hotspot/share/cds/archiveHeapLoader.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -25,7 +25,6 @@ #ifndef SHARE_CDS_ARCHIVEHEAPLOADER_HPP #define SHARE_CDS_ARCHIVEHEAPLOADER_HPP -#include "cds/filemap.hpp" #include "gc/shared/gc_globals.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index d190f987cbe..564298fa5c8 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -25,6 +25,7 @@ #include "cds/archiveHeapLoader.hpp" #include "cds/cdsConfig.hpp" #include "cds/classListWriter.hpp" +#include "cds/filemap.hpp" #include "cds/heapShared.hpp" #include "classfile/classLoaderDataShared.hpp" #include "classfile/moduleEntry.hpp" diff --git a/src/hotspot/share/cds/cdsConstants.cpp b/src/hotspot/share/cds/cdsConstants.cpp index 364f6d74c43..81b5920cc53 100644 --- a/src/hotspot/share/cds/cdsConstants.cpp +++ b/src/hotspot/share/cds/cdsConstants.cpp @@ -37,7 +37,6 @@ CDSConst CDSConstants::offsets[] = { { "GenericCDSFileMapHeader::_base_archive_name_size", offset_of(GenericCDSFileMapHeader, _base_archive_name_size) }, { "CDSFileMapHeaderBase::_regions[0]", offset_of(CDSFileMapHeaderBase, _regions) }, { "FileMapHeader::_jvm_ident", offset_of(FileMapHeader, _jvm_ident) }, - { "FileMapHeader::_common_app_classpath_prefix_size", offset_of(FileMapHeader, _common_app_classpath_prefix_size) }, { "CDSFileMapRegion::_crc", offset_of(CDSFileMapRegion, _crc) }, { "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used) }, { "DynamicArchiveHeader::_base_region_crc", offset_of(DynamicArchiveHeader, _base_region_crc) } diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp index 49152753a83..dc3d8621db1 100644 --- a/src/hotspot/share/cds/cdsProtectionDomain.cpp +++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassLocation.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsProtectionDomain.hpp" #include "classfile/classLoader.hpp" @@ -49,10 +50,10 @@ OopHandle CDSProtectionDomain::_shared_jar_manifests; Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) { int index = ik->shared_classpath_index(); assert(index >= 0, "Sanity"); - SharedClassPathEntry* ent = FileMapInfo::shared_path(index); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index); Symbol* class_name = ik->name(); - if (ent->is_modules_image()) { + if (cl->is_modules_image()) { // For shared app/platform classes originated from the run-time image: // The ProtectionDomains are cached in the corresponding ModuleEntries // for fast access by the VM. @@ -63,15 +64,14 @@ Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlas return get_shared_protection_domain(class_loader, mod_entry, THREAD); } else { // For shared app/platform classes originated from JAR files on the class path: - // Each of the 3 SystemDictionaryShared::_shared_xxx arrays has the same length - // as the shared classpath table in the shared archive (see - // FileMap::_shared_path_table in filemap.hpp for details). + // Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length + // as the shared classpath table in the shared archive. // // If a shared InstanceKlass k is loaded from the class path, let // - // index = k->shared_classpath_index(): + // index = k->shared_classpath_index(); // - // FileMap::_shared_path_table[index] identifies the JAR file that contains k. + // AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k. // // k's protection domain is: // @@ -84,10 +84,10 @@ Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlas // define_shared_package(class_name, class_loader, manifest, url, CHECK_NH); // // Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by - // the corresponding SystemDictionaryShared::get_shared_xxx() function. + // the corresponding CDSProtectionDomain::get_shared_xxx() function. Handle manifest = get_shared_jar_manifest(index, CHECK_NH); Handle url = get_shared_jar_url(index, CHECK_NH); - int index_offset = index - ClassLoaderExt::app_class_paths_start_index(); + int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index(); if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) { if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) { // define_shared_package only needs to be called once for each package in a jar specified @@ -178,14 +178,14 @@ Handle CDSProtectionDomain::create_jar_manifest(const char* manifest_chars, size Handle CDSProtectionDomain::get_shared_jar_manifest(int shared_path_index, TRAPS) { Handle manifest; if (shared_jar_manifest(shared_path_index) == nullptr) { - SharedClassPathEntry* ent = FileMapInfo::shared_path(shared_path_index); - size_t size = (size_t)ent->manifest_size(); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(shared_path_index); + size_t size = cl->manifest_length(); if (size == 0) { return Handle(); } // ByteArrayInputStream bais = new ByteArrayInputStream(buf); - const char* src = ent->manifest(); + const char* src = cl->manifest(); assert(src != nullptr, "No Manifest data"); manifest = create_jar_manifest(src, size, CHECK_NH); atomic_set_shared_jar_manifest(shared_path_index, manifest()); @@ -198,7 +198,7 @@ Handle CDSProtectionDomain::get_shared_jar_manifest(int shared_path_index, TRAPS Handle CDSProtectionDomain::get_shared_jar_url(int shared_path_index, TRAPS) { Handle url_h; if (shared_jar_url(shared_path_index) == nullptr) { - const char* path = FileMapInfo::shared_path_name(shared_path_index); + const char* path = AOTClassLocationConfig::runtime()->class_location_at(shared_path_index)->path(); oop result_oop = to_file_URL(path, url_h, CHECK_(url_h)); atomic_set_shared_jar_url(shared_path_index, result_oop); } diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp index 155bc08dc2d..39849571015 100644 --- a/src/hotspot/share/cds/cppVtables.cpp +++ b/src/hotspot/share/cds/cppVtables.cpp @@ -258,7 +258,6 @@ intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address ob case MetaspaceObj::ConstantPoolCacheType: case MetaspaceObj::AnnotationsType: case MetaspaceObj::MethodCountersType: - case MetaspaceObj::SharedClassPathEntryType: case MetaspaceObj::RecordComponentType: // These have no vtables. break; diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index acdb330d91b..4ccf23ff91c 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -24,6 +24,7 @@ #include "cds/aotArtifactFinder.hpp" #include "cds/aotClassLinker.hpp" +#include "cds/aotClassLocation.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveHeapWriter.hpp" #include "cds/archiveUtils.inline.hpp" @@ -89,7 +90,7 @@ public: void sort_methods(); void sort_methods(InstanceKlass* ik) const; void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; - void write_archive(char* serialized_data); + void write_archive(char* serialized_data, AOTClassLocationConfig* cl_config); void gather_array_klasses(); public: @@ -138,6 +139,7 @@ public: make_klasses_shareable(); char* serialized_data; + AOTClassLocationConfig* cl_config; { // Write the symbol table and system dictionaries to the RO space. // Note that these tables still point to the *original* objects, so @@ -148,6 +150,7 @@ public: ArchiveBuilder::OtherROAllocMark mark; SystemDictionaryShared::write_to_archive(false); + cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); DynamicArchive::dump_array_klasses(); AOTClassLinker::write_to_archive(); @@ -161,7 +164,7 @@ public: relocate_to_requested(); - write_archive(serialized_data); + write_archive(serialized_data, cl_config); release_header(); DynamicArchive::post_dump(); @@ -172,7 +175,6 @@ public: } virtual void iterate_roots(MetaspaceClosure* it) { - FileMapInfo::metaspace_pointers_do(it); AOTArtifactFinder::all_cached_classes_do(it); SystemDictionaryShared::dumptime_classes_do(it); iterate_primitive_array_klasses(it); @@ -335,8 +337,8 @@ void DynamicArchiveBuilder::remark_pointers_for_instance_klass(InstanceKlass* k, } } -void DynamicArchiveBuilder::write_archive(char* serialized_data) { - _header->set_shared_path_table(FileMapInfo::shared_path_table().table()); +void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocationConfig* cl_config) { + _header->set_class_location_config(cl_config); _header->set_serialized_data(serialized_data); FileMapInfo* dynamic_info = FileMapInfo::dynamic_info(); @@ -389,8 +391,7 @@ public: log_warning(cds)("This archive was created with AllowArchivingWithJavaAgent. It should be used " "for testing purposes only and should not be used in a production environment"); } - FileMapInfo::check_nonempty_dir_in_shared_path_table(); - + AOTClassLocationConfig::dumptime_check_nonempty_dirs(); _builder.doit(); } ~VM_PopulateDynamicDumpSharedSpace() { diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 990e4bb77e1..015032b2d38 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassLocation.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveHeapLoader.inline.hpp" #include "cds/archiveHeapWriter.hpp" @@ -156,7 +157,6 @@ void FileMapInfo::populate_header(size_t core_region_alignment) { size_t header_size; size_t base_archive_name_size = 0; size_t base_archive_name_offset = 0; - size_t longest_common_prefix_size = 0; if (is_static()) { c_header_size = sizeof(FileMapHeader); header_size = c_header_size; @@ -173,30 +173,24 @@ void FileMapInfo::populate_header(size_t core_region_alignment) { base_archive_name_offset = c_header_size; } } - ResourceMark rm; - GrowableArray* app_cp_array = create_dumptime_app_classpath_array(); - int len = app_cp_array->length(); - longest_common_prefix_size = longest_common_app_classpath_prefix_len(len, app_cp_array); _header = (FileMapHeader*)os::malloc(header_size, mtInternal); memset((void*)_header, 0, header_size); _header->populate(this, core_region_alignment, header_size, base_archive_name_size, - base_archive_name_offset, - longest_common_prefix_size); + base_archive_name_offset); } void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, size_t header_size, size_t base_archive_name_size, - size_t base_archive_name_offset, size_t common_app_classpath_prefix_size) { + size_t base_archive_name_offset) { // 1. We require _generic_header._magic to be at the beginning of the file // 2. FileMapHeader also assumes that _generic_header is at the beginning of the file assert(offset_of(FileMapHeader, _generic_header) == 0, "must be"); set_header_size((unsigned int)header_size); set_base_archive_name_offset((unsigned int)base_archive_name_offset); set_base_archive_name_size((unsigned int)base_archive_name_size); - set_common_app_classpath_prefix_size((unsigned int)common_app_classpath_prefix_size); set_magic(CDSConfig::is_dumping_dynamic_archive() ? CDS_DYNAMIC_ARCHIVE_MAGIC : CDS_ARCHIVE_MAGIC); set_version(CURRENT_CDS_ARCHIVE_VERSION); @@ -236,22 +230,12 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, // JVM version string ... changes on each build. get_header_version(_jvm_ident); - _app_class_paths_start_index = ClassLoaderExt::app_class_paths_start_index(); - _app_module_paths_start_index = ClassLoaderExt::app_module_paths_start_index(); - _max_used_path_index = ClassLoaderExt::max_used_path_index(); - _num_module_paths = ClassLoader::num_module_path_entries(); - _verify_local = BytecodeVerificationLocal; _verify_remote = BytecodeVerificationRemote; - _has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes(); - _has_non_jar_in_classpath = ClassLoaderExt::has_non_jar_in_classpath(); + _has_platform_or_app_classes = AOTClassLocationConfig::dumptime()->has_platform_or_app_classes(); _requested_base_address = (char*)SharedBaseAddress; _mapped_base_address = (char*)SharedBaseAddress; _allow_archiving_with_java_agent = AllowArchivingWithJavaAgent; - - if (!CDSConfig::is_dumping_dynamic_archive()) { - set_shared_path_table(info->_shared_path_table); - } } void FileMapHeader::copy_base_archive_name(const char* archive) { @@ -268,7 +252,6 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- crc: 0x%08x", crc()); st->print_cr("- version: 0x%x", version()); st->print_cr("- header_size: " UINT32_FORMAT, header_size()); - st->print_cr("- common_app_classpath_size: " UINT32_FORMAT, common_app_classpath_prefix_size()); st->print_cr("- base_archive_name_offset: " UINT32_FORMAT, base_archive_name_offset()); st->print_cr("- base_archive_name_size: " UINT32_FORMAT, base_archive_name_size()); @@ -294,15 +277,10 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- early_serialized_data_offset: 0x%zx", _early_serialized_data_offset); st->print_cr("- serialized_data_offset: 0x%zx", _serialized_data_offset); st->print_cr("- jvm_ident: %s", _jvm_ident); - st->print_cr("- shared_path_table_offset: 0x%zx", _shared_path_table_offset); - st->print_cr("- app_class_paths_start_index: %d", _app_class_paths_start_index); - st->print_cr("- app_module_paths_start_index: %d", _app_module_paths_start_index); - st->print_cr("- num_module_paths: %d", _num_module_paths); - st->print_cr("- max_used_path_index: %d", _max_used_path_index); + st->print_cr("- class_location_config_offset: 0x%zx", _class_location_config_offset); st->print_cr("- verify_local: %d", _verify_local); st->print_cr("- verify_remote: %d", _verify_remote); st->print_cr("- has_platform_or_app_classes: %d", _has_platform_or_app_classes); - st->print_cr("- has_non_jar_in_classpath: %d", _has_non_jar_in_classpath); st->print_cr("- requested_base_address: " INTPTR_FORMAT, p2i(_requested_base_address)); st->print_cr("- mapped_base_address: " INTPTR_FORMAT, p2i(_mapped_base_address)); st->print_cr("- heap_root_segments.roots_count: %d" , _heap_root_segments.roots_count()); @@ -321,676 +299,23 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- has_archived_invokedynamic %d", _has_archived_invokedynamic); } -void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) { - _type = non_existent_entry; - set_name(path, CHECK); -} - -void SharedClassPathEntry::init(bool is_modules_image, - bool is_module_path, - ClassPathEntry* cpe, TRAPS) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - _timestamp = 0; - _filesize = 0; - _from_class_path_attr = false; - - struct stat st; - if (os::stat(cpe->name(), &st) == 0) { - if ((st.st_mode & S_IFMT) == S_IFDIR) { - _type = dir_entry; - } else { - // The timestamp of the modules_image is not checked at runtime. - if (is_modules_image) { - _type = modules_image_entry; - } else { - _type = jar_entry; - _timestamp = st.st_mtime; - _from_class_path_attr = cpe->from_class_path_attr(); - _is_multi_release = cpe->is_multi_release_jar(); - } - _filesize = st.st_size; - _is_module_path = is_module_path; - } - } else { - // The file/dir must exist, or it would not have been added - // into ClassLoader::classpath_entry(). - // - // If we can't access a jar file in the boot path, then we can't - // make assumptions about where classes get loaded from. - log_error(cds)("Unable to open file %s.", cpe->name()); - MetaspaceShared::unrecoverable_loading_error(); - } - - // No need to save the name of the module file, as it will be computed at run time - // to allow relocation of the JDK directory. - const char* name = is_modules_image ? "" : cpe->name(); - set_name(name, CHECK); -} - -void SharedClassPathEntry::set_name(const char* name, TRAPS) { - size_t len = strlen(name) + 1; - _name = MetadataFactory::new_array(ClassLoaderData::the_null_class_loader_data(), (int)len, CHECK); - strcpy(_name->data(), name); -} - -void SharedClassPathEntry::copy_from(SharedClassPathEntry* ent, ClassLoaderData* loader_data, TRAPS) { - assert(ent != nullptr, "sanity"); - _type = ent->_type; - _is_module_path = ent->_is_module_path; - _timestamp = ent->_timestamp; - _filesize = ent->_filesize; - _from_class_path_attr = ent->_from_class_path_attr; - set_name(ent->name(), CHECK); - - if (ent->is_jar() && ent->manifest() != nullptr) { - Array* buf = MetadataFactory::new_array(loader_data, - ent->manifest_size(), - CHECK); - char* p = (char*)(buf->data()); - memcpy(p, ent->manifest(), ent->manifest_size()); - set_manifest(buf); - } -} - -const char* SharedClassPathEntry::name() const { - if (CDSConfig::is_using_archive() && is_modules_image()) { - // In order to validate the runtime modules image file size against the archived - // size information, we need to obtain the runtime modules image path. The recorded - // dump time modules image path in the archive may be different from the runtime path - // if the JDK image has beed moved after generating the archive. - return ClassLoader::get_jrt_entry()->name(); - } else { - return _name->data(); - } -} - -bool SharedClassPathEntry::validate(bool is_class_path) const { +bool FileMapInfo::validate_class_location() { assert(CDSConfig::is_using_archive(), "runtime only"); - struct stat st; - const char* name = this->name(); - - bool ok = true; - log_info(class, path)("checking shared classpath entry: %s", name); - if (os::stat(name, &st) != 0 && is_class_path) { - // If the archived module path entry does not exist at runtime, it is not fatal - // (no need to invalid the shared archive) because the shared runtime visibility check - // filters out any archived module classes that do not have a matching runtime - // module path location. - log_warning(cds)("Required classpath entry does not exist: %s", name); - ok = false; - } else if (is_dir()) { - if (!os::dir_is_empty(name)) { - log_warning(cds)("directory is not empty: %s", name); - ok = false; - } - } else { - bool size_differs = _filesize != st.st_size; - bool time_differs = has_timestamp() && _timestamp != st.st_mtime; - if (time_differs || size_differs) { - ok = false; - if (PrintSharedArchiveAndExit) { - log_warning(cds)(time_differs ? "Timestamp mismatch" : "File size mismatch"); - } else { - const char* bad_file_msg = "This file is not the one used while building the shared archive file:"; - log_warning(cds)("%s %s", bad_file_msg, name); - if (!log_is_enabled(Info, cds)) { - log_warning(cds)("%s %s", bad_file_msg, name); - } - if (time_differs) { - log_warning(cds)("%s timestamp has changed.", name); - } - if (size_differs) { - log_warning(cds)("%s size has changed.", name); - } - } - } - } - - if (PrintSharedArchiveAndExit && !ok) { - // If PrintSharedArchiveAndExit is enabled, don't report failure to the - // caller. Please see above comments for more details. - ok = true; - MetaspaceShared::set_archive_loading_failed(); - } - return ok; -} - -bool SharedClassPathEntry::check_non_existent() const { - assert(_type == non_existent_entry, "must be"); - log_info(class, path)("should be non-existent: %s", name()); - struct stat st; - if (os::stat(name(), &st) != 0) { - log_info(class, path)("ok"); - return true; // file doesn't exist - } else { - return false; - } -} - -void SharedClassPathEntry::metaspace_pointers_do(MetaspaceClosure* it) { - it->push(&_name); - it->push(&_manifest); -} - -void SharedPathTable::metaspace_pointers_do(MetaspaceClosure* it) { - it->push(&_entries); -} - -void SharedPathTable::dumptime_init(ClassLoaderData* loader_data, TRAPS) { - const int num_entries = - ClassLoader::num_boot_classpath_entries() + - ClassLoader::num_app_classpath_entries() + - ClassLoader::num_module_path_entries() + - FileMapInfo::num_non_existent_class_paths(); - _entries = MetadataFactory::new_array(loader_data, num_entries, CHECK); - for (int i = 0; i < num_entries; i++) { - SharedClassPathEntry* ent = - new (loader_data, SharedClassPathEntry::size(), MetaspaceObj::SharedClassPathEntryType, THREAD) SharedClassPathEntry; - _entries->at_put(i, ent); - } -} - -void FileMapInfo::allocate_shared_path_table(TRAPS) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - ClassPathEntry* jrt = ClassLoader::get_jrt_entry(); - - assert(jrt != nullptr, - "No modular java runtime image present when allocating the CDS classpath entry table"); - - _shared_path_table.dumptime_init(loader_data, CHECK); - - // 1. boot class path - int i = 0; - i = add_shared_classpaths(i, "boot", jrt, CHECK); - i = add_shared_classpaths(i, "app", ClassLoader::app_classpath_entries(), CHECK); - i = add_shared_classpaths(i, "module", ClassLoader::module_path_entries(), CHECK); - - for (int x = 0; x < num_non_existent_class_paths(); x++, i++) { - const char* path = _non_existent_class_paths->at(x); - shared_path(i)->init_as_non_existent(path, CHECK); - } - - assert(i == _shared_path_table.size(), "number of shared path entry mismatch"); -} - -int FileMapInfo::add_shared_classpaths(int i, const char* which, ClassPathEntry *cpe, TRAPS) { - while (cpe != nullptr) { - bool is_jrt = (cpe == ClassLoader::get_jrt_entry()); - bool is_module_path = i >= ClassLoaderExt::app_module_paths_start_index(); - const char* type = (is_jrt ? "jrt" : (cpe->is_jar_file() ? "jar" : "dir")); - log_info(class, path)("add %s shared path (%s) %s", which, type, cpe->name()); - SharedClassPathEntry* ent = shared_path(i); - ent->init(is_jrt, is_module_path, cpe, CHECK_0); - if (cpe->is_jar_file()) { - update_jar_manifest(cpe, ent, CHECK_0); - } - if (is_jrt) { - cpe = ClassLoader::get_next_boot_classpath_entry(cpe); + AOTClassLocationConfig* config = header()->class_location_config(); + bool has_extra_module_paths; + if (!config->validate(header()->has_aot_linked_classes(), &has_extra_module_paths)) { + if (PrintSharedArchiveAndExit) { + MetaspaceShared::set_archive_loading_failed(); + return true; } else { - cpe = cpe->next(); - } - i++; - } - - return i; -} - -void FileMapInfo::check_nonempty_dir_in_shared_path_table() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - - bool has_nonempty_dir = false; - - int last = _shared_path_table.size() - 1; - if (last > ClassLoaderExt::max_used_path_index()) { - // no need to check any path beyond max_used_path_index - last = ClassLoaderExt::max_used_path_index(); - } - - for (int i = 0; i <= last; i++) { - SharedClassPathEntry *e = shared_path(i); - if (e->is_dir()) { - const char* path = e->name(); - if (!os::dir_is_empty(path)) { - log_error(cds)("Error: non-empty directory '%s'", path); - has_nonempty_dir = true; - } - } - } - - if (has_nonempty_dir) { - ClassLoader::exit_with_path_failure("Cannot have non-empty directory in paths", nullptr); - } -} - -void FileMapInfo::record_non_existent_class_path_entry(const char* path) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - log_info(class, path)("non-existent Class-Path entry %s", path); - if (_non_existent_class_paths == nullptr) { - _non_existent_class_paths = new (mtClass) GrowableArray(10, mtClass); - } - _non_existent_class_paths->append(os::strdup(path)); -} - -int FileMapInfo::num_non_existent_class_paths() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - if (_non_existent_class_paths != nullptr) { - return _non_existent_class_paths->length(); - } else { - return 0; - } -} - -int FileMapInfo::get_module_shared_path_index(Symbol* location) { - if (location->starts_with("jrt:", 4) && get_number_of_shared_paths() > 0) { - assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image"); - return 0; - } - - if (ClassLoaderExt::app_module_paths_start_index() >= get_number_of_shared_paths()) { - // The archive(s) were created without --module-path option - return -1; - } - - if (!location->starts_with("file:", 5)) { - return -1; - } - - // skip_uri_protocol was also called during dump time -- see ClassLoaderExt::process_module_table() - ResourceMark rm; - const char* file = ClassLoader::uri_to_path(location->as_C_string()); - for (int i = ClassLoaderExt::app_module_paths_start_index(); i < get_number_of_shared_paths(); i++) { - SharedClassPathEntry* ent = shared_path(i); - if (!ent->is_non_existent()) { - assert(ent->in_named_module(), "must be"); - bool cond = strcmp(file, ent->name()) == 0; - log_debug(class, path)("get_module_shared_path_index (%d) %s : %s = %s", i, - location->as_C_string(), ent->name(), cond ? "same" : "different"); - if (cond) { - return i; - } - } - } - - return -1; -} - -class ManifestStream: public ResourceObj { - private: - u1* _buffer_start; // Buffer bottom - u1* _buffer_end; // Buffer top (one past last element) - u1* _current; // Current buffer position - - public: - // Constructor - ManifestStream(u1* buffer, int length) : _buffer_start(buffer), - _current(buffer) { - _buffer_end = buffer + length; - } - - static bool is_attr(u1* attr, const char* name) { - return strncmp((const char*)attr, name, strlen(name)) == 0; - } - - static char* copy_attr(u1* value, size_t len) { - char* buf = NEW_RESOURCE_ARRAY(char, len + 1); - strncpy(buf, (char*)value, len); - buf[len] = 0; - return buf; - } -}; - -void FileMapInfo::update_jar_manifest(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS) { - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - ResourceMark rm(THREAD); - jint manifest_size; - - assert(cpe->is_jar_file() && ent->is_jar(), "the shared class path entry is not a JAR file"); - char* manifest = ClassLoaderExt::read_manifest(THREAD, cpe, &manifest_size); - if (manifest != nullptr) { - ManifestStream* stream = new ManifestStream((u1*)manifest, - manifest_size); - // Copy the manifest into the shared archive - manifest = ClassLoaderExt::read_raw_manifest(THREAD, cpe, &manifest_size); - Array* buf = MetadataFactory::new_array(loader_data, - manifest_size, - CHECK); - char* p = (char*)(buf->data()); - memcpy(p, manifest, manifest_size); - ent->set_manifest(buf); - } -} - -char* FileMapInfo::skip_first_path_entry(const char* path) { - size_t path_sep_len = strlen(os::path_separator()); - char* p = strstr((char*)path, os::path_separator()); - if (p != nullptr) { - debug_only( { - size_t image_name_len = strlen(MODULES_IMAGE_NAME); - assert(strncmp(p - image_name_len, MODULES_IMAGE_NAME, image_name_len) == 0, - "first entry must be the modules image"); - } ); - p += path_sep_len; - } else { - debug_only( { - assert(ClassLoader::string_ends_with(path, MODULES_IMAGE_NAME), - "first entry must be the modules image"); - } ); - } - return p; -} - -int FileMapInfo::num_paths(const char* path) { - if (path == nullptr) { - return 0; - } - int npaths = 1; - char* p = (char*)path; - while (p != nullptr) { - char* prev = p; - p = strstr((char*)p, os::path_separator()); - if (p != nullptr) { - p++; - // don't count empty path - if ((p - prev) > 1) { - npaths++; - } - } - } - return npaths; -} - -// Returns true if a path within the paths exists and has non-zero size. -bool FileMapInfo::check_paths_existence(const char* paths) { - ClasspathStream cp_stream(paths); - bool exist = false; - struct stat st; - while (cp_stream.has_next()) { - const char* path = cp_stream.get_next(); - if (os::stat(path, &st) == 0 && st.st_size > 0) { - exist = true; - break; - } - } - return exist; -} - -GrowableArray* FileMapInfo::create_dumptime_app_classpath_array() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - GrowableArray* path_array = new GrowableArray(10); - ClassPathEntry* cpe = ClassLoader::app_classpath_entries(); - while (cpe != nullptr) { - path_array->append(cpe->name()); - cpe = cpe->next(); - } - return path_array; -} - -GrowableArray* FileMapInfo::create_path_array(const char* paths) { - GrowableArray* path_array = new GrowableArray(10); - JavaThread* current = JavaThread::current(); - ClasspathStream cp_stream(paths); - bool non_jar_in_cp = header()->has_non_jar_in_classpath(); - while (cp_stream.has_next()) { - const char* path = cp_stream.get_next(); - if (!non_jar_in_cp) { - struct stat st; - if (os::stat(path, &st) == 0) { - path_array->append(path); - } - } else { - const char* canonical_path = ClassLoader::get_canonical_path(path, current); - if (canonical_path != nullptr) { - char* error_msg = nullptr; - jzfile* zip = ClassLoader::open_zip_file(canonical_path, &error_msg, current); - if (zip != nullptr && error_msg == nullptr) { - path_array->append(path); - } - } - } - } - return path_array; -} - -bool FileMapInfo::classpath_failure(const char* msg, const char* name) { - ClassLoader::trace_class_path(msg, name); - if (PrintSharedArchiveAndExit) { - MetaspaceShared::set_archive_loading_failed(); - } - return false; -} - -unsigned int FileMapInfo::longest_common_app_classpath_prefix_len(int num_paths, - GrowableArray* rp_array) { - if (num_paths == 0) { - return 0; - } - unsigned int pos; - for (pos = 0; ; pos++) { - for (int i = 0; i < num_paths; i++) { - if (rp_array->at(i)[pos] != '\0' && rp_array->at(i)[pos] == rp_array->at(0)[pos]) { - continue; - } - // search backward for the pos before the file separator char - while (pos > 0) { - if (rp_array->at(0)[--pos] == *os::file_separator()) { - return pos + 1; - } - } - return 0; - } - } - return 0; -} - -bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, GrowableArray* rp_array, - unsigned int dumptime_prefix_len, unsigned int runtime_prefix_len) { - int i = 0; - int j = shared_path_start_idx; - while (i < num_paths) { - while (shared_path(j)->from_class_path_attr()) { - // shared_path(j) was expanded from the JAR file attribute "Class-Path:" - // during dump time. It's not included in the -classpath VM argument. - j++; - } - assert(strlen(shared_path(j)->name()) > (size_t)dumptime_prefix_len, "sanity"); - const char* dumptime_path = shared_path(j)->name() + dumptime_prefix_len; - assert(strlen(rp_array->at(i)) > (size_t)runtime_prefix_len, "sanity"); - const char* runtime_path = rp_array->at(i) + runtime_prefix_len; - if (!os::same_files(dumptime_path, runtime_path)) { return false; } - i++; - j++; - } - return true; -} - -bool FileMapInfo::validate_boot_class_paths() { - // - // - Archive contains boot classes only - relaxed boot path check: - // Extra path elements appended to the boot path at runtime are allowed. - // - // - Archive contains application or platform classes - strict boot path check: - // Validate the entire runtime boot path, which must be compatible - // with the dump time boot path. Appending boot path at runtime is not - // allowed. - // - - // The first entry in boot path is the modules_image (guaranteed by - // ClassLoader::setup_boot_search_path()). Skip the first entry. The - // path of the runtime modules_image may be different from the dump - // time path (e.g. the JDK image is copied to a different location - // after generating the shared archive), which is acceptable. For most - // common cases, the dump time boot path might contain modules_image only. - char* runtime_boot_path = Arguments::get_boot_class_path(); - char* rp = skip_first_path_entry(runtime_boot_path); - assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image"); - int dp_len = header()->app_class_paths_start_index() - 1; // ignore the first path to the module image - bool match = true; - - bool relaxed_check = !header()->has_platform_or_app_classes(); - if (dp_len == 0 && rp == nullptr) { - return true; // ok, both runtime and dump time boot paths have modules_images only - } else if (dp_len == 0 && rp != nullptr) { - if (relaxed_check) { - return true; // ok, relaxed check, runtime has extra boot append path entries - } else { - ResourceMark rm; - if (check_paths_existence(rp)) { - // If a path exists in the runtime boot paths, it is considered a mismatch - // since there's no boot path specified during dump time. - match = false; - } - } - } else if (dp_len > 0 && rp != nullptr) { - int num; - ResourceMark rm; - GrowableArray* rp_array = create_path_array(rp); - int rp_len = rp_array->length(); - if (rp_len >= dp_len) { - if (relaxed_check) { - // only check the leading entries in the runtime boot path, up to - // the length of the dump time boot path - num = dp_len; - } else { - // check the full runtime boot path, must match with dump time - num = rp_len; - } - match = check_paths(1, num, rp_array, 0, 0); - } else { - // create_path_array() ignores non-existing paths. Although the dump time and runtime boot classpath lengths - // are the same initially, after the call to create_path_array(), the runtime boot classpath length could become - // shorter. We consider boot classpath mismatch in this case. - match = false; - } } - if (!match) { - // The paths are different - return classpath_failure("[BOOT classpath mismatch, actual =", runtime_boot_path); - } - return true; -} - -bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) { - const char *appcp = Arguments::get_appclasspath(); - assert(appcp != nullptr, "null app classpath"); - int rp_len = num_paths(appcp); - bool match = false; - if (rp_len < shared_app_paths_len) { - return classpath_failure("Run time APP classpath is shorter than the one at dump time: ", appcp); - } - if (shared_app_paths_len != 0 && rp_len != 0) { - // Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar. - ResourceMark rm; - GrowableArray* rp_array = create_path_array(appcp); - if (rp_array->length() == 0) { - // None of the jar file specified in the runtime -cp exists. - return classpath_failure("None of the jar file specified in the runtime -cp exists: -Djava.class.path=", appcp); - } - if (rp_array->length() < shared_app_paths_len) { - // create_path_array() ignores non-existing paths. Although the dump time and runtime app classpath lengths - // are the same initially, after the call to create_path_array(), the runtime app classpath length could become - // shorter. We consider app classpath mismatch in this case. - return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp); - } - - // Handling of non-existent entries in the classpath: we eliminate all the non-existent - // entries from both the dump time classpath (ClassLoader::update_class_path_entry_list) - // and the runtime classpath (FileMapInfo::create_path_array), and check the remaining - // entries. E.g.: - // - // dump : -cp a.jar:NE1:NE2:b.jar -> a.jar:b.jar -> recorded in archive. - // run 1: -cp NE3:a.jar:NE4:b.jar -> a.jar:b.jar -> matched - // run 2: -cp x.jar:NE4:b.jar -> x.jar:b.jar -> mismatched - - int j = header()->app_class_paths_start_index(); - match = check_paths(j, shared_app_paths_len, rp_array, 0, 0); - if (!match) { - // To facilitate app deployment, we allow the JAR files to be moved *together* to - // a different location, as long as they are still stored under the same directory - // structure. E.g., the following is OK. - // java -Xshare:dump -cp /a/Foo.jar:/a/b/Bar.jar ... - // java -Xshare:auto -cp /x/y/Foo.jar:/x/y/b/Bar.jar ... - unsigned int dumptime_prefix_len = header()->common_app_classpath_prefix_size(); - unsigned int runtime_prefix_len = longest_common_app_classpath_prefix_len(shared_app_paths_len, rp_array); - if (dumptime_prefix_len != 0 || runtime_prefix_len != 0) { - log_info(class, path)("LCP length for app classpath (dumptime: %u, runtime: %u)", - dumptime_prefix_len, runtime_prefix_len); - match = check_paths(j, shared_app_paths_len, rp_array, - dumptime_prefix_len, runtime_prefix_len); - } - if (!match) { - return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp); - } - } - } - return true; -} - -void FileMapInfo::log_paths(const char* msg, int start_idx, int end_idx) { - LogTarget(Info, class, path) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - ls.print("%s", msg); - const char* prefix = ""; - for (int i = start_idx; i < end_idx; i++) { - ls.print("%s%s", prefix, shared_path(i)->name()); - prefix = os::path_separator(); - } - ls.cr(); - } -} - -void FileMapInfo::extract_module_paths(const char* runtime_path, GrowableArray* module_paths) { - GrowableArray* path_array = create_path_array(runtime_path); - int num_paths = path_array->length(); - for (int i = 0; i < num_paths; i++) { - const char* name = path_array->at(i); - ClassLoaderExt::extract_jar_files_from_path(name, module_paths); - } - // module paths are stored in sorted order in the CDS archive. - module_paths->sort(ClassLoaderExt::compare_module_names); -} - -bool FileMapInfo::check_module_paths() { - const char* runtime_path = Arguments::get_property("jdk.module.path"); - int archived_num_module_paths = header()->num_module_paths(); - if (runtime_path == nullptr && archived_num_module_paths == 0) { - return true; - } - if ((runtime_path == nullptr && archived_num_module_paths > 0) || - (runtime_path != nullptr && archived_num_module_paths == 0)) { - return false; - } - ResourceMark rm; - GrowableArray* module_paths = new GrowableArray(3); - extract_module_paths(runtime_path, module_paths); - int num_paths = module_paths->length(); - if (num_paths != archived_num_module_paths) { - return false; - } - return check_paths(header()->app_module_paths_start_index(), num_paths, module_paths, 0, 0); -} - -bool FileMapInfo::validate_shared_path_table() { - assert(CDSConfig::is_using_archive(), "runtime only"); - - _validating_shared_path_table = true; - - // Load the shared path table info from the archive header - _shared_path_table = header()->shared_path_table(); - - bool matched_module_paths = true; - if (CDSConfig::is_dumping_dynamic_archive() || header()->has_full_module_graph()) { - matched_module_paths = check_module_paths(); - } - if (header()->has_full_module_graph() && !matched_module_paths) { + if (header()->has_full_module_graph() && has_extra_module_paths) { CDSConfig::stop_using_optimized_module_handling(); - log_info(cds)("optimized module handling: disabled because of mismatched module paths"); + log_info(cds)("optimized module handling: disabled because extra module path(s) are specified"); } if (CDSConfig::is_dumping_dynamic_archive()) { @@ -998,17 +323,13 @@ bool FileMapInfo::validate_shared_path_table() { // or a simple base archive. // If the base layer archive contains additional path component besides // the runtime image and the -cp, dynamic dumping is disabled. - // - // When dynamic archiving is enabled, the _shared_path_table is overwritten - // to include the application path and stored in the top layer archive. - assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image"); - if (header()->app_class_paths_start_index() > 1) { + if (config->num_boot_classpaths() > 0) { CDSConfig::disable_dumping_dynamic_archive(); log_warning(cds)( "Dynamic archiving is disabled because base layer archive has appended boot classpath"); } - if (header()->num_module_paths() > 0) { - if (!matched_module_paths) { + if (config->num_module_paths() > 0) { + if (has_extra_module_paths) { CDSConfig::disable_dumping_dynamic_archive(); log_warning(cds)( "Dynamic archiving is disabled because base layer archive has a different module path"); @@ -1016,68 +337,11 @@ bool FileMapInfo::validate_shared_path_table() { } } - log_paths("Expecting BOOT path=", 0, header()->app_class_paths_start_index()); - log_paths("Expecting -Djava.class.path=", header()->app_class_paths_start_index(), header()->app_module_paths_start_index()); - - int module_paths_start_index = header()->app_module_paths_start_index(); - int shared_app_paths_len = 0; - - // validate the path entries up to the _max_used_path_index - for (int i=0; i < header()->max_used_path_index() + 1; i++) { - if (i < module_paths_start_index) { - if (shared_path(i)->validate()) { - // Only count the app class paths not from the "Class-path" attribute of a jar manifest. - if (!shared_path(i)->from_class_path_attr() && i >= header()->app_class_paths_start_index()) { - shared_app_paths_len++; - } - log_info(class, path)("ok"); - } else { - if (_dynamic_archive_info != nullptr && _dynamic_archive_info->_is_static) { - assert(!CDSConfig::is_using_archive(), "UseSharedSpaces should be disabled"); - } - return false; - } - } else if (i >= module_paths_start_index) { - if (shared_path(i)->validate(false /* not a class path entry */)) { - log_info(class, path)("ok"); - } else { - if (_dynamic_archive_info != nullptr && _dynamic_archive_info->_is_static) { - assert(!CDSConfig::is_using_archive(), "UseSharedSpaces should be disabled"); - } - return false; - } - } - } - - if (header()->max_used_path_index() == 0) { - // default archive only contains the module image in the bootclasspath - assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image"); - } else { - if (!validate_boot_class_paths() || !validate_app_class_paths(shared_app_paths_len)) { - const char* mismatch_msg = "shared class paths mismatch"; - const char* hint_msg = log_is_enabled(Info, class, path) ? - "" : " (hint: enable -Xlog:class+path=info to diagnose the failure)"; - if (RequireSharedSpaces) { - log_error(cds)("%s%s", mismatch_msg, hint_msg); - MetaspaceShared::unrecoverable_loading_error(); - } else { - log_warning(cds)("%s%s", mismatch_msg, hint_msg); - } - return false; - } - } - - if (!validate_non_existent_class_paths()) { - return false; - } - - _validating_shared_path_table = false; - #if INCLUDE_JVMTI if (_classpath_entries_for_jvmti != nullptr) { os::free(_classpath_entries_for_jvmti); } - size_t sz = sizeof(ClassPathEntry*) * get_number_of_shared_paths(); + size_t sz = sizeof(ClassPathEntry*) * AOTClassLocationConfig::runtime()->length(); _classpath_entries_for_jvmti = (ClassPathEntry**)os::malloc(sz, mtClass); memset((void*)_classpath_entries_for_jvmti, 0, sz); #endif @@ -1085,34 +349,6 @@ bool FileMapInfo::validate_shared_path_table() { return true; } -bool FileMapInfo::validate_non_existent_class_paths() { - // All of the recorded non-existent paths came from the Class-Path: attribute from the JAR - // files on the app classpath. If any of these are found to exist during runtime, - // it will change how classes are loading for the app loader. For safety, disable - // loading of archived platform/app classes (currently there's no way to disable just the - // app classes). - - assert(CDSConfig::is_using_archive(), "runtime only"); - for (int i = header()->app_module_paths_start_index() + header()->num_module_paths(); - i < get_number_of_shared_paths(); - i++) { - SharedClassPathEntry* ent = shared_path(i); - if (!ent->check_non_existent()) { - if (header()->has_aot_linked_classes()) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used because the " - "file %s exists", ent->name()); - return false; - } else { - log_warning(cds)("Archived non-system classes are disabled because the " - "file %s exists", ent->name()); - header()->set_has_platform_or_app_classes(false); - } - } - } - - return true; -} - // A utility class for reading/validating the GenericCDSFileMapHeader portion of // a CDS archive's header. The file header of all CDS archives with versions from // CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION (12) are guaranteed to always start @@ -1365,19 +601,12 @@ bool FileMapInfo::init_from_file(int fd) { return false; } - int common_path_size = header()->common_app_classpath_prefix_size(); - if (common_path_size < 0) { - log_warning(cds)("common app classpath prefix len < 0"); - return false; - } - unsigned int base_offset = header()->base_archive_name_offset(); unsigned int name_size = header()->base_archive_name_size(); unsigned int header_size = header()->header_size(); if (base_offset != 0 && name_size != 0) { if (header_size != base_offset + name_size) { log_info(cds)("_header_size: " UINT32_FORMAT, header_size); - log_info(cds)("common_app_classpath_size: " UINT32_FORMAT, header()->common_app_classpath_prefix_size()); log_info(cds)("base_archive_name_size: " UINT32_FORMAT, header()->base_archive_name_size()); log_info(cds)("base_archive_name_offset: " UINT32_FORMAT, header()->base_archive_name_offset()); log_warning(cds)("The shared archive file has an incorrect header size."); @@ -2457,10 +1686,7 @@ void FileMapInfo::assert_mark(bool check) { FileMapInfo* FileMapInfo::_current_info = nullptr; FileMapInfo* FileMapInfo::_dynamic_archive_info = nullptr; bool FileMapInfo::_heap_pointers_need_patching = false; -SharedPathTable FileMapInfo::_shared_path_table; -bool FileMapInfo::_validating_shared_path_table = false; bool FileMapInfo::_memory_mapping_failed = false; -GrowableArray* FileMapInfo::_non_existent_class_paths = nullptr; // Open the shared archive file, read and validate the header // information (version, boot classpath, etc.). If initialization @@ -2469,7 +1695,7 @@ GrowableArray* FileMapInfo::_non_existent_class_paths = nullptr; // Validation of the archive is done in two steps: // // [1] validate_header() - done here. -// [2] validate_shared_path_table - this is done later, because the table is in the RW +// [2] validate_shared_path_table - this is done later, because the table is in the RO // region of the archive, which is not mapped yet. bool FileMapInfo::initialize() { assert(CDSConfig::is_using_archive(), "UseSharedSpaces expected."); @@ -2690,17 +1916,15 @@ ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) { } ClassPathEntry* ent = _classpath_entries_for_jvmti[i]; if (ent == nullptr) { - SharedClassPathEntry* scpe = shared_path(i); - assert(scpe->is_jar(), "must be"); // other types of scpe will not produce archived classes - - const char* path = scpe->name(); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(i); + const char* path = cl->path(); struct stat st; if (os::stat(path, &st) != 0) { char *msg = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(path) + 128); jio_snprintf(msg, strlen(path) + 127, "error in finding JAR file %s", path); THROW_MSG_(vmSymbols::java_io_IOException(), msg, nullptr); } else { - ent = ClassLoader::create_class_path_entry(THREAD, path, &st, false, false, scpe->is_multi_release()); + ent = ClassLoader::create_class_path_entry(THREAD, path, &st); if (ent == nullptr) { char *msg = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(path) + 128); jio_snprintf(msg, strlen(path) + 127, "error in opening JAR file %s", path); @@ -2724,7 +1948,7 @@ ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) { ClassFileStream* FileMapInfo::open_stream_for_jvmti(InstanceKlass* ik, Handle class_loader, TRAPS) { int path_index = ik->shared_classpath_index(); assert(path_index >= 0, "should be called for shared built-in classes only"); - assert(path_index < (int)get_number_of_shared_paths(), "sanity"); + assert(path_index < AOTClassLocationConfig::runtime()->length(), "sanity"); ClassPathEntry* cpe = get_classpath_entry_for_jvmti(path_index, CHECK_NULL); assert(cpe != nullptr, "must be"); @@ -2734,8 +1958,12 @@ ClassFileStream* FileMapInfo::open_stream_for_jvmti(InstanceKlass* ik, Handle cl const char* const file_name = ClassLoader::file_name_for_class_name(class_name, name->utf8_length()); ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(path_index); ClassFileStream* cfs; - if (class_loader() != nullptr && !cpe->is_modules_image() && cpe->is_multi_release_jar()) { + if (class_loader() != nullptr && cl->is_multi_release_jar()) { + // This class was loaded from a multi-release JAR file during dump time. The + // process for finding its classfile is complex. Let's defer to the Java code + // in java.lang.ClassLoader. cfs = get_stream_from_class_loader(class_loader, cpe, file_name, CHECK_NULL); } else { cfs = cpe->open_stream_for_loader(THREAD, file_name, loader_data); diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index ae11c6c81cc..22f70635e17 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -42,6 +42,7 @@ static const int JVM_IDENT_MAX = 256; +class AOTClassLocationConfig; class ArchiveHeapInfo; class BitMapView; class CHeapBitMap; @@ -51,89 +52,6 @@ class ClassPathEntry; class outputStream; class ReservedSpace; -class SharedClassPathEntry : public MetaspaceObj { - enum { - modules_image_entry, - jar_entry, - dir_entry, - non_existent_entry, - unknown_entry - }; - - void set_name(const char* name, TRAPS); - - u1 _type; - bool _is_module_path; - bool _from_class_path_attr; - bool _is_multi_release; - time_t _timestamp; // jar timestamp, 0 if is directory, modules image or other - int64_t _filesize; // jar/jimage file size, -1 if is directory, -2 if other - Array* _name; - Array* _manifest; - -public: - SharedClassPathEntry() : _type(0), _is_module_path(false), - _from_class_path_attr(false), _is_multi_release(false), _timestamp(0), - _filesize(0), _name(nullptr), _manifest(nullptr) {} - static int size() { - static_assert(is_aligned(sizeof(SharedClassPathEntry), wordSize), "must be"); - return (int)(sizeof(SharedClassPathEntry) / wordSize); - } - void init(bool is_modules_image, bool is_module_path, ClassPathEntry* cpe, TRAPS); - void init_as_non_existent(const char* path, TRAPS); - void metaspace_pointers_do(MetaspaceClosure* it); - MetaspaceObj::Type type() const { return SharedClassPathEntryType; } - bool validate(bool is_class_path = true) const; - - // The _timestamp only gets set for jar files. - bool has_timestamp() const { - return _timestamp != 0; - } - bool is_dir() const { return _type == dir_entry; } - bool is_modules_image() const { return _type == modules_image_entry; } - bool is_jar() const { return _type == jar_entry; } - bool is_non_existent() const { return _type == non_existent_entry; } - bool from_class_path_attr() { return _from_class_path_attr; } - bool is_multi_release() { return _is_multi_release; } - time_t timestamp() const { return _timestamp; } - const char* name() const; - const char* manifest() const { - return (_manifest == nullptr) ? nullptr : (const char*)_manifest->data(); - } - int manifest_size() const { - return (_manifest == nullptr) ? 0 : _manifest->length(); - } - void set_manifest(Array* manifest) { - _manifest = manifest; - } - bool check_non_existent() const; - void copy_from(SharedClassPathEntry* ent, ClassLoaderData* loader_data, TRAPS); - bool in_named_module() { - return is_modules_image() || // modules image doesn't contain unnamed modules - _is_module_path; // module path doesn't contain unnamed modules - } -}; - -class SharedPathTable { - Array* _entries; -public: - SharedPathTable() : _entries(nullptr) {} - SharedPathTable(Array* entries) : _entries(entries) {} - - void dumptime_init(ClassLoaderData* loader_data, TRAPS); - void metaspace_pointers_do(MetaspaceClosure* it); - - int size() { - return _entries == nullptr ? 0 : _entries->length(); - } - SharedClassPathEntry* path_at(int index) { - return _entries->at(index); - } - Array* table() {return _entries;} - void set_table(Array* table) {_entries = table;} -}; - - class FileMapRegion: private CDSFileMapRegion { public: void assert_is_heap_region() const { @@ -203,30 +121,17 @@ private: size_t _cloned_vtables_offset; // The address of the first cloned vtable size_t _early_serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize() size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize() - bool _has_non_jar_in_classpath; // non-jar file entry exists in classpath - unsigned int _common_app_classpath_prefix_size; // size of the common prefix of app class paths - // 0 if no common prefix exists // The following fields are all sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's // invoked with. char _jvm_ident[JVM_IDENT_MAX]; // identifier string of the jvm that created this dump - // The following is a table of all the boot/app/module path entries that were used - // during dumping. At run time, we validate these entries according to their - // SharedClassPathEntry::_type. See: - // check_nonempty_dir_in_shared_path_table() - // validate_shared_path_table() - // validate_non_existent_class_paths() - size_t _shared_path_table_offset; + size_t _class_location_config_offset; - jshort _app_class_paths_start_index; // Index of first app classpath entry - jshort _app_module_paths_start_index; // Index of first module path entry - jshort _max_used_path_index; // max path index referenced during CDS dump - int _num_module_paths; // number of module path entries bool _verify_local; // BytecodeVerificationLocal setting bool _verify_remote; // BytecodeVerificationRemote setting - bool _has_platform_or_app_classes; // Archive contains app classes + bool _has_platform_or_app_classes; // Archive contains app or platform classes char* _requested_base_address; // Archive relocation is not necessary if we map with this base address. char* _mapped_base_address; // Actual base address where archive is mapped. @@ -241,10 +146,14 @@ private: size_t _heap_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap. size_t _rw_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the rw region size_t _ro_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the ro region - char* from_mapped_offset(size_t offset) const { - return mapped_base_address() + offset; + template T from_mapped_offset(size_t offset) const { + return (T)(mapped_base_address() + offset); } void set_as_offset(char* p, size_t *offset); + template void set_as_offset(T p, size_t *offset) { + set_as_offset((char*)p, offset); + } + public: // Accessors -- fields declared in GenericCDSFileMapHeader unsigned int magic() const { return _generic_header._magic; } @@ -253,7 +162,6 @@ public: unsigned int header_size() const { return _generic_header._header_size; } unsigned int base_archive_name_offset() const { return _generic_header._base_archive_name_offset; } unsigned int base_archive_name_size() const { return _generic_header._base_archive_name_size; } - unsigned int common_app_classpath_prefix_size() const { return _common_app_classpath_prefix_size; } void set_magic(unsigned int m) { _generic_header._magic = m; } void set_crc(int crc_value) { _generic_header._crc = crc_value; } @@ -261,7 +169,6 @@ public: void set_header_size(unsigned int s) { _generic_header._header_size = s; } void set_base_archive_name_offset(unsigned int s) { _generic_header._base_archive_name_offset = s; } void set_base_archive_name_size(unsigned int s) { _generic_header._base_archive_name_size = s; } - void set_common_app_classpath_prefix_size(unsigned int s) { _common_app_classpath_prefix_size = s; } bool is_static() const { return magic() == CDS_ARCHIVE_MAGIC; } size_t core_region_alignment() const { return _core_region_alignment; } @@ -272,14 +179,13 @@ public: bool compact_headers() const { return _compact_headers; } uintx max_heap_size() const { return _max_heap_size; } CompressedOops::Mode narrow_oop_mode() const { return _narrow_oop_mode; } - char* cloned_vtables() const { return from_mapped_offset(_cloned_vtables_offset); } - char* early_serialized_data() const { return from_mapped_offset(_early_serialized_data_offset); } - char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); } + char* cloned_vtables() const { return from_mapped_offset(_cloned_vtables_offset); } + char* early_serialized_data() const { return from_mapped_offset(_early_serialized_data_offset); } + char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); } const char* jvm_ident() const { return _jvm_ident; } char* requested_base_address() const { return _requested_base_address; } char* mapped_base_address() const { return _mapped_base_address; } bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; } - bool has_non_jar_in_classpath() const { return _has_non_jar_in_classpath; } bool has_aot_linked_classes() const { return _has_aot_linked_classes; } bool compressed_oops() const { return _compressed_oops; } bool compressed_class_pointers() const { return _compressed_class_ptrs; } @@ -291,11 +197,6 @@ public: size_t heap_ptrmap_start_pos() const { return _heap_ptrmap_start_pos; } size_t rw_ptrmap_start_pos() const { return _rw_ptrmap_start_pos; } size_t ro_ptrmap_start_pos() const { return _ro_ptrmap_start_pos; } - // FIXME: These should really return int - jshort max_used_path_index() const { return _max_used_path_index; } - jshort app_module_paths_start_index() const { return _app_module_paths_start_index; } - jshort app_class_paths_start_index() const { return _app_class_paths_start_index; } - int num_module_paths() const { return _num_module_paths; } void set_has_platform_or_app_classes(bool v) { _has_platform_or_app_classes = v; } void set_cloned_vtables(char* p) { set_as_offset(p, &_cloned_vtables_offset); } @@ -309,8 +210,12 @@ public: void set_ro_ptrmap_start_pos(size_t n) { _ro_ptrmap_start_pos = n; } void copy_base_archive_name(const char* name); - void set_shared_path_table(SharedPathTable table) { - set_as_offset((char*)table.table(), &_shared_path_table_offset); + void set_class_location_config(AOTClassLocationConfig* table) { + set_as_offset(table, &_class_location_config_offset); + } + + AOTClassLocationConfig* class_location_config() { + return from_mapped_offset(_class_location_config_offset); } void set_requested_base(char* b) { @@ -318,11 +223,6 @@ public: _mapped_base_address = nullptr; } - SharedPathTable shared_path_table() const { - return SharedPathTable((Array*) - from_mapped_offset(_shared_path_table_offset)); - } - bool validate(); int compute_crc(); @@ -332,8 +232,7 @@ public: } void populate(FileMapInfo *info, size_t core_region_alignment, size_t header_size, - size_t base_archive_name_size, size_t base_archive_name_offset, - size_t common_app_classpath_size); + size_t base_archive_name_size, size_t base_archive_name_offset); static bool is_valid_region(int region) { return (0 <= region && region < NUM_CDS_REGIONS); } @@ -358,9 +257,6 @@ private: const char* _base_archive_name; FileMapHeader* _header; - static SharedPathTable _shared_path_table; - static bool _validating_shared_path_table; - // FileMapHeader describes the shared space data in the file to be // mapped. This structure gets written to a file. It is not a class, so // that the compilers don't add any compiler-private data to it. @@ -369,20 +265,12 @@ private: static FileMapInfo* _dynamic_archive_info; static bool _heap_pointers_need_patching; static bool _memory_mapping_failed; - static GrowableArray* _non_existent_class_paths; public: FileMapHeader *header() const { return _header; } static bool get_base_archive_name_from_header(const char* archive_name, char** base_archive_name); - static SharedPathTable shared_path_table() { - return _shared_path_table; - } - bool init_from_file(int fd); - static void metaspace_pointers_do(MetaspaceClosure* it) { - _shared_path_table.metaspace_pointers_do(it); - } void log_paths(const char* msg, int start_idx, int end_idx); @@ -408,8 +296,6 @@ public: size_t heap_ptrmap_start_pos() const { return header()->heap_ptrmap_start_pos(); } CompressedOops::Mode narrow_oop_mode() const { return header()->narrow_oop_mode(); } - jshort app_module_paths_start_index() const { return header()->app_module_paths_start_index(); } - jshort app_class_paths_start_index() const { return header()->app_class_paths_start_index(); } char* cloned_vtables() const { return header()->cloned_vtables(); } void set_cloned_vtables(char* p) const { header()->set_cloned_vtables(p); } @@ -494,19 +380,8 @@ public: NOT_CDS(return false;) } - static void allocate_shared_path_table(TRAPS); - static int add_shared_classpaths(int i, const char* which, ClassPathEntry *cpe, TRAPS); - static void check_nonempty_dir_in_shared_path_table(); - bool check_module_paths(); - bool validate_shared_path_table(); - bool validate_non_existent_class_paths(); + bool validate_class_location(); bool validate_aot_class_linking(); - static void set_shared_path_table(FileMapInfo* info) { - _shared_path_table = info->header()->shared_path_table(); - } - static void update_jar_manifest(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS); - static int num_non_existent_class_paths(); - static void record_non_existent_class_path_entry(const char* path); #if INCLUDE_JVMTI // Caller needs a ResourceMark because parts of the returned cfs are resource-allocated. @@ -517,21 +392,6 @@ public: TRAPS); #endif - static SharedClassPathEntry* shared_path(int index) { - return _shared_path_table.path_at(index); - } - - static const char* shared_path_name(int index) { - assert(index >= 0, "Sanity"); - return shared_path(index)->name(); - } - - static int get_number_of_shared_paths() { - return _shared_path_table.size(); - } - - static int get_module_shared_path_index(Symbol* location) NOT_CDS_RETURN_(-1); - // The offset of the first core region in the archive, relative to SharedBaseAddress size_t mapping_base_offset() const { return first_core_region()->mapping_offset(); } // The offset of the (exclusive) end of the last core region in this archive, relative to SharedBaseAddress @@ -564,22 +424,6 @@ public: private: void seek_to_position(size_t pos); - char* skip_first_path_entry(const char* path) NOT_CDS_RETURN_(nullptr); - int num_paths(const char* path) NOT_CDS_RETURN_(0); - bool check_paths_existence(const char* paths) NOT_CDS_RETURN_(false); - GrowableArray* create_dumptime_app_classpath_array() NOT_CDS_RETURN_(nullptr); - GrowableArray* create_path_array(const char* path) NOT_CDS_RETURN_(nullptr); - bool classpath_failure(const char* msg, const char* name) NOT_CDS_RETURN_(false); - unsigned int longest_common_app_classpath_prefix_len(int num_paths, - GrowableArray* rp_array) - NOT_CDS_RETURN_(0); - bool check_paths(int shared_path_start_idx, int num_paths, - GrowableArray* rp_array, - unsigned int dumptime_prefix_len, - unsigned int runtime_prefix_len) NOT_CDS_RETURN_(false); - void extract_module_paths(const char* runtime_path, GrowableArray* module_paths); - bool validate_boot_class_paths() NOT_CDS_RETURN_(false); - bool validate_app_class_paths(int shared_app_paths_len) NOT_CDS_RETURN_(false); bool map_heap_region_impl() NOT_CDS_JAVA_HEAP_RETURN_(false); void dealloc_heap_region() NOT_CDS_JAVA_HEAP_RETURN; bool can_use_heap_region(); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index b619bd81670..5aa456ea438 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -24,6 +24,7 @@ #include "cds/aotArtifactFinder.hpp" #include "cds/aotClassInitializer.hpp" +#include "cds/aotClassLocation.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveHeapLoader.hpp" #include "cds/archiveHeapWriter.hpp" @@ -1171,11 +1172,12 @@ void HeapShared::initialize_from_archived_subgraph(JavaThread* current, Klass* k if (k->name()->equals("jdk/internal/module/ArchivedModuleGraph") && !CDSConfig::is_using_optimized_module_handling() && // archive was created with --module-path - ClassLoaderExt::num_module_paths() > 0) { + AOTClassLocationConfig::runtime()->num_module_paths() > 0) { // ArchivedModuleGraph was created with a --module-path that's different than the runtime --module-path. // Thus, it might contain references to modules that do not exist at runtime. We cannot use it. log_info(cds, heap)("Skip initializing ArchivedModuleGraph subgraph: is_using_optimized_module_handling=%s num_module_paths=%d", - BOOL_TO_STR(CDSConfig::is_using_optimized_module_handling()), ClassLoaderExt::num_module_paths()); + BOOL_TO_STR(CDSConfig::is_using_optimized_module_handling()), + AOTClassLocationConfig::runtime()->num_module_paths()); return; } diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index ba687bea8ba..b95d524cb1d 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -24,6 +24,7 @@ #include "cds/aotArtifactFinder.hpp" #include "cds/aotClassLinker.hpp" +#include "cds/aotClassLocation.hpp" #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/archiveBuilder.hpp" @@ -330,20 +331,9 @@ void MetaspaceShared::initialize_for_static_dump() { // Called by universe_post_init() void MetaspaceShared::post_initialize(TRAPS) { if (CDSConfig::is_using_archive()) { - int size = FileMapInfo::get_number_of_shared_paths(); + int size = AOTClassLocationConfig::runtime()->length(); if (size > 0) { CDSProtectionDomain::allocate_shared_data_arrays(size, CHECK); - if (!CDSConfig::is_dumping_dynamic_archive()) { - FileMapInfo* info; - if (FileMapInfo::dynamic_info() == nullptr) { - info = FileMapInfo::current_info(); - } else { - info = FileMapInfo::dynamic_info(); - } - ClassLoaderExt::init_paths_start_index(info->app_class_paths_start_index()); - ClassLoaderExt::init_app_module_paths_start_index(info->app_module_paths_start_index()); - ClassLoaderExt::init_num_module_paths(info->header()->num_module_paths()); - } } } } @@ -558,7 +548,7 @@ private: SymbolTable::write_to_archive(symbols); } char* dump_early_read_only_tables(); - char* dump_read_only_tables(); + char* dump_read_only_tables(AOTClassLocationConfig*& cl_config); public: @@ -579,7 +569,6 @@ public: StaticArchiveBuilder() : ArchiveBuilder() {} virtual void iterate_roots(MetaspaceClosure* it) { - FileMapInfo::metaspace_pointers_do(it); AOTArtifactFinder::all_cached_classes_do(it); SystemDictionaryShared::dumptime_classes_do(it); Universe::metaspace_pointers_do(it); @@ -614,10 +603,11 @@ char* VM_PopulateDumpSharedSpace::dump_early_read_only_tables() { return start; } -char* VM_PopulateDumpSharedSpace::dump_read_only_tables() { +char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& cl_config) { ArchiveBuilder::OtherROAllocMark mark; SystemDictionaryShared::write_to_archive(); + cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); AOTClassLinker::write_to_archive(); MetaspaceShared::write_method_handle_intrinsics(); @@ -645,7 +635,7 @@ void VM_PopulateDumpSharedSpace::doit() { SystemDictionary::get_all_method_handle_intrinsics(_pending_method_handle_intrinsics); } - FileMapInfo::check_nonempty_dir_in_shared_path_table(); + AOTClassLocationConfig::dumptime_check_nonempty_dirs(); NOT_PRODUCT(SystemDictionary::verify();) @@ -679,7 +669,8 @@ void VM_PopulateDumpSharedSpace::doit() { dump_shared_symbol_table(_builder.symbols()); char* early_serialized_data = dump_early_read_only_tables(); - char* serialized_data = dump_read_only_tables(); + AOTClassLocationConfig* cl_config; + char* serialized_data = dump_read_only_tables(cl_config); SystemDictionaryShared::adjust_lambda_proxy_class_dictionary(); @@ -695,6 +686,7 @@ void VM_PopulateDumpSharedSpace::doit() { _map_info->set_early_serialized_data(early_serialized_data); _map_info->set_serialized_data(serialized_data); _map_info->set_cloned_vtables(CppVtables::vtables_serialized_base()); + _map_info->header()->set_class_location_config(cl_config); } class CollectCLDClosure : public CLDClosure { @@ -791,7 +783,6 @@ void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) { void MetaspaceShared::prepare_for_dumping() { assert(CDSConfig::is_dumping_archive(), "sanity"); CDSConfig::check_unsupported_dumping_module_options(); - ClassLoader::initialize_shared_path(JavaThread::current()); } // Preload classes from a list, populate the shared spaces and dump to a @@ -946,6 +937,7 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS HeapShared::init_for_dumping(CHECK); ArchiveHeapWriter::init(); if (CDSConfig::is_dumping_full_module_graph()) { + ClassLoaderDataShared::ensure_module_entry_tables_exist(); HeapShared::reset_archived_object_states(CHECK); } @@ -1134,11 +1126,8 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() { _relocation_delta = static_mapinfo->relocation_delta(); _requested_base_address = static_mapinfo->requested_base_address(); if (dynamic_mapped) { - FileMapInfo::set_shared_path_table(dynamic_mapinfo); // turn AutoCreateSharedArchive off if successfully mapped AutoCreateSharedArchive = false; - } else { - FileMapInfo::set_shared_path_table(static_mapinfo); } } else { set_shared_metaspace_range(nullptr, nullptr, nullptr); @@ -1641,7 +1630,7 @@ MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped return result; } - if (!mapinfo->validate_shared_path_table()) { + if (!mapinfo->validate_class_location()) { unmap_archive(mapinfo); return MAP_ARCHIVE_OTHER_FAILURE; } diff --git a/src/hotspot/share/cds/unregisteredClasses.cpp b/src/hotspot/share/cds/unregisteredClasses.cpp index 173713e341a..2e985b72310 100644 --- a/src/hotspot/share/cds/unregisteredClasses.cpp +++ b/src/hotspot/share/cds/unregisteredClasses.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/cdsConfig.hpp" #include "cds/unregisteredClasses.hpp" #include "classfile/classFileStream.hpp" #include "classfile/classLoader.inline.hpp" diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index fa003756685..3ca1ec237e8 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -22,9 +22,9 @@ * */ +#include "cds/aotClassLocation.hpp" #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" -#include "cds/filemap.hpp" #include "cds/heapShared.hpp" #include "classfile/classFileStream.hpp" #include "classfile/classLoader.inline.hpp" @@ -158,12 +158,6 @@ ClassPathEntry* ClassLoader::_jrt_entry = nullptr; ClassPathEntry* volatile ClassLoader::_first_append_entry_list = nullptr; ClassPathEntry* volatile ClassLoader::_last_append_entry = nullptr; -#if INCLUDE_CDS -ClassPathEntry* ClassLoader::_app_classpath_entries = nullptr; -ClassPathEntry* ClassLoader::_last_app_classpath_entry = nullptr; -ClassPathEntry* ClassLoader::_module_path_entries = nullptr; -ClassPathEntry* ClassLoader::_last_module_path_entry = nullptr; -#endif // helper routines #if INCLUDE_CDS @@ -301,12 +295,9 @@ ClassFileStream* ClassPathDirEntry::open_stream(JavaThread* current, const char* return nullptr; } -ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name, - bool is_boot_append, bool from_class_path_attr, bool multi_release) : ClassPathEntry() { +ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name) : ClassPathEntry() { _zip = zip; _zip_name = copy_path(zip_name); - _from_class_path_attr = from_class_path_attr; - _multi_release = multi_release; } ClassPathZipEntry::~ClassPathZipEntry() { @@ -463,14 +454,6 @@ bool ClassPathImageEntry::is_modules_image() const { return true; } -#if INCLUDE_CDS -void ClassLoader::exit_with_path_failure(const char* error, const char* message) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - tty->print_cr("Hint: enable -Xlog:class+path=info to diagnose the failure"); - vm_exit_during_cds_dumping(error, message); -} -#endif - ModuleClassPathList::ModuleClassPathList(Symbol* module_name) { _module_name = module_name; _module_first_entry = nullptr; @@ -533,57 +516,6 @@ void ClassLoader::setup_bootstrap_search_path(JavaThread* current) { setup_bootstrap_search_path_impl(current, bootcp); } -#if INCLUDE_CDS -void ClassLoader::setup_app_search_path(JavaThread* current, const char *class_path) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - - ResourceMark rm(current); - ClasspathStream cp_stream(class_path); - - while (cp_stream.has_next()) { - const char* path = cp_stream.get_next(); - update_class_path_entry_list(current, path, /* check_for_duplicates */ true, - /* is_boot_append */ false, /* from_class_path_attr */ false); - } -} - -void ClassLoader::add_to_module_path_entries(const char* path, - ClassPathEntry* entry) { - assert(entry != nullptr, "ClassPathEntry should not be nullptr"); - assert(CDSConfig::is_dumping_archive(), "sanity"); - - // The entry does not exist, add to the list - if (_module_path_entries == nullptr) { - assert(_last_module_path_entry == nullptr, "Sanity"); - _module_path_entries = _last_module_path_entry = entry; - } else { - _last_module_path_entry->set_next(entry); - _last_module_path_entry = entry; - } -} - -// Add a module path to the _module_path_entries list. -void ClassLoader::setup_module_search_path(JavaThread* current, const char* path) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - struct stat st; - if (os::stat(path, &st) != 0) { - tty->print_cr("os::stat error %d (%s). CDS dump aborted (path was \"%s\").", - errno, os::errno_name(errno), path); - vm_exit_during_initialization(); - } - // File or directory found - ClassPathEntry* new_entry = nullptr; - new_entry = create_class_path_entry(current, path, &st, - false /*is_boot_append */, false /* from_class_path_attr */); - if (new_entry != nullptr) { - // ClassLoaderExt::process_module_table() filters out non-jar entries before calling this function. - assert(new_entry->is_jar_file(), "module path entry %s is not a jar file", new_entry->name()); - add_to_module_path_entries(path, new_entry); - } -} - -#endif // INCLUDE_CDS - void ClassLoader::close_jrt_image() { // Not applicable for exploded builds if (!ClassLoader::has_jrt_entry()) return; @@ -616,7 +548,7 @@ void ClassLoader::setup_patch_mod_entries() { struct stat st; if (os::stat(path, &st) == 0) { // File or directory found - ClassPathEntry* new_entry = create_class_path_entry(current, path, &st, false, false); + ClassPathEntry* new_entry = create_class_path_entry(current, path, &st); // If the path specification is valid, enter it into this module's list if (new_entry != nullptr) { module_cpl->add_to_list(new_entry); @@ -690,8 +622,7 @@ void ClassLoader::setup_bootstrap_search_path_impl(JavaThread* current, const ch } else { // Every entry on the boot class path after the initial base piece, // which is set by os::set_boot_path(), is considered an appended entry. - update_class_path_entry_list(current, path, /* check_for_duplicates */ false, - /* is_boot_append */ true, /* from_class_path_attr */ false); + update_class_path_entry_list(current, path); } } } @@ -722,7 +653,7 @@ void ClassLoader::add_to_exploded_build_list(JavaThread* current, Symbol* module struct stat st; if (os::stat(path, &st) == 0) { // Directory found - ClassPathEntry* new_entry = create_class_path_entry(current, path, &st, false, false); + ClassPathEntry* new_entry = create_class_path_entry(current, path, &st); // If the path specification is valid, enter it into this module's list. // There is no need to check for duplicate modules in the exploded entry list, @@ -748,10 +679,7 @@ jzfile* ClassLoader::open_zip_file(const char* canonical_path, char** error_msg, } ClassPathEntry* ClassLoader::create_class_path_entry(JavaThread* current, - const char *path, const struct stat* st, - bool is_boot_append, - bool from_class_path_attr, - bool is_multi_release) { + const char *path, const struct stat* st) { ClassPathEntry* new_entry = nullptr; if ((st->st_mode & S_IFMT) == S_IFREG) { ResourceMark rm(current); @@ -764,11 +692,8 @@ ClassPathEntry* ClassLoader::create_class_path_entry(JavaThread* current, char* error_msg = nullptr; jzfile* zip = open_zip_file(canonical_path, &error_msg, current); if (zip != nullptr && error_msg == nullptr) { - new_entry = new ClassPathZipEntry(zip, path, is_boot_append, from_class_path_attr, is_multi_release); + new_entry = new ClassPathZipEntry(zip, path); } else { -#if INCLUDE_CDS - ClassLoaderExt::set_has_non_jar_in_classpath(); -#endif return nullptr; } log_info(class, path)("opened: %s", path); @@ -784,7 +709,7 @@ ClassPathEntry* ClassLoader::create_class_path_entry(JavaThread* current, // Create a class path zip entry for a given path (return null if not found // or zip/JAR file cannot be opened) -ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path, bool is_boot_append) { +ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path) { // check for a regular file struct stat st; if (os::stat(path, &st) == 0) { @@ -797,7 +722,7 @@ ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path, bo jzfile* zip = open_zip_file(canonical_path, &error_msg, thread); if (zip != nullptr && error_msg == nullptr) { // create using canonical path - return new ClassPathZipEntry(zip, canonical_path, is_boot_append, false, false); + return new ClassPathZipEntry(zip, canonical_path); } } } @@ -820,70 +745,20 @@ void ClassLoader::add_to_boot_append_entries(ClassPathEntry *new_entry) { } } -// Record the path entries specified in -cp during dump time. The recorded -// information will be used at runtime for loading the archived app classes. -// -// Note that at dump time, ClassLoader::_app_classpath_entries are NOT used for -// loading app classes. Instead, the app class are loaded by the -// jdk/internal/loader/ClassLoaders$AppClassLoader instance. -bool ClassLoader::add_to_app_classpath_entries(JavaThread* current, - ClassPathEntry* entry, - bool check_for_duplicates) { -#if INCLUDE_CDS - assert(entry != nullptr, "ClassPathEntry should not be nullptr"); - ClassPathEntry* e = _app_classpath_entries; - if (check_for_duplicates) { - while (e != nullptr) { - if (strcmp(e->name(), entry->name()) == 0 && - e->from_class_path_attr() == entry->from_class_path_attr()) { - // entry already exists - return false; - } - e = e->next(); - } - } - - // The entry does not exist, add to the list - if (_app_classpath_entries == nullptr) { - assert(_last_app_classpath_entry == nullptr, "Sanity"); - _app_classpath_entries = _last_app_classpath_entry = entry; - } else { - _last_app_classpath_entry->set_next(entry); - _last_app_classpath_entry = entry; - } - - if (entry->is_jar_file()) { - ClassLoaderExt::process_jar_manifest(current, entry); - } -#endif - return true; -} - // Returns true IFF the file/dir exists and the entry was successfully created. -bool ClassLoader::update_class_path_entry_list(JavaThread* current, - const char *path, - bool check_for_duplicates, - bool is_boot_append, - bool from_class_path_attr) { +bool ClassLoader::update_class_path_entry_list(JavaThread* current, const char *path) { struct stat st; if (os::stat(path, &st) == 0) { // File or directory found ClassPathEntry* new_entry = nullptr; - new_entry = create_class_path_entry(current, path, &st, is_boot_append, from_class_path_attr); + new_entry = create_class_path_entry(current, path, &st); if (new_entry == nullptr) { return false; } // Do not reorder the bootclasspath which would break get_system_package(). // Add new entry to linked list - if (is_boot_append) { - add_to_boot_append_entries(new_entry); - } else { - if (!add_to_app_classpath_entries(current, new_entry, check_for_duplicates)) { - // new_entry is not saved, free it now - delete new_entry; - } - } + add_to_boot_append_entries(new_entry); return true; } else { return false; @@ -1318,63 +1193,61 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, int classpath_index = -1; PackageEntry* pkg_entry = ik->package(); - if (FileMapInfo::get_number_of_shared_paths() > 0) { + if (!AOTClassLocationConfig::dumptime_is_ready()) { + // The shared path table is set up after module system initialization. + // The path table contains no entry before that. Any classes loaded prior + // to the setup of the shared path table must be from the modules image. + assert(stream->from_boot_loader_modules_image(), "stream must be loaded by boot loader from modules image"); + classpath_index = 0; + } else { // Save the path from the file: protocol or the module name from the jrt: protocol // if no protocol prefix is found, path is the same as stream->source(). This path // must be valid since the class has been successfully parsed. const char* path = ClassLoader::uri_to_path(src); assert(path != nullptr, "sanity"); - for (int i = 0; i < FileMapInfo::get_number_of_shared_paths(); i++) { - SharedClassPathEntry* ent = FileMapInfo::shared_path(i); - // A shared path has been validated during its creation in ClassLoader::create_class_path_entry(), - // it must be valid here. - assert(ent->name() != nullptr, "sanity"); - // If the path (from the class stream source) is the same as the shared - // class or module path, then we have a match. - // src may come from the App/Platform class loaders, which would canonicalize - // the file name. We cannot use strcmp to check for equality against ent->name(). - // We must use os::same_files (which is faster than canonicalizing ent->name()). - if (os::same_files(ent->name(), path)) { + AOTClassLocationConfig::dumptime_iterate([&] (AOTClassLocation* cl) { + int i = cl->index(); + // for index 0 and the stream->source() is the modules image or has the jrt: protocol. + // The class must be from the runtime modules image. + if (cl->is_modules_image() && (stream->from_boot_loader_modules_image() || string_starts_with(src, "jrt:"))) { + classpath_index = i; + } else if (os::same_files(cl->path(), path)) { + // If the path (from the class stream source) is the same as the shared + // class or module path, then we have a match. + // src may come from the App/Platform class loaders, which would canonicalize + // the file name. We cannot use strcmp to check for equality against cs->path(). + // We must use os::same_files (which is faster than canonicalizing cs->path()). + // null pkg_entry and pkg_entry in an unnamed module implies the class // is from the -cp or boot loader append path which consists of -Xbootclasspath/a // and jvmti appended entries. if ((pkg_entry == nullptr) || (pkg_entry->in_unnamed_module())) { // Ensure the index is within the -cp range before assigning // to the classpath_index. - if (SystemDictionary::is_system_class_loader(loader) && - (i >= ClassLoaderExt::app_class_paths_start_index()) && - (i < ClassLoaderExt::app_module_paths_start_index())) { + if (SystemDictionary::is_system_class_loader(loader) && cl->from_app_classpath()) { classpath_index = i; - break; } else { - if ((i >= 1) && - (i < ClassLoaderExt::app_class_paths_start_index())) { + if (cl->from_boot_classpath()) { // The class must be from boot loader append path which consists of // -Xbootclasspath/a and jvmti appended entries. assert(loader == nullptr, "sanity"); classpath_index = i; - break; } } } else { // A class from a named module from the --module-path. Ensure the index is // within the --module-path range before assigning to the classpath_index. - if ((pkg_entry != nullptr) && !(pkg_entry->in_unnamed_module()) && (i > 0)) { - if (i >= ClassLoaderExt::app_module_paths_start_index() && - i < FileMapInfo::get_number_of_shared_paths()) { - classpath_index = i; - break; - } + if ((pkg_entry != nullptr) && !(pkg_entry->in_unnamed_module()) && cl->from_module_path()) { + classpath_index = i; } } } - // for index 0 and the stream->source() is the modules image or has the jrt: protocol. - // The class must be from the runtime modules image. - if (i == 0 && (stream->from_boot_loader_modules_image() || string_starts_with(src, "jrt:"))) { - classpath_index = i; - break; + if (classpath_index >= 0) { + return false; // quit iterating + } else { + return true; // Keep iterating } - } + }); // No path entry found for this class: most likely a shared class loaded by the // user defined classloader. @@ -1384,13 +1257,6 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, SystemDictionaryShared::set_shared_class_misc_info(ik, (ClassFileStream*)stream); return; } - } else { - // The shared path table is set up after module system initialization. - // The path table contains no entry before that. Any classes loaded prior - // to the setup of the shared path table must be from the modules image. - assert(stream->from_boot_loader_modules_image(), "stream must be loaded by boot loader from modules image"); - assert(FileMapInfo::get_number_of_shared_paths() == 0, "shared path table must not have been setup"); - classpath_index = 0; } const char* const class_name = ik->name()->as_C_string(); @@ -1431,7 +1297,7 @@ void ClassLoader::record_hidden_class(InstanceKlass* ik) { } else { // Generated invoker classes. if (classloader_type == ClassLoader::APP_LOADER) { - ik->set_shared_classpath_index(ClassLoaderExt::app_class_paths_start_index()); + ik->set_shared_classpath_index(AOTClassLocationConfig::dumptime()->app_cp_start_index()); } else { ik->set_shared_classpath_index(0); } @@ -1540,34 +1406,6 @@ bool ClassLoader::is_module_observable(const char* module_name) { return (*JImageFindResource)(JImage_file, module_name, jimage_version, "module-info.class", &size) != 0; } -#if INCLUDE_CDS -void ClassLoader::initialize_shared_path(JavaThread* current) { - if (CDSConfig::is_dumping_archive()) { - ClassLoaderExt::setup_search_paths(current); - } -} - -void ClassLoader::initialize_module_path(TRAPS) { - if (CDSConfig::is_dumping_archive()) { - ClassLoaderExt::setup_module_paths(THREAD); - FileMapInfo::allocate_shared_path_table(CHECK); - } -} - -// Helper function used by CDS code to get the number of module path -// entries during shared classpath setup time. -int ClassLoader::num_module_path_entries() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - int num_entries = 0; - ClassPathEntry* e= ClassLoader::_module_path_entries; - while (e != nullptr) { - num_entries ++; - e = e->next(); - } - return num_entries; -} -#endif - jlong ClassLoader::classloader_time_ms() { return UsePerfData ? Management::ticks_to_ms(_perf_accumulated_time->get_value()) : -1; diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index 8eb6593f07a..7827b6066e5 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -58,10 +58,6 @@ public: virtual bool is_modules_image() const { return false; } virtual bool is_jar_file() const { return false; } - virtual bool is_multi_release_jar() const { return false; } - virtual void set_multi_release_jar() {} - // Is this entry created from the "Class-path" attribute from a JAR Manifest? - virtual bool from_class_path_attr() const { return false; } virtual const char* name() const = 0; virtual JImageFile* jimage() const { return nullptr; } virtual void close_jimage() {} @@ -92,15 +88,10 @@ class ClassPathZipEntry: public ClassPathEntry { private: jzfile* _zip; // The zip archive const char* _zip_name; // Name of zip archive - bool _from_class_path_attr; // From the "Class-path" attribute of a jar file - bool _multi_release; // multi-release jar public: bool is_jar_file() const { return true; } - bool is_multi_release_jar() const { return _multi_release; } - void set_multi_release_jar() { _multi_release = true; } - bool from_class_path_attr() const { return _from_class_path_attr; } const char* name() const { return _zip_name; } - ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append, bool from_class_path_attr, bool multi_release); + ClassPathZipEntry(jzfile* zip, const char* zip_name); virtual ~ClassPathZipEntry(); u1* open_entry(JavaThread* current, const char* name, jint* filesize, bool nul_terminate); ClassFileStream* open_stream(JavaThread* current, const char* name); @@ -226,22 +217,7 @@ class ClassLoader: AllStatic { // Last entry in linked list of appended ClassPathEntry instances static ClassPathEntry* volatile _last_append_entry; - // Info used by CDS - CDS_ONLY(static ClassPathEntry* _app_classpath_entries;) - CDS_ONLY(static ClassPathEntry* _last_app_classpath_entry;) - CDS_ONLY(static ClassPathEntry* _module_path_entries;) - CDS_ONLY(static ClassPathEntry* _last_module_path_entry;) - CDS_ONLY(static void setup_app_search_path(JavaThread* current, const char* class_path);) - CDS_ONLY(static void setup_module_search_path(JavaThread* current, const char* path);) - static bool add_to_app_classpath_entries(JavaThread* current, - ClassPathEntry* entry, - bool check_for_duplicates); - CDS_ONLY(static void add_to_module_path_entries(const char* path, - ClassPathEntry* entry);) - public: - CDS_ONLY(static ClassPathEntry* app_classpath_entries() {return _app_classpath_entries;}) - CDS_ONLY(static ClassPathEntry* module_path_entries() {return _module_path_entries;}) static bool has_bootclasspath_append() { return first_append_entry() != nullptr; } @@ -263,10 +239,7 @@ class ClassLoader: AllStatic { static void* zip_library_handle(); static jzfile* open_zip_file(const char* canonical_path, char** error_msg, JavaThread* thread); static ClassPathEntry* create_class_path_entry(JavaThread* current, - const char *path, const struct stat* st, - bool is_boot_append, - bool from_class_path_attr, - bool is_multi_release = false); + const char *path, const struct stat* st); // Canonicalizes path names, so strcmp will work properly. This is mainly // to avoid confusing the zip library @@ -276,10 +249,7 @@ class ClassLoader: AllStatic { static PackageEntry* get_package_entry(Symbol* pkg_name, ClassLoaderData* loader_data); static int crc32(int crc, const char* buf, int len); static bool update_class_path_entry_list(JavaThread* current, - const char *path, - bool check_for_duplicates, - bool is_boot_append, - bool from_class_path_attr); + const char *path); static void print_bootclasspath(); // Timing @@ -363,8 +333,6 @@ class ClassLoader: AllStatic { // Initialization static void initialize(TRAPS); static void classLoader_init2(JavaThread* current); - CDS_ONLY(static void initialize_shared_path(JavaThread* current);) - CDS_ONLY(static void initialize_module_path(TRAPS);) static int compute_Object_vtable(); @@ -373,22 +341,6 @@ class ClassLoader: AllStatic { static bool is_in_patch_mod_entries(Symbol* module_name); #if INCLUDE_CDS - // Sharing dump and restore - - // Helper function used by CDS code to get the number of boot classpath - // entries during shared classpath setup time. - static int num_boot_classpath_entries(); - - static ClassPathEntry* get_next_boot_classpath_entry(ClassPathEntry* e); - - // Helper function used by CDS code to get the number of app classpath - // entries during shared classpath setup time. - static int num_app_classpath_entries(); - - // Helper function used by CDS code to get the number of module path - // entries during shared classpath setup time. - static int num_module_path_entries(); - static void exit_with_path_failure(const char* error, const char* message); static char* uri_to_path(const char* uri); static void record_result(JavaThread* current, InstanceKlass* ik, const ClassFileStream* stream, bool redefined); @@ -419,7 +371,7 @@ class ClassLoader: AllStatic { static void add_to_boot_append_entries(ClassPathEntry* new_entry); // creates a class path zip entry (returns null if JAR file cannot be opened) - static ClassPathZipEntry* create_class_path_zip_entry(const char *apath, bool is_boot_append); + static ClassPathZipEntry* create_class_path_zip_entry(const char *path); static bool string_ends_with(const char* str, const char* str_to_find); diff --git a/src/hotspot/share/classfile/classLoader.inline.hpp b/src/hotspot/share/classfile/classLoader.inline.hpp index 7f158a4c854..ec3993b089e 100644 --- a/src/hotspot/share/classfile/classLoader.inline.hpp +++ b/src/hotspot/share/classfile/classLoader.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -27,7 +27,6 @@ #include "classfile/classLoader.hpp" -#include "cds/cdsConfig.hpp" #include "runtime/atomic.hpp" // Next entry in class path @@ -58,44 +57,4 @@ inline ClassPathEntry* ClassLoader::classpath_entry(int n) { } } -#if INCLUDE_CDS - -// Helper function used by CDS code to get the number of boot classpath -// entries during shared classpath setup time. - -inline int ClassLoader::num_boot_classpath_entries() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - assert(has_jrt_entry(), "must have a java runtime image"); - int num_entries = 1; // count the runtime image - ClassPathEntry* e = first_append_entry(); - while (e != nullptr) { - num_entries ++; - e = e->next(); - } - return num_entries; -} - -inline ClassPathEntry* ClassLoader::get_next_boot_classpath_entry(ClassPathEntry* e) { - if (e == ClassLoader::_jrt_entry) { - return first_append_entry(); - } else { - return e->next(); - } -} - -// Helper function used by CDS code to get the number of app classpath -// entries during shared classpath setup time. -inline int ClassLoader::num_app_classpath_entries() { - assert(CDSConfig::is_dumping_archive(), "sanity"); - int num_entries = 0; - ClassPathEntry* e= ClassLoader::_app_classpath_entries; - while (e != nullptr) { - num_entries ++; - e = e->next(); - } - return num_entries; -} - -#endif // INCLUDE_CDS - #endif // SHARE_CLASSFILE_CLASSLOADER_INLINE_HPP diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 16a16b3a16f..5cfe2df61b1 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -27,10 +27,12 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataShared.hpp" #include "classfile/moduleEntry.hpp" +#include "classfile/modules.hpp" #include "classfile/packageEntry.hpp" #include "classfile/systemDictionary.hpp" #include "logging/log.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/safepoint.hpp" #if INCLUDE_CDS_JAVA_HEAP @@ -143,6 +145,21 @@ static ClassLoaderData* java_system_loader_data_or_null() { return ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_system_loader()); } +// ModuleEntryTables (even if empty) are required for iterate_symbols() to scan the +// platform/system loaders inside the CDS safepoint, but the tables can be created only +// when outside of safepoints. Let's do that now. +void ClassLoaderDataShared::ensure_module_entry_tables_exist() { + assert(!SafepointSynchronize::is_at_safepoint(), "sanity"); + ensure_module_entry_table_exists(SystemDictionary::java_platform_loader()); + ensure_module_entry_table_exists(SystemDictionary::java_system_loader()); +} + +void ClassLoaderDataShared::ensure_module_entry_table_exists(oop class_loader) { + Handle h_loader(JavaThread::current(), class_loader); + ModuleEntryTable* met = Modules::get_module_entry_table(h_loader); + assert(met != nullptr, "sanity"); +} + void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); _archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 957c705afde..b802f751030 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -34,7 +34,9 @@ class SerializeClosure; class ClassLoaderDataShared : AllStatic { static bool _full_module_graph_loaded; + static void ensure_module_entry_table_exists(oop class_loader); public: + static void ensure_module_entry_tables_exist(); static void allocate_archived_tables(); static void iterate_symbols(MetaspaceClosure* closure); static void init_archived_tables(); diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp index d3d098e9a02..3a6fd0d933c 100644 --- a/src/hotspot/share/classfile/classLoaderExt.cpp +++ b/src/hotspot/share/classfile/classLoaderExt.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassLocation.hpp" #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "cds/dynamicArchive.hpp" @@ -51,14 +52,6 @@ #include "utilities/checkedCast.hpp" #include "utilities/stringUtils.hpp" -jshort ClassLoaderExt::_app_class_paths_start_index = ClassLoaderExt::max_classpath_index; -jshort ClassLoaderExt::_app_module_paths_start_index = ClassLoaderExt::max_classpath_index; -jshort ClassLoaderExt::_max_used_path_index = 0; -int ClassLoaderExt::_num_module_paths = 0; -bool ClassLoaderExt::_has_app_classes = false; -bool ClassLoaderExt::_has_platform_classes = false; -bool ClassLoaderExt::_has_non_jar_in_classpath = false; - void ClassLoaderExt::append_boot_classpath(ClassPathEntry* new_entry) { if (CDSConfig::is_using_archive()) { warning("Sharing is only supported for boot loader classes because bootstrap classpath has been appended"); @@ -70,245 +63,10 @@ void ClassLoaderExt::append_boot_classpath(ClassPathEntry* new_entry) { ClassLoader::add_to_boot_append_entries(new_entry); } -void ClassLoaderExt::setup_app_search_path(JavaThread* current) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - int start_index = ClassLoader::num_boot_classpath_entries(); - _app_class_paths_start_index = checked_cast(start_index); - char* app_class_path = os::strdup_check_oom(Arguments::get_appclasspath(), mtClass); - - if (strcmp(app_class_path, ".") == 0) { - // This doesn't make any sense, even for AppCDS, so let's skip it. We - // don't want to throw an error here because -cp "." is usually assigned - // by the launcher when classpath is not specified. - trace_class_path("app loader class path (skipped)=", app_class_path); - } else { - trace_class_path("app loader class path=", app_class_path); - ClassLoader::setup_app_search_path(current, app_class_path); - } - - os::free(app_class_path); -} - int ClassLoaderExt::compare_module_names(const char** p1, const char** p2) { return strcmp(*p1, *p2); } -void ClassLoaderExt::process_module_table(JavaThread* current, ModuleEntryTable* met) { - ResourceMark rm(current); - GrowableArray* module_paths = new GrowableArray(5); - - class ModulePathsGatherer : public ModuleClosure { - JavaThread* _current; - GrowableArray* _module_paths; - public: - ModulePathsGatherer(JavaThread* current, GrowableArray* module_paths) : - _current(current), _module_paths(module_paths) {} - void do_module(ModuleEntry* m) { - char* uri = m->location()->as_C_string(); - if (strncmp(uri, "file:", 5) == 0) { - char* path = ClassLoader::uri_to_path(uri); - extract_jar_files_from_path(path, _module_paths); - } - } - }; - - ModulePathsGatherer gatherer(current, module_paths); - { - MutexLocker ml(Module_lock); - met->modules_do(&gatherer); - } - - // Sort the module paths before storing into CDS archive for simpler - // checking at runtime. - module_paths->sort(compare_module_names); - - for (int i = 0; i < module_paths->length(); i++) { - ClassLoader::setup_module_search_path(current, module_paths->at(i)); - } -} - -void ClassLoaderExt::setup_module_paths(JavaThread* current) { - assert(CDSConfig::is_dumping_archive(), "sanity"); - int start_index = ClassLoader::num_boot_classpath_entries() + - ClassLoader::num_app_classpath_entries(); - _app_module_paths_start_index = checked_cast(start_index); - Handle system_class_loader (current, SystemDictionary::java_system_loader()); - ModuleEntryTable* met = Modules::get_module_entry_table(system_class_loader); - process_module_table(current, met); -} - -bool ClassLoaderExt::has_jar_suffix(const char* filename) { - // In jdk.internal.module.ModulePath.readModule(), it checks for the ".jar" suffix. - // Performing the same check here. - const char* dot = strrchr(filename, '.'); - if (dot != nullptr && strcmp(dot + 1, "jar") == 0) { - return true; - } - return false; -} - -void ClassLoaderExt::extract_jar_files_from_path(const char* path, GrowableArray* module_paths) { - DIR* dirp = os::opendir(path); - if (dirp == nullptr && errno == ENOTDIR && has_jar_suffix(path)) { - module_paths->append(path); - } else { - if (dirp != nullptr) { - struct dirent* dentry; - while ((dentry = os::readdir(dirp)) != nullptr) { - const char* file_name = dentry->d_name; - if (has_jar_suffix(file_name)) { - size_t full_name_len = strlen(path) + strlen(file_name) + strlen(os::file_separator()) + 1; - char* full_name = NEW_RESOURCE_ARRAY(char, full_name_len); - int n = os::snprintf(full_name, full_name_len, "%s%s%s", path, os::file_separator(), file_name); - assert((size_t)n == full_name_len - 1, "Unexpected number of characters in string"); - module_paths->append(full_name); - } - } - os::closedir(dirp); - } - } -} - -char* ClassLoaderExt::read_manifest(JavaThread* current, ClassPathEntry* entry, - jint *manifest_size, bool clean_text) { - const char* name = "META-INF/MANIFEST.MF"; - char* manifest; - jint size; - - assert(entry->is_jar_file(), "must be"); - manifest = (char*) ((ClassPathZipEntry*)entry )->open_entry(current, name, &size, true); - - if (manifest == nullptr) { // No Manifest - *manifest_size = 0; - return nullptr; - } - - - if (clean_text) { - // See http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest - // (1): replace all CR/LF and CR with LF - StringUtils::replace_no_expand(manifest, "\r\n", "\n"); - - // (2) remove all new-line continuation (remove all "\n " substrings) - StringUtils::replace_no_expand(manifest, "\n ", ""); - } - - *manifest_size = (jint)strlen(manifest); - return manifest; -} - -char* ClassLoaderExt::get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size) { - const char* tag = "Class-Path: "; - const int tag_len = (int)strlen(tag); - char* found = nullptr; - char* line_start = manifest; - char* end = manifest + manifest_size; - - assert(*end == 0, "must be nul-terminated"); - - while (line_start < end) { - char* line_end = strchr(line_start, '\n'); - if (line_end == nullptr) { - // JAR spec require the manifest file to be terminated by a new line. - break; - } - if (strncmp(tag, line_start, tag_len) == 0) { - if (found != nullptr) { - // Same behavior as jdk/src/share/classes/java/util/jar/Attributes.java - // If duplicated entries are found, the last one is used. - log_warning(cds)("Warning: Duplicate name in Manifest: %s.\n" - "Ensure that the manifest does not have duplicate entries, and\n" - "that blank lines separate individual sections in both your\n" - "manifest and in the META-INF/MANIFEST.MF entry in the jar file:\n%s\n", tag, jar_path); - } - found = line_start + tag_len; - assert(found <= line_end, "sanity"); - *line_end = '\0'; - } - line_start = line_end + 1; - } - return found; -} - -void ClassLoaderExt::process_jar_manifest(JavaThread* current, ClassPathEntry* entry) { - ResourceMark rm(current); - jint manifest_size; - char* manifest = read_manifest(current, entry, &manifest_size); - - if (manifest == nullptr) { - return; - } - - if (strstr(manifest, "Extension-List:") != nullptr) { - vm_exit_during_cds_dumping(err_msg("-Xshare:dump does not support Extension-List in JAR manifest: %s", entry->name())); - } - - if (strstr(manifest, "Multi-Release: true") != nullptr) { - entry->set_multi_release_jar(); - } - - char* cp_attr = get_class_path_attr(entry->name(), manifest, manifest_size); - - if (cp_attr != nullptr && strlen(cp_attr) > 0) { - trace_class_path("found Class-Path: ", cp_attr); - - char sep = os::file_separator()[0]; - const char* dir_name = entry->name(); - const char* dir_tail = strrchr(dir_name, sep); -#ifdef _WINDOWS - // On Windows, we also support forward slash as the file separator when locating entries in the classpath entry. - const char* dir_tail2 = strrchr(dir_name, '/'); - if (dir_tail == nullptr) { - dir_tail = dir_tail2; - } else if (dir_tail2 != nullptr && dir_tail2 > dir_tail) { - dir_tail = dir_tail2; - } -#endif - int dir_len; - if (dir_tail == nullptr) { - dir_len = 0; - } else { - dir_len = pointer_delta_as_int(dir_tail, dir_name) + 1; - } - - // Split the cp_attr by spaces, and add each file - char* file_start = cp_attr; - char* end = file_start + strlen(file_start); - - while (file_start < end) { - char* file_end = strchr(file_start, ' '); - if (file_end != nullptr) { - *file_end = 0; - file_end += 1; - } else { - file_end = end; - } - - size_t name_len = strlen(file_start); - if (name_len > 0) { - ResourceMark rm(current); - size_t libname_len = dir_len + name_len; - char* libname = NEW_RESOURCE_ARRAY(char, libname_len + 1); - int n = os::snprintf(libname, libname_len + 1, "%.*s%s", dir_len, dir_name, file_start); - assert((size_t)n == libname_len, "Unexpected number of characters in string"); - if (ClassLoader::update_class_path_entry_list(current, libname, true, false, true /* from_class_path_attr */)) { - trace_class_path("library = ", libname); - } else { - trace_class_path("library (non-existent) = ", libname); - FileMapInfo::record_non_existent_class_path_entry(libname); - } - } - - file_start = file_end; - } - } - return; -} - -void ClassLoaderExt::setup_search_paths(JavaThread* current) { - ClassLoaderExt::setup_app_search_path(current); -} - void ClassLoaderExt::record_result(const s2 classpath_index, InstanceKlass* result, bool redefined) { assert(CDSConfig::is_dumping_archive(), "sanity"); @@ -317,14 +75,12 @@ void ClassLoaderExt::record_result(const s2 classpath_index, InstanceKlass* resu s2 classloader_type = ClassLoader::BOOT_LOADER; if (SystemDictionary::is_system_class_loader(loader)) { classloader_type = ClassLoader::APP_LOADER; - ClassLoaderExt::set_has_app_classes(); + AOTClassLocationConfig::dumptime_set_has_app_classes(); } else if (SystemDictionary::is_platform_class_loader(loader)) { classloader_type = ClassLoader::PLATFORM_LOADER; - ClassLoaderExt::set_has_platform_classes(); - } - if (classpath_index > ClassLoaderExt::max_used_path_index()) { - ClassLoaderExt::set_max_used_path_index(classpath_index); + AOTClassLocationConfig::dumptime_set_has_platform_classes(); } + AOTClassLocationConfig::dumptime_update_max_used_index(classpath_index); result->set_shared_classpath_index(classpath_index); result->set_shared_class_loader_type(classloader_type); #if INCLUDE_CDS_JAVA_HEAP @@ -334,7 +90,7 @@ void ClassLoaderExt::record_result(const s2 classpath_index, InstanceKlass* resu // loaders are always loaded from known locations (jimage, classpath or modulepath), // so classpath_index should always be >= 0. // The only exception is when a java agent is used during dump time (for testing - // purposes only). If a class is transformed by the agent, the CodeSource of + // purposes only). If a class is transformed by the agent, the AOTClassLocation of // this class may point to an unknown location. This may break heap object archiving, // which requires all the boot classes to be from known locations. This is an // uncommon scenario (even in test cases). Let's simply disable heap object archiving. diff --git a/src/hotspot/share/classfile/classLoaderExt.hpp b/src/hotspot/share/classfile/classLoaderExt.hpp index ce0013b9d49..86bd5cce7ba 100644 --- a/src/hotspot/share/classfile/classLoaderExt.hpp +++ b/src/hotspot/share/classfile/classLoaderExt.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -34,99 +34,13 @@ class ClassListParser; class ClassLoaderExt: public ClassLoader { // AllStatic public: #if INCLUDE_CDS -private: - enum SomeConstants { - max_classpath_index = 0x7fff - }; - - static char* get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size); - static void setup_app_search_path(JavaThread* current); // Only when -Xshare:dump - static void process_module_table(JavaThread* current, ModuleEntryTable* met); - // index of first app JAR in shared classpath entry table - static jshort _app_class_paths_start_index; - // index of first modular JAR in shared modulepath entry table - static jshort _app_module_paths_start_index; - // the largest path index being used during CDS dump time - static jshort _max_used_path_index; - // number of module paths - static int _num_module_paths; - - static bool _has_app_classes; - static bool _has_platform_classes; - static bool _has_non_jar_in_classpath; - - static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size, bool clean_text); - static bool has_jar_suffix(const char* filename); - public: - static void process_jar_manifest(JavaThread* current, ClassPathEntry* entry); - // Called by JVMTI code to add boot classpath + static void append_boot_classpath(ClassPathEntry* new_entry); - static void setup_search_paths(JavaThread* current); - static void setup_module_paths(JavaThread* current); - static void extract_jar_files_from_path(const char* path, GrowableArray* module_paths); static int compare_module_names(const char** p1, const char** p2); - - static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size) { - // Remove all the new-line continuations (which wrap long lines at 72 characters, see - // http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest), so - // that the manifest is easier to parse. - return read_manifest(current, entry, manifest_size, true); - } - static char* read_raw_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size) { - // Do not remove new-line continuations, so we can easily pass it as an argument to - // java.util.jar.Manifest.getManifest() at run-time. - return read_manifest(current, entry, manifest_size, false); - } - - static jshort app_class_paths_start_index() { return _app_class_paths_start_index; } - - static jshort app_module_paths_start_index() { return _app_module_paths_start_index; } - - static jshort max_used_path_index() { return _max_used_path_index; } - - static int num_module_paths() { return _num_module_paths; } - - static void set_max_used_path_index(jshort used_index) { - _max_used_path_index = used_index; - } - - static void init_paths_start_index(jshort app_start) { - _app_class_paths_start_index = app_start; - } - - static void init_app_module_paths_start_index(jshort module_start) { - _app_module_paths_start_index = module_start; - } - - static void init_num_module_paths(int num_module_paths) { - _num_module_paths = num_module_paths; - } - - static bool is_boot_classpath(int classpath_index) { - return classpath_index < _app_class_paths_start_index; - } - - static bool has_platform_or_app_classes() { - return _has_app_classes || _has_platform_classes; - } - - static bool has_non_jar_in_classpath() { - return _has_non_jar_in_classpath; - } - static void record_result(const s2 classpath_index, InstanceKlass* result, bool redefined); - static void set_has_app_classes() { - _has_app_classes = true; - } - static void set_has_platform_classes() { - _has_platform_classes = true; - } - static void set_has_non_jar_in_classpath() { - _has_non_jar_in_classpath = true; - } #endif // INCLUDE_CDS }; diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index a0f9afdc982..55363d7f41f 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -22,10 +22,10 @@ * */ +#include "cds/aotClassLocation.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" -#include "cds/filemap.hpp" #include "cds/heapShared.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" @@ -61,7 +61,7 @@ void ModuleEntry::set_location(Symbol* location) { if (location != nullptr) { location->increment_refcount(); CDS_ONLY(if (CDSConfig::is_using_archive()) { - _shared_path_index = FileMapInfo::get_module_shared_path_index(location); + _shared_path_index = AOTClassLocationConfig::runtime()->get_module_shared_path_index(_location); }); } } @@ -483,7 +483,7 @@ void ModuleEntry::init_as_archived_entry() { set_archived_reads(write_growable_array(reads())); _loader_data = nullptr; // re-init at runtime - _shared_path_index = FileMapInfo::get_module_shared_path_index(_location); + _shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(_location); if (name() != nullptr) { _name = ArchiveBuilder::get_buffered_symbol(_name); ArchivePtrMarker::mark_pointer((address*)&_name); diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 1943869dbb1..22ccb4e4b04 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -26,7 +26,6 @@ #include "cds/archiveHeapLoader.inline.hpp" #include "cds/archiveHeapWriter.hpp" #include "cds/cdsConfig.hpp" -#include "cds/filemap.hpp" #include "cds/heapShared.hpp" #include "classfile/altHashing.hpp" #include "classfile/compactHashtable.hpp" diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 24273fed12f..68f9556099f 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotClassLocation.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" #include "classfile/classFileParser.hpp" @@ -962,14 +963,14 @@ bool SystemDictionary::is_shared_class_visible_impl(Symbol* class_name, int scp_index = ik->shared_classpath_index(); assert(!ik->is_shared_unregistered_class(), "this function should be called for built-in classes only"); assert(scp_index >= 0, "must be"); - SharedClassPathEntry* scp_entry = FileMapInfo::shared_path(scp_index); + const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(scp_index); if (!Universe::is_module_initialized()) { - assert(scp_entry != nullptr, "must be"); + assert(cl != nullptr, "must be"); // At this point, no modules have been defined yet. KlassSubGraphInfo::check_allowed_klass() // has restricted the classes can be loaded at this step to be only: - // [1] scp_entry->is_modules_image(): classes in java.base, or, + // [1] cs->is_modules_image(): classes in java.base, or, // [2] HeapShared::is_a_test_class_in_unnamed_module(ik): classes in bootstrap/unnamed module - assert(scp_entry->is_modules_image() || HeapShared::is_a_test_class_in_unnamed_module(ik), + assert(cl->is_modules_image() || HeapShared::is_a_test_class_in_unnamed_module(ik), "only these classes can be loaded before the module system is initialized"); assert(class_loader.is_null(), "sanity"); return true; @@ -986,7 +987,7 @@ bool SystemDictionary::is_shared_class_visible_impl(Symbol* class_name, ModuleEntry* mod_entry = (pkg_entry == nullptr) ? nullptr : pkg_entry->module(); bool should_be_in_named_module = (mod_entry != nullptr && mod_entry->is_named()); - bool was_archived_from_named_module = scp_entry->in_named_module(); + bool was_archived_from_named_module = !cl->has_unnamed_module(); bool visible; if (was_archived_from_named_module) { @@ -1587,6 +1588,9 @@ void SystemDictionary::initialize(TRAPS) { PlaceholderTable::initialize(); #if INCLUDE_CDS SystemDictionaryShared::initialize(); + if (CDSConfig::is_dumping_archive()) { + AOTClassLocationConfig::dumptime_init(THREAD); + } #endif // Resolve basic classes vmClasses::resolve_all(CHECK); diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index ae834ec9a72..b67dcd43e4d 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -314,7 +314,6 @@ class MetaspaceObj { f(ConstantPoolCache) \ f(Annotations) \ f(MethodCounters) \ - f(SharedClassPathEntry) \ f(RecordComponent) #define METASPACE_OBJ_TYPE_DECLARE(name) name ## Type, diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 9ea021713fc..e7631f9dfe3 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -166,9 +166,9 @@ class Klass : public Metadata { uint8_t _hash_slot; private: - // This is an index into FileMapHeader::_shared_path_table[], to - // associate this class with the JAR file where it's loaded from during - // dump time. If a class is not loaded from the shared archive, this field is + // This is an index into AOTClassLocationConfig::class_locations(), to + // indicate the AOTClassLocation where this class is loaded from during + // dump time. If a class is not loaded from the AOT cache, this field is // -1. s2 _shared_class_path_index; diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 98b1b796813..aedd56817bf 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -667,7 +667,7 @@ JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) { // terminating the VM so we check one more time. // create the zip entry - ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, true); + ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment); if (zip_entry == nullptr) { return JVMTI_ERROR_ILLEGAL_ARGUMENT; } @@ -709,7 +709,7 @@ JvmtiEnv::AddToSystemClassLoaderSearch(const char* segment) { // create the zip entry (which will open the zip file and hence // check that the segment is indeed a zip file). - ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, false); + ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment); if (zip_entry == nullptr) { return JVMTI_ERROR_ILLEGAL_ARGUMENT; } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index b3f70592f6f..420d6ab39d9 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -24,7 +24,6 @@ #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" -#include "cds/filemap.hpp" #include "classfile/classLoader.hpp" #include "classfile/javaAssertions.hpp" #include "classfile/moduleEntry.hpp" diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 4a0e859a692..006f1be112a 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -799,15 +799,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { initialize_class(vmSymbols::jdk_internal_vm_Continuation(), CHECK_JNI_ERR); } -#if INCLUDE_CDS - // capture the module path info from the ModuleEntryTable - ClassLoader::initialize_module_path(THREAD); - if (HAS_PENDING_EXCEPTION) { - java_lang_Throwable::print(PENDING_EXCEPTION, tty); - vm_exit_during_initialization("ClassLoader::initialize_module_path() failed unexpectedly"); - } -#endif - if (NativeHeapTrimmer::enabled()) { NativeHeapTrimmer::initialize(); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/BootClassPathMismatch.java b/test/hotspot/jtreg/runtime/cds/appcds/BootClassPathMismatch.java index 5190f1f72e4..798275be4e7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/BootClassPathMismatch.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/BootClassPathMismatch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -60,6 +60,11 @@ public class BootClassPathMismatch { // this test is not applicable to dynamic archive since // there is no class to be archived in the top archive test.testBootClassPathMatchWithAppend(); + + // this test is not applicable to dynamic archive since + // there is no class path (-cp) specified at dyanmic archive + // creation. + test.testBootClassPathAppend(); } test.testBootClassPathMatch(); test.testBootClassPathMismatchTwoJars(); @@ -235,6 +240,22 @@ public class BootClassPathMismatch { .assertAbnormalExit(mismatchMessage); } + /* Archive contains Hello class with only hello.jar in bootclasspath at dump time. + * + * No error - bootclasspath can be appended during runtime if no -cp is specified. + */ + public void testBootClassPathAppend() throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String jar2 = ClassFileInstaller.writeJar("jar2.jar", "pkg/C2"); + String jars = appJar + File.pathSeparator + jar2; + String appClasses[] = {"Hello", "pkg/C2"}; + TestCommon.dump( + null, appClasses, "-Xbootclasspath/a:" + appJar); + TestCommon.run( + "-Xbootclasspath/a:" + jars, "Hello") + .assertNormalExit(); + } + private static void copyHelloToNewDir() throws Exception { String classDir = CDSTestUtils.getOutputDir(); String dstDir = classDir + File.separator + "newdir"; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java b/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java index df47e6bebc8..569ba8d20f7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -71,19 +71,13 @@ public class ClassPathAttr { TestCommon.testDump(cp, classlist); - TestCommon.run( - "-cp", cp, - "CpAttr1") - .assertNormalExit(); - - // Logging test for class+path. TestCommon.run( "-Xlog:class+path", "-cp", cp, "CpAttr1") .assertNormalExit(output -> { - output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); - output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + output.shouldMatch("Checking .*cpattr2.jar.*from JAR manifest ClassPath attribute"); + output.shouldMatch("Checking .*cpattr3.jar.*from JAR manifest ClassPath attribute"); }); // Test handling of forward slash ('/') file separator when locating entries @@ -117,8 +111,8 @@ public class ClassPathAttr { "-cp", newCp, "CpAttr1") .assertNormalExit(output -> { - output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); - output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + output.shouldMatch("Checking .*cpattr2.jar.*from JAR manifest ClassPath attribute"); + output.shouldMatch("Checking .*cpattr3.jar.*from JAR manifest ClassPath attribute"); }); // Go one directory up. @@ -150,8 +144,8 @@ public class ClassPathAttr { "-cp", newCp, "CpAttr1") .assertNormalExit(output -> { - output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); - output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + output.shouldMatch("Checking .*cpattr2.jar.*from JAR manifest ClassPath attribute"); + output.shouldMatch("Checking .*cpattr3.jar.*from JAR manifest ClassPath attribute"); }); } } @@ -163,6 +157,7 @@ public class ClassPathAttr { TestCommon.testDump(cp, classlist); TestCommon.run( + "-Xlog:class+path", "-cp", cp, "CpAttr1") .assertNormalExit(); @@ -183,7 +178,7 @@ public class ClassPathAttr { "-cp", cp, "CpAttr6") .assertNormalExit(output -> { - output.shouldMatch("should be non-existent: .*cpattrX.jar"); + output.shouldMatch("Checking .*cpattrX.jar.* not-exist"); }); // Now make nonExistPath exist. CDS still loads, but archived non-system classes will not be used. @@ -198,11 +193,10 @@ public class ClassPathAttr { result.assertAbnormalExit(output -> { output.shouldMatch("CDS archive has aot-linked classes. It cannot be used because the file .*cpattrX.jar exists"); }); - } else { - result.assertNormalExit(output -> { - output.shouldMatch("Archived non-system classes are disabled because the file .*cpattrX.jar exists"); - }); + result.assertAbnormalExit(output -> { + output.shouldMatch("cpattrX.jar.* must not exist"); + }); } } @@ -233,8 +227,8 @@ public class ClassPathAttr { "-cp", cp, "Hello") .assertAbnormalExit(output -> { - output.shouldMatch(".*APP classpath mismatch, actual: -Djava.class.path=.*cpattr1.jar.*cpattr2.jar.*hello.jar") - .shouldContain("Unable to use shared archive."); + output.shouldMatch("app classpath .* does not match: expected .*hello.jar.* got .*cpattr2.jar") + .shouldContain("Unable to use shared archive."); }); // Run with different -cp cpattr2.jar:hello.jar. App classpath mismatch should be detected. @@ -243,8 +237,8 @@ public class ClassPathAttr { "-cp", cp, "Hello") .assertAbnormalExit(output -> { - output.shouldMatch(".*APP classpath mismatch, actual: -Djava.class.path=.*cpattr2.jar.*hello.jar") - .shouldContain("Unable to use shared archive."); + output.shouldMatch("app classpath .* does not match: expected .*cpattr1.jar.* got .*cpattr2.jar") + .shouldContain("Unable to use shared archive."); }); // Dumping with -cp cpattr1.jar:cpattr2.jar:hello.jar diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommonAppClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/CommonAppClasspath.java index 9dff6f3a2af..18045994ea8 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommonAppClasspath.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommonAppClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -41,7 +41,7 @@ import jdk.test.lib.cds.CDSTestUtils; public class CommonAppClasspath { private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir()); - private static final String failedMessage = "APP classpath mismatch"; + private static final String failedMessage = "Archived app classpath validation: failed"; private static final String successMessage1 = "Hello source: shared objects file"; private static final String successMessage2 = "HelloMore source: shared objects file"; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/NonExistClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/NonExistClasspath.java index 7f2af4bdcab..38717310aac 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/NonExistClasspath.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/NonExistClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -54,7 +54,7 @@ public class NonExistClasspath { } static void doTest(String appJar, boolean bootcp) throws Exception { - final String errorMessage3 = (bootcp ? "BOOT" : "APP") + " classpath mismatch"; + final String errorMessage3 = (bootcp ? "boot" : "app") + " classpath validation: failed"; (new File(nonExistPath)).delete(); String classPath = nonExistPath + File.pathSeparator + appJar; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java b/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java index 6794b3987b6..c0dce14ab82 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/PrintSharedArchiveAndExit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -58,9 +58,10 @@ public class PrintSharedArchiveAndExit { public static void main(String[] args) throws Exception { String appJar = JarBuilder.getOrCreateHelloJar(); String appJar2 = JarBuilder.build("PrintSharedArchiveAndExit-more", "HelloMore"); - String cp = appJar + File.pathSeparator + appJar2; - String lastCheckMsg = "checking shared classpath entry: " + appJar2; // the last JAR to check + String firstCheckShortMsg = "Checking 'hello.jar' file"; // the first JAR to check (without directory prefix) + String firstCheckMsg = "Checking '" + appJar + "' file"; // the first JAR to check + String lastCheckMsg = "Checking '" + appJar2 + "' file"; // the last JAR to check TestCommon.testDump(cp, TestCommon.list("Hello", "HelloMore")); @@ -99,7 +100,7 @@ public class PrintSharedArchiveAndExit { TestCommon.run( "-cp", ".", "-XX:+PrintSharedArchiveAndExit") - .ifNoMappingFailure(output -> check(output, 1, true, lastCheckMsg, "Run time APP classpath is shorter than the one at dump time: .")); + .ifNoMappingFailure(output -> check(output, 1, true, firstCheckShortMsg, "app classpath has fewer elements than expected")); log("Use an invalid App CP -- all the JAR paths should be checked.\n" + "Non-existing jar files will be ignored."); @@ -107,28 +108,27 @@ public class PrintSharedArchiveAndExit { TestCommon.run( "-cp", invalidCP, "-XX:+PrintSharedArchiveAndExit") - .ifNoMappingFailure(output -> check(output, 0, true, lastCheckMsg)); + .ifNoMappingFailure(output -> check(output, 0, true, firstCheckShortMsg)); log("Changed modification time of hello.jar -- all the JAR paths should be checked"); (new File(appJar)).setLastModified(System.currentTimeMillis() + 2000); TestCommon.run( "-cp", cp, "-XX:+PrintSharedArchiveAndExit") - .ifNoMappingFailure(output -> check(output, 1, true, lastCheckMsg, "Timestamp mismatch")); + .ifNoMappingFailure(output -> check(output, 1, true, firstCheckMsg, "timestamp has changed")); log("Even if hello.jar is out of date, we should still be able to print the dictionary."); TestCommon.run( "-cp", cp, "-XX:+PrintSharedArchiveAndExit") - .ifNoMappingFailure(output -> check(output, 1, true, lastCheckMsg, "java.lang.Object")); - + .ifNoMappingFailure(output -> check(output, 1, true, firstCheckMsg, "java.lang.Object")); log("Remove hello.jar -- all the JAR paths should be checked"); (new File(appJar)).delete(); TestCommon.run( "-cp", cp, "-XX:+PrintSharedArchiveAndExit") - .ifNoMappingFailure(output -> check(output, 1, true, lastCheckMsg, "Required classpath entry does not exist: " + appJar)); + .ifNoMappingFailure(output -> check(output, 1, true, firstCheckMsg, "Required classpath entry does not exist: " + appJar)); log("Execution with major errors -- with 'major' errors like the JSA file\n" + "is missing, we should stop immediately to avoid crashing the JVM."); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java b/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java index 2aa4bafdf12..090df514966 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -268,15 +268,5 @@ public class SharedArchiveConsistency { baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa); System.out.println("new baseArchiveNameOffset = " + baseArchiveNameOffset); testAndCheck(verifyExecArgs); - - // modify _common_app_classpath_size - String wrongCommonAppClasspathOffset = startNewArchive("wrongCommonAppClasspathOffset"); - copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, wrongCommonAppClasspathOffset); - int commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa); - System.out.println(" commonAppClasspathPrefixSize = " + commonAppClasspathPrefixSize); - CDSArchiveUtils.writeData(copiedJsa, CDSArchiveUtils.offsetCommonAppClasspathPrefixSize(), commonAppClasspathPrefixSize * 2); - commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa); - System.out.println("new commonAppClasspathPrefixSize = " + commonAppClasspathPrefixSize); - testAndCheck(verifyExecArgs); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TraceLongClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/TraceLongClasspath.java index 55d036fec42..c79aca564b5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TraceLongClasspath.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TraceLongClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -88,9 +88,12 @@ public class TraceLongClasspath { // Dump an archive with a specified JAR file in -classpath TestCommon.testDump(dumpCP, TestCommon.list("Hello")); - // Then try to execute the archive with a different classpath and with -Xlog:class+path. - // The diagnostic "expecting" app classpath trace should show the entire classpath (excluding any non-existent dump-time paths). - String recordedCP = dummyJar + ps + appJar; + TestCommon.run( + "-Xlog:class+path", "-Xlog:cds", + "-cp", dumpCP, + "Hello") + .assertNormalExit(); + TestCommon.run( "-Xlog:class+path", "-Xlog:cds", "-cp", appJar, @@ -98,9 +101,6 @@ public class TraceLongClasspath { .assertAbnormalExit(output -> { output.shouldContain("Unable to use shared archive"); output.shouldContain("shared class paths mismatch"); - // the "expecting" app classpath from -Xlog:class+path should not - // be truncated - output.shouldContain(recordedCP); }); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/WrongClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/WrongClasspath.java index 9fb88e0b00c..f9594b9d64d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/WrongClasspath.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/WrongClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -109,6 +109,6 @@ public class WrongClasspath { output = TestCommon.execAuto( "-cp", jars, "Hello"); output.shouldMatch("This file is not the one used while building the shared archive file:.*jar2.jar") - .shouldMatch(".warning..cds.*jar2.jar timestamp has changed."); + .shouldMatch(".warning..cds.*jar2.jar.*timestamp has changed"); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchiveConsistency.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchiveConsistency.java index 00bd63fa3d4..ea95a2c8711 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchiveConsistency.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchiveConsistency.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -150,22 +150,6 @@ public class ArchiveConsistency extends DynamicArchiveTestBase { appJar, mainClass, isAuto ? 0 : 1, "Base archive name is damaged"); - startTest("5a. Modify common app classpath size"); - String wrongCommonAppClasspathOffset = getNewArchiveName("wrongCommonAppClasspathOffset"); - copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongCommonAppClasspathOffset); - int commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa); - CDSArchiveUtils.writeData(copiedJsa, CDSArchiveUtils.offsetCommonAppClasspathPrefixSize(), -1); - runTwo(baseArchiveName, wrongCommonAppClasspathOffset, - appJar, mainClass, isAuto ? 0 : 1, - "common app classpath prefix len < 0"); - - startTest("5b. Modify common app classpath size, run with -XX:-VerifySharedSpaces"); - VERIFY_CRC = true; - runTwo(baseArchiveName, modTop, - appJar, mainClass, isAuto ? 0 : 1, - "Header checksum verification failed"); - VERIFY_CRC = false; - startTest("6. Make base archive name not terminated with '\0'"); String wrongBaseName = getNewArchiveName("wrongBaseName"); copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java index 54c5e66ab18..af8f3acb5e9 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -112,6 +112,6 @@ public class WrongTopClasspath extends DynamicArchiveTestBase { .assertNormalExit(output -> { output.shouldContain(topArchiveMsg); output.shouldMatch("This file is not the one used while building the shared archive file:.*GenericTestApp.jar"); - output.shouldMatch(".warning..cds.*GenericTestApp.jar timestamp has changed.");}); + output.shouldMatch(".warning..cds.*GenericTestApp.jar.*timestamp has changed");}); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java index fe777142046..e5ead078696 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -226,29 +226,30 @@ public class ModulePathAndFMG { "-m", MAIN_MODULE); TestCommon.checkDump(output); - tty("5. run with CDS on, without the extra module specified in dump time, should pass"); + tty("5. run with CDS on, without the extra module specified in dump time, should fail"); TestCommon.runWithModules(prefix, null, // --upgrade-module-path libsDir.toString(), // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(OPTIMIZE_DISABLED) - .shouldContain(OPTIMIZE_ENABLED) - .shouldNotContain(FMG_DISABLED) - .shouldContain(FMG_ENABLED) + out.shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldNotContain(FMG_ENABLED) .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only .shouldContain(CLASS_FOUND_MESSAGE); }); + tty("6. run with CDS on, with the extra module specified in dump time"); TestCommon.runWithModules(prefix, null, // --upgrade-module-path extraModulePath, // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(OPTIMIZE_ENABLED) - .shouldContain(OPTIMIZE_DISABLED) - .shouldNotContain(FMG_ENABLED) - .shouldContain(FMG_DISABLED) + out.shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldNotContain(FMG_DISABLED) .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only .shouldContain(CLASS_FOUND_MESSAGE); }); @@ -262,16 +263,16 @@ public class ModulePathAndFMG { extraJarPath, "-m", MAIN_MODULE); TestCommon.checkDump(output); - tty("7. run with CDS on, without the extra module specified in dump time, should pass"); + tty("7. run with CDS on, without the extra module specified in dump time, should fail"); TestCommon.runWithModules(prefix, null, // --upgrade-module-path modularJarPath, // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(OPTIMIZE_DISABLED) - .shouldContain(OPTIMIZE_ENABLED) - .shouldNotContain(FMG_DISABLED) - .shouldContain(FMG_ENABLED) + out.shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldNotContain(FMG_ENABLED) .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only .shouldContain(CLASS_FOUND_MESSAGE); }); @@ -282,10 +283,10 @@ public class ModulePathAndFMG { extraJarPath, // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(OPTIMIZE_ENABLED) - .shouldContain(OPTIMIZE_DISABLED) - .shouldNotContain(FMG_ENABLED) - .shouldContain(FMG_DISABLED) + out.shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldNotContain(FMG_DISABLED) .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only .shouldContain(CLASS_FOUND_MESSAGE); }); @@ -295,10 +296,10 @@ public class ModulePathAndFMG { extraModulePath, // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(OPTIMIZE_ENABLED) - .shouldContain(OPTIMIZE_DISABLED) - .shouldNotContain(FMG_ENABLED) - .shouldContain(FMG_DISABLED) + out.shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldNotContain(FMG_DISABLED) .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only .shouldContain(CLASS_FOUND_MESSAGE); }); diff --git a/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java b/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java index fd76df92ef6..f616b22ef38 100644 --- a/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSArchiveUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, 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 @@ -53,7 +53,6 @@ public class CDSArchiveUtils { private static int offsetCrc; // offset of GenericCDSFileMapHeader::_crc private static int offsetVersion; // offset of GenericCDSFileMapHeader::_version private static int offsetHeaderSize; // offset of GenericCDSFileMapHeader::_header_size - private static int offsetCommonAppClasspathPrefixSize;// offset of GenericCDSFileMapHeader::_common_app_classpath_size private static int offsetBaseArchiveNameOffset;// offset of GenericCDSFileMapHeader::_base_archive_name_offset private static int offsetBaseArchiveNameSize; // offset of GenericCDSFileMapHeader::_base_archive_name_size private static int offsetJvmIdent; // offset of FileMapHeader::_jvm_ident @@ -91,7 +90,6 @@ public class CDSArchiveUtils { offsetCrc = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_crc"); offsetVersion = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_version"); offsetHeaderSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_header_size"); - offsetCommonAppClasspathPrefixSize = wb.getCDSOffsetForName("FileMapHeader::_common_app_classpath_prefix_size"); offsetBaseArchiveNameOffset = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_base_archive_name_offset"); offsetBaseArchiveNameSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_base_archive_name_size"); offsetJvmIdent = wb.getCDSOffsetForName("FileMapHeader::_jvm_ident"); @@ -130,7 +128,6 @@ public class CDSArchiveUtils { public static int offsetCrc() { return offsetCrc; } public static int offsetVersion() { return offsetVersion; } public static int offsetHeaderSize() { return offsetHeaderSize; } - public static int offsetCommonAppClasspathPrefixSize() { return offsetCommonAppClasspathPrefixSize; } public static int offsetBaseArchiveNameOffset() { return offsetBaseArchiveNameOffset; } public static int offsetBaseArchiveNameSize() { return offsetBaseArchiveNameSize; } public static int offsetJvmIdent() { return offsetJvmIdent; } @@ -159,10 +156,6 @@ public class CDSArchiveUtils { return alignUpWithAlignment(size); } - public static int commonAppClasspathPrefixSize(File jsaFile) throws Exception { - return (int)readInt(jsaFile, offsetCommonAppClasspathPrefixSize, 4); - } - public static int baseArchiveNameOffset(File jsaFile) throws Exception { return (int)readInt(jsaFile, offsetBaseArchiveNameOffset, 4); }