在MSVC2017的IDE環境中對c++的虛基類函式表的研究
阿新 • • 發佈:2021-01-11
??疑問點,為何在獲取VBTable的偏移地址時,在x64中需要使用 int**指標型別呢, 莫非VBTable的實現,在VS中使用的 int[] 型別,而不是跟著x86,x64改變?
// vbptr.cpp : 此檔案包含 "main" 函式。程式執行將在此處開始並結束。 // #include <iostream> #include <stdint.h> struct VB { virtual void f1() { std::cout << "VB b1" << std::endl; }; int vb; }; class V1 :virtual public VB { virtual void f1() {}; virtual void f2() {}; int v1; }; class V2 :virtual public VB { virtual void f1() {}; virtual void f2() {}; int v2; }; class V3 :public V1, public V2 { virtual void f1() { std::cout << "V3 b1" << std::endl; }; int v3; }; /* 記憶體佈局 class V3 size(72): +--- 0 | +--- (base class V1) 0 | | {vfptr} 8 | | {vbptr} 16 | | v1 | | <alignment member> (size=4) | +--- 24 | +--- (base class V2) 24 | | {vfptr} 32 | | {vbptr} 40 | | v2 | | <alignment member> (size=4) | +--- 48 | v3 | <alignment member> (size=4) +--- +--- (virtual base VB) 56 | {vfptr} 64 | vb | <alignment member> (size=4) +--- V3::$vftable@V1@: | &V3_meta | 0 0 | &V1::f2 V3::$vftable@V2@: | -24 0 | &V2::f2 V3::$vbtable@V1@: 0 | -8 1 | 48 (V3d(V1+8)VB) V3::$vbtable@V2@: 0 | -8 1 | 24 (V3d(V2+8)VB) V3::$vftable@VB@: | -56 0 | &V3::f1 關於內部佈局中VTable的說明 vtable的裡面儲存的有兩個偏移量, 第一個偏移量表示,從從vbtable所在的地址+偏移量為直接繼承類的例項地址 從vbtable所在的地址,比如在0x000008 加上【-8,負號表示方向】個子節 那麼V1【直接繼承類】的地址也就是虛擬函式表的地址則為 0x000008-8 = 0x00000; 第二個偏移量表示,從從vbtable所在的地址+偏移量為直接繼承類的直接繼承類的例項地址 比如 從vbtable所在的地址,比如在0x000008 加上48個子節,那麼就是vbtable的實際地址 例如上圖 (virtual base VB)的地址在直接繼承類的直接繼承類的偏移地址為56子節,如果取得例項地址,就可以計算偏移地址了 */ typedef void(*F1)(); using F11 = void(*)(void); int main() { V3 v; // std::cout << "vf address"<< ((int**)&v + 0) << std::endl; std::cout << "vb address" << ((int**)&v + 1) << std::endl; #ifdef _X86_P int v1_vbtable = (int)((int**)&v + 1); //call 獲取偏移量 std::cout<< *(*((int**)&v + 1)+0) <<std::endl; int vb_v1_index2 =(int)(*(*((int**)&v + 1) + 1)); std::cout<< vb_v1_index2 <<std::endl; //std::cout << std::hex<< *((*(int**)&v + 1) + 1) << std::endl; int vb_in_v3 = v1_vbtable+ vb_v1_index2; //獲取VB的虛擬函式表的位置 ((F11)(**(int**)vb_in_v3))(); #else std::cout << "vf address" << ((uint64_t**)&v + 0) << std::endl; std::cout << "vb address" << ((uint64_t**)&v + 1) << std::endl; uint64_t v1_vbtable = (uint64_t)((uint64_t**)&v + 1); //??疑問點,為何在獲取VBTable的偏移地址時,在x64中需要使用 int**指標型別呢, 莫非VBTable的實現,在VS中使用的 int[] 型別,而不是跟著x86,x64改變? //call 獲取偏移量,為什麼偏移地址是int**取出來,使用uint64_t**就會出錯呢 std::cout << *(*((int**)&v + 1) + 0) << std::endl; uint64_t vb_v1_index2 = (*(*((int**)&v + 1) + 1)); std::cout<<std::hex << vb_v1_index2 << std::endl; //std::cout << std::hex<< *((*(int**)&v + 1) + 1) << std::endl; uint64_t vb_in_v3 = v1_vbtable + vb_v1_index2; //獲取VB的虛擬函式表的位置 ((F11)(*(uint64_t*)(*(uint64_t*)vb_in_v3)))(); #endif std::cout << "Hello World!\n"; std::cin.get(); } /* 輸出結果 x64 vf address000000B4D5D6FC30 vb address000000B4D5D6FC38 vf address000000B4D5D6FC30 vb address000000B4D5D6FC38 -8 30 V3 b1 x86 vf address0117F8E4 vb address0117F8E8 vf address0117F8E4 vb address0117F8E8 -4 18 V3 b1 */