8280682: Refactor AOT code source validation checks

Co-authored-by: Ioi Lam <iklam@openjdk.org>
Reviewed-by: iklam, asmehra, dholmes, kvn
This commit is contained in:
Calvin Cheung 2025-02-24 19:54:48 +00:00
parent 65f79c145b
commit ddb2569110
40 changed files with 1537 additions and 1793 deletions

View File

@ -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 <sys/stat.h>
#include <errno.h>
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<const char*> _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) {
// <path> 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<AOTClassLocation*>(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 = <none> (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<AOTClassLocation*>* archived_copy = ArchiveBuilder::new_ro_array<AOTClassLocation*>(_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;
}

View File

@ -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<mtClassShared> {
using Group = AOTClassLocation::Group;
using GrowableClassLocationArray = GrowableArrayCHeap<AOTClassLocation*, mtClassShared>;
// 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<AOTClassLocation*>* _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<AOTClassLocation*>* 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 <typename FUNC> 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 <typename FUNC> 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 <typename FUNC> 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

View File

@ -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"

View File

@ -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"

View File

@ -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) }

View File

@ -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);
}

View File

@ -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;

View File

@ -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() {

View File

@ -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<const char*>* 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<char>(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<u1>* buf = MetadataFactory::new_array<u1>(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<SharedClassPathEntry*>(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<const char*>(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<u1>* buf = MetadataFactory::new_array<u1>(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<const char*>* FileMapInfo::create_dumptime_app_classpath_array() {
assert(CDSConfig::is_dumping_archive(), "sanity");
GrowableArray<const char*>* path_array = new GrowableArray<const char*>(10);
ClassPathEntry* cpe = ClassLoader::app_classpath_entries();
while (cpe != nullptr) {
path_array->append(cpe->name());
cpe = cpe->next();
}
return path_array;
}
GrowableArray<const char*>* FileMapInfo::create_path_array(const char* paths) {
GrowableArray<const char*>* path_array = new GrowableArray<const char*>(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<const char*>* 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<const char*>* 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<const char*>* 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<const char*>* 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<const char*>* module_paths) {
GrowableArray<const char*>* 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<const char*>* module_paths = new GrowableArray<const char*>(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<const char*>* 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<const char*>* 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);

View File

@ -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<char>* _name;
Array<u1>* _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<u1>* 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<SharedClassPathEntry*>* _entries;
public:
SharedPathTable() : _entries(nullptr) {}
SharedPathTable(Array<SharedClassPathEntry*>* 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<SharedClassPathEntry*>* table() {return _entries;}
void set_table(Array<SharedClassPathEntry*>* 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 <typename T> T from_mapped_offset(size_t offset) const {
return (T)(mapped_base_address() + offset);
}
void set_as_offset(char* p, size_t *offset);
template <typename T> 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<char*>(_cloned_vtables_offset); }
char* early_serialized_data() const { return from_mapped_offset<char*>(_early_serialized_data_offset); }
char* serialized_data() const { return from_mapped_offset<char*>(_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<AOTClassLocationConfig*>(_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<SharedClassPathEntry*>*)
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<const char*>* _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<const char*>* create_dumptime_app_classpath_array() NOT_CDS_RETURN_(nullptr);
GrowableArray<const char*>* 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<const char*>* rp_array)
NOT_CDS_RETURN_(0);
bool check_paths(int shared_path_start_idx, int num_paths,
GrowableArray<const char*>* 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<const char*>* 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();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -22,6 +22,7 @@
*
*/
#include "cds/cdsConfig.hpp"
#include "cds/unregisteredClasses.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.inline.hpp"

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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<jshort>(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<const char*>* module_paths = new GrowableArray<const char*>(5);
class ModulePathsGatherer : public ModuleClosure {
JavaThread* _current;
GrowableArray<const char*>* _module_paths;
public:
ModulePathsGatherer(JavaThread* current, GrowableArray<const char*>* 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<jshort>(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<const char*>* 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.

View File

@ -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<const char*>* 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
};

View File

@ -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);

View File

@ -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"

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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"

View File

@ -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();
}

View File

@ -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";

View File

@ -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

View File

@ -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";

View File

@ -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;

View File

@ -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.");

View File

@ -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);
}
}

View File

@ -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);
});
}
}

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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");});
}
}

View File

@ -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);
});

View File

@ -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);
}