详解如何实现C++虚函数调用汇编代码

 更新时间:2021年11月15日 11:19:50   作者:link-初扬  
多态是C++中最重要的特性之一,对虚函数的调用在C++代码中是随处可见的,本篇文章我们详细探讨一下,感兴趣的朋友快来看看吧

虚函数(代码段地址)被存放在虚函数表中,调用虚函数的流程是这样子的:先获取虚函数表的首地址,然后根据目标虚函数在虚函数表的位置(offset偏移)取出虚函数表中的虚函数地址,最后去call这个虚函数(地址),就完成虚函数的调用。这个虚函数调用的流程在汇编代码中可以最直观的反映出来。

在排查软件异常或崩溃时,我们时常要借助汇编代码的上下文去辅助分析问题。读懂C++虚函数调用的汇编代码实现,对于搞懂汇编代码的上下文时很有好处的。今天我们就来看看虚函数调用的汇编代码实现。

比如如下的C++代码:

// 1、虚接口类中定义了纯虚接口
class IContactInterface
{
    //...
 
    virtual BOOL IsLoadFinish() == 0;  // 虚接口
 
    //...
}
 
 
// 2、子类中实现虚接口
class Contact : public IContactInterface
{
    //...
 
    BOOL IsLoadFinish(); 
 
    //...
}
 
 
// 3、new出子类的对象,存放到父类的指针变量中
IContactInterface* g_pContactPtr = new Contact;
 
 
// 4、获取子类对象的接口实现
IContactInterface* GetContactPtr()
{
    return g_pContactPtr;
} 
 
 
// 5、调用GetContactPtr获取子类的对象去调用虚函数IsLoadFinish
GetContactPtr()->IsLoadFinish();

上述C++代码片中,主要包含了以下几点:

1)定义了虚接口类IContactInterface,在类中有个IsLoadFinish虚函数;

2)定义了一个子类Contact,继承于IContactInterface接口类,并实现了虚函数IsLoadFinish;

3)new出一个Contact类的对象,赋值给父类IContactInterface的指针变量;

4)调用GetContactPtr接口获取IContactInterface指针变量,调用虚函数IsLoadFinish。

其中,GetContactPtr()->IsLoadFinish()这句虚函数调用的汇编代码如下所示:

.text:005103EB call ds:__imp__GetContactPtr
.text:005103F1 mov [ebp+var_2AF4], eax
.text:005103F7 mov ecx, [ebp+var_2AF4]
.text:005103FD mov edx, [ecx]
.text:005103FF mov ecx, [ebp+var_2AF4]
.text:00510405 mov eax, [edx+78h]
.text:00510408 call eax

现在我们就来详细解读一下这段实现虚函数调用的汇编代码。

1、汇编代码段1:

.text:005103EB call ds:__imp__GetContactPtr
.text:005103F1 mov [ebp+var_2AF4], eax
.text:005103F7 mov ecx, [ebp+var_2AF4]

调用GetContactPtr接口,返回IContactInterface*指针,其中存放的是子类Contact对象。GetContactPtr接口返回的IContactInterface*指针中的值,放到eax寄存器中。

在C++的汇编代码中,调用函数的返回值是放到eax寄存器中的。

所以eax中存放的是子类Contact对象的地址。然后将eax中存放的子类Contact对象的地址,放到函数栈内存[ebp+var_2AF4]中。然后又将子类Contact对象的地址放到ecx寄存器中。

2、汇编代码段2:

.text:005103FD mov edx, [ecx]

代码运行至此,此时ecx中存放的就是子类Contact对象的地址。该句汇编代码是以ecx中的值作为内存地址,取出该地址中的值放到edx寄存器中。

在C++中,C++类中如果有虚函数,则类中会掩藏一个虚函数指针成员变量,是排放在C++类所有数据成员的首位,所以C++对象的地址,就是虚函数表指针变量的内存首地址(是指针变量的地址,不是指针变量中的值),所以此处的[ecx]操作,是取出虚函数表指针变量中存放的内容,即虚函数表的首地址。所以edx寄存器中存放的是Contact对象中的虚函数表的首地址。

