文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

虚函数(代码段地址)被存放在虚函数表中,调用虚函数的流程是这样子的:先获取虚函数表的首地址,然后根据目标虚函数在虚函数表的位置(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++ 虚函数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