|
| 1 | +/* |
| 2 | + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | + * |
| 5 | + * This code is free software; you can redistribute it and/or modify it |
| 6 | + * under the terms of the GNU General Public License version 2 only, as |
| 7 | + * published by the Free Software Foundation. |
| 8 | + * |
| 9 | + * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | + * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | + * accompanied this code). |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU General Public License version |
| 16 | + * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | + * |
| 19 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | + * or visit www.oracle.com if you need additional information or have any |
| 21 | + * questions. |
| 22 | + * |
| 23 | + */ |
| 24 | + |
| 25 | +#include "precompiled.hpp" |
| 26 | +#include "logging/log.hpp" |
| 27 | +#include "memory/archiveUtils.hpp" |
| 28 | +#include "memory/cppVtables.hpp" |
| 29 | +#include "memory/metaspaceShared.hpp" |
| 30 | +#include "oops/instanceClassLoaderKlass.hpp" |
| 31 | +#include "oops/instanceMirrorKlass.hpp" |
| 32 | +#include "oops/instanceRefKlass.hpp" |
| 33 | +#include "oops/methodData.hpp" |
| 34 | +#include "oops/objArrayKlass.hpp" |
| 35 | +#include "oops/typeArrayKlass.hpp" |
| 36 | +#include "runtime/arguments.hpp" |
| 37 | +#include "utilities/globalDefinitions.hpp" |
| 38 | + |
| 39 | +// Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables. |
| 40 | +// (In GCC this is the field <Type>::_vptr, i.e., first word in the object.) |
| 41 | +// |
| 42 | +// Addresses of the vtables and the methods may be different across JVM runs, |
| 43 | +// if libjvm.so is dynamically loaded at a different base address. |
| 44 | +// |
| 45 | +// To ensure that the Metadata objects in the CDS archive always have the correct vtable: |
| 46 | +// |
| 47 | +// + at dump time: we redirect the _vptr to point to our own vtables inside |
| 48 | +// the CDS image |
| 49 | +// + at run time: we clone the actual contents of the vtables from libjvm.so |
| 50 | +// into our own tables. |
| 51 | + |
| 52 | +// Currently, the archive contain ONLY the following types of objects that have C++ vtables. |
| 53 | +#define CPP_VTABLE_PATCH_TYPES_DO(f) \ |
| 54 | + f(ConstantPool) \ |
| 55 | + f(InstanceKlass) \ |
| 56 | + f(InstanceClassLoaderKlass) \ |
| 57 | + f(InstanceMirrorKlass) \ |
| 58 | + f(InstanceRefKlass) \ |
| 59 | + f(Method) \ |
| 60 | + f(ObjArrayKlass) \ |
| 61 | + f(TypeArrayKlass) |
| 62 | + |
| 63 | +class CppVtableInfo { |
| 64 | + intptr_t _vtable_size; |
| 65 | + intptr_t _cloned_vtable[1]; |
| 66 | +public: |
| 67 | + static int num_slots(int vtable_size) { |
| 68 | + return 1 + vtable_size; // Need to add the space occupied by _vtable_size; |
| 69 | + } |
| 70 | + int vtable_size() { return int(uintx(_vtable_size)); } |
| 71 | + void set_vtable_size(int n) { _vtable_size = intptr_t(n); } |
| 72 | + intptr_t* cloned_vtable() { return &_cloned_vtable[0]; } |
| 73 | + void zero() { memset(_cloned_vtable, 0, sizeof(intptr_t) * vtable_size()); } |
| 74 | + // Returns the address of the next CppVtableInfo that can be placed immediately after this CppVtableInfo |
| 75 | + static size_t byte_size(int vtable_size) { |
| 76 | + CppVtableInfo i; |
| 77 | + return pointer_delta(&i._cloned_vtable[vtable_size], &i, sizeof(u1)); |
| 78 | + } |
| 79 | +}; |
| 80 | + |
| 81 | +static inline intptr_t* vtable_of(Metadata* m) { |
| 82 | + return *((intptr_t**)m); |
| 83 | +} |
| 84 | + |
| 85 | +static inline DumpRegion* mc_region() { |
| 86 | + return MetaspaceShared::misc_code_dump_space(); |
| 87 | +} |
| 88 | + |
| 89 | +template <class T> class CppVtableCloner : public T { |
| 90 | + static CppVtableInfo* _info; |
| 91 | + |
| 92 | + static int get_vtable_length(const char* name); |
| 93 | + |
| 94 | +public: |
| 95 | + // Allocate and initialize the C++ vtable, starting from top, but do not go past end. |
| 96 | + static intptr_t* allocate(const char* name); |
| 97 | + |
| 98 | + // Clone the vtable to ... |
| 99 | + static intptr_t* clone_vtable(const char* name, CppVtableInfo* info); |
| 100 | + |
| 101 | + static void zero_vtable_clone() { |
| 102 | + assert(DumpSharedSpaces, "dump-time only"); |
| 103 | + _info->zero(); |
| 104 | + } |
| 105 | + |
| 106 | + static bool is_valid_shared_object(const T* obj) { |
| 107 | + intptr_t* vptr = *(intptr_t**)obj; |
| 108 | + return vptr == _info->cloned_vtable(); |
| 109 | + } |
| 110 | + |
| 111 | + static void init_orig_cpp_vtptr(int kind); |
| 112 | +}; |
| 113 | + |
| 114 | +template <class T> CppVtableInfo* CppVtableCloner<T>::_info = NULL; |
| 115 | + |
| 116 | +template <class T> |
| 117 | +intptr_t* CppVtableCloner<T>::allocate(const char* name) { |
| 118 | + assert(is_aligned(mc_region()->top(), sizeof(intptr_t)), "bad alignment"); |
| 119 | + int n = get_vtable_length(name); |
| 120 | + _info = (CppVtableInfo*)mc_region()->allocate(CppVtableInfo::byte_size(n), sizeof(intptr_t)); |
| 121 | + _info->set_vtable_size(n); |
| 122 | + |
| 123 | + intptr_t* p = clone_vtable(name, _info); |
| 124 | + assert((char*)p == mc_region()->top(), "must be"); |
| 125 | + |
| 126 | + return _info->cloned_vtable(); |
| 127 | +} |
| 128 | + |
| 129 | +template <class T> |
| 130 | +intptr_t* CppVtableCloner<T>::clone_vtable(const char* name, CppVtableInfo* info) { |
| 131 | + if (!DumpSharedSpaces) { |
| 132 | + assert(_info == 0, "_info is initialized only at dump time"); |
| 133 | + _info = info; // Remember it -- it will be used by MetaspaceShared::is_valid_shared_method() |
| 134 | + } |
| 135 | + T tmp; // Allocate temporary dummy metadata object to get to the original vtable. |
| 136 | + int n = info->vtable_size(); |
| 137 | + intptr_t* srcvtable = vtable_of(&tmp); |
| 138 | + intptr_t* dstvtable = info->cloned_vtable(); |
| 139 | + |
| 140 | + // We already checked (and, if necessary, adjusted n) when the vtables were allocated, so we are |
| 141 | + // safe to do memcpy. |
| 142 | + log_debug(cds, vtables)("Copying %3d vtable entries for %s", n, name); |
| 143 | + memcpy(dstvtable, srcvtable, sizeof(intptr_t) * n); |
| 144 | + return dstvtable + n; |
| 145 | +} |
| 146 | + |
| 147 | +// To determine the size of the vtable for each type, we use the following |
| 148 | +// trick by declaring 2 subclasses: |
| 149 | +// |
| 150 | +// class CppVtableTesterA: public InstanceKlass {virtual int last_virtual_method() {return 1;} }; |
| 151 | +// class CppVtableTesterB: public InstanceKlass {virtual void* last_virtual_method() {return NULL}; }; |
| 152 | +// |
| 153 | +// CppVtableTesterA and CppVtableTesterB's vtables have the following properties: |
| 154 | +// - Their size (N+1) is exactly one more than the size of InstanceKlass's vtable (N) |
| 155 | +// - The first N entries have are exactly the same as in InstanceKlass's vtable. |
| 156 | +// - Their last entry is different. |
| 157 | +// |
| 158 | +// So to determine the value of N, we just walk CppVtableTesterA and CppVtableTesterB's tables |
| 159 | +// and find the first entry that's different. |
| 160 | +// |
| 161 | +// This works on all C++ compilers supported by Oracle, but you may need to tweak it for more |
| 162 | +// esoteric compilers. |
| 163 | + |
| 164 | +template <class T> class CppVtableTesterB: public T { |
| 165 | +public: |
| 166 | + virtual int last_virtual_method() {return 1;} |
| 167 | +}; |
| 168 | + |
| 169 | +template <class T> class CppVtableTesterA : public T { |
| 170 | +public: |
| 171 | + virtual void* last_virtual_method() { |
| 172 | + // Make this different than CppVtableTesterB::last_virtual_method so the C++ |
| 173 | + // compiler/linker won't alias the two functions. |
| 174 | + return NULL; |
| 175 | + } |
| 176 | +}; |
| 177 | + |
| 178 | +template <class T> |
| 179 | +int CppVtableCloner<T>::get_vtable_length(const char* name) { |
| 180 | + CppVtableTesterA<T> a; |
| 181 | + CppVtableTesterB<T> b; |
| 182 | + |
| 183 | + intptr_t* avtable = vtable_of(&a); |
| 184 | + intptr_t* bvtable = vtable_of(&b); |
| 185 | + |
| 186 | + // Start at slot 1, because slot 0 may be RTTI (on Solaris/Sparc) |
| 187 | + int vtable_len = 1; |
| 188 | + for (; ; vtable_len++) { |
| 189 | + if (avtable[vtable_len] != bvtable[vtable_len]) { |
| 190 | + break; |
| 191 | + } |
| 192 | + } |
| 193 | + log_debug(cds, vtables)("Found %3d vtable entries for %s", vtable_len, name); |
| 194 | + |
| 195 | + return vtable_len; |
| 196 | +} |
| 197 | + |
| 198 | +#define ALLOC_CPP_VTABLE_CLONE(c) \ |
| 199 | + _cloned_cpp_vtptrs[c##_Kind] = CppVtableCloner<c>::allocate(#c); \ |
| 200 | + ArchivePtrMarker::mark_pointer(&_cloned_cpp_vtptrs[c##_Kind]); |
| 201 | + |
| 202 | +#define CLONE_CPP_VTABLE(c) \ |
| 203 | + p = CppVtableCloner<c>::clone_vtable(#c, (CppVtableInfo*)p); |
| 204 | + |
| 205 | +#define ZERO_CPP_VTABLE(c) \ |
| 206 | + CppVtableCloner<c>::zero_vtable_clone(); |
| 207 | + |
| 208 | +#define INIT_ORIG_CPP_VTPTRS(c) \ |
| 209 | + CppVtableCloner<c>::init_orig_cpp_vtptr(c##_Kind); |
| 210 | + |
| 211 | +#define DECLARE_CLONED_VTABLE_KIND(c) c ## _Kind, |
| 212 | + |
| 213 | +enum ClonedVtableKind { |
| 214 | + // E.g., ConstantPool_Kind == 0, InstanceKlass_Kind == 1, etc. |
| 215 | + CPP_VTABLE_PATCH_TYPES_DO(DECLARE_CLONED_VTABLE_KIND) |
| 216 | + _num_cloned_vtable_kinds |
| 217 | +}; |
| 218 | + |
| 219 | +// This is a map of all the original vtptrs. E.g., for |
| 220 | +// ConstantPool *cp = new (...) ConstantPool(...) ; // a dynamically allocated constant pool |
| 221 | +// the following holds true: |
| 222 | +// _orig_cpp_vtptrs[ConstantPool_Kind] == ((intptr_t**)cp)[0] |
| 223 | +static intptr_t* _orig_cpp_vtptrs[_num_cloned_vtable_kinds]; |
| 224 | +static bool _orig_cpp_vtptrs_inited = false; |
| 225 | + |
| 226 | +template <class T> |
| 227 | +void CppVtableCloner<T>::init_orig_cpp_vtptr(int kind) { |
| 228 | + assert(kind < _num_cloned_vtable_kinds, "sanity"); |
| 229 | + T tmp; // Allocate temporary dummy metadata object to get to the original vtable. |
| 230 | + intptr_t* srcvtable = vtable_of(&tmp); |
| 231 | + _orig_cpp_vtptrs[kind] = srcvtable; |
| 232 | +} |
| 233 | + |
| 234 | +// This is the index of all the cloned vtables. E.g., for |
| 235 | +// ConstantPool* cp = ....; // an archived constant pool |
| 236 | +// InstanceKlass* ik = ....;// an archived class |
| 237 | +// the following holds true: |
| 238 | +// _cloned_cpp_vtptrs[ConstantPool_Kind] == ((intptr_t**)cp)[0] |
| 239 | +// _cloned_cpp_vtptrs[InstanceKlass_Kind] == ((intptr_t**)ik)[0] |
| 240 | +static intptr_t** _cloned_cpp_vtptrs = NULL; |
| 241 | + |
| 242 | +void CppVtables::allocate_cloned_cpp_vtptrs() { |
| 243 | + assert(DumpSharedSpaces, "must"); |
| 244 | + size_t vtptrs_bytes = _num_cloned_vtable_kinds * sizeof(intptr_t*); |
| 245 | + _cloned_cpp_vtptrs = (intptr_t**)mc_region()->allocate(vtptrs_bytes, sizeof(intptr_t*)); |
| 246 | +} |
| 247 | + |
| 248 | +void CppVtables::serialize_cloned_cpp_vtptrs(SerializeClosure* soc) { |
| 249 | + soc->do_ptr((void**)&_cloned_cpp_vtptrs); |
| 250 | +} |
| 251 | + |
| 252 | +intptr_t* CppVtables::get_archived_cpp_vtable(MetaspaceObj::Type msotype, address obj) { |
| 253 | + if (!_orig_cpp_vtptrs_inited) { |
| 254 | + CPP_VTABLE_PATCH_TYPES_DO(INIT_ORIG_CPP_VTPTRS); |
| 255 | + _orig_cpp_vtptrs_inited = true; |
| 256 | + } |
| 257 | + |
| 258 | + Arguments::assert_is_dumping_archive(); |
| 259 | + int kind = -1; |
| 260 | + switch (msotype) { |
| 261 | + case MetaspaceObj::SymbolType: |
| 262 | + case MetaspaceObj::TypeArrayU1Type: |
| 263 | + case MetaspaceObj::TypeArrayU2Type: |
| 264 | + case MetaspaceObj::TypeArrayU4Type: |
| 265 | + case MetaspaceObj::TypeArrayU8Type: |
| 266 | + case MetaspaceObj::TypeArrayOtherType: |
| 267 | + case MetaspaceObj::ConstMethodType: |
| 268 | + case MetaspaceObj::ConstantPoolCacheType: |
| 269 | + case MetaspaceObj::AnnotationsType: |
| 270 | + case MetaspaceObj::MethodCountersType: |
| 271 | + case MetaspaceObj::RecordComponentType: |
| 272 | + // These have no vtables. |
| 273 | + break; |
| 274 | + case MetaspaceObj::MethodDataType: |
| 275 | + // We don't archive MethodData <-- should have been removed in removed_unsharable_info |
| 276 | + ShouldNotReachHere(); |
| 277 | + break; |
| 278 | + default: |
| 279 | + for (kind = 0; kind < _num_cloned_vtable_kinds; kind ++) { |
| 280 | + if (vtable_of((Metadata*)obj) == _orig_cpp_vtptrs[kind]) { |
| 281 | + break; |
| 282 | + } |
| 283 | + } |
| 284 | + if (kind >= _num_cloned_vtable_kinds) { |
| 285 | + fatal("Cannot find C++ vtable for " INTPTR_FORMAT " -- you probably added" |
| 286 | + " a new subtype of Klass or MetaData without updating CPP_VTABLE_PATCH_TYPES_DO", |
| 287 | + p2i(obj)); |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + if (kind >= 0) { |
| 292 | + assert(kind < _num_cloned_vtable_kinds, "must be"); |
| 293 | + return _cloned_cpp_vtptrs[kind]; |
| 294 | + } else { |
| 295 | + return NULL; |
| 296 | + } |
| 297 | +} |
| 298 | + |
| 299 | +// This can be called at both dump time and run time: |
| 300 | +// - clone the contents of the c++ vtables into the space |
| 301 | +// allocated by allocate_cpp_vtable_clones() |
| 302 | +void CppVtables::clone_cpp_vtables(intptr_t* p) { |
| 303 | + assert(DumpSharedSpaces || UseSharedSpaces, "sanity"); |
| 304 | + CPP_VTABLE_PATCH_TYPES_DO(CLONE_CPP_VTABLE); |
| 305 | +} |
| 306 | + |
| 307 | +void CppVtables::zero_cpp_vtable_clones_for_writing() { |
| 308 | + assert(DumpSharedSpaces, "dump-time only"); |
| 309 | + CPP_VTABLE_PATCH_TYPES_DO(ZERO_CPP_VTABLE); |
| 310 | +} |
| 311 | + |
| 312 | +// Allocate and initialize the C++ vtables, starting from top, but do not go past end. |
| 313 | +char* CppVtables::allocate_cpp_vtable_clones() { |
| 314 | + char* cloned_vtables = mc_region()->top(); // This is the beginning of all the cloned vtables |
| 315 | + |
| 316 | + assert(DumpSharedSpaces, "dump-time only"); |
| 317 | + // Layout (each slot is a intptr_t): |
| 318 | + // [number of slots in the first vtable = n1] |
| 319 | + // [ <n1> slots for the first vtable] |
| 320 | + // [number of slots in the first second = n2] |
| 321 | + // [ <n2> slots for the second vtable] |
| 322 | + // ... |
| 323 | + // The order of the vtables is the same as the CPP_VTAB_PATCH_TYPES_DO macro. |
| 324 | + CPP_VTABLE_PATCH_TYPES_DO(ALLOC_CPP_VTABLE_CLONE); |
| 325 | + |
| 326 | + return cloned_vtables; |
| 327 | +} |
| 328 | + |
| 329 | +bool CppVtables::is_valid_shared_method(const Method* m) { |
| 330 | + assert(MetaspaceShared::is_in_shared_metaspace(m), "must be"); |
| 331 | + return CppVtableCloner<Method>::is_valid_shared_object(m); |
| 332 | +} |
0 commit comments