Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
8293979: Resolve JVM_CONSTANT_Class references at CDS dump time
Reviewed-by: coleenp, ccheung
- Loading branch information
Showing
8 changed files
with
371 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
/* | ||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
* | ||
*/ | ||
|
||
#include "precompiled.hpp" | ||
#include "cds/archiveBuilder.hpp" | ||
#include "cds/classPrelinker.hpp" | ||
#include "classfile/systemDictionary.hpp" | ||
#include "classfile/vmClasses.hpp" | ||
#include "memory/resourceArea.hpp" | ||
#include "oops/constantPool.inline.hpp" | ||
#include "oops/instanceKlass.hpp" | ||
#include "oops/klass.inline.hpp" | ||
#include "runtime/handles.inline.hpp" | ||
|
||
ClassPrelinker::ClassesTable* ClassPrelinker::_processed_classes = NULL; | ||
ClassPrelinker::ClassesTable* ClassPrelinker::_vm_classes = NULL; | ||
|
||
bool ClassPrelinker::is_vm_class(InstanceKlass* ik) { | ||
return (_vm_classes->get(ik) != NULL); | ||
} | ||
|
||
void ClassPrelinker::add_one_vm_class(InstanceKlass* ik) { | ||
bool created; | ||
_vm_classes->put_if_absent(ik, &created); | ||
if (created) { | ||
InstanceKlass* super = ik->java_super(); | ||
if (super != NULL) { | ||
add_one_vm_class(super); | ||
} | ||
Array<InstanceKlass*>* ifs = ik->local_interfaces(); | ||
for (int i = 0; i < ifs->length(); i++) { | ||
add_one_vm_class(ifs->at(i)); | ||
} | ||
} | ||
} | ||
|
||
void ClassPrelinker::initialize() { | ||
assert(_vm_classes == NULL, "must be"); | ||
_vm_classes = new (ResourceObj::C_HEAP, mtClass)ClassesTable(); | ||
_processed_classes = new (ResourceObj::C_HEAP, mtClass)ClassesTable(); | ||
for (auto id : EnumRange<vmClassID>{}) { | ||
add_one_vm_class(vmClasses::klass_at(id)); | ||
} | ||
} | ||
|
||
void ClassPrelinker::dispose() { | ||
assert(_vm_classes != NULL, "must be"); | ||
delete _vm_classes; | ||
delete _processed_classes; | ||
_vm_classes = NULL; | ||
_processed_classes = NULL; | ||
} | ||
|
||
bool ClassPrelinker::can_archive_resolved_klass(ConstantPool* cp, int cp_index) { | ||
assert(!is_in_archivebuilder_buffer(cp), "sanity"); | ||
assert(cp->tag_at(cp_index).is_klass(), "must be resolved"); | ||
|
||
Klass* resolved_klass = cp->resolved_klass_at(cp_index); | ||
assert(resolved_klass != NULL, "must be"); | ||
|
||
return can_archive_resolved_klass(cp->pool_holder(), resolved_klass); | ||
} | ||
|
||
bool ClassPrelinker::can_archive_resolved_klass(InstanceKlass* cp_holder, Klass* resolved_klass) { | ||
assert(!is_in_archivebuilder_buffer(cp_holder), "sanity"); | ||
assert(!is_in_archivebuilder_buffer(resolved_klass), "sanity"); | ||
|
||
if (resolved_klass->is_instance_klass()) { | ||
InstanceKlass* ik = InstanceKlass::cast(resolved_klass); | ||
if (is_vm_class(ik)) { // These are safe to resolve. See is_vm_class declaration. | ||
assert(ik->is_shared_boot_class(), "vmClasses must be loaded by boot loader"); | ||
if (cp_holder->is_shared_boot_class()) { | ||
// For now, do this for boot loader only. Other loaders | ||
// must go through ConstantPool::klass_at_impl at runtime | ||
// to put this class in their directory. | ||
|
||
// TODO: we can support the platform and app loaders as well, if we | ||
// preload the vmClasses into these two loaders during VM bootstrap. | ||
return true; | ||
} | ||
} | ||
|
||
if (cp_holder->is_subtype_of(ik)) { | ||
// All super types of ik will be resolved in ik->class_loader() before | ||
// ik is defined in this loader, so it's safe to archive the resolved klass reference. | ||
return true; | ||
} | ||
|
||
// TODO -- allow objArray classes, too | ||
} | ||
|
||
return false; | ||
} | ||
|
||
void ClassPrelinker::dumptime_resolve_constants(InstanceKlass* ik, TRAPS) { | ||
constantPoolHandle cp(THREAD, ik->constants()); | ||
if (cp->cache() == NULL || cp->reference_map() == NULL) { | ||
// The cache may be NULL if the pool_holder klass fails verification | ||
// at dump time due to missing dependencies. | ||
return; | ||
} | ||
|
||
bool first_time; | ||
_processed_classes->put_if_absent(ik, &first_time); | ||
if (!first_time) { | ||
// We have already resolved the constants in class, so no need to do it again. | ||
return; | ||
} | ||
|
||
for (int cp_index = 1; cp_index < cp->length(); cp_index++) { // Index 0 is unused | ||
switch (cp->tag_at(cp_index).value()) { | ||
case JVM_CONSTANT_UnresolvedClass: | ||
maybe_resolve_class(cp, cp_index, CHECK); | ||
break; | ||
|
||
case JVM_CONSTANT_String: | ||
resolve_string(cp, cp_index, CHECK); // may throw OOM when interning strings. | ||
break; | ||
} | ||
} | ||
} | ||
|
||
Klass* ClassPrelinker::find_loaded_class(JavaThread* THREAD, oop class_loader, Symbol* name) { | ||
Handle h_loader(THREAD, class_loader); | ||
Klass* k = SystemDictionary::find_instance_or_array_klass(THREAD, name, | ||
h_loader, | ||
Handle()); | ||
if (k != NULL) { | ||
return k; | ||
} | ||
if (class_loader == SystemDictionary::java_system_loader()) { | ||
return find_loaded_class(THREAD, SystemDictionary::java_platform_loader(), name); | ||
} else if (class_loader == SystemDictionary::java_platform_loader()) { | ||
return find_loaded_class(THREAD, NULL, name); | ||
} | ||
|
||
return NULL; | ||
} | ||
|
||
Klass* ClassPrelinker::maybe_resolve_class(constantPoolHandle cp, int cp_index, TRAPS) { | ||
assert(!is_in_archivebuilder_buffer(cp()), "sanity"); | ||
InstanceKlass* cp_holder = cp->pool_holder(); | ||
if (!cp_holder->is_shared_boot_class() && | ||
!cp_holder->is_shared_platform_class() && | ||
!cp_holder->is_shared_app_class()) { | ||
// Don't trust custom loaders, as they may not be well-behaved | ||
// when resolving classes. | ||
return NULL; | ||
} | ||
|
||
Symbol* name = cp->klass_name_at(cp_index); | ||
Klass* resolved_klass = find_loaded_class(THREAD, cp_holder->class_loader(), name); | ||
if (resolved_klass != NULL && can_archive_resolved_klass(cp_holder, resolved_klass)) { | ||
Klass* k = cp->klass_at(cp_index, CHECK_NULL); // Should fail only with OOM | ||
assert(k == resolved_klass, "must be"); | ||
} | ||
|
||
return resolved_klass; | ||
} | ||
|
||
#if INCLUDE_CDS_JAVA_HEAP | ||
void ClassPrelinker::resolve_string(constantPoolHandle cp, int cp_index, TRAPS) { | ||
if (!DumpSharedSpaces) { | ||
// The archive heap is not supported for the dynamic archive. | ||
return; | ||
} | ||
|
||
int cache_index = cp->cp_to_object_index(cp_index); | ||
ConstantPool::string_at_impl(cp, cp_index, cache_index, CHECK); | ||
} | ||
#endif | ||
|
||
#ifdef ASSERT | ||
bool ClassPrelinker::is_in_archivebuilder_buffer(address p) { | ||
if (!Thread::current()->is_VM_thread() || ArchiveBuilder::current() == NULL) { | ||
return false; | ||
} else { | ||
return ArchiveBuilder::current()->is_in_buffer_space(p); | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Copyright (c) 2022, 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_CLASSPRELINKER_HPP | ||
#define SHARE_CDS_CLASSPRELINKER_HPP | ||
|
||
#include "oops/oopsHierarchy.hpp" | ||
#include "memory/allStatic.hpp" | ||
#include "memory/allocation.hpp" | ||
#include "runtime/handles.hpp" | ||
#include "utilities/exceptions.hpp" | ||
#include "utilities/macros.hpp" | ||
#include "utilities/resourceHash.hpp" | ||
|
||
class ConstantPool; | ||
class constantPoolHandle; | ||
class InstanceKlass; | ||
class Klass; | ||
|
||
// ClassPrelinker is used to perform ahead-of-time linking of ConstantPool entries | ||
// for archived InstanceKlasses. | ||
// | ||
// At run time, Java classes are loaded dynamically and may be replaced with JVMTI. | ||
// Therefore, we take care to prelink only the ConstantPool entries that are | ||
// guatanteed to resolve to the same results at both dump time and run time. | ||
// | ||
// For example, a JVM_CONSTANT_Class reference to a supertype can be safely resolved | ||
// at dump time, because at run time we will load a class from the CDS archive only | ||
// if all of its supertypes are loaded from the CDS archive. | ||
class ClassPrelinker : AllStatic { | ||
using ClassesTable = ResourceHashtable<InstanceKlass*, bool, 15889, ResourceObj::C_HEAP, mtClassShared> ; | ||
static ClassesTable* _processed_classes; | ||
static ClassesTable* _vm_classes; | ||
|
||
static void add_one_vm_class(InstanceKlass* ik); | ||
|
||
#ifdef ASSERT | ||
static bool is_in_archivebuilder_buffer(address p); | ||
#endif | ||
|
||
template <typename T> | ||
static bool is_in_archivebuilder_buffer(T p) { | ||
return is_in_archivebuilder_buffer((address)(p)); | ||
} | ||
static void resolve_string(constantPoolHandle cp, int cp_index, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; | ||
static Klass* maybe_resolve_class(constantPoolHandle cp, int cp_index, TRAPS); | ||
static bool can_archive_resolved_klass(InstanceKlass* cp_holder, Klass* resolved_klass); | ||
static Klass* find_loaded_class(JavaThread* THREAD, oop class_loader, Symbol* name); | ||
|
||
public: | ||
static void initialize(); | ||
static void dispose(); | ||
|
||
// Is this class resolved as part of vmClasses::resolve_all()? If so, these | ||
// classes are guatanteed to be loaded at runtime (and cannot be replaced by JVMTI) | ||
// when CDS is enabled. Therefore, we can safely keep a direct reference to these | ||
// classes. | ||
static bool is_vm_class(InstanceKlass* ik); | ||
|
||
// Resolve all constant pool entries that are safe to be stored in the | ||
// CDS archive. | ||
static void dumptime_resolve_constants(InstanceKlass* ik, TRAPS); | ||
|
||
// Can we resolve the klass entry at cp_index in this constant pool, and store | ||
// the result in the CDS archive? Returns true if cp_index is guaranteed to | ||
// resolve to the same InstanceKlass* at both dump time and run time. | ||
static bool can_archive_resolved_klass(ConstantPool* cp, int cp_index); | ||
}; | ||
|
||
#endif // SHARE_CDS_CLASSPRELINKER_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
aad81f2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review
Issues