C++类中如果有虚函数,则该类中会掩藏一个用来存放虚函数表地址的虚函数表指针变量,该虚函数表指针变量放置在该C++对象所有数据成员的首位,所以C++对象的地址就是其虚函数指针变量的内存首地址。

3、汇编代码段3:

.text:005103FF mov ecx, [ebp+var_2AF4]

回到整段汇编代码最开始的地方,[ebp+var_2AF4]中存放的是子类Contact对象的地址。因为下面要调用Contact类对象的虚函数,调用时要将Contact类对象的地址传给被调用的函数,即this指针。C++汇编代码中,是通过ecx寄存器将类对象的地址传给被调用函数,所以此句代码是为下面调用类Contact的虚函数IsLoadFinish做准备的。

在调用C++类的函数时,是通过ecx寄存器传递C++类对象的地址的,即this指针的存放的C++对象的地址。

4、汇编代码段4:

.text:00510405 mov eax, [edx+78h]
.text:00510408 call eax

接着上面,当前edx寄存器中存放的值是虚函数表的首地址(虚函数表指针中存放的值),78h是目标虚函数IsLoadFinish在虚函数中的偏移,所以对edx+78h地址取址,是读出虚函数表该位置存放的就是目标虚函数IsLoadFinish的地址(代码段地址),所以执行这行代码后,eax寄存器中存放的就是目标虚函数IsLoadFinish的地址,然后call eax就是调用虚函数IsLoadFinish了。

在C++中,函数名称其实就是该函数在代码段的首地址,去call这个首地址,就是去调用这个虚函数了。要注意区分数据段地址和代码段的地址。

到此这篇关于详解如何实现C++虚函数调用汇编代码的文章就介绍到这了,更多相关C++ 虚函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于C/C++ 常见误区详解

    基于C/C++ 常见误区详解

    本篇文章介绍了在c和c++中一些常见误区的详细概述。需要的朋友参考下
    2013-05-05
  • 关于C语言 const 和 define 区别

    关于C语言 const 和 define 区别

    这篇文章主要介绍了关于C语言 const 和 define 区别 的相关资料,需要的朋友可以参考下面文章内容
    2021-09-09
  • C++踩坑实战之构造和析构函数

    C++踩坑实战之构造和析构函数

    不论是构造函数,还是析构函数,都是C++、C#语言相对于其他语言而言特殊的地方,它是为了方便类中对象的初始化,这篇文章主要给大家介绍了关于C++踩坑实战之构造和析构函数的相关资料,需要的朋友可以参考下
    2021-07-07
  • C语言实现单词小助手改进版

    C语言实现单词小助手改进版

    这篇文章主要为大家详细介绍了C语言实现单词小助手的改进版,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • C语言详解实现链式二叉树的遍历与相关接口

    C语言详解实现链式二叉树的遍历与相关接口

    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址
    2022-04-04
  • linux c多线程编程实例代码

    linux c多线程编程实例代码

    这篇文章主要介绍了linux系统中的c多线程编程实例,大家可以参考使用以下代码
    2013-11-11
  • C语言实现万年历小功能

    C语言实现万年历小功能

    这篇文章主要为大家详细介绍了C语言实现万年历小功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • C++17之std::visit的具体使用

    C++17之std::visit的具体使用

    本文主要介绍了C++17之std::visit的具体使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • C/C++实现快速排序算法的两种方式实例

    C/C++实现快速排序算法的两种方式实例

    快速排序是一种采用分治思想,在实践中通常运行较快一种排序算法,这篇文章主要给大家介绍了关于C/C++实现快速排序的两种方式的相关资料,文中给出了详细的示例代码,需要的朋友可以参考下
    2021-08-08
  • C语言指针入门的简单实例教程

    C语言指针入门的简单实例教程

    这篇文章主要给大家介绍了关于C语言指针入门的简单实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01

最新评论