I imagine this is implementation-specific, but for armv7, arm64, and x86_64 builds using libstdc++ and libc++ (gcc or clang), it seems that vtables always have 8 bytes (16 on 64-bit) of padding at the beginning, and fetching a vtable usually looks something like this:
ldr.w r0, <address of vtable>
adds r0, 0x8
str r0, [r1] ; where r1 is the instance
And the vtable looks something like this:
vtable+0x00: 0x00000000
vtable+0x04: 0x00000000
vtable+0x08: 0xfirstfunc
vtable+0x0c: 0xsecondfunc
vtable+0x10: 0xthirdfunc
etc...
Anyone know why this is?
There should be only one zero (of size void *) at the beginning (unless compiled without RTTI). It actually doesn't have to be but it commonly is, I'll explain later.
The VTable for (at least gcc originated) ABIs looks like:
class_offset
type_info
first_virtual_function
second_virtual_function
etc.
The type_info can be NULL
(0
) in case the code was compiled without RTTI.
The class_offset
from above explains why you see zero there. This is the class offset within the owning class. I.e. having:
class A { virtual meth() {} };
class B { virtual meth() {} };
class C: public A, public B { virtual meth() {} };
would result into main class C
, A
starting at position 0
within class C
and B
starting at position 4
(or 8
) within class C
.
The pointer is there so you can find from any class pointer the pointer to the owning object. So for any "main" class it will be always 0
but for the B
class virtual table valid in C
context it will be -4
or -8
. You actually need to check the the VTable for C (the second half) as the compiler usually doesn't generate the VTables separately:
_ZTV1C:
// VTable for C and A within C
.quad 0
.quad _ZTI1C
.quad _ZN1CD1Ev
.quad _ZN1CD0Ev
.quad _ZN1C4methEv
// VTable for B within C
.quad -8
.quad _ZTI1C
.quad _ZThn8_N1CD1Ev
.quad _ZThn8_N1CD0Ev
.quad _ZThn8_N1C4methEv
In the earlier compilers the offset was used to compute the real pointer to the owning class before invoking the method. But as it slowed down the cases when invoking the method directly on the owning class, the modern compilers rather generate stub which subtracts the offset directly and jumps to the main implementation of the method (as you can guess from the method name - note the 8
):
_ZThn8_N1C4methEv:
subq $8, %rdi
jmp _ZN1C4methEv
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With