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