--- old/src/hotspot/share/classfile/classLoader.cpp 2019-08-27 10:05:26.022450388 -0700 +++ new/src/hotspot/share/classfile/classLoader.cpp 2019-08-27 10:05:25.786441861 -0700 @@ -73,9 +73,6 @@ #include "utilities/events.hpp" #include "utilities/hashtable.inline.hpp" #include "utilities/macros.hpp" -#if INCLUDE_CDS -#include "classfile/sharedPathsMiscInfo.hpp" -#endif // Entry points in zip.dll for loading zip/jar file entries @@ -147,7 +144,6 @@ ClassPathEntry* ClassLoader::_last_app_classpath_entry = NULL; ClassPathEntry* ClassLoader::_module_path_entries = NULL; ClassPathEntry* ClassLoader::_last_module_path_entry = NULL; -SharedPathsMiscInfo* ClassLoader::_shared_paths_misc_info = NULL; #endif // helper routines @@ -250,13 +246,12 @@ return pkgEntryTable->lookup_only(pkg_symbol); } -ClassPathDirEntry::ClassPathDirEntry(const char* dir) : ClassPathEntry() { - char* copy = NEW_C_HEAP_ARRAY(char, strlen(dir)+1, mtClass); - strcpy(copy, dir); - _dir = copy; +const char* ClassPathEntry::copy_path(const char* path) { + char* copy = NEW_C_HEAP_ARRAY(char, strlen(path)+1, mtClass); + strcpy(copy, path); + return copy; } - ClassFileStream* ClassPathDirEntry::open_stream(const char* name, TRAPS) { // construct full path name assert((_dir != NULL) && (name != NULL), "sanity"); @@ -296,9 +291,7 @@ ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append, bool from_class_path_attr) : ClassPathEntry() { _zip = zip; - char *copy = NEW_C_HEAP_ARRAY(char, strlen(zip_name)+1, mtClass); - strcpy(copy, zip_name); - _zip_name = copy; + _zip_name = copy_path(zip_name); _from_class_path_attr = from_class_path_attr; } @@ -383,8 +376,7 @@ assert(_singleton == NULL, "VM supports only one jimage"); DEBUG_ONLY(_singleton = this); size_t len = strlen(name) + 1; - _name = NEW_C_HEAP_ARRAY(const char, len, mtClass); - strncpy((char *)_name, name, len); + _name = copy_path(name); } ClassPathImageEntry::~ClassPathImageEntry() { @@ -537,30 +529,10 @@ } else { trace_class_path("bootstrap loader class path=", sys_class_path); } -#if INCLUDE_CDS - if (DumpSharedSpaces || DynamicDumpSharedSpaces) { - _shared_paths_misc_info->add_boot_classpath(sys_class_path); - } -#endif setup_boot_search_path(sys_class_path); } #if INCLUDE_CDS -int ClassLoader::get_shared_paths_misc_info_size() { - return _shared_paths_misc_info->get_used_bytes(); -} - -void* ClassLoader::get_shared_paths_misc_info() { - return _shared_paths_misc_info->buffer(); -} - -bool ClassLoader::check_shared_paths_misc_info(void *buf, int size, bool is_static) { - SharedPathsMiscInfo* checker = new SharedPathsMiscInfo((char*)buf, size); - bool result = checker->check(is_static); - delete checker; - return result; -} - void ClassLoader::setup_app_search_path(const char *class_path) { assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity"); @@ -943,11 +915,6 @@ } return true; } else { -#if INCLUDE_CDS - if (DumpSharedSpaces || DynamicDumpSharedSpaces) { - _shared_paths_misc_info->add_nonexist_path(path); - } -#endif return false; } } @@ -1567,12 +1534,6 @@ load_zip_library(); // lookup jimage library entry points load_jimage_library(); -#if INCLUDE_CDS - // initialize search path - if (DumpSharedSpaces || DynamicDumpSharedSpaces) { - _shared_paths_misc_info = new SharedPathsMiscInfo(); - } -#endif setup_bootstrap_search_path(); } @@ -1580,7 +1541,6 @@ void ClassLoader::initialize_shared_path() { if (DumpSharedSpaces || DynamicDumpSharedSpaces) { ClassLoaderExt::setup_search_paths(); - _shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check() } } --- old/src/hotspot/share/classfile/classLoader.hpp 2019-08-27 10:05:26.594471053 -0700 +++ new/src/hotspot/share/classfile/classLoader.hpp 2019-08-27 10:05:26.358462527 -0700 @@ -47,17 +47,19 @@ class ClassPathEntry : public CHeapObj { private: ClassPathEntry* volatile _next; +protected: + const char* copy_path(const char*path); public: ClassPathEntry* next() const; virtual ~ClassPathEntry() {} void set_next(ClassPathEntry* next); - virtual bool is_modules_image() const = 0; - virtual bool is_jar_file() const = 0; + virtual bool is_modules_image() const { return false; } + virtual bool is_jar_file() const { return false; } // Is this entry created from the "Class-path" attribute from a JAR Manifest? - virtual bool from_class_path_attr() const = 0; + virtual bool from_class_path_attr() const { return false; } virtual const char* name() const = 0; - virtual JImageFile* jimage() const = 0; - virtual void close_jimage() = 0; + virtual JImageFile* jimage() const { return NULL; } + virtual void close_jimage() {} // Constructor ClassPathEntry() : _next(NULL) {} // Attempt to locate file_name through this class path entry. @@ -73,18 +75,14 @@ private: const char* _dir; // Name of directory public: - bool is_modules_image() const { return false; } - bool is_jar_file() const { return false; } - bool from_class_path_attr() const { return false; } const char* name() const { return _dir; } - JImageFile* jimage() const { return NULL; } - void close_jimage() {} - ClassPathDirEntry(const char* dir); + ClassPathDirEntry(const char* dir) { + _dir = copy_path(dir); + } virtual ~ClassPathDirEntry() {} ClassFileStream* open_stream(const char* name, TRAPS); }; - // Type definitions for zip file and zip file entry typedef void* jzfile; typedef struct { @@ -104,12 +102,9 @@ const char* _zip_name; // Name of zip archive bool _from_class_path_attr; // From the "Class-path" attribute of a jar file public: - bool is_modules_image() const { return false; } bool is_jar_file() const { return true; } bool from_class_path_attr() const { return _from_class_path_attr; } const char* name() const { return _zip_name; } - JImageFile* jimage() const { return NULL; } - void close_jimage() {} ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append, bool from_class_path_attr); virtual ~ClassPathZipEntry(); u1* open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS); @@ -126,8 +121,6 @@ DEBUG_ONLY(static ClassPathImageEntry* _singleton;) public: bool is_modules_image() const; - bool is_jar_file() const { return false; } - bool from_class_path_attr() const { return false; } bool is_open() const { return _jimage != NULL; } const char* name() const { return _name == NULL ? "" : _name; } JImageFile* jimage() const { return _jimage; } @@ -156,8 +149,6 @@ void add_to_list(ClassPathEntry* new_entry); }; -class SharedPathsMiscInfo; - class ClassLoader: AllStatic { public: enum ClassLoaderType { @@ -230,8 +221,6 @@ static ClassPathEntry* _last_append_entry; // Info used by CDS - CDS_ONLY(static SharedPathsMiscInfo * _shared_paths_misc_info;) - CDS_ONLY(static ClassPathEntry* _app_classpath_entries;) CDS_ONLY(static ClassPathEntry* _last_app_classpath_entry;) CDS_ONLY(static ClassPathEntry* _module_path_entries;) @@ -416,10 +405,6 @@ } return num_entries; } - static void finalize_shared_paths_misc_info(); - static int get_shared_paths_misc_info_size(); - static void* get_shared_paths_misc_info(); - static bool check_shared_paths_misc_info(void* info, int size, bool is_static); static void exit_with_path_failure(const char* error, const char* message); static char* skip_uri_protocol(char* source); static void record_result(InstanceKlass* ik, const ClassFileStream* stream, TRAPS); --- old/src/hotspot/share/classfile/classLoaderExt.cpp 2019-08-27 10:05:27.158491430 -0700 +++ new/src/hotspot/share/classfile/classLoaderExt.cpp 2019-08-27 10:05:26.922482903 -0700 @@ -30,7 +30,6 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/klassFactory.hpp" #include "classfile/modules.hpp" -#include "classfile/sharedPathsMiscInfo.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "memory/allocation.inline.hpp" @@ -74,7 +73,6 @@ trace_class_path("app loader class path (skipped)=", app_class_path); } else { trace_class_path("app loader class path=", app_class_path); - shared_paths_misc_info()->add_app_classpath(app_class_path); ClassLoader::setup_app_search_path(app_class_path); } } @@ -212,8 +210,12 @@ 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"); - trace_class_path("library = ", libname); - ClassLoader::update_class_path_entry_list(libname, true, false, true /* from_class_path_attr */); + if (ClassLoader::update_class_path_entry_list(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; @@ -222,7 +224,6 @@ } void ClassLoaderExt::setup_search_paths() { - shared_paths_misc_info()->record_app_offset(); ClassLoaderExt::setup_app_search_path(); } @@ -248,12 +249,6 @@ result->set_class_loader_type(classloader_type); } -void ClassLoaderExt::finalize_shared_paths_misc_info() { - if (!_has_app_classes) { - shared_paths_misc_info()->pop_app(); - } -} - // Load the class of the given name from the location given by path. The path is specified by // the "source:" in the class list file (see classListParser.cpp), and can be a directory or // a JAR file. --- old/src/hotspot/share/classfile/classLoaderExt.hpp 2019-08-27 10:05:27.714511517 -0700 +++ new/src/hotspot/share/classfile/classLoaderExt.hpp 2019-08-27 10:05:27.478502991 -0700 @@ -47,9 +47,6 @@ static char* get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size); static void setup_app_search_path(); // Only when -Xshare:dump static void process_module_table(ModuleEntryTable* met, TRAPS); - static SharedPathsMiscInfo* shared_paths_misc_info() { - return (SharedPathsMiscInfo*)_shared_paths_misc_info; - } // 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 @@ -84,8 +81,6 @@ return read_manifest(entry, manifest_size, false, THREAD); } - static void finalize_shared_paths_misc_info(); - 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; } --- old/src/hotspot/share/include/cds.h 2019-08-27 10:05:28.334533917 -0700 +++ new/src/hotspot/share/include/cds.h 2019-08-27 10:05:28.098525391 -0700 @@ -36,7 +36,7 @@ #define NUM_CDS_REGIONS 8 // this must be the same as MetaspaceShared::n_regions #define CDS_ARCHIVE_MAGIC 0xf00baba2 #define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 -#define CURRENT_CDS_ARCHIVE_VERSION 6 +#define CURRENT_CDS_ARCHIVE_VERSION 7 #define INVALID_CDS_ARCHIVE_VERSION -1 struct CDSFileMapRegion { --- old/src/hotspot/share/memory/filemap.cpp 2019-08-27 10:05:28.890554004 -0700 +++ new/src/hotspot/share/memory/filemap.cpp 2019-08-27 10:05:28.654545478 -0700 @@ -241,7 +241,6 @@ // JVM version string ... changes on each build. get_header_version(_jvm_ident); - ClassLoaderExt::finalize_shared_paths_misc_info(); _app_class_paths_start_index = ClassLoaderExt::app_class_paths_start_index(); _app_module_paths_start_index = ClassLoaderExt::app_module_paths_start_index(); _num_module_paths = ClassLoader::num_module_path_entries(); @@ -257,6 +256,11 @@ _base_archive_is_default = false; } +void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) { + _type = non_existent_entry; + set_name(path, THREAD); +} + void SharedClassPathEntry::init(bool is_modules_image, ClassPathEntry* cpe, TRAPS) { assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only"); @@ -288,26 +292,35 @@ FileMapInfo::fail_stop("Unable to open file %s.", cpe->name()); } - size_t len = strlen(cpe->name()) + 1; + // 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, THREAD); +} + +void SharedClassPathEntry::set_name(const char* name, TRAPS) { + size_t len = strlen(name) + 1; _name = MetadataFactory::new_array(ClassLoaderData::the_null_class_loader_data(), (int)len, THREAD); - strcpy(_name->data(), cpe->name()); + strcpy(_name->data(), name); +} + +const char* SharedClassPathEntry::name() const { + if (UseSharedSpaces && 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) { +bool SharedClassPathEntry::validate(bool is_class_path) const { assert(UseSharedSpaces, "runtime only"); struct stat st; - const char* name; - - // 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. - if (is_modules_image()) { - name = ClassLoader::get_jrt_entry()->name(); - } else { - name = this->name(); - } + const char* name = this->name(); bool ok = true; log_info(class, path)("checking shared classpath entry: %s", name); @@ -345,6 +358,19 @@ 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); @@ -359,10 +385,11 @@ void SharedPathTable::dumptime_init(ClassLoaderData* loader_data, Thread* THREAD) { size_t entry_size = sizeof(SharedClassPathEntry); - int num_boot_classpath_entries = ClassLoader::num_boot_classpath_entries(); - int num_app_classpath_entries = ClassLoader::num_app_classpath_entries(); - int num_module_path_entries = ClassLoader::num_module_path_entries(); - int num_entries = num_boot_classpath_entries + num_app_classpath_entries + num_module_path_entries; + int num_entries = 0; + num_entries += ClassLoader::num_boot_classpath_entries(); + num_entries += ClassLoader::num_app_classpath_entries(); + num_entries += ClassLoader::num_module_path_entries(); + num_entries += FileMapInfo::num_non_existent_class_paths(); size_t bytes = entry_size * num_entries; _table = MetadataFactory::new_array(loader_data, (int)(bytes + 7 / 8), THREAD); @@ -372,7 +399,7 @@ void FileMapInfo::allocate_shared_path_table() { assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity"); - Thread* THREAD = Thread::current(); + EXCEPTION_MARK; // The following calls should never throw, but would exit VM on error. ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); ClassPathEntry* jrt = ClassLoader::get_jrt_entry(); @@ -383,47 +410,37 @@ // 1. boot class path int i = 0; - ClassPathEntry* cpe = jrt; + i = add_shared_classpaths(i, "boot", jrt, THREAD); + i = add_shared_classpaths(i, "app", ClassLoader::app_classpath_entries(), THREAD); + i = add_shared_classpaths(i, "module", ClassLoader::module_path_entries(), THREAD); + + 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, THREAD); + } + + 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 != NULL) { - bool is_jrt = (cpe == jrt); + bool is_jrt = (cpe == ClassLoader::get_jrt_entry()); const char* type = (is_jrt ? "jrt" : (cpe->is_jar_file() ? "jar" : "dir")); - log_info(class, path)("add main shared path (%s) %s", type, cpe->name()); + log_info(class, path)("add %s shared path (%s) %s", which, type, cpe->name()); SharedClassPathEntry* ent = shared_path(i); ent->init(is_jrt, cpe, THREAD); - if (!is_jrt) { // No need to do the modules image. - EXCEPTION_MARK; // The following call should never throw, but would exit VM on error. - update_shared_classpath(cpe, ent, THREAD); + if (cpe->is_jar_file()) { + update_jar_manifest(cpe, ent, THREAD); + } + if (is_jrt) { + cpe = ClassLoader::get_next_boot_classpath_entry(cpe); + } else { + cpe = cpe->next(); } - cpe = ClassLoader::get_next_boot_classpath_entry(cpe); - i++; - } - assert(i == ClassLoader::num_boot_classpath_entries(), - "number of boot class path entry mismatch"); - - // 2. app class path - ClassPathEntry *acpe = ClassLoader::app_classpath_entries(); - while (acpe != NULL) { - log_info(class, path)("add app shared path %s", acpe->name()); - SharedClassPathEntry* ent = shared_path(i); - ent->init(false, acpe, THREAD); - EXCEPTION_MARK; - update_shared_classpath(acpe, ent, THREAD); - acpe = acpe->next(); i++; } - // 3. module path - ClassPathEntry *mpe = ClassLoader::module_path_entries(); - while (mpe != NULL) { - log_info(class, path)("add module path %s",mpe->name()); - SharedClassPathEntry* ent = shared_path(i); - ent->init(false, mpe, THREAD); - EXCEPTION_MARK; - update_shared_classpath(mpe, ent, THREAD); - mpe = mpe->next(); - i++; - } - assert(i == _shared_path_table.size(), "number of shared path entry mismatch"); + return i; } void FileMapInfo::check_nonempty_dir_in_shared_path_table() { @@ -453,6 +470,24 @@ } } +void FileMapInfo::record_non_existent_class_path_entry(const char* path) { + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only"); + log_info(class, path)("non-existent Class-Path entry %s", path); + if (_non_existent_class_paths == NULL) { + _non_existent_class_paths = new (ResourceObj::C_HEAP, mtInternal)GrowableArray(10, true); + } + _non_existent_class_paths->append(os::strdup(path)); +} + +int FileMapInfo::num_non_existent_class_paths() { + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only"); + if (_non_existent_class_paths != NULL) { + return _non_existent_class_paths->length(); + } else { + return 0; + } +} + class ManifestStream: public ResourceObj { private: u1* _buffer_start; // Buffer bottom @@ -501,29 +536,27 @@ } }; -void FileMapInfo::update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS) { +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; - if (cpe->is_jar_file()) { - assert(ent->is_jar(), "the shared class path entry is not a JAR file"); - char* manifest = ClassLoaderExt::read_manifest(cpe, &manifest_size, CHECK); - if (manifest != NULL) { - ManifestStream* stream = new ManifestStream((u1*)manifest, - manifest_size); - if (stream->check_is_signed()) { - ent->set_is_signed(); - } else { - // Copy the manifest into the shared archive - manifest = ClassLoaderExt::read_raw_manifest(cpe, &manifest_size, CHECK); - Array* buf = MetadataFactory::new_array(loader_data, - manifest_size, - THREAD); - char* p = (char*)(buf->data()); - memcpy(p, manifest, manifest_size); - ent->set_manifest(buf); - } + assert(cpe->is_jar_file() && ent->is_jar(), "the shared class path entry is not a JAR file"); + char* manifest = ClassLoaderExt::read_manifest(cpe, &manifest_size, CHECK); + if (manifest != NULL) { + ManifestStream* stream = new ManifestStream((u1*)manifest, + manifest_size); + if (stream->check_is_signed()) { + ent->set_is_signed(); + } else { + // Copy the manifest into the shared archive + manifest = ClassLoaderExt::read_raw_manifest(cpe, &manifest_size, CHECK); + Array* buf = MetadataFactory::new_array(loader_data, + manifest_size, + THREAD); + char* p = (char*)(buf->data()); + memcpy(p, manifest, manifest_size); + ent->set_manifest(buf); } } } @@ -680,6 +713,16 @@ // None of the jar file specified in the runtime -cp exists. return fail("None of the jar file specified in the runtime -cp exists: -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; mismatch = check_paths(j, shared_app_paths_len, rp_array); if (mismatch) { @@ -689,6 +732,20 @@ 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(); + } +} + bool FileMapInfo::validate_shared_path_table() { assert(UseSharedSpaces, "runtime only"); @@ -717,6 +774,9 @@ } } + 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; @@ -757,6 +817,8 @@ } } + validate_non_existent_class_paths(); + _validating_shared_path_table = false; #if INCLUDE_JVMTI @@ -771,6 +833,26 @@ return true; } +void 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(UseSharedSpaces, "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()) { + warning("Archived non-system classes are disabled because the " + "file %s exists", ent->name()); + _header->_has_platform_or_app_classes = false; + } + } +} + bool FileMapInfo::check_archive(const char* archive_name, bool is_static) { int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); if (fd < 0) { @@ -840,9 +922,6 @@ if (dynamic_header->_base_archive_is_default) { *base_archive_name = Arguments::get_default_shared_archive_path(); } else { - // skip over the _paths_misc_info - sz = dynamic_header->_paths_misc_info_size; - lseek(fd, (long)sz, SEEK_CUR); // read the base archive name size_t name_size = dynamic_header->_base_archive_name_size; if (name_size == 0) { @@ -933,18 +1012,7 @@ } } - _file_offset = n; - - size_t info_size = _header->_paths_misc_info_size; - _paths_misc_info = NEW_C_HEAP_ARRAY(char, info_size, mtClass); - n = os::read(fd, _paths_misc_info, (unsigned int)info_size); - if (n != info_size) { - fail_continue("Unable to read the shared path info header."); - FREE_C_HEAP_ARRAY(char, _paths_misc_info); - _paths_misc_info = NULL; - return false; - } - _file_offset += n + _header->_base_archive_name_size; // accounts for the size of _base_archive_name + _file_offset = n + _header->_base_archive_name_size; // accounts for the size of _base_archive_name if (is_static) { // just checking the last region is sufficient since the archive is written @@ -1026,10 +1094,6 @@ // Write the header to the file, seek to the next allocation boundary. void FileMapInfo::write_header() { - int info_size = ClassLoader::get_shared_paths_misc_info_size(); - - _header->_paths_misc_info_size = info_size; - char* base_archive_name = NULL; if (_header->_magic == CDS_DYNAMIC_ARCHIVE_MAGIC) { base_archive_name = (char*)Arguments::GetSharedArchivePath(); @@ -1039,7 +1103,6 @@ assert(is_file_position_aligned(), "must be"); write_bytes(_header, _header->_header_size); - write_bytes(ClassLoader::get_shared_paths_misc_info(), (size_t)info_size); if (base_archive_name != NULL) { write_bytes(base_archive_name, (size_t)_header->_base_archive_name_size); } @@ -1728,6 +1791,7 @@ SharedPathTable FileMapInfo::_shared_path_table; bool FileMapInfo::_validating_shared_path_table = false; bool FileMapInfo::_memory_mapping_failed = false; +GrowableArray* FileMapInfo::_non_existent_class_paths = NULL; // Open the shared archive file, read and validate the header // information (version, boot classpath, etc.). If initialization @@ -1736,7 +1800,7 @@ // // Validation of the archive is done in two steps: // -// [1] validate_header() - done here. This checks the header, including _paths_misc_info. +// [1] validate_header() - done here. // [2] validate_shared_path_table - this is done later, because the table is in the RW // region of the archive, which is not mapped yet. bool FileMapInfo::initialize(bool is_static) { @@ -1840,22 +1904,7 @@ } bool FileMapInfo::validate_header(bool is_static) { - bool status = _header->validate(); - - if (status) { - if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size, is_static)) { - if (!PrintSharedArchiveAndExit) { - fail_continue("shared class paths mismatch (hint: enable -Xlog:class+path=info to diagnose the failure)"); - status = false; - } - } - } - - if (_paths_misc_info != NULL) { - FREE_C_HEAP_ARRAY(char, _paths_misc_info); - _paths_misc_info = NULL; - } - return status; + return _header->validate(); } // Check if a given address is within one of the shared regions @@ -1907,7 +1956,7 @@ ClassPathEntry* ent = _classpath_entries_for_jvmti[i]; if (ent == NULL) { if (i == 0) { - ent = ClassLoader:: get_jrt_entry(); + ent = ClassLoader::get_jrt_entry(); assert(ent != NULL, "must be"); } else { SharedClassPathEntry* scpe = shared_path(i); --- old/src/hotspot/share/memory/filemap.hpp 2019-08-27 10:05:29.422573225 -0700 +++ new/src/hotspot/share/memory/filemap.hpp 2019-08-27 10:05:29.182564554 -0700 @@ -49,8 +49,12 @@ jar_entry, signed_jar_entry, dir_entry, + non_existent_entry, unknown_entry }; + + void set_name(const char* name, TRAPS); + protected: u1 _type; bool _from_class_path_attr; @@ -61,24 +65,25 @@ public: void init(bool is_modules_image, ClassPathEntry* cpe, TRAPS); + void init_as_non_existent(const char* path, TRAPS); void metaspace_pointers_do(MetaspaceClosure* it); - bool validate(bool is_class_path = true); + bool validate(bool is_class_path = true) const; // The _timestamp only gets set for jar files. - bool has_timestamp() { + bool has_timestamp() const { return _timestamp != 0; } - bool is_dir() { return _type == dir_entry; } - bool is_modules_image() { return _type == modules_image_entry; } - bool is_jar() { return _type == jar_entry; } - bool is_signed() { return _type == signed_jar_entry; } - void set_is_signed() { + 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_signed() const { return _type == signed_jar_entry; } + void set_is_signed() { _type = signed_jar_entry; } bool from_class_path_attr() { return _from_class_path_attr; } time_t timestamp() const { return _timestamp; } long filesize() const { return _filesize; } - const char* name() const { return _name->data(); } + const char* name() const; const char* manifest() const { return (_manifest == NULL) ? NULL : (const char*)_manifest->data(); } @@ -88,6 +93,7 @@ void set_manifest(Array* manifest) { _manifest = manifest; } + bool check_non_existent() const; }; struct ArchiveHeapOopmapInfo { @@ -147,30 +153,12 @@ // size of the base archive name including NULL terminator int _base_archive_name_size; - // The _paths_misc_info is a variable-size structure that records "miscellaneous" - // information during dumping. It is generated and validated by the - // SharedPathsMiscInfo class. See SharedPathsMiscInfo.hpp for - // detailed description. - // - // The _paths_misc_info data is stored as a byte array in the archive file header, - // immediately after the _header field. This information is used only when - // checking the validity of the archive and is deallocated after the archive is loaded. - // - // Note that the _paths_misc_info does NOT include information for JAR files - // that existed during dump time. Their information is stored in _shared_path_table. - int _paths_misc_info_size; - - // The following is a table of all the class path entries that were used - // during dumping. At run time, we require these files to exist and have the same - // size/modification time, or else the archive will refuse to load. - // - // All of these entries must be JAR files. The dumping process would fail if a non-empty - // directory was specified in the classpaths. If an empty directory was specified - // it is checked by the _paths_misc_info as described above. - // - // FIXME -- if JAR files in the tail of the list were specified but not used during dumping, - // they should be removed from this table, to save space and to avoid spurious - // loading failures during runtime. + // The following is a table of all the boot/app/module path entries that were used + // during dumping. At run time, we 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() SharedPathTable _shared_path_table; jshort _app_class_paths_start_index; // Index of first app classpath entry @@ -232,13 +220,14 @@ FileMapHeader * _header; const char* _full_path; - char* _paths_misc_info; char* _base_archive_name; static FileMapInfo* _current_info; static FileMapInfo* _dynamic_archive_info; static bool _heap_pointers_need_patching; static bool _memory_mapping_failed; + static GrowableArray* _non_existent_class_paths; + static bool get_base_archive_name_from_header(const char* archive_name, int* size, char** base_archive_name); static bool check_archive(const char* archive_name, bool is_static); @@ -246,6 +235,8 @@ bool init_from_file(int fd, bool is_static); static void metaspace_pointers_do(MetaspaceClosure* it); + void log_paths(const char* msg, int start_idx, int end_idx); + public: FileMapInfo(bool is_static); ~FileMapInfo(); @@ -353,9 +344,13 @@ static void stop_sharing_and_unmap(const char* msg); static void allocate_shared_path_table(); + static int add_shared_classpaths(int i, const char* which, ClassPathEntry *cpe, TRAPS); static void check_nonempty_dir_in_shared_path_table(); bool validate_shared_path_table(); - static void update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS); + void validate_non_existent_class_paths(); + 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 static ClassFileStream* open_stream_for_jvmti(InstanceKlass* ik, Handle class_loader, TRAPS); --- old/src/hotspot/share/prims/cdsoffsets.cpp 2019-08-27 10:05:29.982593457 -0700 +++ new/src/hotspot/share/prims/cdsoffsets.cpp 2019-08-27 10:05:29.746584930 -0700 @@ -53,7 +53,6 @@ ADD_NEXT(_all, "FileMapHeader::_space[0]", offset_of(FileMapHeader, _space)); \ ADD_NEXT(_all, "CDSFileMapRegion::_crc", offset_of(CDSFileMapRegion, _crc)); \ ADD_NEXT(_all, "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used)); \ - ADD_NEXT(_all, "FileMapHeader::_paths_misc_info_size", offset_of(FileMapHeader, _paths_misc_info_size)); \ ADD_NEXT(_all, "file_header_size", sizeof(FileMapHeader)); \ ADD_NEXT(_all, "DynamicArchiveHeader::_base_archive_crc", offset_of(DynamicArchiveHeader, _base_archive_crc)); \ ADD_NEXT(_all, "CDSFileMapRegion_size", sizeof(CDSFileMapRegion)); --- old/test/hotspot/jtreg/runtime/cds/appcds/AppendClasspath.java 2019-08-27 10:05:30.610616145 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/AppendClasspath.java 2019-08-27 10:05:30.370607474 -0700 @@ -95,19 +95,5 @@ "-cp", appJar2 + File.pathSeparator + appJar, "HelloMore") .assertAbnormalExit(errorMessage1, errorMessage2); - - // FAIL: 4) non-existing jar during dump time but jar exists during runtime - TestCommon.testDump(classPath, TestCommon.list("Hello")); - - Files.copy(Paths.get(classDir, "hello.jar"), - Paths.get(classDir, newFile), - StandardCopyOption.REPLACE_EXISTING); - - TestCommon.run( - "-cp", classPath, - "-Xlog:class+path=trace", - "Hello") - .assertAbnormalExit(errorMessage1, errorMessage2); - } } --- old/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java 2019-08-27 10:05:31.178636666 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java 2019-08-27 10:05:30.934627851 -0700 @@ -42,6 +42,11 @@ public class ClassPathAttr { public static void main(String[] args) throws Exception { + testNormalOps(); + testNonExistentJars(); + } + + static void testNormalOps() throws Exception { buildCpAttr("cpattr1", "cpattr1.mf", "CpAttr1", "CpAttr1"); buildCpAttr("cpattr1_long", "cpattr1_long.mf", "CpAttr1", "CpAttr1"); buildCpAttr("cpattr2", "cpattr2.mf", "CpAttr2", "CpAttr2"); @@ -93,6 +98,37 @@ } } + static void testNonExistentJars() throws Exception { + buildCpAttr("cpattr6", "cpattr6.mf", "CpAttr6", "CpAttr6"); + + String cp = TestCommon.getTestJar("cpattr6.jar"); + String nonExistPath = System.getProperty("test.classes") + File.separator + "cpattrX.jar"; + (new File(nonExistPath)).delete(); + + TestCommon.testDump(cp, TestCommon.list("CpAttr6"), + "-Xlog:class+path"); + + TestCommon.run( + "-Xlog:class+path", + "-cp", cp, + "CpAttr6") + .assertNormalExit(output -> { + output.shouldMatch("should be non-existent: .*cpattrX.jar"); + }); + + // Now make nonExistPath exist. CDS still loads, but archived non-system classes will not be used. + Files.copy(Paths.get(cp), Paths.get(nonExistPath), + StandardCopyOption.REPLACE_EXISTING); + + TestCommon.run( + "-Xlog:class+path", + "-cp", cp, + "CpAttr6") + .assertNormalExit(output -> { + output.shouldMatch("Archived non-system classes are disabled because the file .*/cpattrX.jar exists"); + }); + } + private static void buildCpAttr(String jarName, String manifest, String enclosingClassName, String ...testClassNames) throws Exception { String jarClassesDir = System.getProperty("test.classes") + File.separator + jarName + "_classes"; try { Files.createDirectory(Paths.get(jarClassesDir)); } catch (FileAlreadyExistsException e) { } --- old/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java 2019-08-27 10:05:31.738656898 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java 2019-08-27 10:05:31.498648227 -0700 @@ -61,7 +61,6 @@ public static int offset_version; // CDSFileMapHeaderBase::_version public static int offset_jvm_ident; // FileMapHeader::_jvm_ident public static int sp_offset_crc; // CDSFileMapRegion::_crc - public static int offset_paths_misc_info_size; public static int file_header_size = -1;// total size of header, variant, need calculation public static int CDSFileMapRegion_size; // size of CDSFileMapRegion public static int sp_offset; // offset of CDSFileMapRegion @@ -117,12 +116,6 @@ // this is not real header size, it is struct size int_size = wb.getOffsetForName("int_size"); file_header_size = wb.getOffsetForName("file_header_size"); - offset_paths_misc_info_size = wb.getOffsetForName("FileMapHeader::_paths_misc_info_size") - - offset_magic; - int path_misc_info_size = (int)readInt(fc, offset_paths_misc_info_size, int_size); - file_header_size += path_misc_info_size; - System.out.println("offset_paths_misc_info_size = " + offset_paths_misc_info_size); - System.out.println("path_misc_info_size = " + path_misc_info_size); System.out.println("file_header_size = " + file_header_size); file_header_size = (int)align_up_page(file_header_size); System.out.println("file_header_size (aligned to page) = " + file_header_size); @@ -405,10 +398,9 @@ output.shouldNotContain("Checksum verification failed"); copyFile(orgJsaFile, jsa); - // modify _jvm_ident and _paths_misc_info_size, test should fail - System.out.println("\n2a. Corrupt _jvm_ident and _paths_misc_info_size, should fail\n"); + // modify _jvm_ident, test should fail + System.out.println("\n2a. Corrupt _jvm_ident, should fail\n"); modifyJvmIdent(); - modifyHeaderIntField(offset_paths_misc_info_size, Integer.MAX_VALUE); output = TestCommon.execCommon(execArgs); output.shouldContain("The shared archive file was created by a different version or build of HotSpot"); output.shouldNotContain("Checksum verification failed"); @@ -422,19 +414,17 @@ output.shouldContain("Hello World"); copyFile(orgJsaFile, jsa); - // modify _magic and _paths_misc_info_size, test should fail - System.out.println("\n2c. Corrupt _magic and _paths_misc_info_size, should fail\n"); + // modify _magic, test should fail + System.out.println("\n2c. Corrupt _magic, should fail\n"); modifyHeaderIntField(offset_magic, 0x00000000); - modifyHeaderIntField(offset_paths_misc_info_size, Integer.MAX_VALUE); output = TestCommon.execCommon(execArgs); output.shouldContain("The shared archive file has a bad magic number"); output.shouldNotContain("Checksum verification failed"); copyFile(orgJsaFile, jsa); - // modify _version and _paths_misc_info_size, test should fail - System.out.println("\n2d. Corrupt _version and _paths_misc_info_size, should fail\n"); + // modify _version, test should fail + System.out.println("\n2d. Corrupt _version, should fail\n"); modifyHeaderIntField(offset_version, 0x00000000); - modifyHeaderIntField(offset_paths_misc_info_size, Integer.MAX_VALUE); output = TestCommon.execCommon(execArgs); output.shouldContain("The shared archive file has the wrong version"); output.shouldNotContain("Checksum verification failed"); --- old/test/hotspot/jtreg/runtime/cds/appcds/TraceLongClasspath.java 2019-08-27 10:05:32.366679586 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/TraceLongClasspath.java 2019-08-27 10:05:32.126670915 -0700 @@ -24,7 +24,7 @@ /* * @test - * @summary ensure -XX:+TraceClassPaths showing entire expecting app classpath + * @summary ensure -Xlog:class+path showing entire expecting app classpath * @requires vm.cds * @library /test/lib * @modules jdk.jartool/sun.tools.jar @@ -85,22 +85,23 @@ "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/jdk/lib/tools.jar" + ps + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.ooo_12.1.3/ooo-manifest.jar"; - String myCP = longClassPath + ps + appJar; + String dumpCP = longClassPath + ps + appJar; // Dump an archive with a specified JAR file in -classpath - TestCommon.testDump(myCP, TestCommon.list("Hello")); + TestCommon.testDump(dumpCP, TestCommon.list("Hello")); - // Then try to execute the archive with a different classpath and with -XX:+TraceClassPaths. - // The diagnosis "expecting" app classpath trace should show the entire classpath. + // 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( - "-XX:+TraceClassPaths", "-Xlog:cds", + "-Xlog:class+path", "-Xlog:cds", "-cp", appJar, "Hello") .assertAbnormalExit(output -> { output.shouldContain("Unable to use shared archive"); output.shouldContain("shared class paths mismatch"); - // the "expecting" app classpath from -XX:+TraceClassPaths should not + // the "expecting" app classpath from -Xlog:class+path should not // be truncated - output.shouldContain(myCP); + output.shouldContain(recordedCP); }); } } --- /dev/null 2019-02-25 13:26:02.045529497 -0800 +++ new/test/hotspot/jtreg/runtime/cds/appcds/NonExistClasspath.java 2019-08-27 10:05:32.746693315 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +/* + * @test + * @summary Handling of non-existent classpath elements during dump time and run time + * @requires vm.cds + * @library /test/lib + * @modules jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @compile test-classes/HelloMore.java + * @run driver NonExistClasspath + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import jdk.test.lib.process.OutputAnalyzer; + +public class NonExistClasspath { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + doTest(appJar, false); + doTest(appJar, true); + } + + static void doTest(String appJar, boolean bootcp) throws Exception { + String classDir = System.getProperty("test.classes"); + String newFile = "non-exist.jar"; + String nonExistPath = classDir + File.separator + newFile; + final String errorMessage1 = "Unable to use shared archive"; + final String errorMessage2 = "shared class paths mismatch"; + final String errorMessage3 = (bootcp ? "BOOT" : "APP") + " classpath mismatch"; + + (new File(nonExistPath)).delete(); + + String classPath = nonExistPath + File.pathSeparator + appJar; + TestCommon.testDump("foobar", TestCommon.list("Hello"), make_args(bootcp, classPath)); + + // The nonExistPath doesn't exist yet, so we should be able to run without problem + TestCommon.run(make_args(bootcp, + classPath, + "-Xlog:class+path=trace", + "Hello")) + .assertNormalExit(); + + // Replace nonExistPath with another non-existent file in the CP, it should still work + TestCommon.run(make_args(bootcp, + nonExistPath + ".duh" + File.pathSeparator + appJar, + "-Xlog:class+path=trace", + "Hello")) + .assertNormalExit(); + + // Add a few more non-existent files in the CP, it should still work + TestCommon.run(make_args(bootcp, + nonExistPath + ".duh" + File.pathSeparator + + nonExistPath + ".daa" + File.pathSeparator + + nonExistPath + ".boo" + File.pathSeparator + + appJar, + "-Xlog:class+path=trace", + "Hello")) + .assertNormalExit(); + + // Or, remove all non-existent paths from the CP, it should still work + TestCommon.run(make_args(bootcp, + appJar, + "-Xlog:class+path=trace", + "Hello")) + .assertNormalExit(); + + // Now make nonExistPath exist. CDS will fail to load. + Files.copy(Paths.get(classDir, "hello.jar"), + Paths.get(classDir, newFile), + StandardCopyOption.REPLACE_EXISTING); + + TestCommon.run(make_args(bootcp, + classPath, + "-Xlog:class+path=trace", + "Hello")) + .assertAbnormalExit(errorMessage1, errorMessage2, errorMessage3); + } + + static String[] make_args(boolean bootcp, String cp, String... suffix) { + String args[]; + if (bootcp) { + args = TestCommon.concat("-Xbootclasspath/a:" + cp); + } else { + args = TestCommon.concat("-cp", cp); + } + + return TestCommon.concat(args, suffix); + } +} --- /dev/null 2019-02-25 13:26:02.045529497 -0800 +++ new/test/hotspot/jtreg/runtime/cds/appcds/test-classes/CpAttr6.java 2019-08-27 10:05:33.510720917 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +public class CpAttr6 { + public static void main(String args[]) { + System.out.println("Test passed"); + } +} --- old/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp 2019-08-27 10:05:34.514757190 -0700 +++ /dev/null 2019-02-25 13:26:02.045529497 -0800 @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2014, 2019, 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 "precompiled.hpp" -#include "classfile/classLoader.hpp" -#include "classfile/sharedPathsMiscInfo.hpp" -#include "logging/log.hpp" -#include "logging/logStream.hpp" -#include "memory/allocation.inline.hpp" -#include "memory/filemap.hpp" -#include "memory/metaspaceShared.hpp" -#include "memory/resourceArea.hpp" -#include "runtime/arguments.hpp" -#include "runtime/os.inline.hpp" -#include "utilities/ostream.hpp" - -SharedPathsMiscInfo::SharedPathsMiscInfo() { - _app_offset = 0; - _buf_size = INITIAL_BUF_SIZE; - _cur_ptr = _buf_start = NEW_C_HEAP_ARRAY(char, _buf_size, mtClass); - _allocated = true; -} - -SharedPathsMiscInfo::~SharedPathsMiscInfo() { - if (_allocated) { - FREE_C_HEAP_ARRAY(char, _buf_start); - } -} - -void SharedPathsMiscInfo::add_path(const char* path, int type) { - log_info(class, path)("type=%s ", type_name(type)); - ClassLoader::trace_class_path("add misc shared path ", path); - write(path, strlen(path) + 1); - write_jint(jint(type)); -} - -void SharedPathsMiscInfo::ensure_size(size_t needed_bytes) { - assert(_allocated, "cannot modify buffer during validation."); - int used = get_used_bytes(); - int target = used + int(needed_bytes); - if (target > _buf_size) { - _buf_size = _buf_size * 2 + (int)needed_bytes; - _buf_start = REALLOC_C_HEAP_ARRAY(char, _buf_start, _buf_size, mtClass); - _cur_ptr = _buf_start + used; - _end_ptr = _buf_start + _buf_size; - } -} - -void SharedPathsMiscInfo::write(const void* ptr, size_t size) { - ensure_size(size); - memcpy(_cur_ptr, ptr, size); - _cur_ptr += size; -} - -bool SharedPathsMiscInfo::read(void* ptr, size_t size) { - if (_cur_ptr + size <= _end_ptr) { - memcpy(ptr, _cur_ptr, size); - _cur_ptr += size; - return true; - } - return false; -} - -bool SharedPathsMiscInfo::fail(const char* msg, const char* name) { - ClassLoader::trace_class_path(msg, name); - MetaspaceShared::set_archive_loading_failed(); - return false; -} - -void SharedPathsMiscInfo::print_path(outputStream* out, int type, const char* path) { - switch (type) { - case BOOT_PATH: - out->print("Expecting BOOT path=%s", path); - break; - case NON_EXIST: - out->print("Expecting that %s does not exist", path); - break; - case APP_PATH: - ClassLoader::trace_class_path("Expecting -Djava.class.path=", path); - break; - default: - ShouldNotReachHere(); - } -} - -bool SharedPathsMiscInfo::check(bool is_static) { - // The whole buffer must be 0 terminated so that we can use strlen and strcmp - // without fear. - _end_ptr -= sizeof(jint); - if (_cur_ptr >= _end_ptr) { - return fail("Truncated archive file header"); - } - if (*_end_ptr != 0) { - return fail("Corrupted archive file header"); - } - - jshort cur_index = 0; - FileMapHeader* header = is_static ? FileMapInfo::current_info()->header() : - FileMapInfo::dynamic_info()->header(); - jshort max_cp_index = header->max_used_path_index(); - jshort module_paths_start_index = header->app_module_paths_start_index(); - while (_cur_ptr < _end_ptr) { - jint type; - const char* path = _cur_ptr; - _cur_ptr += strlen(path) + 1; - - if (!read_jint(&type)) { - return fail("Corrupted archive file header"); - } - LogTarget(Info, class, path) lt; - if (lt.is_enabled()) { - lt.print("type=%s ", type_name(type)); - LogStream ls(lt); - print_path(&ls, type, path); - ls.cr(); - } - // skip checking the class path(s) which was not referenced during CDS dump - if ((cur_index <= max_cp_index) || (cur_index >= module_paths_start_index)) { - if (!check(type, path, is_static)) { - if (!PrintSharedArchiveAndExit) { - return false; - } - } else { - ClassLoader::trace_class_path("ok"); - } - } else { - ClassLoader::trace_class_path("skipped check"); - } - cur_index++; - } - - return true; -} - -bool SharedPathsMiscInfo::check(jint type, const char* path, bool is_static) { - assert(UseSharedSpaces, "runtime only"); - switch (type) { - case BOOT_PATH: - break; - case NON_EXIST: - { - struct stat st; - if (os::stat(path, &st) == 0) { - // The file actually exists - // But we want it to not exist -> fail - return fail("File must not exist"); - } - } - break; - case APP_PATH: - break; - default: - return fail("Corrupted archive file header"); - } - - return true; -} --- old/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp 2019-08-27 10:05:34.978773953 -0700 +++ /dev/null 2019-02-25 13:26:02.045529497 -0800 @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2014, 2019, 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_CLASSFILE_SHAREDPATHSMISCINFO_HPP -#define SHARE_CLASSFILE_SHAREDPATHSMISCINFO_HPP - -#include "classfile/classLoader.hpp" -#include "runtime/os.hpp" - -class outputStream; -// During dumping time, when processing class paths, we build up the dump-time -// classpath. The JAR files that exist are stored in the list ClassLoader::_first_append_entry. -// However, we need to store other "misc" information for run-time checking, such as -// -// + The values of Arguments::get_sysclasspath() used during dumping. -// -// + The class path elements specified during dumping but did not exist -- -// these elements must also be specified at run time, and they also must not -// exist at run time. -// -// These misc items are stored in a linear buffer in SharedPathsMiscInfo. -// The storage format is stream oriented to minimize its size. -// -// When writing the information to the archive file, SharedPathsMiscInfo is stored in -// the archive file header. At run-time, this information is used only during initialization -// (accessed using read() instead of mmap()), and is deallocated afterwards to save space. -// -// The SharedPathsMiscInfo class is used for both creating the the information (during -// dumping time) and validation (at run time). Different constructors are used in the -// two situations. See below. - -class SharedPathsMiscInfo : public CHeapObj { -private: - int _app_offset; -protected: - char* _buf_start; - char* _cur_ptr; - char* _end_ptr; - int _buf_size; - bool _allocated; // was _buf_start allocated by me? - void ensure_size(size_t needed_bytes); - void add_path(const char* path, int type); - - void write(const void* ptr, size_t size); - bool read(void* ptr, size_t size); - -protected: - static bool fail(const char* msg, const char* name = NULL); - bool check(jint type, const char* path, bool is_static); - -public: - enum { - INITIAL_BUF_SIZE = 128 - }; - // This constructor is used when creating the misc information (during dump) - SharedPathsMiscInfo(); - // This constructor is used when validating the misc info (during run time) - SharedPathsMiscInfo(char *buff, int size) { - _app_offset = 0; - _cur_ptr = _buf_start = buff; - _end_ptr = _buf_start + size; - _buf_size = size; - _allocated = false; - } - ~SharedPathsMiscInfo(); - - int get_used_bytes() { - return _cur_ptr - _buf_start; - } - void* buffer() { - return _buf_start; - } - - // writing -- - - // The path must not exist at run-time - void add_nonexist_path(const char* path) { - add_path(path, NON_EXIST); - } - - // The path must exist, and must contain exactly files/dirs - void add_boot_classpath(const char* path) { - add_path(path, BOOT_PATH); - } - - void add_app_classpath(const char* path) { - add_path(path, APP_PATH); - } - void record_app_offset() { - _app_offset = get_used_bytes(); - } - void pop_app() { - _cur_ptr = _buf_start + _app_offset; - write_jint(0); - } - - int write_jint(jint num) { - write(&num, sizeof(num)); - return 0; - } - void write_time(time_t t) { - write(&t, sizeof(t)); - } - void write_long(long l) { - write(&l, sizeof(l)); - } - - bool dump_to_file(int fd) { - int n = get_used_bytes(); - return (os::write(fd, _buf_start, n) == (size_t)n); - } - - // reading -- - -private: - enum { - BOOT_PATH = 1, - APP_PATH = 2, - NON_EXIST = 3 - }; - - const char* type_name(int type) { - switch (type) { - case BOOT_PATH: return "BOOT"; - case APP_PATH: return "APP"; - case NON_EXIST: return "NON_EXIST"; - default: ShouldNotReachHere(); return "?"; - } - } - - void print_path(outputStream* os, int type, const char* path); - - bool read_jint(jint *ptr) { - return read(ptr, sizeof(jint)); - } - bool read_long(long *ptr) { - return read(ptr, sizeof(long)); - } - bool read_time(time_t *ptr) { - return read(ptr, sizeof(time_t)); - } - -public: - bool check(bool is_static); -}; - -#endif // SHARE_CLASSFILE_SHAREDPATHSMISCINFO_HPP