> 文章列表 > 虚函数继承与虚函数表-汇编码分析

虚函数继承与虚函数表-汇编码分析

虚函数继承与虚函数表-汇编码分析

(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. 三个类的共同特点

三个类的共同特点:

  1. 没有成员变量,对象上只会有一个vptr虚函数表指针,sizeof对象的话,大小等于一个指针的大小sizeof(void*);
  2. 析构函数为虚函数,这样就可以借由父指针来删除创建的子对象;
  3. vfptr的位置缺省在创建对象的首个内存位置,所以*this,就可以获取到vfptr的指针;
  4. 类代码翻译的最后部分,都有一个classname::vector deleting destructotr汇编代码段封装,封装了对该类的析构函数调用,还封装了基于参数确定是否调用delete this调用;
  5. 名词解释
    vfptr: virtual function pointer 虚函数表指针/虚函数指针
    vftable: virtual funciton table 虚函数表

3. grandparent类分析

grandparent的函数汇编码:

  1. 可以看到两个纯虚函数func1, func2并未生成翻译源码;
  2. 可以看到构造函数中,先把grandparent::'vftable’地址加载到了rcx, 并放入到*this指向的vfptr内容上;
  3. 可以看到析构函数中,也做了一遍动作,先把grandparent::'vftable’地址放入到*this指向的vfptr内容上;
  4. 基于2/3可以看出,这个虚函数表地址是要在构造函数,析构函数中都要对vfptr属性重新赋值的。
  5. 最下面的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的函数汇编码:

  1. 可以看到构造函数中先调用grandparent::grandparant构造函数;然后把parent::'vftable’地址加载到了vfptr上;
    这样在grandparent构造函数中赋值grandparent::'vftable’的vfptr属性,就会被改到parent::‘vftable’,然后再往下执行了。
  2. 可以看到析构函数中,先把parent::'vftable’地址放入到*this指向的vfptr内容上;然后再往下执行;
    parent::parent自身析构函数执行后并未直接退出,还附加了汇编代码段,调用grandparent::grandparent父类的析构函数;
  3. 基于1、2可以看出,这个虚函数表地址vfptr是要在构造函数,析构函数中都要重新赋值parent::'vftable’的,以调用自身实现的方法。
  4. 基于1、2还可以看出,构造函数是,先调父类构造(vfptr->父类vftable),再调自身构造(vfptr->自身vftable);
    析构函数是,先调自身析构(vfptr->自身vftable),再调父类析构(vfptr->父类vftable);
  5. func1,func2的虚函数实现有编译出来;编译出的内容里面是没有vfptr的处理。
  6. 基于上面的推到可以看出,如果在parent的构造函数,析构函数中调用func1/func2的话,可以调用到parent中自身的实现;
    同时注意,如果在其它函数中,vfptr指向不变时,不会有这个效果;vfptr构造后是指向创建类的,以那个类的vftable为准发生调用。
    也可以确定一个点:在构造函数,析构函数中,如果调用虚函数,只能调用到自身vtable中指向的虚函数(自身或父类实现的虚函数),调用不到子类实现的虚函数。
  7. 基于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的函数汇编码:

  1. 可以看到构造函数中先调用parent::parant构造函数;然后把child::'vftable’地址加载到了vfptr上;
    这样在parent构造函数中赋值parent::'vftable’的vfptr属性,就会被改到child::‘vftable’,然后再往下执行了。
  2. 可以看到析构函数中,先把child::'vftable’地址放入到*this指向的vfptr内容上;然后再往下执行;
    child::child自身析构函数执行后并未直接退出,还附加了汇编代码段,调用parent::parent父类的析构函数;
  3. 基于1、2可以看出,构造函数是,先调父类构造(vfptr->父类vftable),再调自身构造(vfptr->自身vftable);
    析构函数是,先调自身析构(vfptr->自身vftable),再调父类析构(vfptr->父类vftable);
  4. 基于上面的推到可以看出,如果在child的构造函数,析构函数中调用func1/func2的话,可以调用到child中自身的实现;如果在其它函数中,vfptr指向不变时,不会有这个效果;
  5. 基于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函数汇编码:

  1. 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]
  2. 基于以上的综合分析也可以看出,虚函数表中的方法,对于上层下层,同一虚函数名,对应的vfptr偏移位置应该是一致对应的;
    这样编译器才可以不知道创建出的对象类型的情况下,调用到正确的函数;
    例如parent的方法func1, func2,分别是[vfptr+8, vfptr+16]位置;
    那么就必然要求child的继承方法func1, func2,分别是[vfptr+8, vfptr+16]位置;
  3. 也就是说,上层的方法的虚函数位置占前面的位置;如果子类新加虚函数方法,需要放在之后的位置,以和上层做好对应;
    假如上层方法添加了虚函数,则必然导致子类的虚函数方法在vftable中向后移动。
    假如上层删除了中间的虚函数,则必然也会导致子类的该同名方法位置发生移动,要保证上层方法在前,下层方法在后的次序;
    假如上层方法的位置放生移动,也会导致虚函数表中的位置发生变更,从而子类中也会同步变更,要保证下层继承方法和上层方法锁再的vfptr偏移一致;
  4. 简而言之,下层方法首先复制上层的虚函数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内存存储:

  1. 从grandparent::vftable中可以看出,对于纯虚函数,虽然未生成代码,单在vftabel中的位置已经进行了固定,这样方便后续继承类中对应进行函数覆盖;
    纯虚函数缺省映射到一个定义的__imp__purecall位置,以便运行时报出错误。
  2. vftable虚函数表,除了放置定义的虚函数外,会最后会留一个函数指针位,放置一个异常地址,调用到这个位置的时候会报出异常;
  3. 从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)