有
我需要将虚拟呼叫转换为非虚拟呼叫。所以将虚拟指针转换为已解析的指针。
问题是:我该怎么做,例如在GCC中(如果没有标准提供的方法)。我不想像下面描述的那样使用hacks,但只是将一些调用隐藏在包含的文件中。
为了澄清这个主题,我将在调试GCC编译的汇编代码时解释我到目前为止看到的内容。 我已经看到泛型方法指针包含两个数据:
当涉及多重继承时使用偏移量,对于第二个和另一个父类的方法,如果应用于它们的对象,则将其数据放在不同的起始点。
该方法的“指针”如下:
之前的编码显然假设所有正常方法都从偶数地址开始(因此编译器应该在偶数地址处对齐它们)。 该偏移量是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