mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-06 14:10:36 +00:00
8153312: Constrain AppCDS behavior
Reviewed-by: iklam, acorn, mschoene
This commit is contained in:
parent
2a09a3923a
commit
bd07a43e61
@ -60,6 +60,7 @@ bool MetaspaceShared::_link_classes_made_progress;
|
||||
bool MetaspaceShared::_check_classes_made_progress;
|
||||
bool MetaspaceShared::_has_error_classes;
|
||||
bool MetaspaceShared::_archive_loading_failed = false;
|
||||
bool MetaspaceShared::_remapped_readwrite = false;
|
||||
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
|
||||
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
|
||||
SharedMiscRegion MetaspaceShared::_mc;
|
||||
@ -1185,6 +1186,7 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
|
||||
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
|
||||
return false;
|
||||
}
|
||||
_remapped_readwrite = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -125,6 +125,7 @@ class MetaspaceShared : AllStatic {
|
||||
static bool _check_classes_made_progress;
|
||||
static bool _has_error_classes;
|
||||
static bool _archive_loading_failed;
|
||||
static bool _remapped_readwrite;
|
||||
static address _cds_i2i_entry_code_buffers;
|
||||
static size_t _cds_i2i_entry_code_buffers_size;
|
||||
|
||||
@ -205,6 +206,10 @@ class MetaspaceShared : AllStatic {
|
||||
// sharing is enabled. Simply returns true if sharing is not enabled
|
||||
// or if the remapping has already been done by a prior call.
|
||||
static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true);
|
||||
static bool remapped_readwrite() {
|
||||
CDS_ONLY(return _remapped_readwrite);
|
||||
NOT_CDS(return false);
|
||||
}
|
||||
|
||||
static void print_shared_spaces();
|
||||
|
||||
|
||||
@ -609,7 +609,12 @@ bool InstanceKlass::link_class_impl(
|
||||
// methods have been rewritten since rewrite may
|
||||
// fabricate new Method*s.
|
||||
// also does loader constraint checking
|
||||
if (!this_k()->is_shared()) {
|
||||
//
|
||||
// initialize_vtable and initialize_itable need to be rerun for
|
||||
// a shared class if the class is not loaded by the NULL classloader.
|
||||
ClassLoaderData * loader_data = this_k->class_loader_data();
|
||||
if (!(this_k->is_shared() &&
|
||||
loader_data->is_the_null_class_loader_data())) {
|
||||
ResourceMark rm(THREAD);
|
||||
this_k->vtable()->initialize_vtable(true, CHECK_false);
|
||||
this_k->itable()->initialize_itable(true, CHECK_false);
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "gc/shared/gcLocker.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "memory/metaspaceShared.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.inline.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
@ -42,6 +43,10 @@ inline InstanceKlass* klassVtable::ik() const {
|
||||
return InstanceKlass::cast(_klass());
|
||||
}
|
||||
|
||||
bool klassVtable::is_preinitialized_vtable() {
|
||||
return _klass->is_shared() && !MetaspaceShared::remapped_readwrite();
|
||||
}
|
||||
|
||||
|
||||
// this function computes the vtable size (including the size needed for miranda
|
||||
// methods) and the number of miranda methods in this class.
|
||||
@ -126,6 +131,12 @@ int klassVtable::index_of(Method* m, int len) const {
|
||||
int klassVtable::initialize_from_super(KlassHandle super) {
|
||||
if (super.is_null()) {
|
||||
return 0;
|
||||
} else if (is_preinitialized_vtable()) {
|
||||
// A shared class' vtable is preinitialized at dump time. No need to copy
|
||||
// methods from super class for shared class, as that was already done
|
||||
// during archiving time. However, if Jvmti has redefined a class,
|
||||
// copy super class's vtable in case the super class has changed.
|
||||
return super->vtable()->length();
|
||||
} else {
|
||||
// copy methods from superKlass
|
||||
klassVtable* superVtable = super->vtable();
|
||||
@ -152,6 +163,8 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
KlassHandle super (THREAD, klass()->java_super());
|
||||
int nofNewEntries = 0;
|
||||
|
||||
bool is_shared = _klass->is_shared();
|
||||
|
||||
if (!klass()->is_array_klass()) {
|
||||
ResourceMark rm(THREAD);
|
||||
log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string());
|
||||
@ -164,6 +177,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
#endif
|
||||
|
||||
if (Universe::is_bootstrapping()) {
|
||||
assert(!is_shared, "sanity");
|
||||
// just clear everything
|
||||
for (int i = 0; i < _length; i++) table()[i].clear();
|
||||
return;
|
||||
@ -203,6 +217,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
if (len > 0) {
|
||||
Array<int>* def_vtable_indices = NULL;
|
||||
if ((def_vtable_indices = ik()->default_vtable_indices()) == NULL) {
|
||||
assert(!is_shared, "shared class def_vtable_indices does not exist");
|
||||
def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK);
|
||||
} else {
|
||||
assert(def_vtable_indices->length() == len, "reinit vtable len?");
|
||||
@ -217,7 +232,15 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
|
||||
// needs new entry
|
||||
if (needs_new_entry) {
|
||||
put_method_at(mh(), initialized);
|
||||
def_vtable_indices->at_put(i, initialized); //set vtable index
|
||||
if (is_preinitialized_vtable()) {
|
||||
// At runtime initialize_vtable is rerun for a shared class
|
||||
// (loaded by the non-boot loader) as part of link_class_impl().
|
||||
// The dumptime vtable index should be the same as the runtime index.
|
||||
assert(def_vtable_indices->at(i) == initialized,
|
||||
"dump time vtable index is different from runtime index");
|
||||
} else {
|
||||
def_vtable_indices->at_put(i, initialized); //set vtable index
|
||||
}
|
||||
initialized++;
|
||||
}
|
||||
}
|
||||
@ -378,7 +401,8 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
}
|
||||
|
||||
// we need a new entry if there is no superclass
|
||||
if (klass->super() == NULL) {
|
||||
Klass* super = klass->super();
|
||||
if (super == NULL) {
|
||||
return allocate_new;
|
||||
}
|
||||
|
||||
@ -407,7 +431,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
|
||||
Symbol* target_classname = target_klass->name();
|
||||
for(int i = 0; i < super_vtable_len; i++) {
|
||||
Method* super_method = method_at(i);
|
||||
Method* super_method;
|
||||
if (is_preinitialized_vtable()) {
|
||||
// If this is a shared class, the vtable is already in the final state (fully
|
||||
// initialized). Need to look at the super's vtable.
|
||||
klassVtable* superVtable = super->vtable();
|
||||
super_method = superVtable->method_at(i);
|
||||
} else {
|
||||
super_method = method_at(i);
|
||||
}
|
||||
// Check if method name matches
|
||||
if (super_method->name() == name && super_method->signature() == signature) {
|
||||
|
||||
@ -475,7 +507,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
target_method()->set_vtable_index(i);
|
||||
} else {
|
||||
if (def_vtable_indices != NULL) {
|
||||
def_vtable_indices->at_put(default_index, i);
|
||||
if (is_preinitialized_vtable()) {
|
||||
// At runtime initialize_vtable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader.
|
||||
// The dumptime vtable index should be the same as the runtime index.
|
||||
assert(def_vtable_indices->at(default_index) == i,
|
||||
"dump time vtable index is different from runtime index");
|
||||
} else {
|
||||
def_vtable_indices->at_put(default_index, i);
|
||||
}
|
||||
}
|
||||
assert(super_method->is_default_method() || super_method->is_overpass()
|
||||
|| super_method->is_abstract(), "default override error");
|
||||
@ -490,17 +530,26 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
||||
}
|
||||
|
||||
void klassVtable::put_method_at(Method* m, int index) {
|
||||
if (log_develop_is_enabled(Trace, vtables)) {
|
||||
ResourceMark rm;
|
||||
outputStream* logst = Log(vtables)::trace_stream();
|
||||
const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>";
|
||||
logst->print("adding %s at index %d, flags: ", sig, index);
|
||||
if (m != NULL) {
|
||||
m->print_linkage_flags(logst);
|
||||
if (is_preinitialized_vtable()) {
|
||||
// At runtime initialize_vtable is rerun as part of link_class_impl()
|
||||
// for shared class loaded by the non-boot loader to obtain the loader
|
||||
// constraints based on the runtime classloaders' context. The dumptime
|
||||
// method at the vtable index should be the same as the runtime method.
|
||||
assert(table()[index].method() == m,
|
||||
"archived method is different from the runtime method");
|
||||
} else {
|
||||
if (log_develop_is_enabled(Trace, vtables)) {
|
||||
ResourceMark rm;
|
||||
outputStream* logst = Log(vtables)::trace_stream();
|
||||
const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>";
|
||||
logst->print("adding %s at index %d, flags: ", sig, index);
|
||||
if (m != NULL) {
|
||||
m->print_linkage_flags(logst);
|
||||
}
|
||||
logst->cr();
|
||||
}
|
||||
logst->cr();
|
||||
table()[index].set(m);
|
||||
}
|
||||
table()[index].set(m);
|
||||
}
|
||||
|
||||
// Find out if a method "m" with superclass "super", loader "classloader" and
|
||||
@ -950,7 +999,15 @@ bool klassVtable::is_initialized() {
|
||||
void itableMethodEntry::initialize(Method* m) {
|
||||
if (m == NULL) return;
|
||||
|
||||
_method = m;
|
||||
if (MetaspaceShared::is_in_shared_space((void*)&_method) &&
|
||||
!MetaspaceShared::remapped_readwrite()) {
|
||||
// At runtime initialize_itable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader.
|
||||
// The dumptime itable method entry should be the same as the runtime entry.
|
||||
assert(_method == m, "sanity");
|
||||
} else {
|
||||
_method = m;
|
||||
}
|
||||
}
|
||||
|
||||
klassItable::klassItable(instanceKlassHandle klass) {
|
||||
@ -1054,7 +1111,11 @@ int klassItable::assign_itable_indices_for_interface(Klass* klass) {
|
||||
logst->cr();
|
||||
}
|
||||
if (!m->has_vtable_index()) {
|
||||
assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable");
|
||||
// A shared method could have an initialized itable_index that
|
||||
// is < 0.
|
||||
assert(m->vtable_index() == Method::pending_itable_index ||
|
||||
m->is_shared(),
|
||||
"set by initialize_vtable");
|
||||
m->set_itable_index(ime_num);
|
||||
// Progress to next itable entry
|
||||
ime_num++;
|
||||
@ -1248,7 +1309,6 @@ void klassItable::dump_itable() {
|
||||
}
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
|
||||
// Setup
|
||||
class InterfaceVisiterClosure : public StackObj {
|
||||
public:
|
||||
|
||||
@ -153,6 +153,19 @@ class klassVtable : public ResourceObj {
|
||||
Array<Klass*>* local_interfaces);
|
||||
void verify_against(outputStream* st, klassVtable* vt, int index);
|
||||
inline InstanceKlass* ik() const;
|
||||
// When loading a class from CDS archive at run time, and no class redefintion
|
||||
// has happened, it is expected that the class's itable/vtables are
|
||||
// laid out exactly the same way as they had been during dump time.
|
||||
// Therefore, in klassVtable::initialize_[iv]table, we do not layout the
|
||||
// tables again. Instead, we only rerun the process to create/check
|
||||
// the class loader constraints. In non-product builds, we add asserts to
|
||||
// guarantee that the table's layout would be the same as at dump time.
|
||||
//
|
||||
// If JVMTI redefines any class, the read-only shared memory are remapped
|
||||
// as read-write. A shared class' vtable/itable are re-initialized and
|
||||
// might have different layout due to class redefinition of the shared class
|
||||
// or its super types.
|
||||
bool is_preinitialized_vtable();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -313,6 +313,33 @@ void Method::remove_unshareable_info() {
|
||||
unlink_method();
|
||||
}
|
||||
|
||||
void Method::set_vtable_index(int index) {
|
||||
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
|
||||
// At runtime initialize_vtable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader to obtain the loader
|
||||
// constraints based on the runtime classloaders' context.
|
||||
return; // don't write into the shared class
|
||||
} else {
|
||||
_vtable_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
void Method::set_itable_index(int index) {
|
||||
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
|
||||
// At runtime initialize_itable is rerun as part of link_class_impl()
|
||||
// for a shared class loaded by the non-boot loader to obtain the loader
|
||||
// constraints based on the runtime classloaders' context. The dumptime
|
||||
// itable index should be the same as the runtime index.
|
||||
assert(_vtable_index == itable_index_max - index,
|
||||
"archived itable index is different from runtime index");
|
||||
return; // don’t write into the shared class
|
||||
} else {
|
||||
_vtable_index = itable_index_max - index;
|
||||
}
|
||||
assert(valid_itable_index(), "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Method::was_executed_more_than(int n) {
|
||||
// Invocation counter is reset when the Method* is compiled.
|
||||
|
||||
@ -470,12 +470,12 @@ class Method : public Metadata {
|
||||
DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; })
|
||||
bool has_vtable_index() const { return _vtable_index >= 0; }
|
||||
int vtable_index() const { return _vtable_index; }
|
||||
void set_vtable_index(int index) { _vtable_index = index; }
|
||||
void set_vtable_index(int index);
|
||||
DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; })
|
||||
bool has_itable_index() const { return _vtable_index <= itable_index_max; }
|
||||
int itable_index() const { assert(valid_itable_index(), "");
|
||||
return itable_index_max - _vtable_index; }
|
||||
void set_itable_index(int index) { _vtable_index = itable_index_max - index; assert(valid_itable_index(), ""); }
|
||||
void set_itable_index(int index);
|
||||
|
||||
// interpreter entry
|
||||
address interpreter_entry() const { return _i2i_entry; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user