Gcc如何将虚方法解析为已解析的普通方法

时间:2016-04-06 10:11:04

标签: c++ virtual

  • 方法指针(最终为虚拟)
  • 类的类型

我需要将虚拟呼叫转换为非虚拟呼叫。所以将虚拟指针转换为已解析的指针。

问题是:我该怎么做,例如在GCC中(如果没有标准提供的方法)。我不想像下面描述的那样使用hacks,但只是将一些调用隐藏在包含的文件中。

为了澄清这个主题,我将在调试GCC编译的汇编代码时解释我到目前为止看到的内容。 我已经看到泛型方法指针包含两个数据:

  • 方法的“指针”
  • 最终添加到类实例起始地址
  • 的偏移量

当涉及多重继承时使用偏移量,对于第二个和另一个父类的方法,如果应用于它们的对象,则将其数据放在不同的起始点。

该方法的“指针”如下:

  1. 如果“指针”是偶数,则将其解释为普通(非虚拟)函数指针。
  2. 如果“指针”为奇数,则应减去1,其余值应为0或4或8或12(假设指针大小为4字节)。
  3. 之前的编码显然假设所有正常方法都从偶数地址开始(因此编译器应该在偶数地址处对齐它们)。 该偏移量是vtable的偏移量,其中可以找到“真实”非虚方法指针的地址。

    所以我要求的一种方法是:

    创建一个所需类型的虚拟对象,获取其地址,在其数据的开头有指向vtable的指针,得到它。

    获取方法指针,拆分为方法指针(地址或索引和偏移量。将偏移量添加到对象地址。 通过查找将索引(如果方法是虚拟的)转换为非虚方法。

    实现描述和用于回答另一个相关但稍微不同的问题的代码([如何获得“直接”函数指向虚拟成员函数的指针?] [1])如下:

    #include <stdio.h>
    #include <string.h>
    #include <typeinfo>
    #include <typeindex>
    #include <cstdint>
    
    struct Animal {
        int weight = 0x11111111;
        virtual int mm() {printf("Animal1 mm\n");return 0x77;};
        virtual int nn() {printf("Animal1 nn\n");return 0x99;};
    };
    struct Tiger : Animal {
        int weight = 0x22222222, height = 0x33333333;
        virtual int mm() {printf("Tigerxx\n");return 0xCC;}
        virtual int nn() {printf("Tigerxx\n");return 0x99;};
    };
    typedef int (Animal::*methodPointerT)();
    
    typedef struct {
        void** functionPtr;
        size_t offset;
    } MP;
    
    void devirtualize(methodPointerT& mp0,const Animal& a) {
        MP& t = *(MP*)&mp0;
        if ((intptr_t)t.functionPtr & 1) {
            size_t index = (t.functionPtr-(void**)1); // there is obviously a more
            void **vTable = (void**)(*(void**)&a);    // efficient way. Just for clarity!
            t.functionPtr = (void**)vTable[index];
        }
    };
    
    int main()
    {
        Animal x;Tiger y;
        methodPointerT mp1 = &Animal::nn;
    
        (x.*mp1)(); (y.*mp1)();
    
        devirtualize(mp1,x);
    
        (x.*mp1)(); (y.*mp1)();
    }
    

    输出将显示如下内容:

    Animal1 nn
    Tigerxx
    Animal1 nn
    Animal1 nn
    

0 个答案:

没有答案