虚函数继承与虚函数表-汇编码分析
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:https://www.equestionanswers.com/cpp/vptr-and-vtable.php
虚函数继承是如何继承的呢?
我们听说的虚函数表是怎么回事?
虚函数表指针是如何变化与赋值呢?
虚函数表存储的内容格式是什么样子的?
下面通过汇编码分析,来看一看
1. 分析的代码
先写一些继承类的测试代码,来确定一下汇编内容的范围:
继承关系 child->parent->grandparent,关联代码实现:
class grandparent
{
public:grandparent() {printf("construct of grandparent\\n");}virtual ~grandparent() {printf("virtual destructor of grandparent\\n");}virtual void func1() = 0;virtual void func2() = 0;
};class parent : public grandparent
{
public:parent() {printf("construct of parent\\n");}virtual ~parent() {printf("virtual destructor of parent\\n");}virtual void func1() {printf("virtual parent.func1\\n");}virtual void func2() {printf("virtual parent.func2\\n");}
};
class child : public parent{
public:child() {printf("construct of child\\n");}virtual ~child() {printf("virtual destructor of child\\n");}virtual void func1() {printf("virtual child.func1\\n");}virtual void func2() {printf("virtual child.func2\\n");}
};
函数测试使用下面的方法进行测试:
int main()
{parent* p = new child();p->func1();p->func2();//p->~parent();//p->~parent();//p->func1();//p->func2();delete p;return 0;
}
2. 三个类的共同特点
三个类的共同特点:
- 没有成员变量,对象上只会有一个vptr虚函数表指针,sizeof对象的话,大小等于一个指针的大小sizeof(void*);
- 析构函数为虚函数,这样就可以借由父指针来删除创建的子对象;
- vfptr的位置缺省在创建对象的首个内存位置,所以*this,就可以获取到vfptr的指针;
- 类代码翻译的最后部分,都有一个classname::
vector deleting destructotr
汇编代码段封装,封装了对该类的析构函数调用,还封装了基于参数确定是否调用delete this调用; - 名词解释
vfptr: virtual function pointer 虚函数表指针/虚函数指针
vftable: virtual funciton table 虚函数表
3. grandparent类分析
grandparent的函数汇编码:
- 可以看到两个纯虚函数func1, func2并未生成翻译源码;
- 可以看到构造函数中,先把grandparent::'vftable’地址加载到了rcx, 并放入到*this指向的vfptr内容上;
- 可以看到析构函数中,也做了一遍动作,先把grandparent::'vftable’地址放入到*this指向的vfptr内容上;
- 基于2/3可以看出,这个虚函数表地址是要在构造函数,析构函数中都要对vfptr属性重新赋值的。
- 最下面的grandparent::
vector deleting destructotr
封装了对析构函数的调用,还封装了基于参数确定是否调用delete this调用;
7: class grandparent8: {9: public:10: grandparent() {
00007FF7B28610C0 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B28610C5 48 83 EC 28 sub rsp,28h
00007FF7B28610C9 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B28610CE 48 8D 0D 23 23 00 00 lea rcx,[grandparent::`vftable' (07FF7B28633F8h)]
00007FF7B28610D5 48 89 08 mov qword ptr [rax],rcx 11: printf("construct of grandparent\\n");
00007FF7B28610D8 48 8D 0D D9 21 00 00 lea rcx,[string "construct of grandparent\\n" (07FF7B28632B8h)]
00007FF7B28610DF E8 7C FF FF FF call printf (07FF7B2861060h) 12: }
00007FF7B28610E4 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B28610E9 48 83 C4 28 add rsp,28h
00007FF7B28610ED C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B28610EE CC int 3
00007FF7B28610EF CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------13: virtual ~grandparent() {
00007FF7B28610F0 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B28610F5 48 83 EC 28 sub rsp,28h
00007FF7B28610F9 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B28610FE 48 8D 0D F3 22 00 00 lea rcx,[grandparent::`vftable' (07FF7B28633F8h)]
00007FF7B2861105 48 89 08 mov qword ptr [rax],rcx 14: printf("virtual destructor of grandparent\\n");
00007FF7B2861108 48 8D 0D C9 21 00 00 lea rcx,[string "virtual destructor of grandpare@"... (07FF7B28632D8h)]
00007FF7B286110F E8 4C FF FF FF call printf (07FF7B2861060h) 15: }
00007FF7B2861114 48 83 C4 28 add rsp,28h 15: }
00007FF7B2861118 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B2861119 CC int 3
00007FF7B286111A CC int 3
00007FF7B286111B CC int 3
00007FF7B286111C CC int 3
00007FF7B286111D CC int 3
00007FF7B286111E CC int 3
00007FF7B286111F CC int 3
grandparent::`vector deleting destructor':
00007FF7B2861120 89 54 24 10 mov dword ptr [rsp+10h],edx
00007FF7B2861124 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861129 48 83 EC 28 sub rsp,28h
00007FF7B286112D 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B2861132 E8 B9 FF FF FF call grandparent::~grandparent (07FF7B28610F0h)
00007FF7B2861137 8B 44 24 38 mov eax,dword ptr [rsp+38h]
00007FF7B286113B 83 E0 01 and eax,1
00007FF7B286113E 85 C0 test eax,eax
00007FF7B2861140 74 0F je grandparent::`scalar deleting destructor'+31h (07FF7B2861151h)
00007FF7B2861142 BA 08 00 00 00 mov edx,8
00007FF7B2861147 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B286114C E8 47 03 00 00 call operator delete (07FF7B2861498h)
00007FF7B2861151 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B2861156 48 83 C4 28 add rsp,28h
00007FF7B286115A C3 ret
00007FF7B286115B CC int 3
00007FF7B286115C CC int 3
00007FF7B286115D CC int 3
00007FF7B286115E CC int 3
00007FF7B286115F CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------16: virtual void func1() = 0;17: virtual void func2() = 0;18: };
4. parent类分析
Parent的函数汇编码:
- 可以看到构造函数中先调用grandparent::grandparant构造函数;然后把parent::'vftable’地址加载到了vfptr上;
这样在grandparent构造函数中赋值grandparent::'vftable’的vfptr属性,就会被改到parent::‘vftable’,然后再往下执行了。 - 可以看到析构函数中,先把parent::'vftable’地址放入到*this指向的vfptr内容上;然后再往下执行;
parent::parent自身析构函数执行后并未直接退出,还附加了汇编代码段,调用grandparent::grandparent父类的析构函数; - 基于1、2可以看出,这个虚函数表地址vfptr是要在构造函数,析构函数中都要重新赋值parent::'vftable’的,以调用自身实现的方法。
- 基于1、2还可以看出,构造函数是,先调父类构造(vfptr->父类vftable),再调自身构造(vfptr->自身vftable);
析构函数是,先调自身析构(vfptr->自身vftable),再调父类析构(vfptr->父类vftable); - func1,func2的虚函数实现有编译出来;编译出的内容里面是没有vfptr的处理。
- 基于上面的推到可以看出,如果在parent的构造函数,析构函数中调用func1/func2的话,可以调用到parent中自身的实现;
同时注意,如果在其它函数中,vfptr指向不变时,不会有这个效果;vfptr构造后是指向创建类的,以那个类的vftable为准发生调用。
也可以确定一个点:在构造函数,析构函数中,如果调用虚函数,只能调用到自身vtable中指向的虚函数(自身或父类实现的虚函数),调用不到子类实现的虚函数。 - 基于4可以推导一个奇特的问题,如果手动调用了析构的话,vfptr最终指向的是顶层类的vftable:
delete时,就不会再调用子类的析构了;
虚函数调用时,就只在最顶层的找定义了,如果没定义,会崩溃;例如代码 ~parent析构后,vfptr指向grandparent::vftable
.
p->~parent();p->~parent();p->func1();p->func2();
汇编码详情:
class parent : public grandparent21: {22: public:23: parent() {
00007FF7B2861160 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861165 48 83 EC 28 sub rsp,28h
00007FF7B2861169 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B286116E E8 4D FF FF FF call grandparent::grandparent (07FF7B28610C0h)
00007FF7B2861173 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B2861178 48 8D 0D 59 22 00 00 lea rcx,[parent::`vftable' (07FF7B28633D8h)]
00007FF7B286117F 48 89 08 mov qword ptr [rax],rcx 24: printf("construct of parent\\n");
00007FF7B2861182 48 8D 0D 77 21 00 00 lea rcx,[string "construct of parent\\n" (07FF7B2863300h)]
00007FF7B2861189 E8 D2 FE FF FF call printf (07FF7B2861060h) 25: }
00007FF7B286118E 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B2861193 48 83 C4 28 add rsp,28h
00007FF7B2861197 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B2861198 CC int 3
00007FF7B2861199 CC int 3
00007FF7B286119A CC int 3
00007FF7B286119B CC int 3
00007FF7B286119C CC int 3
00007FF7B286119D CC int 3
00007FF7B286119E CC int 3
00007FF7B286119F CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------26: virtual ~parent() {
00007FF7B28611A0 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B28611A5 48 83 EC 28 sub rsp,28h
00007FF7B28611A9 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B28611AE 48 8D 0D 23 22 00 00 lea rcx,[parent::`vftable' (07FF7B28633D8h)]
00007FF7B28611B5 48 89 08 mov qword ptr [rax],rcx 27: printf("virtual destructor of parent\\n");
00007FF7B28611B8 48 8D 0D 59 21 00 00 lea rcx,[string "virtual destructor of parent\\n" (07FF7B2863318h)]
00007FF7B28611BF E8 9C FE FF FF call printf (07FF7B2861060h) 28: }
00007FF7B28611C4 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B28611C9 E8 22 FF FF FF call grandparent::~grandparent (07FF7B28610F0h)
00007FF7B28611CE 48 83 C4 28 add rsp,28h
00007FF7B28611D2 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B28611D3 CC int 3
00007FF7B28611D4 CC int 3
00007FF7B28611D5 CC int 3
00007FF7B28611D6 CC int 3
00007FF7B28611D7 CC int 3
00007FF7B28611D8 CC int 3
00007FF7B28611D9 CC int 3
00007FF7B28611DA CC int 3
00007FF7B28611DB CC int 3
00007FF7B28611DC CC int 3
00007FF7B28611DD CC int 3
00007FF7B28611DE CC int 3
00007FF7B28611DF CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------29: virtual void func1() {
00007FF7B28611E0 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B28611E5 48 83 EC 28 sub rsp,28h 30: printf("virtual parent.func1\\n");
00007FF7B28611E9 48 8D 0D 48 21 00 00 lea rcx,[string "virtual parent.func1\\n" (07FF7B2863338h)]
00007FF7B28611F0 E8 6B FE FF FF call printf (07FF7B2861060h) 31: }
00007FF7B28611F5 48 83 C4 28 add rsp,28h
00007FF7B28611F9 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B28611FA CC int 3
00007FF7B28611FB CC int 3
00007FF7B28611FC CC int 3
00007FF7B28611FD CC int 3
00007FF7B28611FE CC int 3
00007FF7B28611FF CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------32: virtual void func2() {
00007FF7B2861200 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861205 48 83 EC 28 sub rsp,28h 33: printf("virtual parent.func2\\n");
00007FF7B2861209 48 8D 0D 40 21 00 00 lea rcx,[string "virtual parent.func2\\n" (07FF7B2863350h)]
00007FF7B2861210 E8 4B FE FF FF call printf (07FF7B2861060h) 34: }
00007FF7B2861215 48 83 C4 28 add rsp,28h
00007FF7B2861219 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B286121A CC int 3
00007FF7B286121B CC int 3
00007FF7B286121C CC int 3
00007FF7B286121D CC int 3
00007FF7B286121E CC int 3
00007FF7B286121F CC int 3
parent::`vector deleting destructor':
00007FF7B2861220 89 54 24 10 mov dword ptr [rsp+10h],edx
00007FF7B2861224 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861229 48 83 EC 28 sub rsp,28h
00007FF7B286122D 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B2861232 E8 69 FF FF FF call parent::~parent (07FF7B28611A0h)
00007FF7B2861237 8B 44 24 38 mov eax,dword ptr [rsp+38h]
00007FF7B286123B 83 E0 01 and eax,1
00007FF7B286123E 85 C0 test eax,eax
00007FF7B2861240 74 0F je parent::`scalar deleting destructor'+31h (07FF7B2861251h)
00007FF7B2861242 BA 08 00 00 00 mov edx,8
00007FF7B2861247 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B286124C E8 47 02 00 00 call operator delete (07FF7B2861498h)
00007FF7B2861251 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B2861256 48 83 C4 28 add rsp,28h
00007FF7B286125A C3 ret
00007FF7B286125B CC int 3
00007FF7B286125C CC int 3
00007FF7B286125D CC int 3
00007FF7B286125E CC int 3
00007FF7B286125F CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------35: };
5. child类分析
Child的函数汇编码:
- 可以看到构造函数中先调用parent::parant构造函数;然后把child::'vftable’地址加载到了vfptr上;
这样在parent构造函数中赋值parent::'vftable’的vfptr属性,就会被改到child::‘vftable’,然后再往下执行了。 - 可以看到析构函数中,先把child::'vftable’地址放入到*this指向的vfptr内容上;然后再往下执行;
child::child自身析构函数执行后并未直接退出,还附加了汇编代码段,调用parent::parent父类的析构函数; - 基于1、2可以看出,构造函数是,先调父类构造(vfptr->父类vftable),再调自身构造(vfptr->自身vftable);
析构函数是,先调自身析构(vfptr->自身vftable),再调父类析构(vfptr->父类vftable); - 基于上面的推到可以看出,如果在child的构造函数,析构函数中调用func1/func2的话,可以调用到child中自身的实现;如果在其它函数中,vfptr指向不变时,不会有这个效果;
- 基于3同样可以推导parent中的问题,如果手动调用了析构的话,vfptr最终指向的是最顶层类的vftable,因为析构是自低向上,析构中都会先修改vfptr到自身vftable。
38: class child : public parent{39: public:40: child() {
00007FF7B2861260 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861265 48 83 EC 28 sub rsp,28h
00007FF7B2861269 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B286126E E8 ED FE FF FF call parent::parent (07FF7B2861160h)
00007FF7B2861273 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B2861278 48 8D 0D 99 21 00 00 lea rcx,[child::`vftable' (07FF7B2863418h)]
00007FF7B286127F 48 89 08 mov qword ptr [rax],rcx 41: printf("construct of child\\n");
00007FF7B2861282 48 8D 0D DF 20 00 00 lea rcx,[string "construct of child\\n" (07FF7B2863368h)]
00007FF7B2861289 E8 D2 FD FF FF call printf (07FF7B2861060h) 42: }
00007FF7B286128E 48 8B 44 24 30 mov rax,qword ptr [this] 42: }
00007FF7B2861293 48 83 C4 28 add rsp,28h
00007FF7B2861297 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B2861298 CC int 3
00007FF7B2861299 CC int 3
00007FF7B286129A CC int 3
00007FF7B286129B CC int 3
00007FF7B286129C CC int 3
00007FF7B286129D CC int 3
00007FF7B286129E CC int 3
00007FF7B286129F CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------43: virtual ~child() {
00007FF7B28612A0 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B28612A5 48 83 EC 28 sub rsp,28h
00007FF7B28612A9 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B28612AE 48 8D 0D 63 21 00 00 lea rcx,[child::`vftable' (07FF7B2863418h)]
00007FF7B28612B5 48 89 08 mov qword ptr [rax],rcx 44: printf("virtual destructor of child\\n");
00007FF7B28612B8 48 8D 0D C1 20 00 00 lea rcx,[string "virtual destructor of child\\n" (07FF7B2863380h)]
00007FF7B28612BF E8 9C FD FF FF call printf (07FF7B2861060h) 45: }
00007FF7B28612C4 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B28612C9 E8 D2 FE FF FF call parent::~parent (07FF7B28611A0h)
00007FF7B28612CE 48 83 C4 28 add rsp,28h
00007FF7B28612D2 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B28612D3 CC int 3
00007FF7B28612D4 CC int 3
00007FF7B28612D5 CC int 3
00007FF7B28612D6 CC int 3
00007FF7B28612D7 CC int 3
00007FF7B28612D8 CC int 3
00007FF7B28612D9 CC int 3
00007FF7B28612DA CC int 3
00007FF7B28612DB CC int 3
00007FF7B28612DC CC int 3
00007FF7B28612DD CC int 3
00007FF7B28612DE CC int 3
00007FF7B28612DF CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------46: virtual void func1() {
00007FF7B28612E0 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B28612E5 48 83 EC 28 sub rsp,28h 47: printf("virtual child.func1\\n");
00007FF7B28612E9 48 8D 0D B0 20 00 00 lea rcx,[string "virtual child.func1\\n" (07FF7B28633A0h)]
00007FF7B28612F0 E8 6B FD FF FF call printf (07FF7B2861060h) 48: }
00007FF7B28612F5 48 83 C4 28 add rsp,28h
00007FF7B28612F9 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B28612FA CC int 3
00007FF7B28612FB CC int 3
00007FF7B28612FC CC int 3
00007FF7B28612FD CC int 3
00007FF7B28612FE CC int 3
00007FF7B28612FF CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------49: virtual void func2() {
00007FF7B2861300 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861305 48 83 EC 28 sub rsp,28h 50: printf("virtual child.func2\\n");
00007FF7B2861309 48 8D 0D A8 20 00 00 lea rcx,[string "virtual child.func2\\n" (07FF7B28633B8h)]
00007FF7B2861310 E8 4B FD FF FF call printf (07FF7B2861060h) 51: }
00007FF7B2861315 48 83 C4 28 add rsp,28h
00007FF7B2861319 C3 ret
--- 无源文件 -----------------------------------------------------------------------
00007FF7B286131A CC int 3
00007FF7B286131B CC int 3
00007FF7B286131C CC int 3
00007FF7B286131D CC int 3
00007FF7B286131E CC int 3
00007FF7B286131F CC int 3
child::`vector deleting destructor':
00007FF7B2861320 89 54 24 10 mov dword ptr [rsp+10h],edx
00007FF7B2861324 48 89 4C 24 08 mov qword ptr [rsp+8],rcx
00007FF7B2861329 48 83 EC 28 sub rsp,28h
00007FF7B286132D 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B2861332 E8 69 FF FF FF call child::~child (07FF7B28612A0h)
00007FF7B2861337 8B 44 24 38 mov eax,dword ptr [rsp+38h]
00007FF7B286133B 83 E0 01 and eax,1
00007FF7B286133E 85 C0 test eax,eax
00007FF7B2861340 74 0F je child::`scalar deleting destructor'+31h (07FF7B2861351h)
00007FF7B2861342 BA 08 00 00 00 mov edx,8
00007FF7B2861347 48 8B 4C 24 30 mov rcx,qword ptr [this]
00007FF7B286134C E8 47 01 00 00 call operator delete (07FF7B2861498h)
00007FF7B2861351 48 8B 44 24 30 mov rax,qword ptr [this]
00007FF7B2861356 48 83 C4 28 add rsp,28h
00007FF7B286135A C3 ret
00007FF7B286135B CC int 3
00007FF7B286135C CC int 3
00007FF7B286135D CC int 3
00007FF7B286135E CC int 3
00007FF7B286135F CC int 3
--- e:\\project\\demo1-0-asm-test\\demo1\\demo1.cpp --------------------------------52: };
6. main函数分析
main函数汇编码:
- main函数中调用的vfptr情况:
函数调用分别是虚函数表中的[虚析构函数指针,func1指针,func2指针]
00007FF6296613BF FF 50 08 call qword ptr [rax+8]
00007FF6296613CF FF 50 10 call qword ptr [rax+10h]
00007FF6296613E1 FF 10 call qword ptr [rax] - 基于以上的综合分析也可以看出,虚函数表中的方法,对于上层下层,同一虚函数名,对应的vfptr偏移位置应该是一致对应的;
这样编译器才可以不知道创建出的对象类型的情况下,调用到正确的函数;
例如parent的方法func1, func2,分别是[vfptr+8, vfptr+16]位置;
那么就必然要求child的继承方法func1, func2,分别是[vfptr+8, vfptr+16]位置; - 也就是说,上层的方法的虚函数位置占前面的位置;如果子类新加虚函数方法,需要放在之后的位置,以和上层做好对应;
假如上层方法添加了虚函数,则必然导致子类的虚函数方法在vftable中向后移动。
假如上层删除了中间的虚函数,则必然也会导致子类的该同名方法位置发生移动,要保证上层方法在前,下层方法在后的次序;
假如上层方法的位置放生移动,也会导致虚函数表中的位置发生变更,从而子类中也会同步变更,要保证下层继承方法和上层方法锁再的vfptr偏移一致; - 简而言之,下层方法首先复制上层的虚函数vftable表,并在那个基础上,对继承的虚函数自身有实现的,修改指向自身实现;
然后再在vftable后面,追加放入父类没有,自身新增的虚函数;
大体看起来是,先复制上层,再对覆盖实现的指向做修改,最后追加新增的指向。
汇编码:
53: int main()54: {
00007FF629661360 48 83 EC 68 sub rsp,68h
00007FF629661364 48 C7 44 24 58 FE FF FF FF mov qword ptr [rsp+58h],0FFFFFFFFFFFFFFFEh 55: parent* p = new child();
00007FF62966136D B9 08 00 00 00 mov ecx,8
00007FF629661372 E8 B5 00 00 00 call operator new (07FF62966142Ch)
00007FF629661377 48 89 44 24 30 mov qword ptr [rsp+30h],rax
00007FF62966137C 48 83 7C 24 30 00 cmp qword ptr [rsp+30h],0
00007FF629661382 74 11 je main+35h (07FF629661395h)
00007FF629661384 48 8B 4C 24 30 mov rcx,qword ptr [rsp+30h]
00007FF629661389 E8 D2 FE FF FF call child::child (07FF629661260h)
00007FF62966138E 48 89 44 24 38 mov qword ptr [rsp+38h],rax
00007FF629661393 EB 09 jmp main+3Eh (07FF62966139Eh)
00007FF629661395 48 C7 44 24 38 00 00 00 00 mov qword ptr [rsp+38h],0
00007FF62966139E 48 8B 44 24 38 mov rax,qword ptr [rsp+38h]
00007FF6296613A3 48 89 44 24 40 mov qword ptr [rsp+40h],rax
00007FF6296613A8 48 8B 44 24 40 mov rax,qword ptr [rsp+40h]
00007FF6296613AD 48 89 44 24 20 mov qword ptr [p],rax 56: p->func1();
00007FF6296613B2 48 8B 44 24 20 mov rax,qword ptr [p] 56: p->func1();
00007FF6296613B7 48 8B 00 mov rax,qword ptr [rax]
00007FF6296613BA 48 8B 4C 24 20 mov rcx,qword ptr [p]
00007FF6296613BF FF 50 08 call qword ptr [rax+8] 57: p->func2();
00007FF6296613C2 48 8B 44 24 20 mov rax,qword ptr [p]
00007FF6296613C7 48 8B 00 mov rax,qword ptr [rax]
00007FF6296613CA 48 8B 4C 24 20 mov rcx,qword ptr [p]
00007FF6296613CF FF 50 10 call qword ptr [rax+10h] 58: p->~parent();
00007FF6296613D2 48 8B 44 24 20 mov rax,qword ptr [p]
00007FF6296613D7 48 8B 00 mov rax,qword ptr [rax]
00007FF6296613DA 33 D2 xor edx,edx
00007FF6296613DC 48 8B 4C 24 20 mov rcx,qword ptr [p]
00007FF6296613E1 FF 10 call qword ptr [rax] 59: //p->~parent();60: //p->func1();61: //p->func2();62: delete p;
00007FF6296613E3 48 8B 44 24 20 mov rax,qword ptr [p]
00007FF6296613E8 48 89 44 24 48 mov qword ptr [rsp+48h],rax
00007FF6296613ED 48 8B 44 24 48 mov rax,qword ptr [rsp+48h]
00007FF6296613F2 48 89 44 24 28 mov qword ptr [rsp+28h],rax
00007FF6296613F7 48 83 7C 24 28 00 cmp qword ptr [rsp+28h],0
00007FF6296613FD 74 1B je main+0BAh (07FF62966141Ah)
00007FF6296613FF 48 8B 44 24 28 mov rax,qword ptr [rsp+28h]
00007FF629661404 48 8B 00 mov rax,qword ptr [rax]
00007FF629661407 BA 01 00 00 00 mov edx,1
00007FF62966140C 48 8B 4C 24 28 mov rcx,qword ptr [rsp+28h]
00007FF629661411 FF 10 call qword ptr [rax]
00007FF629661413 48 89 44 24 50 mov qword ptr [rsp+50h],rax
00007FF629661418 EB 09 jmp main+0C3h (07FF629661423h)
00007FF62966141A 48 C7 44 24 50 00 00 00 00 mov qword ptr [rsp+50h],0 63: 64: return 0;
00007FF629661423 33 C0 xor eax,eax 65: }
00007FF629661425 48 83 C4 68 add rsp,68h
00007FF629661429 C3 ret
00007FF62966142A CC int 3
00007FF62966142B CC int 3
7. vftable分析
vftable内存存储:
- 从grandparent::
vftable
中可以看出,对于纯虚函数,虽然未生成代码,单在vftabel中的位置已经进行了固定,这样方便后续继承类中对应进行函数覆盖;
纯虚函数缺省映射到一个定义的__imp__purecall位置,以便运行时报出错误。 - vftable虚函数表,除了放置定义的虚函数外,会最后会留一个函数指针位,放置一个异常地址,调用到这个位置的时候会报出异常;
- 从func1/func2的虚函数指针位置可以看出,vftable中继承类中的继承虚函数在vftable偏移地址都是一致的。
00007FF77E6010FE 48 8D 0D F3 22 00 00 lea rcx,[grandparent::`vftable' (07FF77E6033F8h)]
grandparent::`vftable`
0x00007FF77E6033F8 20 11 60 7e f7 7f 00 00 .`~?... 指向 grandparent::`vector deleting destructor':
0x00007FF77E603400 86 20 60 7e f7 7f 00 00 ? `~?... 指向00007FF77E602086 FF 25 04 10 00 00 jmp qword ptr [__imp__purecall (07FF77E603090h)]
0x00007FF77E603408 86 20 60 7e f7 7f 00 00 ? `~?... 指向00007FF77E602086 FF 25 04 10 00 00 jmp qword ptr [__imp__purecall (07FF77E603090h)]
0x00007FF77E603410 20 38 60 7e f7 7f 00 00 8`~?... 指向 存储一个异常值 0x00007FF77E603820 01 00 00 00 00 00 00 0000007FF77E601178 48 8D 0D 59 22 00 00 lea rcx,[parent::`vftable' (07FF77E6033D8h)]
parent::`vftable`
0x00007FF77E6033D8 20 12 60 7e f7 7f 00 00 .`~?... 指向 parent::`vector deleting destructor'
0x00007FF77E6033E0 e0 11 60 7e f7 7f 00 00 ?.`~?... 指向 virtual void parent::func1
0x00007FF77E6033E8 00 12 60 7e f7 7f 00 00 ..`~?... 指向 virtual void parent::func2
0x00007FF77E6033F0 48 38 60 7e f7 7f 00 00 H8`~?... 指向 存储一个异常值 0x00007FF77E603848 01 00 00 00 00 00 00 0000007FF77E601278 48 8D 0D 99 21 00 00 lea rcx,[child::`vftable' (07FF77E603418h)]
child::`vftable`
0x00007FF77E603418 20 13 60 7e f7 7f 00 00 .`~?... 指向 child::`vector deleting destructor'
0x00007FF77E603420 e0 12 60 7e f7 7f 00 00 ?.`~?... 指向 virtual void child::func1
0x00007FF77E603428 00 13 60 7e f7 7f 00 00 ..`~?... 指向 virtual void child::func2
0x00007FF77E603430 22 05 93 19 01 00 00 00 ".?..... 指向一个非法内存地址 0x0000000119930522 ?? ?? ?? ?? ?? ?? ?? ??
其它汇编代码参考:
00007FF77E602080 FF 25 1A 10 00 00 jmp qword ptr [__imp___CxxFrameHandler3 (07FF77E6030A0h)]
00007FF77E602086 FF 25 04 10 00 00 jmp qword ptr [__imp__purecall (07FF77E603090h)]
00007FF77E60208C FF 25 F6 0F 00 00 jmp qword ptr [__imp___C_specific_handler (07FF77E603088h)]
00007FF77E602092 FF 25 E8 0F 00 00 jmp qword ptr [__imp___std_exception_copy (07FF77E603080h)]
00007FF77E602098 FF 25 DA 0F 00 00 jmp qword ptr [__imp___std_exception_destroy (07FF77E603078h)]
00007FF77E60209E FF 25 CC 0F 00 00 jmp qword ptr [__imp__CxxThrowException (07FF77E603070h)]
00007FF77E6020A4 FF 25 EE 0F 00 00 jmp qword ptr [__imp_memset (07FF77E603098h)]
00007FF77E6020AA FF 25 00 10 00 00 jmp qword ptr [__imp__callnewh (07FF77E6030B0h)]
00007FF77E6020B0 FF 25 02 10 00 00 jmp qword ptr [__imp_malloc (07FF77E6030B8h)]
00007FF77E6020B6 FF 25 C4 10 00 00 jmp qword ptr [__imp__seh_filter_exe (07FF77E603180h)]
00007FF77E6020BC FF 25 B6 10 00 00 jmp qword ptr [__imp__set_app_type (07FF77E603178h)]
00007FF77E6020C2 FF 25 20 10 00 00 jmp qword ptr [__imp___setusermatherr (07FF77E6030E8h)]
00007FF77E6020C8 FF 25 9A 10 00 00 jmp qword ptr [__imp__configure_narrow_argv (07FF77E603168h)]
00007FF77E6020CE FF 25 8C 10 00 00 jmp qword ptr [__imp__initialize_narrow_environment (07FF77E603160h)]
00007FF77E6020D4 FF 25 7E 10 00 00 jmp qword ptr [__imp__get_initial_narrow_environment (07FF77E603158h)]
00007FF77E6020DA FF 25 70 10 00 00 jmp qword ptr [__imp__initterm (07FF77E603150h)]
00007FF77E6020E0 FF 25 62 10 00 00 jmp qword ptr [__imp__initterm_e (07FF77E603148h)]
00007FF77E6020E6 FF 25 54 10 00 00 jmp qword ptr [__imp_exit (07FF77E603140h)]
00007FF77E6020EC FF 25 7E 10 00 00 jmp qword ptr [__imp__exit (07FF77E603170h)]
00007FF77E6020F2 FF 25 B0 10 00 00 jmp qword ptr [__imp__set_fmode (07FF77E6031A8h)]
00007FF77E6020F8 FF 25 0A 10 00 00 jmp qword ptr [__imp___p___argc (07FF77E603108h)]
00007FF77E6020FE FF 25 14 10 00 00 jmp qword ptr [__imp___p___argv (07FF77E603118h)]
00007FF77E602104 FF 25 1E 10 00 00 jmp qword ptr [__imp__cexit (07FF77E603128h)]
00007FF77E60210A FF 25 10 10 00 00 jmp qword ptr [__imp__c_exit (07FF77E603120h)]
00007FF77E602110 FF 25 1A 10 00 00 jmp qword ptr [__imp__register_thread_local_exe_atexit_callback (07FF77E603130h)]
00007FF77E602116 FF 25 BC 0F 00 00 jmp qword ptr [__imp__configthreadlocale (07FF77E6030D8h)]
00007FF77E60211C FF 25 9E 0F 00 00 jmp qword ptr [__imp__set_new_mode (07FF77E6030C0h)]
00007FF77E602122 FF 25 78 10 00 00 jmp qword ptr [__imp___p__commode (07FF77E6031A0h)]
00007FF77E602128 FF 25 9A 0F 00 00 jmp qword ptr [__imp_free (07FF77E6030C8h)]
00007FF77E60212E FF 25 C4 0F 00 00 jmp qword ptr [__imp__initialize_onexit_table (07FF77E6030F8h)]
00007FF77E602134 FF 25 C6 0F 00 00 jmp qword ptr [__imp__register_onexit_function (07FF77E603100h)]
00007FF77E60213A FF 25 F8 0F 00 00 jmp qword ptr [__imp__crt_atexit (07FF77E603138h)]
00007FF77E602140 FF 25 CA 0F 00 00 jmp qword ptr [__imp_terminate (07FF77E603110h)]
00007FF77E602146 FF 25 FC 0E 00 00 jmp qword ptr [__imp_IsProcessorFeaturePresent (07FF77E603048h)]
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)