zoukankan      html  css  js  c++  java
  • C++编译器检索VTABLE的具体方法不同

    以前没太注意过这个问题,只知道C++代码调用某个虚函数时要到虚函数表里去查找,然后执行特定的函数。这两天跟别人调程序时发现,VC和gcc对这个检索操作的实现方法是不一样的:

    早期的gcc(2001年5月前?)使用的是一个比较直观的查表方法。gcc中指向成员函数的指针其实是一个结构,类似于最早期的cfront编译器:

    struct
    {
    ??? short __delta;
    ??? short __index;
    ??? union
    ??? {
    ??????? void * __pfn;
    ??????? short _delta2;
    ??? } _pfn_or_delta2;
    };

    如果指向非虚成员函数,__pfn中就保存了内存中实际的函数指针。如果指向虚成员函数,__pfn无效,__index为该函数在虚函数表中的索引。调用虚函数时,根据虚函数表的地址和索引查到特定函数地址。

    后续版本的gcc在不断改进这种虚函数表的检索方法(实际上后续的gcc对C++ ABI的改动比较多),现在的gcc 3.3.3的源码里,gcc/cp/cp-tree.h等文件包含了相关代码。不过现在的实现代码比较复杂,我还没有彻底看明白。

    而VC的实现与此大不相同。VC为一棵继承树上的每个虚函数生成一个vcall代码(可以被看成一个小的中介函数),根据每个虚函数在虚函数表中的偏移不同,每个vcall调用时为eax增加的偏移量不同。这实际上是一种快速的查表机制,而且不用传虚函数表的index就可以调用到特定函数了。

    `vcall':
    00401010? mov???????? eax,dword ptr [ecx]
    00401012? jmp???????? dword ptr [eax]
    `vcall':
    00401020? mov???????? eax,dword ptr [ecx]
    00401022? jmp???????? dword ptr [eax+4]
    `vcall':
    00401030? mov???????? eax,dword ptr [ecx]
    00401032? jmp???????? dword ptr [eax+8]
    `vcall':
    00401040? mov???????? eax,dword ptr [ecx]
    00401042? jmp???????? dword ptr [eax+0Ch]

    调用时,对同名虚函数(无论该函数在属于一个类),使用的都是同一个vcall,但调用不同类的虚函数时,传入vcall的虚函数表起始地址不同(下面C2类是C1类的派生类,f1和f2是虚函数)。

    & C1::f1 - offset `vcall' (401010h)
    & C1::f2 - offset `vcall' (401020h)
    & C2::f1 - offset `vcall' (401010h)
    & C2::f2 - offset `vcall' (401020h)

  • 相关阅读:
    利用ResultFilter实现asp.net mvc3 页面静态化
    c#的DateTime.Now函数详解
    C#中使用DES和AES加密解密
    C#实现DES加密解密,AES加密解密
    MyEclipse的破解代码,适用各个版本
    C# DataGridView分页显示
    C# 对话框使用大全
    C# 通过串口发送短信
    C# axWindowsMediaPlayer制作播放器
    C#关于log4net(Log For Net)
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157307.html
Copyright © 2011-2022 走看看