--- make/data/hotspot-symbols/symbols-unix | 1 + src/hotspot/share/cds/archiveBuilder.cpp | 3 +- src/hotspot/share/cds/dynamicArchive.cpp | 1 - src/hotspot/share/cds/filemap.cpp | 76 ++- src/hotspot/share/cds/metaspaceShared.cpp | 5 +- src/hotspot/share/classfile/classLoader.cpp | 2 +- src/hotspot/share/classfile/dictionary.cpp | 2 +- src/hotspot/share/classfile/dictionary.hpp | 1 + src/hotspot/share/classfile/javaClasses.cpp | 38 ++ src/hotspot/share/classfile/javaClasses.hpp | 28 + src/hotspot/share/classfile/klassFactory.cpp | 53 +- .../classfile/systemDictionaryShared.cpp | 522 +++++++++++++++++- .../classfile/systemDictionaryShared.hpp | 17 + src/hotspot/share/include/cds.h | 1 + src/hotspot/share/include/jvm.h | 5 + .../jbooster/client/clientDataManager.cpp | 36 ++ src/hotspot/share/logging/logTag.hpp | 1 + src/hotspot/share/prims/jvm.cpp | 23 +- src/hotspot/share/runtime/arguments.cpp | 20 +- src/hotspot/share/runtime/arguments.hpp | 4 + src/hotspot/share/runtime/java.cpp | 3 +- src/hotspot/share/utilities/macros.hpp | 8 + .../share/classes/java/lang/ClassLoader.java | 20 + .../classes/java/net/AggressiveCDSPlugin.java | 203 +++++++ .../classes/java/net/URLClassLoader.java | 30 + .../java/security/SecureClassLoader.java | 14 + .../jdk/internal/loader/URLClassPath.java | 18 + .../share/native/libjava/ClassLoader.c | 26 + 28 files changed, 1133 insertions(+), 28 deletions(-) create mode 100644 src/java.base/share/classes/java/net/AggressiveCDSPlugin.java diff --git a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix index 63e5a6946..ac8434902 100644 --- a/make/data/hotspot-symbols/symbols-unix +++ b/make/data/hotspot-symbols/symbols-unix @@ -213,3 +213,4 @@ JVM_JBoosterHandleConnection JVM_JBoosterPrintStoredClientData JVM_JBoosterGetMetaspaceMethodData JVM_JBoosterStartupNativeCallback +JVM_DefineTrustedSharedClass diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 8e12cdabb..a263f8ecc 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -1093,7 +1093,8 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, print_region_stats(mapinfo, closed_heap_regions, open_heap_regions); mapinfo->set_requested_base((char*)MetaspaceShared::requested_base_address()); - if (mapinfo->header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC) { + if (mapinfo->header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC + AGGRESSIVE_CDS_ONLY(|| mapinfo->header()->magic() == CDS_AGGRESSIVE_ARCHIVE_MAGIC)) { mapinfo->set_header_base_archive_name_size(strlen(Arguments::GetSharedArchivePath()) + 1); mapinfo->set_header_base_archive_is_default(FLAG_IS_DEFAULT(SharedArchiveFile)); } diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 8e3929bc2..5b1176071 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -48,7 +48,6 @@ #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" - class DynamicArchiveBuilder : public ArchiveBuilder { public: void mark_pointer(address* ptr_loc) { diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 242b57ee5..fa981d38c 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -67,6 +67,10 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/heapRegion.hpp" #endif +#if INCLUDE_AGGRESSIVE_CDS +#include "jbooster/client/clientDataManager.hpp" +#include "jbooster/jbooster_globals.hpp" +#endif // INCLUDE_AGGRESSIVE_CDS # include # include @@ -209,7 +213,14 @@ void FileMapInfo::populate_header(size_t core_region_alignment) { void FileMapHeader::populate(FileMapInfo* mapinfo, size_t core_region_alignment) { if (DynamicDumpSharedSpaces) { - _magic = CDS_DYNAMIC_ARCHIVE_MAGIC; +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS) { + _magic = CDS_AGGRESSIVE_ARCHIVE_MAGIC; + } else +#endif // INCLUDE_AGGRESSIVE_CDS + { + _magic = CDS_DYNAMIC_ARCHIVE_MAGIC; + } } else { _magic = CDS_ARCHIVE_MAGIC; } @@ -396,14 +407,16 @@ bool SharedClassPathEntry::validate(bool is_class_path) const { 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. - FileMapInfo::fail_continue("Required classpath entry does not exist: %s", name); + if (!SkipSharedClassPathCheck || !is_dir()) { + // 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. + FileMapInfo::fail_continue("Required classpath entry does not exist: %s", name); + } ok = false; } else if (is_dir()) { - if (!os::dir_is_empty(name)) { + if (!SkipSharedClassPathCheck && !os::dir_is_empty(name)) { FileMapInfo::fail_continue("directory is not empty: %s", name); ok = false; } @@ -533,6 +546,10 @@ int FileMapInfo::add_shared_classpaths(int i, const char* which, ClassPathEntry } void FileMapInfo::check_nonempty_dir_in_shared_path_table() { + if (SkipSharedClassPathCheck) { + return; + } + Arguments::assert_is_dumping_archive(); bool has_nonempty_dir = false; @@ -597,7 +614,7 @@ int FileMapInfo::get_module_shared_path_index(Symbol* location) { const char* file = ClassLoader::skip_uri_protocol(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); - assert(ent->in_named_module(), "must be"); + assert(ent->in_named_module() || SkipSharedClassPathCheck, "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"); @@ -798,6 +815,9 @@ bool FileMapInfo::validate_boot_class_paths() { } bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) { + if (SkipSharedClassPathCheck) { + return true; + } const char *appcp = Arguments::get_appclasspath(); assert(appcp != NULL, "NULL app classpath"); int rp_len = num_paths(appcp); @@ -988,7 +1008,8 @@ bool FileMapInfo::check_archive(const char* archive_name, bool is_static) { } } else { DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)header; - if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { + if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC + AGGRESSIVE_CDS_ONLY(&& dynamic_header->magic() != CDS_AGGRESSIVE_ARCHIVE_MAGIC)) { os::free(header); os::close(fd); vm_exit_during_initialization("Not a top shared archive", archive_name); @@ -1018,7 +1039,8 @@ bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name, os::close(fd); return false; } - if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { + if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC + AGGRESSIVE_CDS_ONLY(&& dynamic_header->magic() != CDS_AGGRESSIVE_ARCHIVE_MAGIC)) { // Not a dynamic header, no need to proceed further. *size = 0; os::free(dynamic_header); @@ -1068,6 +1090,13 @@ bool FileMapInfo::init_from_file(int fd) { } unsigned int expected_magic = is_static() ? CDS_ARCHIVE_MAGIC : CDS_DYNAMIC_ARCHIVE_MAGIC; + +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS && !is_static()) { + expected_magic = CDS_AGGRESSIVE_ARCHIVE_MAGIC; + } +#endif // INCLUDE_AGGRESSIVE_CDS + if (header()->magic() != expected_magic) { log_info(cds)("_magic expected: 0x%08x", expected_magic); log_info(cds)(" actual: 0x%08x", header()->magic()); @@ -1186,10 +1215,21 @@ void FileMapInfo::open_for_write(const char* path) { chmod(_full_path, _S_IREAD | _S_IWRITE); #endif - // Use remove() to delete the existing file because, on Unix, this will - // allow processes that have it open continued access to the file. - remove(_full_path); - int fd = os::open(_full_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0444); + int fd; +#if INCLUDE_JBOOSTER + if (UseJBooster && ClientDataManager::get().is_cds_allowed()) { + // The _full_path points to the tmp file in the JBooster environment. + // The tmp file should have been created before (see dump_cds() in + // clientMessageHandler.cpp). So do not remove it or try to create it. + fd = os::open(_full_path, O_RDWR | O_TRUNC | O_BINARY, 0); + } else +#endif // INCLUDE_JBOOSTER + { + // Use remove() to delete the existing file because, on Unix, this will + // allow processes that have it open continued access to the file. + remove(_full_path); + fd = os::open(_full_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0444); + } if (fd < 0) { fail_stop("Unable to create shared archive file %s: (%s).", _full_path, os::strerror(errno)); @@ -1200,7 +1240,8 @@ void FileMapInfo::open_for_write(const char* path) { // Seek past the header. We will write the header after all regions are written // and their CRCs computed. size_t header_bytes = header()->header_size(); - if (header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC) { + if (header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC + AGGRESSIVE_CDS_ONLY(|| header()->magic() == CDS_AGGRESSIVE_ARCHIVE_MAGIC)) { header_bytes += strlen(Arguments::GetSharedArchivePath()) + 1; } @@ -1218,7 +1259,8 @@ void FileMapInfo::write_header() { assert(is_file_position_aligned(), "must be"); write_bytes(header(), header()->header_size()); - if (header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC) { + if (header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC + AGGRESSIVE_CDS_ONLY(|| header()->magic() == CDS_AGGRESSIVE_ARCHIVE_MAGIC)) { char* base_archive_name = (char*)Arguments::GetSharedArchivePath(); if (base_archive_name != NULL) { write_bytes(base_archive_name, header()->base_archive_name_size()); @@ -2328,7 +2370,7 @@ ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) { ClassPathEntry* ent = _classpath_entries_for_jvmti[i]; if (ent == NULL) { SharedClassPathEntry* scpe = shared_path(i); - assert(scpe->is_jar(), "must be"); // other types of scpe will not produce archived classes + assert(scpe->is_jar() || SkipSharedClassPathCheck, "must be"); // other types of scpe will not produce archived classes const char* path = scpe->name(); struct stat st; diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 87ff93fb4..f4c8d0f81 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -76,6 +76,9 @@ #if INCLUDE_G1GC #include "gc/g1/g1CollectedHeap.inline.hpp" #endif +#if INCLUDE_AGGRESSIVE_CDS +#include "jbooster/jbooster_globals.hpp" +#endif // INCLUDE_AGGRESSIVE_CDS ReservedSpace MetaspaceShared::_symbol_rs; VirtualSpace MetaspaceShared::_symbol_vs; @@ -573,7 +576,7 @@ public: bool MetaspaceShared::linking_required(InstanceKlass* ik) { // For static CDS dump, do not link old classes. // For dynamic CDS dump, only link classes loaded by the builtin class loaders. - return DumpSharedSpaces ? ik->can_be_verified_at_dumptime() : !ik->is_shared_unregistered_class(); + return DumpSharedSpaces ? ik->can_be_verified_at_dumptime() : !ik->is_shared_unregistered_class() AGGRESSIVE_CDS_ONLY(|| UseAggressiveCDS); } bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) { diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 05561110a..6e9b9bcdf 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1315,7 +1315,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, const Cl (i < ClassLoaderExt::app_class_paths_start_index())) { // The class must be from boot loader append path which consists of // -Xbootclasspath/a and jvmti appended entries. - assert(loader == NULL, "sanity"); + assert(loader == NULL || SkipSharedClassPathCheck, "sanity"); classpath_index = i; break; } diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index 9905eb4fc..3034a5eba 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -640,4 +640,4 @@ void Dictionary::verify() { stringStream tempst; tempst.print("System Dictionary for %s class loader", cld->loader_name_and_id()); verify_table(tempst.as_string()); -} +} \ No newline at end of file diff --git a/src/hotspot/share/classfile/dictionary.hpp b/src/hotspot/share/classfile/dictionary.hpp index e42b60cf0..28dfff309 100644 --- a/src/hotspot/share/classfile/dictionary.hpp +++ b/src/hotspot/share/classfile/dictionary.hpp @@ -44,6 +44,7 @@ class Dictionary : public Hashtable { static bool _some_dictionary_needs_resizing; bool _resizable; bool _needs_resizing; + void check_if_needs_resize(); ClassLoaderData* _loader_data; // backpointer to owning loader diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index bf817989d..24cc928e4 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -5066,6 +5066,44 @@ void java_lang_InternalError::serialize_offsets(SerializeClosure* f) { } #endif +#if INCLUDE_AGGRESSIVE_CDS +int java_security_ProtectionDomain::_code_source_offset; + +oop java_security_ProtectionDomain::codeSource(oop protection_domain) { + return protection_domain->obj_field(_code_source_offset); +} + +#define PROTECTIONDOMAIN_FIELDS_DO(macro) \ + macro(_code_source_offset, k, "codesource", codesource_signature, false) + +void java_security_ProtectionDomain::compute_offsets() { + InstanceKlass* k = vmClasses::ProtectionDomain_klass(); + PROTECTIONDOMAIN_FIELDS_DO(FIELD_COMPUTE_OFFSET); +} + +void java_security_ProtectionDomain::serialize_offsets(SerializeClosure* f) { + PROTECTIONDOMAIN_FIELDS_DO(FIELD_SERIALIZE_OFFSET); +} + +int java_security_CodeSource::_locationNoFragString_offset; + +oop java_security_CodeSource::locationNoFragString(oop code_source) { + return code_source->obj_field(_locationNoFragString_offset); +} + +#define CODESOURCE_FIELDS_DO(macro) \ + macro(_locationNoFragString_offset, k, "locationNoFragString", string_signature, false) + +void java_security_CodeSource::compute_offsets() { + InstanceKlass* k = vmClasses::CodeSource_klass(); + CODESOURCE_FIELDS_DO(FIELD_COMPUTE_OFFSET); +} + +void java_security_CodeSource::serialize_offsets(SerializeClosure* f) { + CODESOURCE_FIELDS_DO(FIELD_SERIALIZE_OFFSET); +} +#endif // INCLUDE_AGGRESSIVE_CDS + #define DO_COMPUTE_OFFSETS(k) k::compute_offsets(); // Compute field offsets of all the classes in this file diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index f18838781..2a769ea83 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -79,6 +79,8 @@ class RecordComponent; f(jdk_internal_misc_UnsafeConstants) \ f(java_lang_boxing_object) \ f(vector_VectorPayload) \ + AGGRESSIVE_CDS_ONLY(f(java_security_ProtectionDomain)) \ + AGGRESSIVE_CDS_ONLY(f(java_security_CodeSource)) \ //end #define BASIC_JAVA_CLASSES_DO(f) \ @@ -1744,6 +1746,32 @@ class java_lang_InternalError : AllStatic { static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; }; +#if INCLUDE_AGGRESSIVE_CDS +class java_security_ProtectionDomain : AllStatic { + private: + static int _code_source_offset; + + public: + static void compute_offsets(); + + static void serialize_offsets(SerializeClosure* f); + + static oop codeSource(oop protection_domain); +}; + +class java_security_CodeSource : AllStatic { + private: + static int _locationNoFragString_offset; + + public: + static void compute_offsets(); + + static void serialize_offsets(SerializeClosure* f); + + static oop locationNoFragString(oop code_source); +}; +#endif // INCLUDE_AGGRESSIVE_CDS + // Use to declare fields that need to be injected into Java classes // for the JVM to use. The name_index and signature_index are // declared in vmSymbols. The may_be_java flag is used to declare diff --git a/src/hotspot/share/classfile/klassFactory.cpp b/src/hotspot/share/classfile/klassFactory.cpp index 7b5960ee3..52079b898 100644 --- a/src/hotspot/share/classfile/klassFactory.cpp +++ b/src/hotspot/share/classfile/klassFactory.cpp @@ -40,6 +40,10 @@ #if INCLUDE_JFR #include "jfr/support/jfrKlassExtension.hpp" #endif +#if INCLUDE_AGGRESSIVE_CDS +#include "classfile/systemDictionaryShared.hpp" +#include "jbooster/jbooster_globals.hpp" +#endif // INCLUDE_AGGRESSIVE_CDS // called during initial loading of a shared class @@ -58,7 +62,18 @@ InstanceKlass* KlassFactory::check_shared_class_file_load_hook( // Post the CFLH JvmtiCachedClassFileData* cached_class_file = NULL; if (cfs == NULL) { - cfs = FileMapInfo::open_stream_for_jvmti(ik, class_loader, CHECK_NULL); +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS && !SystemDictionaryShared::is_builtin(ik)) { + assert(UseAggressiveCDS, "sanity check"); + cfs = SystemDictionaryShared::get_shared_class_file_stream(ik); + if (cfs == NULL) { + cfs = SystemDictionaryShared::get_byte_code_from_cache(class_name, class_loader, CHECK_NULL); + } + } else +#endif // INCLUDE_AGGRESSIVE_CDS + { + cfs = FileMapInfo::open_stream_for_jvmti(ik, class_loader, CHECK_NULL); + } } unsigned char* ptr = (unsigned char*)cfs->buffer(); unsigned char* end_ptr = ptr + cfs->length(); @@ -69,7 +84,7 @@ InstanceKlass* KlassFactory::check_shared_class_file_load_hook( &ptr, &end_ptr, &cached_class_file); - if (old_ptr != ptr) { + if (old_ptr != ptr AGGRESSIVE_CDS_ONLY(|| (UseAggressiveCDS && !SystemDictionaryShared::is_builtin(ik)))) { // JVMTI agent has modified class file data. // Set new class file stream using JVMTI agent modified class file data. ClassLoaderData* loader_data = @@ -79,6 +94,19 @@ InstanceKlass* KlassFactory::check_shared_class_file_load_hook( end_ptr - ptr, cfs->source(), ClassFileStream::verify); +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS) { + int stream_size = stream->length(); + int stream_crc32 = ClassLoader::crc32(0, (const char*)stream->buffer(), stream->length()); + uint64_t fingerprint = (uint64_t(stream_size) << 32) | uint64_t(uint32_t(stream_crc32)); + if (ik->get_stored_fingerprint() == fingerprint) { + if (cached_class_file != NULL) { + ik->set_cached_class_file(cached_class_file); + } + return NULL; + } + } +#endif // INCLUDE_AGGRESSIVE_CDS ClassLoadInfo cl_info(protection_domain); ClassFileParser parser(stream, class_name, @@ -212,6 +240,27 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream, #if INCLUDE_CDS if (Arguments::is_dumping_archive()) { ClassLoader::record_result(THREAD, result, stream); +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS && !loader_data->is_builtin_class_loader_data()) { + if (Arguments::init_agents_at_startup() && !cl_info.is_hidden()) { + SystemDictionaryShared::set_shared_class_file(result, old_stream); + } + Handle protection_domain = cl_info.protection_domain(); + if (protection_domain.not_null()) { + Handle codesource(THREAD, java_security_ProtectionDomain::codeSource(protection_domain())); + if (codesource.not_null()) { + Handle str(THREAD, java_security_CodeSource::locationNoFragString(codesource())); + if (str.not_null()) { + char* string_value = java_lang_String::as_utf8_string(str()); + if (strlen(string_value) != 0) { + SystemDictionaryShared::set_url_string(result, string_value); + SystemDictionaryShared::save_timestamp(result, string_value); + } + } + } + } + } +#endif // INCLUDE_AGGRESSIVE_CDS } #endif // INCLUDE_CDS diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index a055ad741..ee4bd6f24 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -69,7 +69,14 @@ #include "utilities/hashtable.inline.hpp" #include "utilities/resourceHash.hpp" #include "utilities/stringUtils.hpp" - +#if INCLUDE_AGGRESSIVE_CDS +#include "jbooster/client/clientStartupSignal.hpp" +#include "jbooster/jbooster_globals.hpp" +#include "jbooster/utilities/debugUtils.inline.hpp" +#include "jbooster/utilities/scalarHashMap.inline.hpp" +#include "memory/oopFactory.hpp" +#include "runtime/javaCalls.hpp" +#endif // INCLUDE_AGGRESSIVE_CDS OopHandle SystemDictionaryShared::_shared_protection_domains; OopHandle SystemDictionaryShared::_shared_jar_urls; @@ -107,6 +114,14 @@ public: } }; +#if INCLUDE_AGGRESSIVE_CDS + struct DTSharedData { + int length; + u1 data[1]; + int obj_size() { return sizeof(length) + length; } + }; +#endif // INCLUDE_AGGRESSIVE_CDS + InstanceKlass* _klass; InstanceKlass* _nest_host; bool _failed_verification; @@ -117,6 +132,11 @@ public: GrowableArray* _verifier_constraints; GrowableArray* _verifier_constraint_flags; GrowableArray* _loader_constraints; +#if INCLUDE_AGGRESSIVE_CDS + DTSharedData* _shared_class_file; + DTSharedData* _url_string; + int64_t _classfile_timestamp; +#endif // INCLUDE_AGGRESSIVE_CDS DumpTimeSharedClassInfo() { _klass = NULL; @@ -132,6 +152,11 @@ public: _verifier_constraints = NULL; _verifier_constraint_flags = NULL; _loader_constraints = NULL; +#if INCLUDE_AGGRESSIVE_CDS + _shared_class_file = NULL; + _url_string = NULL; + _classfile_timestamp = 0; +#endif // INCLUDE_AGGRESSIVE_CDS } void add_verification_constraint(InstanceKlass* k, Symbol* name, @@ -194,6 +219,72 @@ public: void set_failed_verification() { _failed_verification = true; } InstanceKlass* nest_host() const { return _nest_host; } void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; } + +#if INCLUDE_AGGRESSIVE_CDS + DTSharedData* shared_class_file() { + return _shared_class_file; + } + + int shared_class_file_size() { + if (_shared_class_file != NULL) { + return _shared_class_file->obj_size(); + } + return 0; + } + + void copy_shared_class_file(ClassFileStream* cfs) { + assert(_shared_class_file == NULL, "already have _shared_class_file"); + int stream_length = cfs->length(); + int size = offset_of(DTSharedData, data) + stream_length; + _shared_class_file = (DTSharedData*) NEW_C_HEAP_ARRAY(u1, size, mtJBooster); + _shared_class_file->length = stream_length; + memcpy(_shared_class_file->data, cfs->buffer(), stream_length); + assert(size == _shared_class_file->obj_size(), "sanity"); + } + + void free_shared_class_file() { + if (_shared_class_file != NULL) { + FREE_C_HEAP_ARRAY(u1, _shared_class_file); + _shared_class_file = NULL; + } + } + + DTSharedData* url_string() { + return _url_string; + } + + int url_string_size() { + if (_url_string != NULL) { + return _url_string->obj_size(); + } + return 0; + } + + void copy_url_string(char* string_value) { + assert(strlen(string_value) != 0, "sanity"); + int string_len = strlen(string_value) + 1; + int size = offset_of(DTSharedData, data) + string_len; + _url_string = (DTSharedData*) NEW_C_HEAP_ARRAY(u1, size, mtJBooster); + _url_string->length = string_len; + memcpy(_url_string->data, string_value, string_len); + assert(size == _url_string->obj_size(), "sanity"); + } + + void free_url_string() { + if (_url_string != NULL) { + FREE_C_HEAP_ARRAY(u1, _url_string); + _url_string = NULL; + } + } + + int64_t classfile_timestamp() { + return _classfile_timestamp; + } + + void set_classfile_timestamp(int64_t classfile_timestamp) { + _classfile_timestamp = classfile_timestamp; + } +#endif // INCLUDE_AGGRESSIVE_CDS }; inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) { @@ -472,7 +563,20 @@ public: } }; +#if INCLUDE_AGGRESSIVE_CDS + struct RTSharedData { + int length; + u1 data[1]; + int obj_size() { return sizeof(length) + length; } + }; +#endif // INCLUDE_AGGRESSIVE_CDS + InstanceKlass* _klass; +#if INCLUDE_AGGRESSIVE_CDS + RTSharedData* _shared_class_file; + RTSharedData* _url_string; + int64_t _classfile_timestamp; +#endif // INCLUDE_AGGRESSIVE_CDS int _num_verifier_constraints; int _num_loader_constraints; @@ -509,6 +613,11 @@ private: return 0; } } +#if INCLUDE_AGGRESSIVE_CDS + static size_t shared_class_file_size(DumpTimeSharedClassInfo& info) { + return info.shared_class_file_size(); + } +#endif // INCLUDE_AGGRESSIVE_CDS public: static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) { @@ -520,6 +629,23 @@ public: verifier_constraint_flags_size(num_verifier_constraints); } +#if INCLUDE_AGGRESSIVE_CDS + static size_t byte_size(DumpTimeSharedClassInfo& info) { + size_t previous_size = byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); + if (UseAggressiveCDS) { + size_t cf_size = shared_class_file_size(info); + if (cf_size != 0) { + previous_size = align_up(previous_size, sizeof(int)) + cf_size; + } + cf_size = info.url_string_size(); + if (cf_size != 0) { + return align_up(previous_size, sizeof(int)) + cf_size; + } + } + return previous_size; + } +#endif // INCLUDE_AGGRESSIVE_CDS + private: size_t crc_offset() const { return header_size_size(); @@ -547,6 +673,20 @@ private: assert(0 <= i && i < _num_loader_constraints, "sanity"); } +#if INCLUDE_AGGRESSIVE_CDS + size_t shared_class_file_offset() const { + return align_up(verifier_constraint_flags_offset() + verifier_constraint_flags_size(_num_verifier_constraints), + sizeof(int)); + } + size_t url_string_offset() const { + size_t offset = shared_class_file_offset(); + if (_shared_class_file != NULL) { + return align_up(offset + _shared_class_file->obj_size(), sizeof(int)); + } + return offset; + } +#endif // INCLUDE_AGGRESSIVE_CDS + public: CrcInfo* crc() const { assert(crc_size(_klass) > 0, "must be"); @@ -588,6 +728,23 @@ public: return loader_constraints() + i; } +#if INCLUDE_AGGRESSIVE_CDS + RTSharedData* shared_class_file() { + return (RTSharedData*)(address(this) + shared_class_file_offset()); + } + RTSharedData* url_string() { + return (RTSharedData*)(address(this) + url_string_offset()); + } + + int64_t classfile_timestamp() { + return _classfile_timestamp; + } + + void set_classfile_timestamp(int64_t classfile_timestamp) { + _classfile_timestamp = classfile_timestamp; + } +#endif // INCLUDE_AGGRESSIVE_CDS + void init(DumpTimeSharedClassInfo& info) { ArchiveBuilder* builder = ArchiveBuilder::current(); assert(builder->is_in_buffer_space(info._klass), "must be"); @@ -625,6 +782,28 @@ public: InstanceKlass* n_h = info.nest_host(); set_nest_host(n_h); } + +#if INCLUDE_AGGRESSIVE_CDS + if (info.shared_class_file_size() != 0) { + assert(_url_string == NULL, "must assigned before _url_string"); + _shared_class_file = shared_class_file(); + memcpy(_shared_class_file, info.shared_class_file(), info.shared_class_file_size()); + ArchivePtrMarker::mark_pointer(&_shared_class_file); + info.free_shared_class_file(); + } else { + _shared_class_file = NULL; + } + if (info.url_string_size() != 0) { + _url_string = url_string(); + memcpy(_url_string, info.url_string(), info.url_string_size()); + ArchivePtrMarker::mark_pointer(&_url_string); + info.free_url_string(); + } else { + _url_string = NULL; + } + set_classfile_timestamp(info.classfile_timestamp()); +#endif // INCLUDE_AGGRESSIVE_CDS + ArchivePtrMarker::mark_pointer(&_klass); } @@ -668,6 +847,77 @@ public: const RunTimeSharedClassInfo* value, Symbol* key, int len_unused) { return (value->_klass->name() == key); } + +#if INCLUDE_AGGRESSIVE_CDS + ClassFileStream* get_shared_class_file_stream() { + if (_shared_class_file != NULL) { + return new ClassFileStream(_shared_class_file->data, + _shared_class_file->length, + "__VM_AggressiveCDS__", + ClassFileStream::verify); + } + return NULL; + } + + + // check timestamp in the load time when UseAggressiveCDS. + // regular_file(*.class): need to check timestamp. + // jar_file(*.jar): no need to check timestamp here,already checked + // somewhere else, see SharedClassPathEntry::validate. + // other_file: not supported when UseAggressiveCDS. + bool check_classfile_timestamp(char* url_string, TRAPS) { + if (SystemDictionaryShared::is_regular_file(url_string)) { + ResourceMark rm(THREAD); + char* dir = SystemDictionaryShared::get_filedir(url_string); + if (dir == NULL) { + return false; + } + int64_t timestamp = SystemDictionaryShared::get_timestamp(dir, _klass->name()); + if (timestamp != _classfile_timestamp) { + log_trace(cds, aggressive)("%s, timestamp mismatch: " INT64_FORMAT " -> " INT64_FORMAT, + _klass->name()->as_C_string(), + _classfile_timestamp, timestamp); + return false; + } + } else if (!SystemDictionaryShared::is_jar_file(url_string)) { + log_trace(cds, aggressive)("Unsupported URL:%s", url_string); + return false; + } + return true; + } + + Handle get_protection_domain(Handle class_loader, TRAPS) { + if (_url_string == NULL) { + return Handle(); + } + char* data_ptr = (char*)(_url_string->data); + + if (CheckClassFileTimeStamp) { + if (!check_classfile_timestamp(data_ptr, THREAD)) { + return Handle(); + } + } + + Handle url_string = java_lang_String::create_from_str(data_ptr, THREAD); + JavaValue result(T_OBJECT); + JavaCalls::call_virtual(&result, + class_loader, + class_loader->klass(), + vmSymbols::getProtectionDomainByURLString_name(), + vmSymbols::getProtectionDomainByURLString_signature(), + url_string, THREAD); + if (!HAS_PENDING_EXCEPTION) { + return Handle(THREAD, result.get_oop()); + } else { + LogTarget(Warning, cds, aggressive) lt; + if (lt.is_enabled()) { + lt.print("Unknown exception in get_protection_domain():"); + } + DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD); + } + return Handle(); + } +#endif // INCLUDE_AGGRESSIVE_CDS }; class RunTimeSharedDictionary : public OffsetCompactHashtable< @@ -2036,7 +2286,11 @@ public: bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { if (!info.is_excluded()) { +#if INCLUDE_AGGRESSIVE_CDS + size_t byte_size = RunTimeSharedClassInfo::byte_size(info); +#else size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); +#endif // INCLUDE_AGGRESSIVE_CDS _shared_class_info_size += align_up(byte_size, SharedSpaceObjectAlignment); } return true; // keep on iterating @@ -2095,6 +2349,17 @@ public: // if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) { // return true; //} + +#if INCLUDE_AGGRESSIVE_CDS + // In Dynamic dump, info is from _dumptime_lambda_proxy_class_dictionary, which is created by + // runtime JNI call, see Java_java_lang_invoke_LambdaProxyClassArchive_addToArchive. + // check_excluded_classes can't exclude DumpTimeLambdaProxyClassInfo, check excluded here + if (UseAggressiveCDS && DynamicDumpSharedSpaces + && SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) { + return true; + } +#endif // INCLUDE_AGGRESSIVE_CDS + ResourceMark rm; log_info(cds,dynamic)("Archiving hidden %s", info._proxy_klasses->at(0)->external_name()); size_t byte_size = sizeof(RunTimeLambdaProxyClassInfo); @@ -2112,6 +2377,12 @@ class AdjustLambdaProxyClassInfo : StackObj { public: AdjustLambdaProxyClassInfo() {} bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS && DynamicDumpSharedSpaces + && SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) { + return true; + } +#endif // INCLUDE_AGGRESSIVE_CDS int len = info._proxy_klasses->length(); if (len > 1) { for (int i = 0; i < len-1; i++) { @@ -2134,6 +2405,39 @@ public: } }; +#if INCLUDE_AGGRESSIVE_CDS +class ExcludeDuplicateKlass : StackObj { +public: + static const int INITIAL_TABLE_SIZE = 15889; + + ExcludeDuplicateKlass(int size) : _has_been_visited(size) {} + + bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { + if (!info.is_excluded()) { + bool created; + Symbol* name = info._klass->name(); + address* info_ptr = _has_been_visited.add_if_absent((address)name, (address)&info, &created); + if (!created) { + info.set_excluded(); + DumpTimeSharedClassInfo* first_info = (DumpTimeSharedClassInfo*)(*info_ptr); + if (!first_info->is_excluded()) { + first_info->set_excluded(); + } + LogTarget(Trace, cds, aggressive) lt; + if (lt.is_enabled()) { + ResourceMark rm; + lt.print("Skipping duplicate class (excluded): %s", name->as_C_string()); + } + } + } + return true; + } + +private: + ScalarHashMap _has_been_visited; +}; +#endif // INCLUDE_AGGRESSIVE_CDS + class CopySharedClassInfoToArchive : StackObj { CompactHashtableWriter* _writer; bool _is_builtin; @@ -2145,7 +2449,11 @@ public: bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { if (!info.is_excluded() && info.is_builtin() == _is_builtin) { +#if INCLUDE_AGGRESSIVE_CDS + size_t byte_size = RunTimeSharedClassInfo::byte_size(info); +#else size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); +#endif // INCLUDE_AGGRESSIVE_CDS RunTimeSharedClassInfo* record; record = (RunTimeSharedClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); record->init(info); @@ -2184,6 +2492,12 @@ void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionar bool is_builtin) { CompactHashtableStats stats; dictionary->reset(); +#if INCLUDE_AGGRESSIVE_CDS + if (UseAggressiveCDS && !is_builtin) { + ExcludeDuplicateKlass dup(ExcludeDuplicateKlass::INITIAL_TABLE_SIZE); + _dumptime_table->iterate(&dup); + } +#endif // INCLUDE_AGGRESSIVE_CDS CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats); CopySharedClassInfoToArchive copy(&writer, is_builtin); assert_lock_strong(DumpTimeTable_lock); @@ -2493,4 +2807,210 @@ void SystemDictionaryShared::update_archived_mirror_native_pointers() { ArchivedMirrorPatcher::update_array_klasses(k); } } + +#if INCLUDE_AGGRESSIVE_CDS +ClassFileStream* SystemDictionaryShared::get_shared_class_file_stream(InstanceKlass* k) { + assert(UseAggressiveCDS, "sanity"); + RunTimeSharedClassInfo* info = RunTimeSharedClassInfo::get_for(k); + return info->get_shared_class_file_stream(); +} + +ClassFileStream* SystemDictionaryShared::get_byte_code_from_cache(Symbol* class_name, Handle class_loader, TRAPS) { + assert(UseAggressiveCDS, "sanity"); + + TempNewSymbol plugin_name = SymbolTable::new_symbol("java/net/AggressiveCDSPlugin"); + InstanceKlass* plugin_klass = SystemDictionary::find_instance_klass(plugin_name, Handle(), Handle()); + assert(plugin_klass != NULL, "sanity"); + JavaValue result(T_OBJECT); + Handle name = java_lang_String::create_from_symbol(class_name, CHECK_NULL); + TempNewSymbol method_name = SymbolTable::new_symbol("getByteCodeFromCache"); + TempNewSymbol method_signature = SymbolTable::new_symbol("(Ljava/net/URLClassLoader;Ljava/lang/String;)[B"); + + JavaCalls::call_static(&result, + plugin_klass, + method_name, + method_signature, + class_loader, + name, + CHECK_NULL); + + typeArrayHandle res_h(THREAD, (typeArrayOop) result.get_oop()); + if (res_h.is_null()) { + return NULL; + } + int len = res_h->length(); + u1* buf = NEW_RESOURCE_ARRAY(u1, len); + memcpy(buf, (u1*) res_h->byte_at_addr(0), len); + return new ClassFileStream(buf, len, "__VM_AggressiveCDS__", ClassFileStream::verify); +} + +void SystemDictionaryShared::set_shared_class_file(InstanceKlass* k, ClassFileStream* cfs) { + assert(UseAggressiveCDS, "sanity"); + Arguments::assert_is_dumping_archive(); + DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); + if (info != NULL && info->_shared_class_file == NULL) { + info->copy_shared_class_file(cfs); + } +} + +static const char* JAR_FILE_PREFIX = "jar://"; // 1: "jar://file:", 2: "jar://nested:" +static const char* FILE_SEPARATOR = "file://"; +static const char* CLASSFILE_SUFFIX = ".class"; + +static bool start_with(char* str, const char* prefix) { + if (str == NULL || prefix == NULL || strlen(str) < strlen(prefix)) { + return false; + } + if (strncmp(str, prefix, strlen(prefix)) == 0) { + return true; + } + return false; +} + +bool SystemDictionaryShared::is_jar_file(char* url_string) { + if (start_with(url_string, JAR_FILE_PREFIX)) { + return true; + } + return false; +} + +bool SystemDictionaryShared::is_regular_file(char* url_string) { + if (start_with(url_string, FILE_SEPARATOR)) { + return true; + } + return false; +} + +char* SystemDictionaryShared::get_filedir(char* url_string) { + if (!is_regular_file(url_string)) { + return NULL; + } + char* dir = url_string + strlen(FILE_SEPARATOR); + struct stat st; + if (os::stat(dir, &st) == 0) { + if ((st.st_mode & S_IFDIR) == S_IFDIR) { + return dir; + } + } + return NULL; +} + +int64_t SystemDictionaryShared::get_timestamp(char* dir, Symbol* class_name) { + char* name = class_name->as_C_string(); + size_t name_len = strlen(name); + size_t dir_len = strlen(dir); + size_t classfile_suffix_len = strlen(CLASSFILE_SUFFIX); + char* file_path = NEW_RESOURCE_ARRAY(char, dir_len + name_len + classfile_suffix_len + 1); + memcpy(file_path, dir, dir_len); + memcpy(file_path + dir_len, name, name_len); + memcpy(file_path + dir_len + name_len, CLASSFILE_SUFFIX, classfile_suffix_len + 1); + assert(strlen(file_path) == dir_len + name_len + classfile_suffix_len, "sanity"); + struct stat st; + if (os::stat(file_path, &st) == 0) { + return st.st_mtime; + } + log_trace(cds, aggressive)("get timestamp failed:%s", file_path); + return 0; +} + +Handle SystemDictionaryShared::get_protection_domain(InstanceKlass* k, Handle class_loader, TRAPS) { + assert(UseAggressiveCDS, "sanity"); + RunTimeSharedClassInfo* info = RunTimeSharedClassInfo::get_for(k); + return info->get_protection_domain(class_loader, CHECK_NH); +} + +void SystemDictionaryShared::set_url_string(InstanceKlass* k, char* string_value) { + assert(UseAggressiveCDS, "sanity"); + Arguments::assert_is_dumping_archive(); + DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); + if (info != NULL && info->_url_string == NULL) { + info->copy_url_string(string_value); + } +} + +void SystemDictionaryShared::save_timestamp(InstanceKlass* k, char* string_value) { + if (SystemDictionaryShared::is_regular_file(string_value)) { + char* dir = SystemDictionaryShared::get_filedir(string_value); + if (dir != NULL) { + int64_t timestamp = SystemDictionaryShared::get_timestamp(dir, k->name()); + SystemDictionaryShared::set_classfile_timestamp(k, timestamp); + } else { + log_trace(cds, aggressive)("Unsupported URL:%s", string_value); + } + } else if (!SystemDictionaryShared::is_jar_file(string_value)) { + log_trace(cds, aggressive)("Unsupported URL:%s", string_value); + } +} + +void SystemDictionaryShared::set_classfile_timestamp(InstanceKlass* k, int64_t classfile_timestamp) { + assert(UseAggressiveCDS, "sanity"); + Arguments::assert_is_dumping_archive(); + DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); + if (info != NULL) { + info->set_classfile_timestamp(classfile_timestamp); + } +} + +InstanceKlass* SystemDictionaryShared::lookup_trusted_share_class(Symbol* class_name, + Handle class_loader, + TRAPS) { + assert(UseAggressiveCDS, "sanity"); + if (!UseSharedSpaces) { + return NULL; + } + if (class_name == NULL) { // don't do this for hidden classes + return NULL; + } + if (class_loader.is_null() || + SystemDictionary::is_system_class_loader(class_loader()) || + SystemDictionary::is_platform_class_loader(class_loader())) { + // Do nothing for the BUILTIN loaders. + return NULL; + } + + // We may want to inject some code into the klass. + // So do not load the klass from jsa. + if (UseJBooster && JBoosterStartupSignal != nullptr) { + if (ClientStartupSignal::is_target_klass(class_name)) { + return NULL; + } + } + + Handle lock = get_loader_lock_or_null(class_loader); + ObjectLocker ol(lock, THREAD); + + register_loader(class_loader); + + if (log_is_enabled(Info, cds)) { + ResourceMark rm(THREAD); + log_info(cds)("lookup_trusted_share_class %s: %s", class_name->as_C_string(), + class_loader()->klass()->name()->as_C_string()); + } + + const RunTimeSharedClassInfo* record = find_record(&_unregistered_dictionary, + &_dynamic_unregistered_dictionary, + class_name); + if (record == NULL) { + log_info(cds)("not find class name : %s ", class_name->as_C_string()); + return NULL; + } + + Handle protection_domain = SystemDictionaryShared::get_protection_domain(record->_klass, class_loader, CHECK_NULL); + if (protection_domain.is_null()) { + // The protection_domain is rebuilt based on the RunTimeSharedClassInfo::_url_string. + // We lookup the URL of _url_string from the URLClassPath of the URLClassLoader. + // The URLClassPath returns null if _url_string is not in its url array, which also + // means that this class was not defined by this class loader at dump run. + return NULL; + } + + InstanceKlass* k = acquire_class_for_current_thread(record->_klass, class_loader, protection_domain, NULL, THREAD); + if (k != NULL) { + SharedClassLoadingMark slm(THREAD, k); + find_or_define_instance_class(class_name, class_loader, k, CHECK_NULL); + } + return k; +} +#endif // INCLUDE_AGGRESSIVE_CDS + #endif diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 5ba59378e..6dafba669 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -376,6 +376,23 @@ private: public: static void update_archived_mirror_native_pointers() NOT_CDS_RETURN; #endif + +#if INCLUDE_AGGRESSIVE_CDS + static bool is_jar_file(char* url_string); + static bool is_regular_file(char* url_string); + static char* get_filedir(char* url_string); + static int64_t get_timestamp(char* dir, Symbol* class_name); + static ClassFileStream* get_shared_class_file_stream(InstanceKlass* k); + static ClassFileStream* get_byte_code_from_cache(Symbol* class_name, Handle class_loader, TRAPS); + static void set_shared_class_file(InstanceKlass* k, ClassFileStream* cfs); + static Handle get_protection_domain(InstanceKlass* k, Handle class_loader, TRAPS); + static void set_url_string(InstanceKlass* k, char* string_value); + static void save_timestamp(InstanceKlass* k, char* string_value); + static void set_classfile_timestamp(InstanceKlass* k, int64_t classfile_timestamp); + static int64_t get_classfile_timestamp(InstanceKlass* k); + + static InstanceKlass* lookup_trusted_share_class(Symbol* class_name, Handle class_loader, TRAPS); +#endif // INCLUDE_AGGRESSIVE_CDS }; #endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP diff --git a/src/hotspot/share/include/cds.h b/src/hotspot/share/include/cds.h index ee821eb73..784d3026a 100644 --- a/src/hotspot/share/include/cds.h +++ b/src/hotspot/share/include/cds.h @@ -35,6 +35,7 @@ #define NUM_CDS_REGIONS 7 // this must be the same as MetaspaceShared::n_regions #define CDS_ARCHIVE_MAGIC 0xf00baba2 +#define CDS_AGGRESSIVE_ARCHIVE_MAGIC 0xf00baba4 #define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 #define CURRENT_CDS_ARCHIVE_VERSION 11 #define INVALID_CDS_ARCHIVE_VERSION -1 diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 0f6fba8ba..4cdc9cfb6 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -1131,6 +1131,11 @@ JVM_JBoosterGetMetaspaceMethodData(JNIEnv *env, jint session_id, jlong metaspace JNIEXPORT void JNICALL JVM_JBoosterStartupNativeCallback(JNIEnv *env); +/** + * Define the trusted shared class. + */ +JNIEXPORT jclass JNICALL +JVM_DefineTrustedSharedClass(JNIEnv *env, const char *name, jobject loader); /* * This structure is used by the launcher to get the default thread diff --git a/src/hotspot/share/jbooster/client/clientDataManager.cpp b/src/hotspot/share/jbooster/client/clientDataManager.cpp index 1e4c8f2b5..55b7d2a82 100644 --- a/src/hotspot/share/jbooster/client/clientDataManager.cpp +++ b/src/hotspot/share/jbooster/client/clientDataManager.cpp @@ -140,6 +140,42 @@ jint ClientDataManager::init_clr_options() { jint ClientDataManager::init_cds_options() { if (!is_cds_allowed()) return JNI_OK; + + if (FLAG_IS_CMDLINE(SharedArchiveFile) || FLAG_IS_CMDLINE(ArchiveClassesAtExit)) { + vm_exit_during_initialization("Do not set CDS manually whe using JBooster."); + } + + if (is_cds_being_used()) { + if (FLAG_SET_CMDLINE(SharedArchiveFile, cache_cds_path()) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + + if (FLAG_SET_CMDLINE(RequireSharedSpaces, JBoosterExitIfUnsupported) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + } else if (is_server_available()) { + // Dump data to the tmp file to prevent other processes from reading the + // cache file that is not completely written. + const char* cds_tmp_path = JBoosterManager::calc_tmp_cache_path(cache_cds_path()); + if (FLAG_SET_CMDLINE(ArchiveClassesAtExit, cds_tmp_path) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + } + + // It's OK to Use traditional Dynamic CDS if the user manually + // set UseAggressiveCDS to false. + if (FLAG_IS_DEFAULT(UseAggressiveCDS)) { + if (FLAG_SET_CMDLINE(UseAggressiveCDS, true) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + } + + if (Arguments::init_agents_at_startup()) { + if (FLAG_SET_CMDLINE(AllowArchivingWithJavaAgent, true) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + } + return JNI_OK; } diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 8bcdd16a7..8ac082bf1 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -34,6 +34,7 @@ #define LOG_TAG_LIST \ LOG_TAG(add) \ LOG_TAG(age) \ + AGGRESSIVE_CDS_ONLY(LOG_TAG(aggressive)) \ LOG_TAG(alloc) \ LOG_TAG(annotation) \ AOT_ONLY(LOG_TAG(aot)) \ diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 0d501e494..bafbfe08d 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3900,4 +3900,25 @@ JVM_ENTRY(void, JVM_JBoosterStartupNativeCallback(JNIEnv *env)) ClientMessageHandler::trigger_cache_generation_tasks(ClientMessageHandler::TriggerTaskPhase::ON_STARTUP, THREAD); log_debug(jbooster, start)("End of the startup callback."); #endif // INCLUDE_JBOOSTER -JVM_END \ No newline at end of file +JVM_END + +JVM_ENTRY(jclass, JVM_DefineTrustedSharedClass(JNIEnv *env, const char *name, jobject loader)) +#if INCLUDE_AGGRESSIVE_CDS + assert(UseAggressiveCDS, "sanity"); + TempNewSymbol class_name = name == NULL ? NULL : + SystemDictionary::class_name_symbol(name, + vmSymbols::java_lang_NoClassDefFoundError(), + CHECK_NULL); + Handle class_loader (THREAD, JNIHandles::resolve(loader)); + InstanceKlass* k = SystemDictionaryShared::lookup_trusted_share_class(class_name, + class_loader, + CHECK_NULL); + if (k == NULL) { + return NULL; + } + + return (jclass) JNIHandles::make_local(THREAD, k->java_mirror()); +#else + return NULL; +#endif // INCLUDE_AGGRESSIVE_CDS +JVM_END diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 406434ab2..6a432ed6b 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -4034,6 +4034,11 @@ jint Arguments::apply_ergo() { result = set_shared_spaces_flags_and_archive_paths(); if (result != JNI_OK) return result; +#if INCLUDE_AGGRESSIVE_CDS + result = init_aggressive_cds_properties(); + if (result != JNI_OK) return result; +#endif // INCLUDE_AGGRESSIVE_CDS + // Initialize Metaspace flags and alignments Metaspace::ergo_initialize(); @@ -4385,4 +4390,17 @@ jint Arguments::init_jbooster_startup_signal_properties(const char* klass_name, return JNI_OK; } -#endif // INCLUDE_JBOOSTER \ No newline at end of file +#endif // INCLUDE_JBOOSTER + +#if INCLUDE_AGGRESSIVE_CDS + +jint Arguments::init_aggressive_cds_properties() { + if (!is_dumping_archive() && SharedDynamicArchivePath != NULL && UseAggressiveCDS) { + bool added = false; + added = add_property("jdk.jbooster.aggressivecds.load=true", UnwriteableProperty, InternalProperty); + if (!added) return JNI_ENOMEM; + } + return JNI_OK; +} + +#endif // INCLUDE_AGGRESSIVE_CDS diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 7f52419a5..a66cc0f4d 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -647,6 +647,10 @@ class Arguments : AllStatic { const char* method_signature); #endif // INCLUDE_JBOOSTER +#if INCLUDE_AGGRESSIVE_CDS + static jint init_aggressive_cds_properties(); +#endif // INCLUDE_AGGRESSIVE_CDS + DEBUG_ONLY(static bool verify_special_jvm_flags(bool check_globals);) }; diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index e27a135fc..f57a95533 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -94,6 +94,7 @@ #include "jfr/jfr.hpp" #endif #if INCLUDE_JBOOSTER +#include "jbooster/client/clientDataManager.hpp" #include "jbooster/client/clientMessageHandler.hpp" #endif // INCLUDE_JBOOSTER #if INCLUDE_AOT @@ -522,7 +523,7 @@ void before_exit(JavaThread* thread, bool halt) { os::terminate_signal_thread(); #if INCLUDE_CDS - if (DynamicDumpSharedSpaces JBOOSTER_ONLY(&& !UseJBooster)) { + if (DynamicDumpSharedSpaces JBOOSTER_ONLY(&& !(UseJBooster && ClientDataManager::get().is_cds_allowed()))) { ExceptionMark em(thread); DynamicArchive::dump(); if (thread->has_pending_exception()) { diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index 6f8cc606f..c25da52ed 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -129,6 +129,14 @@ #define JBOOSTER_ONLY(x) #endif // INCLUDE_JBOOSTER +#if INCLUDE_JBOOSTER && INCLUDE_CDS +#define INCLUDE_AGGRESSIVE_CDS 1 +#define AGGRESSIVE_CDS_ONLY(x) x +#else +#define INCLUDE_AGGRESSIVE_CDS 0 +#define AGGRESSIVE_CDS_ONLY(x) +#endif // INCLUDE_JBOOSTER && INCLUDE_CDS + #ifndef INCLUDE_MANAGEMENT #define INCLUDE_MANAGEMENT 1 #endif // INCLUDE_MANAGEMENT diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index c1caeb18c..64aca03ee 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -930,6 +930,21 @@ public abstract class ClassLoader { } } + /** + * Determine protection domain, and check it. + * This method is only for AggressiveCDS. + * + * @param name the name of the class + * @param c the class + * @param pd the ProtectionDomain of the class + */ + protected void defineClassProtectionDomain(String name, Class c, ProtectionDomain pd) + { + // Determine protection domain + pd = preDefineClass(name, pd); + postDefineClass(c, pd); + } + /** * Converts an array of bytes into an instance of class {@code Class}, * with a given {@code ProtectionDomain}. @@ -1117,6 +1132,11 @@ public abstract class ClassLoader { int off, int len, ProtectionDomain pd, String source); + /** + * This method is only invoked in java.net.AggressiveCDSPlugin. + */ + private static native Class defineClass3(ClassLoader loader, String name); + /** * Defines a class of the given flags via Lookup.defineClass. * diff --git a/src/java.base/share/classes/java/net/AggressiveCDSPlugin.java b/src/java.base/share/classes/java/net/AggressiveCDSPlugin.java new file mode 100644 index 000000000..b9fcc630c --- /dev/null +++ b/src/java.base/share/classes/java/net/AggressiveCDSPlugin.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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. + */ + +package java.net; + +import jdk.internal.loader.Resource; +import jdk.internal.loader.URLClassPath; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; + +import sun.security.action.GetBooleanAction; + +/** + * The Aggressive CDS plugin for {@link java.net.URLClassLoader}. + */ +final class AggressiveCDSPlugin { + private static final boolean IS_ENABLED = GetBooleanAction.privilegedGetProperty("jdk.jbooster.aggressivecds.load"); + + /** + * Check whether Aggressive CDS is enabled. + * + * @return Is Aggressive CDS enabled + */ + public static boolean isEnabled() { + return IS_ENABLED; + } + + /** + * Define the class by Aggressive CDS. The class is trusted and shared, + * + * @param loader The class loader of the class (should be URLClassLoader) + * @param name The name of the class + * @return The defined class, or null if not found + */ + public static Class defineTrustedSharedClass(URLClassLoader loader, String name) { + return ClassLoaderUtil.defineClass3(loader, name); + } + + /** + * get URL from URLClassPath by Aggressive CDS. + * + * @param ucp The URLClassPath + * @param urlNoFragString The name string of the url + * @return The URL, or null if not found + */ + public static URL getURLFromURLClassPath(URLClassPath ucp, String urlNoFragString) { + return URLClassPathTool.getURL(ucp, urlNoFragString); + } + + /** + * Finds the byte code with the specified name on the URL search path. + * This method is invoked only in C++ ({@code SystemDictionaryShared::get_byte_code_from_cache}). + * + * @param loader The class loader of the class + * @param name The name of the class + * @return Byte code of the resource, or {@code null} if not found + * @throws IOException Resource.getBytes() + */ + private static byte[] getByteCodeFromCache(URLClassLoader loader, String name) throws IOException { + String path = name.replace('.', '/').concat(".class"); + Resource resource = getResourceFromCache(loader, path); + if (resource == null) { + return null; + } else { + return resource.getBytes(); + } + } + + /** + * Finds the byte code with the specified name on the URL search path. + * This method is invoked only in C++. + * + * @param loader The class loader of the class + * @param name The name of the class + * @return The resource in cache + */ + private static Resource getResourceFromCache(URLClassLoader loader, final String name) { + URL url = loader.findResource(name); + if (url == null) { + return null; + } + final URLConnection uc; + try { + uc = url.openConnection(); + } catch (IOException e) { + return null; + } + return new Resource() { + @Override + public String getName() { + return name; + } + + @Override + public URL getURL() { + return url; + } + + @Override + public URL getCodeSourceURL() { + return url; + } + + @Override + public InputStream getInputStream() throws IOException { + return uc.getInputStream(); + } + + @Override + public int getContentLength() throws IOException { + return uc.getContentLength(); + } + }; + } +} + +/** + * We don't want to add new public methods in {@link java.lang.ClassLoader}. So we add + * a private method (defineClass3) and use a method handle to invoke it. + */ +final class ClassLoaderUtil { + private static final MethodHandle classDefiner3; + + static { + MethodHandle mh3 = null; + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Method m3 = ClassLoader.class.getDeclaredMethod("defineClass3", ClassLoader.class, String.class); + m3.setAccessible(true); + mh3 = lookup.unreflect(m3); + } catch (NoSuchMethodException | IllegalAccessException e) { + e.printStackTrace(); + System.exit(1); + } + classDefiner3 = mh3; + } + + public static Class defineClass3(ClassLoader loader, String name) { + try { + return (Class) classDefiner3.invoke(loader, name); + } catch (Throwable throwable) { + throwable.printStackTrace(); + System.exit(1); + } + return null; + } +} + +/** + * We don't want to add new public methods in {@link jdk.internal.loader.URLClassPath}. So we add + * a private method (getURL) and use a method handle to invoke it. + */ +final class URLClassPathTool { + private static final MethodHandle getURLMethodHandle; + + static { + MethodHandle getURL = null; + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Method getURLMethod = URLClassPath.class.getDeclaredMethod("getURL", String.class); + getURLMethod.setAccessible(true); + getURL = lookup.unreflect(getURLMethod); + } catch (NoSuchMethodException | IllegalAccessException e) { + e.printStackTrace(); + System.exit(1); + } + getURLMethodHandle = getURL; + } + + public static URL getURL(URLClassPath ucp, String urlNoFragString) { + try { + return (URL) getURLMethodHandle.invoke(ucp, urlNoFragString); + } catch (Throwable throwable) { + throwable.printStackTrace(); + System.exit(1); + } + return null; + } +} \ No newline at end of file diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 97c95bc9f..8314d5bb3 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -38,6 +38,7 @@ import java.security.Permission; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; import java.security.SecureClassLoader; import java.util.Enumeration; import java.util.List; @@ -420,6 +421,17 @@ public class URLClassLoader extends SecureClassLoader implements Closeable { result = AccessController.doPrivileged( new PrivilegedExceptionAction<>() { public Class run() throws ClassNotFoundException { + if (AggressiveCDSPlugin.isEnabled()) { + try { + Class trustedClass = AggressiveCDSPlugin + .defineTrustedSharedClass(URLClassLoader.this, name); + if (trustedClass != null) { + ProtectionDomain pd = trustedClass.getProtectionDomain(); + defineClassProtectionDomain(name, trustedClass, pd); + return trustedClass; + } + } catch (Throwable ignored) {} + } String path = name.replace('.', '/').concat(".class"); Resource res = ucp.getResource(path, false); if (res != null) { @@ -447,6 +459,24 @@ public class URLClassLoader extends SecureClassLoader implements Closeable { return result; } + /** + * get ProtectionDomain By URL String. + * This method is invoked only in C++ for AggressiveCDS. + * + * @param urlNoFragString the URL String. + * + * @return ProtectionDomain create from URL. + */ + private ProtectionDomain getProtectionDomainByURLString(String urlNoFragString) { + if (AggressiveCDSPlugin.isEnabled()) { + URL url = AggressiveCDSPlugin.getURLFromURLClassPath(ucp, urlNoFragString); + if (url != null) { + return getProtectionDomainFromURL(url); + } + } + return null; + } + /* * Retrieve the package using the specified package name. * If non-null, verify the package using the specified code diff --git a/src/java.base/share/classes/java/security/SecureClassLoader.java b/src/java.base/share/classes/java/security/SecureClassLoader.java index 9ea5e3e50..2e0613700 100644 --- a/src/java.base/share/classes/java/security/SecureClassLoader.java +++ b/src/java.base/share/classes/java/security/SecureClassLoader.java @@ -25,6 +25,7 @@ package java.security; +import java.net.URL; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -235,6 +236,19 @@ public class SecureClassLoader extends ClassLoader { }); } + /** + * get ProtectionDomain From URL. + * This method is only for AggressiveCDS. + * + * @param url the URL. + * + * @return ProtectionDomain create from URL. + */ + protected ProtectionDomain getProtectionDomainFromURL(URL url) { + CodeSource cs = new CodeSource(url, (CodeSigner[]) null); + return getProtectionDomain(cs); + } + private static class CodeSourceKey { private final CodeSource cs; diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 02b961497..0cc500127 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -324,6 +324,24 @@ public class URLClassPath { return null; } + /** + * Finds the URL which has the specified name. + * This method is only for AggressiveCDS. + * + * @param urlNoFragString the name of URL + * @return the URL, or null if not found + */ + private URL getURL(String urlNoFragString) { + if (!unopenedUrls.isEmpty()) { + int index = loaders.size(); + while(getLoader(index) != null) { + index++; + } + } + Loader loader = lmap.get(urlNoFragString); + return loader != null ? loader.getBaseURL() : null; + } + /** * Finds all resources on the URL search path with the given name. * Returns an enumeration of the URL objects. diff --git a/src/java.base/share/native/libjava/ClassLoader.c b/src/java.base/share/native/libjava/ClassLoader.c index bbdff87b0..cc8cb32aa 100644 --- a/src/java.base/share/native/libjava/ClassLoader.c +++ b/src/java.base/share/native/libjava/ClassLoader.c @@ -316,3 +316,29 @@ Java_java_lang_ClassLoader_findLoadedClass0(JNIEnv *env, jobject loader, return JVM_FindLoadedClass(env, loader, name); } } + +JNIEXPORT jclass JNICALL +Java_java_lang_ClassLoader_defineClass3(JNIEnv *env, + jclass cls, + jobject loader, + jstring name) +{ + jclass result = 0; + char *utfName; + char buf[128]; + + if (name != NULL) { + utfName = getUTF(env, name, buf, sizeof(buf)); + if (utfName == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + return result; + } + fixClassname(utfName); + } else { + utfName = NULL; + } + + result = JVM_DefineTrustedSharedClass(env, utfName, loader); + + return result; +} -- 2.19.1