在MSVC2017的IDE环境中对c++的虚基类函数表的研究
??疑问点,为何在获取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
*/
浙公网安备 33010602011771号