Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
999 changes: 999 additions & 0 deletions src/hotspot/share/cds/aotClassLocation.cpp

Large diffs are not rendered by default.

269 changes: 269 additions & 0 deletions src/hotspot/share/cds/aotClassLocation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#ifndef SHARE_CDS_AOTCLASSLOCATION_HPP
#define SHARE_CDS_AOTCLASSLOCATION_HPP

#include "memory/allocation.hpp"
#include "oops/array.hpp"
#include "utilities/exceptions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"

class AllClassLocationStreams;
class ClassLocationStream;
class LogStream;

// An AOTClassLocation is a location where the application is configured to load Java classes
// from. It can be:
// - the location of $JAVA_HOME/lib/modules
// - an entry in -Xbootclasspath/a
// - an entry in -classpath
// - a JAR file specified using --module-path.
//
// AOTClassLocation is similar to java.security.CodeSource, except:
// - Only local files/dirs are allowed. Directories must be empty. Network locations are not allowed.
// - No code signing information is recorded.
//
// We avoid using pointers in AOTClassLocation to avoid runtime pointer relocation. Each AOTClassLocation
// is a variable-size structure:
// [ all fields specified below (sizeof(AOTClassLocation) bytes) ]
// [ path (_path_length bytes, including the terminating zero) ]
// [ manifest (_manifest_length bytes, including the terminating zero) ]
class AOTClassLocation {
public:
enum class Group : int {
MODULES_IMAGE,
BOOT_CLASSPATH,
APP_CLASSPATH,
MODULE_PATH
};
private:
enum class FileType : int {
NORMAL,
DIR,
NOT_EXIST
};
size_t _path_length; // does NOT include terminating zero
size_t _manifest_length; // does NOT include terminating zero
bool _check_time;
bool _from_cpattr;
bool _is_multi_release_jar; // is this a JAR file that has multi-release classes?
FileType _file_type;
Group _group;
int _index; // index of this AOTClassLocation inside AOTClassLocationConfig::_class_locations
time_t _timestamp;
int64_t _filesize;

static size_t header_size() { return sizeof(AOTClassLocation); } // bytes
size_t path_offset() const { return header_size(); }
size_t manifest_offset() const { return path_offset() + _path_length + 1; }
static char* read_manifest(JavaThread* current, const char* path, size_t& manifest_length);

public:
static AOTClassLocation* allocate(JavaThread* current, const char* path, int index, Group group,
bool from_cpattr = false, bool is_jrt = false);

size_t total_size() const { return manifest_offset() + _manifest_length + 1; }
const char* path() const { return ((const char*)this) + path_offset(); }
size_t manifest_length() const { return _manifest_length; }
const char* manifest() const { return ((const char*)this) + manifest_offset(); }
bool must_exist() const { return _file_type != FileType::NOT_EXIST; }
bool must_not_exist() const { return _file_type == FileType::NOT_EXIST; }
bool is_dir() const { return _file_type == FileType::DIR; }
int index() const { return _index; }
bool is_modules_image() const { return _group == Group::MODULES_IMAGE; }
bool from_boot_classpath() const { return _group == Group::BOOT_CLASSPATH; }
bool from_app_classpath() const { return _group == Group::APP_CLASSPATH; }
bool from_module_path() const { return _group == Group::MODULE_PATH; }
bool is_multi_release_jar() const { return _is_multi_release_jar; }

// Only boot/app classpaths can contain unnamed module
bool has_unnamed_module() const { return from_boot_classpath() || from_app_classpath(); }

char* get_cpattr() const;
AOTClassLocation* write_to_archive() const;

// Returns true IFF this AOTClassLocation is discovered from the -classpath or -Xbootclasspath/a by parsing the
// "Class-Path" attribute of a JAR file.
bool from_cpattr() const { return _from_cpattr; }
const char* file_type_string() const;
bool check(const char* runtime_path, bool has_aot_linked_classes) const;
};

