The magic of self-patching vtable
Ioi Lam
ioi.lam at oracle.com
Fri Dec 14 14:29:41 PST 2012
On 12/14/2012 02:10 PM, Coleen Phillimore wrote:
> On 12/14/2012 5:04 PM, Ioi Lam wrote:
>> Moving this discussion to hotspot-dev per John's suggestion.
>>
>> Actually, strictly from the point of view of CDS, it would be OK for
>> objects in the RO region to have vtables. This is because the _vptr
>> is fixed during dump time, and never modified during runtime.
>>
>> Only the contents of the vtables (pointed to by _vptr) are updated at
>> CDS image load time.
>
> The way it works today is that the _vptr in the object is replaced, so
> you'd have to change that.
>
> Coleen
>
Hi Coleen,
As far as I can see in today's code, _vptr is replaced here
(metaspaceShared.cpp, version 3823:070d523b96a7), which is called at
dumping time. During dumping time, the "RO" section is actually writable.
// Assumes the vtable is in first slot in object.
static void patch_klass_vtables(void** vtbl_list, void* new_vtable_start) {
int n = _global_klass_objects->length();
for (int i = 0; i < n; i++) {
Klass* obj = _global_klass_objects->at(i);
// Note oop_is_instance() is a virtual call. After patching vtables
// all virtual calls on the dummy vtables will restore the original!
if (obj->oop_is_instance()) {
InstanceKlass* ik = InstanceKlass::cast(obj);
*(void**)ik = find_matching_vtbl_ptr(vtbl_list, new_vtable_start,
ik);
ConstantPool* cp = ik->constants();
*(void**)cp = find_matching_vtbl_ptr(vtbl_list, new_vtable_start,
cp);
for (int j = 0; j < ik->methods()->length(); j++) {
Method* m = ik->methods()->at(j);
*(void**)m = find_matching_vtbl_ptr(vtbl_list,
new_vtable_start, m);
}
} else {
// Array klasses
Klass* k = obj;
*(void**)k = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, k);
}
}
}
- Ioi
>>
>> - Ioi
>>
>>
>> On 12/14/2012 01:31 PM, John Rose wrote:
>>> This is really interesting.
>>>
>>> I'll bet there could be a shell script that could detect and flag
>>> unintentional vtables on RO classes, at least on Linux or Solaris.
>>> That would be enough to block JPRT submissions.
>>>
>>> -- John
>>>
>>> On Dec 14, 2012, at 12:56 PM, Coleen Phillimore
>>> <coleen.phillimore at oracle.com> wrote:
>>>
>>>> On 12/14/2012 3:46 PM, Ioi Lam wrote:
>>>>> Hi Coleen,
>>>>>
>>>>> Yes, I could find lots comments for 'how' the self-patching vtable
>>>>> works. I just couldn't find a justification for 'why' it needs to
>>>>> be done this way.
>>>>>
>>>>> The _vptr of all the Metadata objects in the CDS image are already
>>>>> fixed during dump time. They are never changed at run-time. I.e.,
>>>>> immediately after the CDS image is loaded, we have this invariant:
>>>>>
>>>>> Universe::boolArrayKlassObj()->_vptr == cds_TypeArrayKlass_vtable
>>>>> Universe::byteArrayKlassObj()->_vptr == cds_TypeArrayKlass_vtable
>>>>> Universe::charArrayKlassObj()->_vptr == cds_TypeArrayKlass_vtable
>>>>> ...
>>>>>
>>>>> where
>>>>>
>>>>> cds_TypeArrayKlass_vtable points to somewhere slightly above the
>>>>> "md" region of the mapped CDS image.
>>>>>
>>>>> If we can find out the size of the vtable for TypeArrayKlass,
>>>>> using the method I proposed below, why can't we simply do this as
>>>>> part of CDS image loading?
>>>>>
>>>>> {
>>>>> TypeArrayKlass o;
>>>>> void * real_vptr = *(void**)(&o);// == o._vptr;
>>>>> memcpy(cds_TypeArrayKlass_vtable, real_vptr, sizeof(void*) *
>>>>> num_vtable_slots_TypeArrayKlass);
>>>>> }
>>>> Yeah, that would be good except I can't figure out how that works,
>>>> but I'll take your word for it rather than trying to work it out
>>>> right now.
>>>>
>>>> You'd do this restoration in the function
>>>> restore_unshareable_info() instead of loading (lazily) though.
>>>>
>>>> Coleen
>>>>
>>>>> Thanks
>>>>> - Ioi
>>>>>
>>>>> On 12/14/2012 05:03 AM, Coleen Phillimore wrote:
>>>>>> Hi Ioi,
>>>>>>
>>>>>> There are comments about self-patching vtables in
>>>>>> memory/universe.cpp and memory/metaspaceShared.cpp and the target
>>>>>> dependent metaspaceShared_<cpu>.cpp code. You have summarized
>>>>>> how/why it works quite nicely. The main reason for vtable
>>>>>> patching is that the .text is in different places for the
>>>>>> executables which load the shared archive, so we have this code
>>>>>> to fix it up in the miscellaneous code section.
>>>>>>
>>>>>> The other thing you might have left off which people should be
>>>>>> very aware of is that metadata that is shared read-only, like
>>>>>> classes ConstMethod, Array<alltypes>, and Symbols. Adding virtual
>>>>>> function calls to these will result in them having a
>>>>>> self-patching vtable and they will no longer be read only. Do
>>>>>> not add virtual functions to these types! I hope there are enough
>>>>>> comments warning of this. I cannot think of a programmatic way
>>>>>> to disallow this.
>>>>>>
>>>>>> I have another comment for your [5]-[6] below. The cpu
>>>>>> dependent code is to create the self patching entries per
>>>>>> platform. We can't tell how long the vtable is so picked 200 for
>>>>>> historical purposes. But we still need the cpu dependent code
>>>>>> because we still need vtable patching because of point [2] and
>>>>>> it's done with macro assembler. If the macro assembler was made
>>>>>> to be a high level macro assembler, we wouldn't need cpu
>>>>>> dependent code. Knowing in advance the size of the vtable would
>>>>>> save the hard-coded constant, but that's not really a big benefit.
>>>>>>
>>>>>> An aside why 200 was picked. In the pre-permgen world, the
>>>>>> metadata in the shared space were Java objects and were inherited
>>>>>> from oopDesc. Any new virtual function added for GC in oopDesc
>>>>>> would regularly blow out the length of the vtable size. This
>>>>>> isn't the case anymore since the base class for these types is
>>>>>> Metadata and there aren't very many virtual functions there.
>>>>>> The type Klass* has a lot more, but still way under 200. A
>>>>>> sanity check in debug mode might be useful.
>>>>>>
>>>>>> thanks,
>>>>>> Coleen
>>>>>>
>>>>>> On 12/14/2012 1:17 AM, Ioi Lam wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> I am reading the CDS code and came upon the rather intriguing
>>>>>>> concept of "self patching vtables" -- i.e.,
>>>>>>> patch_klass_vtables() and friends in
>>>>>>> hotspot/src/share/vm/memory/metaspaceShared.cpp.
>>>>>>>
>>>>>>> I couldn't find any comments in the source code for the reasons
>>>>>>> for such a complicated mechanism. Here's my understanding of it.
>>>>>>> Please let me know if this is correct:
>>>>>>>
>>>>>>> [1] Objects of the Metadata types (such as Klass and
>>>>>>> ConstantPool) have vtables.
>>>>>>> In GCC this is the field <Type>::_vptr, i.e., first word in
>>>>>>> the object.
>>>>>>>
>>>>>>> [2] Addresses of the vtables and the methods may be different
>>>>>>> across JVM runs,
>>>>>>> if libjvm.so is loaded at a different base address.
>>>>>>>
>>>>>>> [3] Although all of the Metadata objects are mapped R/W in the
>>>>>>> CDS image,
>>>>>>> at load time, we don't want to rewrite _vptr of each
>>>>>>> Metadata object
>>>>>>> (to maximize sharing).
>>>>>>>
>>>>>>> [4] Therefore, we redirect _vptr to our own vtables at CDS image
>>>>>>> dump time.
>>>>>>> Then,we patch our own vtables at run time.
>>>>>>>
>>>>>>> [5] The problem with [4] is with most C++ compilers (all?),
>>>>>>> there is no
>>>>>>> easy way to tell the size of the vtable of a given type.
>>>>>>>
>>>>>>> [6] We cannot safely copy more than the size of the real vtable,
>>>>>>> because the
>>>>>>> real vtable may be at the end of the code section; reading
>>>>>>> past its end
>>>>>>> would cause the VM to crash.
>>>>>>>
>>>>>>> As a result, the current design of the 'self patching vtable' is to
>>>>>>> create a vtable that's "big enough" (currently with 200 method
>>>>>>> slots).
>>>>>>> Each slot points to a generated stub that knows the C++ type and
>>>>>>> virtual method index of the invoked method. When the stub is
>>>>>>> invoked,
>>>>>>> it will look up the real method and patch the vtable accordingly.
>>>>>>>
>>>>>>> -----
>>>>>>>
>>>>>>> If I am right that the whole reason for the self patching
>>>>>>> vtables is getting the length of the vtable, wouldn't it be much
>>>>>>> easier if we do:
>>>>>>>
>>>>>>> class InstanceKlassVTableLengthFinder: public InstanceKlass {
>>>>>>> public:
>>>>>>> virtual void ___end_of_vtable_marker();
>>>>>>> };
>>>>>>>
>>>>>>> and then just search for ___end_of_vtable_marker() from the _vptr?
>>>>>>>
>>>>>>> That way we can get rid of all the CPU dependent code related to
>>>>>>> self-patching vtables.
>>>>>>>
>>>>>>> - Ioi
>>
>
More information about the hotspot-dev
mailing list