Visual C++ 2015(x86)のアセンブリ出力で混乱します。Visual C++で仮想テーブルのアセンブリ出力に混乱2015
私はVCの仮想テーブルのレイアウトを知りたいので、仮想関数を使って次の単純なクラスを記述します。
#include <stdio.h>
struct Foo
{
virtual int GetValue()
{
uintptr_t vtbl = *(uintptr_t *)this;
uintptr_t slot0 = ((uintptr_t *)vtbl)[0];
uintptr_t slot1 = ((uintptr_t *)vtbl)[1];
printf("vtbl = 0x%08X\n", vtbl);
printf(" [0] = 0x%08X\n", slot0);
printf(" [1] = 0x%08X\n", slot1);
return 0xA11BABA;
}
};
extern "C" void Check();
int main()
{
Foo *pFoo = new Foo;
int x = pFoo->GetValue();
printf("x = 0x%08X\n", x);
printf("\n");
Check();
}
とレイアウトを確認するため、私は(魔法の名前はvtab.cpp
のアセンブリ出力vtab.asm
から来ている、とFoo::GetValue
のマングルされたバージョンである)のアセンブリ機能を実装しています。
.model flat
extern _printf : proc
extern [email protected]@@UAEHXZ : proc
.const
FUNC_ADDR db "Address of Foo::GetValue = 0x%08X", 10, 0
.code
_Check proc
push ebp
mov esp, ebp
push offset [email protected]@@UAEHXZ
push offset FUNC_ADDR
call _printf
add esp, 8
pop ebp
ret
_Check endp
end
次に、私はコンパイルして実行します。
ml /c check.asm
cl /Fa vtab.cpp check.obj
vtab
私のコンピュータで次のように出力してください。
vtbl = 0x00FF2174
[0] = 0x00FE1300
[1] = 0x6C627476
x = 0x0A11BABA
Address of Foo::GetValue = 0x00FE1300
これは明らかに、仮想関数GetValue
は、仮想テーブルの0でオフセットされることを示します。しかし、vtab.cpp
のアセンブリ出力は、オフセット4にあるGetValue
を暗示しているようです(次のコメントは3セミコロンから始まります)。
; COMDAT [email protected]@[email protected]
CONST SEGMENT
[email protected]@[email protected] DD FLAT:[email protected]@[email protected] ; Foo::`vftable'
DD FLAT:[email protected]@@UAEHXZ ;;; GetValue at offset 4
CONST ENDS
; Function compile flags: /Odtp
; COMDAT [email protected]@[email protected]
_TEXT SEGMENT
_this$ = -4 ; size = 4
[email protected]@[email protected] PROC ; Foo::Foo, COMDAT
; _this$ = ecx
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], OFFSET [email protected]@[email protected] ;;; Init ptr to virtual table
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 0
[email protected]@[email protected] ENDP ; Foo::Foo
お返事ありがとうございます!
更新
@Hansアンパッサンはこれはバグのようです。 I ml /c
アセンブリー出力vtab.asm
(記号をいくつか削除して)check.obj
にリンクしてexe vtab2.exe
を取得します。しかし、vtab2.exe
は正しく動作しません。それから私は、次のコード
; COMDAT [email protected]@[email protected]
CONST SEGMENT
[email protected]@[email protected] DD FLAT:[email protected]@[email protected] ; Foo::`vftable'
DD FLAT:[email protected]@@UAEHXZ
CONST ENDS
はvtab3.exe
を取得するために再び
; COMDAT [email protected]@[email protected]
CONST SEGMENT
__NOT_USED_ DD FLAT:[email protected]@[email protected] ; Foo::`vftable'
[email protected]@[email protected] DD FLAT:[email protected]@@UAEHXZ
CONST ENDS
とml
とlink
に変更します。今すぐvtab3.exe
が実行され、vtab.exe
のような出力が生成されます。
いいえ、vテーブルには1つのエントリしかありません。 Just Foo :: GetValue()。 ?_ R4Foo @@ 6B @のエントリは、vテーブルに属していません。これはRTTIオブジェクトロケータです。より明白にするために/ GR-でコンパイルしてください。また、/ d1reportAllClassLayoutを使用すると、レイアウトに関する詳細をコンパイラに伝えることができます。 –
私はFoo :: GetValue()がvテーブルにあることを理解します。私の混乱はなぜ 'mov DWORD PTR [eax]、OFFSET ?? _ 7Foo @@ 6B @'が 'mov DWORD PTR [eax]、OFFSET ?? _ 7F @ @ 6B @ + 4'でない理由です。とにかく、RTTIオブジェクトは '?? _ 7Foo @@ 6B @'というラベルで始まり、その後にFoo :: GetValue()が続きます。 –
'GetValue'のアセンブリコードは何ですか? – Jester