I know this is compiler/ABI dependent, not necessarily standardized, etc. I've always assumed, from what I've read in several places (e.g. an answer here or the example in wikipedia), that a typical thing a compiler does is having a pointer to the vtable of Class
at the start of an object of type Class
.
I'm now debugging and instrumenting a function that receives a Class*
parameter. Class
has virtual functions. I want to determine if it belongs to a class that I care about. I probably also want to do other things with it, so I'm trying to get a good understanding.
I look at the disassembly in Ghidra, and I find this about the vtable of that class (paste is coming from an example I wrote, but it's equivalent in a real binary that I want to RE):
**************************************************************
* vtable for DerivedClass *
**************************************************************
_ZTV12DerivedClass XREF[3]: Entry Point(*),
DerivedClass::vtable operator():001039d1(*),
_elfSectionHeaders::00000650(*)
00107c70 00 00 00 ptrdiff_
00 00 00
00 00
00107c70 [0] 0h
00107c78 88 7c 10 addr DerivedClass::typeinfo =
00 00 00
00 00
PTR_ARRAY_00107c80 XREF[2]: operator():001039d8(*),
operator():001039dc(*)
00107c80 94 42 10 addr[1]
00 00 00
00 00
00107c80 94 42 10 00 00 addr DerivedClass::someVirt [0] XREF[2]: operator():001039d8(*),
00 00 00 operator():001039dc(*)
And here is where my surprise is:
- I get the address of
_ZTV12DerivedClass
(e.g. via Frida,Module.findExportByName(null, "_ZTV12DerivedClass")
or in GDB,info address _ZTV12DerivedClass
). - I get the address of the vtable of the object (it's 64 bits, so 8 first bytes of the object), again, in the debugger or with Frida.
- I compare the two, and I get that they are 16 bytes away, the size of 2 pointers: the pointer in the object instance is not to the start of the vtable, but at the start of the array of pointers to the virtual functions!
This is not Earth-shattering in order to compare the pointers, of course. Just add 16. I've seen explanations of what those first two pointers should be. But I have some doubts:
- Is it reliable to assume that the difference of 16 is going to be consistent in all classes in the same binary?
- Is this common in more compilers?
- Do the two positions in the table (the very start of it and the start of the array of pointers to the virtual functions) have any naming convention? I find it surprising that there is a pointer to something and it's not the start of the vtable as found on the disassembly.
- Could be some historical legacy? If C++ first had virtual functions but without virtual inheritance or RTTI, it might explain that those two extra pointers were added to the top to not change some other assumptions.