Learn C++ virtual table

探究一下 C++ 虚表继承的实现。

class 关系

设计一个菱形继承关系,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        +------------+                                                   
| |
| Base |
| |
+------------+
/ \
/ \
/ \
/ \
+------------+ +-------------+
| | | |
| LeftDerive | | RightDerive |
| | | |
+------------+ +-------------+
\ /
\ /
\ /
+------------+
| |
| Boss |
| |
+------------+

class 的具体实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
class Base{
public:
Base(){
BasePriv2=20;
BasePriv1=10;
BaseProt1=1;
}
virtual int FunctionA()=0;
virtual int FunctionB(){
puts("Base::FunctionB");
return 0;
};
virtual int FunctionC()=0;
virtual int FunctionD(){
puts("Base::FunctionD");
return 0;
};


int NonVirtual(){
puts("Base::NonVirtual");
counter=1;
return 0;
}

int count(){return counter;};

protected:
int counter=0;
int BaseProt1;
private:
int BasePriv1;
int BasePriv2;
};

class LeftDerive: public Base{
public:
LeftDerive(){
LeftProt2=2;
LeftProt1=1;
LeftPriv3=3;
}
int FunctionA() override{
printf("Left DeriveA\n");
counter=4;
return 0;
}

virtual int LeftV1(){
puts("Left V1");
return 0;
}
virtual int LeftV2()=0;


int FunctionB() override{
printf("Left B\n");
return 0;
}

int FunctionC() override{
printf("Left C\n");
return 0;
}

int FunctionD() override{
printf("Left D\n");
return 0;
}

int NonVirtual(){
puts("LeftDerive::NonVirtual");
counter=3;
return 0;
}

protected:
int LeftProt1;
int LeftProt2;
private:
int LeftPriv3;


};


class RightDerive: public Base{
public:
RightDerive(){
RightProt2=2;
RightProt1=1;
RightPriv4=4;
RightPriv5=5;
RightPriv6=6;
RightProt3=3;
}
int FunctionA() override {
printf("Right Derive\n");
counter=5;
return 0;
}

// Right B is not derived
// int FunctionB() override{
// printf("Right B\n");
// return 0;
// }

int FunctionC() override{
printf("Right C\n");
return 0;
}

int FunctionD() override{
printf("Right D\n");
return 0;
}


int NonVirtual(){
puts("RightDerive::NonVirtual");
counter=6;
return 0;
}

virtual int RightV1(){
puts("Right V1");
return 0;
}

virtual int RightV2(){
puts("Right V2");
return 0;
}

protected:
int RightProt1;
int RightProt2;
int RightProt3;
private:
int RightPriv4;
int RightPriv5;
int RightPriv6;


};

class Boss:public LeftDerive, public RightDerive{
public:
Boss(){
printf("Boss() start,this=%p\n",this);

Init1=100;
Init6=600;
Init5=500;
Init2=200;
Init3=300;
Init4="400";

printf("Boss() end\n");

}
int FunctionA(){
printf("Boss Derive\n");


return 0;
}
int NonVirtual(){
puts("Boss::NonVirtual");
return 0;
}

virtual int BossV1(){
puts("Boss V1");
return 0;
}

int RightV1(){
puts("Boss:RightV1");
return 0;
}

int LeftV2(){
puts("Boss:LeftV2");
return 0;
}
protected:
int Init0;
int Init1;
int Init2;
short Init3;
const char * Init4;
double Init5;
char Init6;
};
int main(){

Boss boss=Boss();
Boss * pBoss = &boss;

pBoss->FunctionA();

pBoss->BossV1();
pBoss->NonVirtual();


pBoss->LeftV1();
pBoss->LeftV2();

pBoss->RightV1();
pBoss->RightV2();

return 0;
}

Base, LeftDerive, RightDerive 均存在虚函数、纯虚函数,同时加入了成员变量,方便观察继承的变量内存排布。

编译、反编译

使用 Clang 编译,使用 IDA Pro 7.5 反编译。

反编译 Boss 的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void __fastcall Boss::Boss(Boss *this)
{
LeftDerive::LeftDerive(this);

RightDerive::RightDerive((Boss *)((char *)this + 40));

*(_QWORD *)this = off_100004038; // vftable of LeftDerive

*((_QWORD *)this + 5) = off_100004088; // vftable of RightDerive

// Boss's constructor
printf("Boss() start,this=%p\n", this);
*((_DWORD *)this + 23) = 100;
*((_BYTE *)this + 120) = 88;
*((_QWORD *)this + 14) = 0x407F400000000000LL;
*((_DWORD *)this + 24) = 200;
*((_WORD *)this + 50) = 300;
*((_QWORD *)this + 13) = "400";
printf("Boss() end\n");
}
  1. 按继承顺序,调用父类的构造函数(LeftDerive,RightDerive)

    可以发现,相应的构造函数的参数不同,是每个父类对应的内存空间。

    LeftDerive 的空间是 this,RightDerive 的空间是 this + 40

  2. 将每一个父类对应的虚表,覆盖到父类对应的内存空间的开始处(LeftDerive::vftable, RightDerive::vftable)

  3. 如果父类对应的内存空间不是 this,对于子类重写的虚函数,编译器会在子类的虚表加入这个虚函数,同时,在对应父类的虚表添加跳板函数(IDA 中显示的 non-virtual thunk),将传入的 this 减去父类对应内存空间的偏移,再调用真正的虚函数。

    1
    2
    3
    4
    __int64 __fastcall `non-virtual thunk to'Boss::RightV1(Boss *this)
    {
    return Boss::RightV1((Boss *)((char *)this - 40));
    }

    子类没有重写的虚函数,编译器直接找到对应的虚表进行调用。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!