2016-11-27 3 views
11

これは実装固有のものだと思いますが、libstdC++とlibC++(gccまたはclang)を使ってarmv7、arm64、およびx86_64をビルドすると、vtablesは常に先頭に8バイト(64ビットで16)そして、のvtableを取得することは、通常、このようなものになります。vtablesにsizeof(void *)* 2バイトの0x00埋め込みがあるのはなぜですか?

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

をそしてvtableのは、このようなものになります。これはなぜ

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

等...

誰もが知っているの?

+0

派生クラスのテーブルを調べます。私は正面にゼロではない*賭ける。 – wallyk

+0

いいえ、私が見たすべてのvtableのために、正面に0があります。派生クラスかどうか、vtableは常に+ 8です。 –

答えて

10

RTTIなしでコンパイルした場合を除き、冒頭にゼロ(size void *)の値が1つだけ存在する必要があります。実際にはそうである必要はありませんが、一般的には後で説明します。

仮想テーブルは、のために(少なくともGCCが発信)のABIは、次のようになりますTYPE_INFOコードがRTTIなしでコンパイルした場合にNULL0)とすることができる

class_offset 
type_info 
first_virtual_function 
second_virtual_function 
etc. 

上記のclass_offsetは、そこにゼロが表示される理由を説明しています。これは、所有するクラス内のクラスオフセットです。私。有する:

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

クラスC内に位置4(又は8)から始まるクラスCB内に位置0始まるメインクラスCAに生じるであろう。

ポインタがあるので、クラスポインタから所有オブジェクトへのポインタを見つけることができます。したがって、「メイン」クラスの場合は常に0になりますが、コンテキストで有効なBクラス仮想テーブルの場合は-4または-8になります。あなたは、実際には通常、別々のvtableを生成しないコンパイラとしてCのための仮想テーブル(後半)をチェックする必要があります。以前のコンパイラで

_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

ああ、ありがとう。これも多種の継承の場合に非仮想サンクが存在する理由を理解するのに役立ちます。とにかく、おそらく、バイナリの場合、vtableの上に2つのvoid *があるということは、RTTIがないということです。 –

+0

@RyanTerry:はい、その理由が考えられます。コードがRTTIなしでコンパイルされる場合、構造体は保持されますが、型情報メンバはNULLです。私は答えを更新しました。 –

関連する問題