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