// AOTClassLocationConfig
//
// Keep track of the set of AOTClassLocations used when an AOTCache is created.
// To load the AOTCache in a production run, the JVM must be using a compatible set of
// AOTClassLocations (subjected to AOTClassLocationConfig::validate()).
//
// In general, validation is performed on the AOTClassLocations to ensure the code locations used
// during AOTCache creation are the same as when the AOTCache is used during runtime.
// Non-existent entries are recorded during AOTCache creation. Those non-existent entries,
// if they are specified at runtime, must not exist.
//
// Some details on validation:
// - the boot classpath can be appended to at runtime if there's no app classpath and no
// module path specified when an AOTCache is created;
// - the app classpath can be appended to at runtime;
// - the module path at runtime can be a superset of the one specified during AOTCache creation.

class AOTClassLocationConfig : public CHeapObj<mtClassShared> {
using Group = AOTClassLocation::Group;
using GrowableClassLocationArray = GrowableArrayCHeap<AOTClassLocation*, mtClassShared>;

// Note: both of the following are non-null if we are dumping a dynamic archive.
static AOTClassLocationConfig* _dumptime_instance;
static const AOTClassLocationConfig* _runtime_instance;

Array<AOTClassLocation*>* _class_locations; // jrt -> -Xbootclasspath/a -> -classpath -> --module_path
int _boot_classpath_end;
int _app_classpath_end;
int _module_end;
bool _has_non_jar_modules;
bool _has_platform_classes;
bool _has_app_classes;
int _max_used_index;
size_t _dumptime_lcp_len;

// accessors
Array<AOTClassLocation*>* class_locations() const { return _class_locations; }

void parse(JavaThread* current, GrowableClassLocationArray& tmp_array, ClassLocationStream& css,
Group group, bool parse_manifest);
void add_class_location(JavaThread* current, GrowableClassLocationArray& tmp_array, const char* path,
Group group, bool parse_manifest, bool from_cpattr);
void dumptime_init_helper(TRAPS);

bool check_classpaths(bool is_boot_classpath, bool has_aot_linked_classes,
int index_start, int index_end, ClassLocationStream& runtime_css,
bool use_lcp_match, const char* runtime_lcp, size_t runtime_lcp_len) const;
bool check_module_paths(bool has_aot_linked_classes, int index_start, int index_end, ClassLocationStream& runtime_css,
bool* has_extra_module_paths) const;
bool file_exists(const char* filename) const;
bool check_paths_existence(ClassLocationStream& runtime_css) const;

static const char* substitute(const char* path, size_t remove_prefix_len,
const char* prepend, size_t prepend_len);
static const char* find_lcp(ClassLocationStream& css, size_t& lcp_len);
bool need_lcp_match(AllClassLocationStreams& all_css) const;
bool need_lcp_match_helper(int start, int end, ClassLocationStream& css) const;

template <typename FUNC> void dumptime_iterate_helper(FUNC func) const {
assert(_class_locations != nullptr, "sanity");
int n = _class_locations->length();
for (int i = 0; i < n; i++) {
if (!func(_class_locations->at(i))) {
break;
}
}
}

template <typename FUNC> void iterate(FUNC func) const {
int n = class_locations()->length();
for (int i = 0; i < n; i++) {
if (!func(class_locations()->at(i))) {
break;
}
}
}

void check_nonempty_dirs() const;
bool need_to_check_app_classpath() const {
return (num_app_classpaths() > 0) && (_max_used_index >= app_cp_start_index()) && has_platform_or_app_classes();
}

void print_dumptime_classpath(LogStream& ls, int index_start, int index_limit,
bool do_substitute, size_t remove_prefix_len,
const char* prepend, size_t prepend_len) const;
public:
static AOTClassLocationConfig* dumptime() {
assert(_dumptime_instance != nullptr, "can only be called when dumping an AOT cache");
return _dumptime_instance;
}

static const AOTClassLocationConfig* runtime() {
assert(_runtime_instance != nullptr, "can only be called when using an AOT cache");
return _runtime_instance;
}

// Common accessors
int boot_cp_start_index() const { return 1; }
int boot_cp_end_index() const { return _boot_classpath_end; }
int app_cp_start_index() const { return boot_cp_end_index(); }
int app_cp_end_index() const { return _app_classpath_end; }
int module_path_start_index() const { return app_cp_end_index(); }
int module_path_end_index() const { return _module_end; }
bool has_platform_or_app_classes() const { return _has_app_classes || _has_platform_classes; }
bool has_non_jar_modules() const { return _has_non_jar_modules; }
int num_boot_classpaths() const { return boot_cp_end_index() - boot_cp_start_index(); }
int num_app_classpaths() const { return app_cp_end_index() - app_cp_start_index(); }
int num_module_paths() const { return module_path_end_index() - module_path_start_index(); }

int length() const {
return _class_locations->length();
}

const AOTClassLocation* class_location_at(int index) const;
int get_module_shared_path_index(Symbol* location) const;

// Functions used only during dumptime
static void dumptime_init(JavaThread* current);

static void dumptime_set_has_app_classes() {
_dumptime_instance->_has_app_classes = true;
}

static void dumptime_set_has_platform_classes() {
_dumptime_instance->_has_platform_classes = true;
}

static void dumptime_update_max_used_index(int index) {
if (_dumptime_instance == nullptr) {
assert(index == 0, "sanity");
} else if (_dumptime_instance->_max_used_index < index) {
_dumptime_instance->_max_used_index = index;
}
}

static void dumptime_check_nonempty_dirs() {
_dumptime_instance->check_nonempty_dirs();
}

static bool dumptime_is_ready() {
return _dumptime_instance != nullptr;
}
template <typename FUNC> static void dumptime_iterate(FUNC func) {
_dumptime_instance->dumptime_iterate_helper(func);
}

AOTClassLocationConfig* write_to_archive() const;

// Functions used only during runtime
bool validate(bool has_aot_linked_classes, bool* has_extra_module_paths) const;
};


