1

我想查看A类的vtable的内容,尤其是虚拟析构函数,但是我不能通过函数指针调用它。这是我的代码:

typedef void (*fun)();


class A {
public:
     virtual func() {printf("A::func() is called\n");}
     virtual ~A() {printf("A::~A() is called\n");}
};

//enter in the vtable 
void *getvtable (void* p, int off){
     return (void*)*((unsigned int*)p+off);
}

//off_obj is used for multiple inherence(so not here), off_vtable is used to specify the   position of function in vtable
fun getfun (A* obj, unsigned int off_obj,int off_vtable){
     void *vptr = getvtable(obj,off_obj);
     unsigned char *p = (unsigned char *)vptr;
     p += sizeof(void*) * off_vtable;
     return (fun)getvtable(p,0);
}


void main() {
     A* ptr_a = new A;
     fun pfunc = getfun(ptr_a,0,0);
     (*pfunc)();
     pfunc = getfun(ptr_a,0,1);
     (*pfunc)(); //error occurred here, this is supposed to be the virtual desctrutor, why?
}
4

2 回答 2

1

为了论证的目的,我们假设所讨论的 vtable 确实按照您认为的方式布置,作为普通内存地址的表,并且当将这些地址转换为函数指针时,它们是可调用的。

你至少有两个问题:

  1. 成员函数的调用约定不一定与普通函数相同。Microsoft 的默认调用约定是thiscall,它将指向正在调用其方法的对象的指针放在 ECX 寄存器中。没有手动指定的工具;实现这一点的唯一方法是以调用成员函数的方式调用成员函数,这涉及到类似obj.f()or的语法pobj->f()。你不能用指向函数的指针(甚至不是成员函数指针)来做到这一点,除非你编写机器代码或汇编程序来正确获取所有低级细节。

    你碰巧没有遇到这个问题,func因为它没有引用this(直接或隐式引用其他成员)。不过,析构函数确实如此。析构函数是特殊的,实际上存储在 vtable 中的是指向编译器生成的辅助函数的指针,该函数调用真正的析构函数,然后检查作为隐藏参数传递的一些标志以确定它是否应该释放对象的内存。恰好在 ECX 中的值对调用无关紧要func,但对~A调用正确非常重要。

  2. 析构函数不像普通函数。正如我上面提到的,编译器可以生成一个或多个辅助函数,它们除了接收this. 您还没有在代码中考虑到这一点。编译器为数组和非数组析构函数生成单独的帮助器,所以现在我们甚至不知道你在 vtable 的索引 1 找到了哪个。但是由于您没有将有效的标志参数传递给它,并且没有办法将this值传递给它,所以无论如何您在 vtable 中找到什么并不重要。

您可以尝试通过指定不同的调用约定来解决第一个问题,例如stdcall。这会将this参数与其余参数一起放回堆栈,并允许您在调用函数指针时传递它。对于func,fun需要有这样的声明:

typedef void (__stdcall * fun)(A*);

像这样调用pfunc

pfunc(ptr_a);

要解决第二个问题,您需要确定 vtable 函数的实际顺序,以便知道找到正确的析构函数助手。要调用它,您还需要一个不同的函数指针声明。析构函数在技术上没有返回类型,但void工作得很好。你可以使用这样的东西:

typedef void (__stdcall * destr)(A*, unsigned flags);

对于这个答案的大部分内容,我使用了 Igorsk 的一篇关于识别程序中某些模式的文章,以便将其反编译回 C++。第 2 部分涵盖类。

于 2012-05-18T14:51:31.520 回答
-1

你不调用析构函数。你调用 operator delete(),它会计算出析构函数。直接调用析构函数是未定义的行为,与取消引用 NULL 的意义相同,即在我见过的每个平台上都会爆炸。

于 2012-05-18T08:44:37.900 回答