blob: 460788930a4ee27d108dab3c239c1a2752809f65 [file]
/*
* 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"
#include "utilities/ostream.hpp"
class AllClassLocationStreams;
class ClassLocationStream;
class ClassPathZipEntry;
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
static Array<ClassPathZipEntry*>* _dumptime_jar_files;
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;
void print_on(outputStream* st) const;
void log_locations(const char* cache_filename, bool is_writing) 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(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const;
bool is_valid_classpath_index(int classpath_index, InstanceKlass* ik);
static void print();
};
#endif // SHARE_CDS_AOTCLASSLOCATION_HPP