The magic of self-patching vtable

Coleen Phillimore coleen.phillimore at oracle.com
Fri Dec 14 14:10:01 PST 2012


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

>
> - 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