Determining the size of C++ vtables

Ioi Lam ioi.lam at oracle.com
Fri Feb 24 18:56:02 UTC 2017



On 2/23/17 7:55 PM, Ioi Lam wrote:
>
>
> On 2/23/17 7:47 PM, Ioi Lam wrote:
>> Hi,
>>
>> I am working on https://bugs.openjdk.java.net/browse/JDK-8005165 (Remove
>> CPU-dependent code in self-patching vtables), I need a way find out 
>> the size
>> of a C++ vtable. I ended up doing this:
>>
>>
>> // Objects of the Metadata types (such as Klass and ConstantPool) 
>> have C++ vtables.
>> // (In GCC this is the field <Type>::_vptr, i.e., first word in the 
>> object.)
>> //
>> // Addresses of the vtables and the methods may be different across 
>> JVM runs,
>> // if libjvm.so is dynamically loaded at a different base address.
>> //
>> // To ensure that the Metadata objects in the CDS archive always have 
>> the correct vtable:
>> //
>> // + at dump time:  we redirect the _vptr to point to our own vtables 
>> inside
>> //                  the CDS image
>> // + at run time:   we clone the actual contents of the vtables from 
>> libjvm.so
>> //                  into our own tables.
>> //
>> // To determine the size of the vtable for each type, we use the 
>> following
>> // trick by declaring 2 subclasses:
>> //
>> //   class CppVtabTesterA: public InstanceKlass {
>> //          virtual int   last_virtual_method() {return 1;}
>> //   };
>> //   class CppVtabTesterB: public InstanceKlass {
>> //          virtual void* last_virtual_method() {return NULL};
>> //   };
>> //
>> // CppVtabTesterA and CppVtabTesterB's vtables have the following 
>> properties:
>> // - Their size (N+1) is exactly one more than the size of 
>> InstanceKlass's vtable (N)
>> // - The first N entries have are exactly the same as in 
>> InstanceKlass's vtable.
>> // - Their last entry is different.
>> //
>> // So to determine the value of N, we just walk CppVtabTesterA and 
>> CppVtabTesterB's tables
>> // and find the first entry that's different
>>
>>
>> Could anyone comment if this is acceptable? I know it's not 100% 
>> portable (C++ doesn't
>> specify where to find the vtable, or what's inside), but my 
>> assumptions is the same as
>> the existing code. I.e., _vptr is a pointer located at offset 0 of 
>> the object, and it
>> points to a one-dimensional array.
>>
>> So at least it's not any worse than before?
>>
>> Thanks
>> - Ioi
>>
> By the way, I first tried having only a single "tester" class and walk 
> the vtable to look for &last_virtual_method, but the C++ compiler told 
> me that taking the address of a non-static function is not allowed 
> ..... so I ended up creating two tester classes and checking their 
> differences.
>
>

Just to clarify the above comments: taking a "reference" to a virtual 
function is allowed, but it's not specified what the "reference" is. 
With gcc, it's a 16-bit value:

    #include <stdio.h>

    class CppVtabTester {
    public:
       virtual int func1() {return 1;}
       int func2()         {return 2;}
    };

    int main() {
       union {
         int (CppVtabTester::*ptr)();
         void* val[2];
       } a, b;

       a.ptr = &CppVtabTester::func1;
       b.ptr = &CppVtabTester::func2;

       printf("%d\n", (int)sizeof(a.ptr));
       printf("ref: %p %p\n", a.val[0], a.val[1]);
       printf("ref: %p %p\n", b.val[0], b.val[1]);

       return 0;
    }

    ioimac ~/tmp$ gcc t.cpp
    ioimac ~/tmp$ ./a.out
    16
    ref: 0x1 0x0
    ref: 0x102d93f70 0x0

Unfortunately, the reference for a virtual function is just its vtable 
index :-(

Taking the "address" of a virtual function in some versions of gcc will 
give the real address (with a warning), and some versions of gcc will 
disallow it:

    printf("ptr: %p\n", (void*)(&CppVtabTester::func1));
    .....
    t.cpp:21:35: error: cannot cast from type 'int (CppVtabTester::*)()'
    to pointer
           type 'void *'
       printf("ptr : %p\n", (void*)(&CppVtabTester::func1));








More information about the hotspot-dev mailing list