我想这是特定于实现的,但是对于使用libstdc ++和libc ++(gcc或clang)的armv7,arm64和x86_64构建,似乎vtable在开头总是有8个字节(64位为16)的填充vtable通常看起来像这样:
ldr.w r0, <address of vtable>
adds r0, 0x8
str r0, [r1] ; where r1 is the instance
Run Code Online (Sandbox Code Playgroud)
而vtable看起来像这样:
vtable+0x00: 0x00000000
vtable+0x04: 0x00000000
vtable+0x08: 0xfirstfunc
vtable+0x0c: 0xsecondfunc
vtable+0x10: 0xthirdfunc
Run Code Online (Sandbox Code Playgroud)
等等...
有谁知道这是为什么?
Zby*_*000 10
开头应该只有一个零(大小为void*)(除非没有RTTI编译).它实际上不一定是,但它通常是,我稍后会解释.
用于(至少gcc发起的)ABI的VTable看起来像:
class_offset
type_info
first_virtual_function
second_virtual_function
etc.
Run Code Online (Sandbox Code Playgroud)
如果代码是在没有RTTI的情况下编译的,则type_info可以是NULL(0).
在class_offset上面解释了为什么你看到零那里.这是拥有类中的类偏移量.即:
class A { virtual meth() {} };
class B { virtual meth() {} };
class C: public A, public B { virtual meth() {} };
Run Code Online (Sandbox Code Playgroud)
将导致主类C,A从类中的位置开始并从0类中的位置(或)开始.CB48C
指针在那里,所以你可以从任何类指针找到指向拥有对象的指针.因此,对于任何"主"类,它将始终是0但对于B类虚拟表在C上下文中有效,它将是-4或-8.实际上你需要检查VTable for C(下半部分),因为编译器通常不会单独生成VTable:
_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
Run Code Online (Sandbox Code Playgroud)
在早期的编译器中,偏移量用于在调用方法之前计算指向拥有类的实际指针.但是当它直接在拥有类上调用方法时减慢了这种情况,现代编译器会生成存根,它会直接减去偏移量并跳转到方法的主要实现(正如您可以从方法名称中猜到的那样 - 注意8) :
_ZThn8_N1C4methEv:
subq $8, %rdi
jmp _ZN1C4methEv
Run Code Online (Sandbox Code Playgroud)