Determining the size of C++ vtables (original) (raw)

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 ::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 lastvirtualmethod() {return 1;} // }; // class CppVtabTesterB: public InstanceKlass { // virtual void* lastvirtualmethod() {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 &lastvirtualmethod, 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