#endif // SHARE_CDS_AOTCLASSLOCATION_HPP
3 changes: 1 addition & 2 deletions src/hotspot/share/cds/archiveHeapLoader.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,7 +25,6 @@
#ifndef SHARE_CDS_ARCHIVEHEAPLOADER_HPP
#define SHARE_CDS_ARCHIVEHEAPLOADER_HPP

#include "cds/filemap.hpp"
#include "gc/shared/gc_globals.hpp"
#include "memory/allocation.hpp"
#include "memory/allStatic.hpp"
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/cds/cdsConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "cds/archiveHeapLoader.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/classListWriter.hpp"
#include "cds/filemap.hpp"
#include "cds/heapShared.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/moduleEntry.hpp"
Expand Down
1 change: 0 additions & 1 deletion src/hotspot/share/cds/cdsConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ CDSConst CDSConstants::offsets[] = {
{ "GenericCDSFileMapHeader::_base_archive_name_size", offset_of(GenericCDSFileMapHeader, _base_archive_name_size) },
{ "CDSFileMapHeaderBase::_regions[0]", offset_of(CDSFileMapHeaderBase, _regions) },
{ "FileMapHeader::_jvm_ident", offset_of(FileMapHeader, _jvm_ident) },
{ "FileMapHeader::_common_app_classpath_prefix_size", offset_of(FileMapHeader, _common_app_classpath_prefix_size) },
{ "CDSFileMapRegion::_crc", offset_of(CDSFileMapRegion, _crc) },
{ "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used) },
{ "DynamicArchiveHeader::_base_region_crc", offset_of(DynamicArchiveHeader, _base_region_crc) }
Expand Down
26 changes: 13 additions & 13 deletions src/hotspot/share/cds/cdsProtectionDomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*
*/

#include "cds/aotClassLocation.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/cdsProtectionDomain.hpp"
#include "classfile/classLoader.hpp"
Expand Down Expand Up @@ -49,10 +50,10 @@ OopHandle CDSProtectionDomain::_shared_jar_manifests;
Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) {
int index = ik->shared_classpath_index();
assert(index >= 0, "Sanity");
SharedClassPathEntry* ent = FileMapInfo::shared_path(index);
const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index);
Symbol* class_name = ik->name();

if (ent->is_modules_image()) {
if (cl->is_modules_image()) {
// For shared app/platform classes originated from the run-time image:
// The ProtectionDomains are cached in the corresponding ModuleEntries
// for fast access by the VM.
Expand All @@ -63,15 +64,14 @@ Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlas
return get_shared_protection_domain(class_loader, mod_entry, THREAD);
} else {
// For shared app/platform classes originated from JAR files on the class path:
// Each of the 3 SystemDictionaryShared::_shared_xxx arrays has the same length
// as the shared classpath table in the shared archive (see
// FileMap::_shared_path_table in filemap.hpp for details).
// Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length
// as the shared classpath table in the shared archive.
//
// If a shared InstanceKlass k is loaded from the class path, let
//
// index = k->shared_classpath_index():
// index = k->shared_classpath_index();
//
// FileMap::_shared_path_table[index] identifies the JAR file that contains k.
// AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k.
//
// k's protection domain is:
//
Expand All @@ -84,10 +84,10 @@ Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlas
// define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
//
// Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by
// the corresponding SystemDictionaryShared::get_shared_xxx() function.
// the corresponding CDSProtectionDomain::get_shared_xxx() function.
Handle manifest = get_shared_jar_manifest(index, CHECK_NH);
Handle url = get_shared_jar_url(index, CHECK_NH);
int index_offset = index - ClassLoaderExt::app_class_paths_start_index();
int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index();
if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) {
if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) {
// define_shared_package only needs to be called once for each package in a jar specified
Expand Down Expand Up @@ -178,14 +178,14 @@ Handle CDSProtectionDomain::create_jar_manifest(const char* manifest_chars, size
Handle CDSProtectionDomain::get_shared_jar_manifest(int shared_path_index, TRAPS) {
Handle manifest;
if (shared_jar_manifest(shared_path_index) == nullptr) {
SharedClassPathEntry* ent = FileMapInfo::shared_path(shared_path_index);
size_t size = (size_t)ent->manifest_size();
const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(shared_path_index);
size_t size = cl->manifest_length();
if (size == 0) {
return Handle();
}

// ByteArrayInputStream bais = new ByteArrayInputStream(buf);
const char* src = ent->manifest();
const char* src = cl->manifest();
assert(src != nullptr, "No Manifest data");
manifest = create_jar_manifest(src, size, CHECK_NH);
atomic_set_shared_jar_manifest(shared_path_index, manifest());
Expand All @@ -198,7 +198,7 @@ Handle CDSProtectionDomain::get_shared_jar_manifest(int shared_path_index, TRAPS
Handle CDSProtectionDomain::get_shared_jar_url(int shared_path_index, TRAPS) {
Handle url_h;
if (shared_jar_url(shared_path_index) == nullptr) {
const char* path = FileMapInfo::shared_path_name(shared_path_index);
const char* path = AOTClassLocationConfig::runtime()->class_location_at(shared_path_index)->path();
oop result_oop = to_file_URL(path, url_h, CHECK_(url_h));
atomic_set_shared_jar_url(shared_path_index, result_oop);
}
Expand Down
1 change: 0 additions & 1 deletion src/hotspot/share/cds/cppVtables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address ob
case MetaspaceObj::ConstantPoolCacheType:
case MetaspaceObj::AnnotationsType:
case MetaspaceObj::MethodCountersType:
case MetaspaceObj::SharedClassPathEntryType:
case MetaspaceObj::RecordComponentType:
// These have no vtables.
break;
Expand Down
Loading