2016-11-27 98 views
11

我想象這是特定於實現的,但對ARMv7,arm64,和x86_64構建使用的libstdC++和的libC++(GCC或鐺),似乎的vtables總是有填充的8個字節(16在64位)開頭,並取出一個虛函數表通常看起來是這樣的:爲什麼vtables有sizeof(void *)* 2個字節的0x00填充?

ldr.w r0, <address of vtable> 
adds r0, 0x8 
str r0, [r1] ; where r1 is the instance 

而虛函數表看起來是這樣的:

vtable+0x00: 0x00000000 
vtable+0x04: 0x00000000 
vtable+0x08: 0xfirstfunc 
vtable+0x0c: 0xsecondfunc 
vtable+0x10: 0xthirdfunc 

等等

任何人都知道這是爲什麼?

+0

檢查派生類的表。我敢打賭,前面沒有*零。 – wallyk

+0

不,每一個我所見過的虛函數表,還有在前面的0。派生類或不,Vtable總是+ 8. –

回答

10

在開始處應該只有一個零(大小爲void *)(除非在沒有RTTI的情況下編譯)。它實際上不一定是,但它通常是,我會稍後解釋。

V表爲(至少GCC起源)的ABI看起來像:

class_offset 
type_info 
first_virtual_function 
second_virtual_function 
etc. 

的TYPE_INFO可以是NULL0)的情況下的代碼未經RTTI編譯。

上面的class_offset解釋了爲什麼你在那裏看到零。這是擁有班級中的班級抵消。即具有:

class A { virtual meth() {} }; 
class B { virtual meth() {} }; 
class C: public A, public B { virtual meth() {} }; 

將導致進入主類CA開始0位置C類和B內開始在C類內的位置4(或8)。

指針在那裏,所以你可以從任何類指針找到指向擁有對象的指針。因此,對於任何「主」類,它總是0,但對於B類虛擬表在C有效的上下文中將是-4-8。實際上,你需要檢查的虛函數表爲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 

在早期編譯器被用來計算實際指向所屬的偏移類在調用方法之前。但是,因爲它直接在所屬的類調用方法時放緩的情況下,現代的編譯器而產生存根其中減去直接偏移並跳轉到主要實施方法(因爲你可以從方法名猜 - 注意8 ):

_ZThn8_N1C4methEv: 
    subq $8, %rdi 
    jmp  _ZN1C4methEv 
+0

嗯,非常感謝。這也有助於我理解爲什麼在多重繼承的情況下存在非虛擬thunk。不管怎樣,也許是在我拆開有2無效* V表上面的二進制文件的情況下是不存在RTTI的原因是什麼? –

+0

@RyanTerry:是的,這可能是原因。在情況下,代碼編譯而不RTTI,該結構被保持,但類型信息構件是NULL。我更新了答案。 –

相關問題