检测特定虚函数的vtable索引/序号(使用Visual C ++)

时间:2011-04-12 12:32:29

标签: c++ visual-c++

继我的问题:Detect the the vtable offset of a specific virtual function (using Visual C++)

假设:

struct A 
{
   virtual void a() {}
   virtual void b() {}
   virtual void c() {} 
};

如何在Visual C ++中编写函数(可能是非可移植的),例如:

int x = GetVtableIndex(&A::a); //returns 0
int x = GetVtableIndex(&A::b); //returns 1
int x = GetVtableIndex(&A::c); //returns 2

我想要这样做的原因是在链接问题中。

3 个答案:

答案 0 :(得分:7)

受到imre答案的启发,如果您只是想知道vtable的总大小;即,有多少虚拟功能,这可能有效。此方法不会实例化该类。

template <typename T>
int VTableSize()
{
    class VTableCounter
    {
    public:
        virtual int Get1() { return 0; }
        virtual int Get2() { return 1; }
        virtual int Get3() { return 2; }
        // ... 994 more ...
        virtual int Get998() { return 997; }
        virtual int Get999() { return 998; }
        virtual int Get1000() { return 999; }
    };

    class A : public T
    {
    public:
        virtual int LastVirtual() {return -1;}
    };

    VTableCounter vt;
    return reinterpret_cast<A*>(&vt)->LastVirtual();
}

注意我没有使用Boost.PP,因为我可以告诉它限制为256。您应该能够使用vim宏或其他东西来获取任意数量的虚拟内容。但是,如果使用多重或虚拟继承,它可能会给出不正确的值。

答案 1 :(得分:7)

受到imre和Ivan的回答的启发,我认为这可以完全解决:

template <class T, typename F>
int VTableIndex(F f)
{
    struct VTableCounter
    {
        virtual int Get1() { return 1; }
        virtual int Get2() { return 2; }
        virtual int Get3() { return 3; }
        virtual int Get4() { return 4; }
        // ... more ...
    } vt;

    T* t = reinterpret_cast<T*>(&vt);

    typedef int (T::*GetIndex)(); 
    GetIndex getIndex = (GetIndex)f;
    return (t->*getIndex)();
}

int n = VTableIndex<A>(&A::c); //returns 3

答案 2 :(得分:4)

试试这个:

#include <boost/preprocessor/repeat.hpp>

struct VtableIndexCalculator
{
    #define VTIC_GET_INDEX(z, i, d) virtual int GetIndex_ ## i() { return i; }
    BOOST_PP_REPEAT(128, VTIC_GET_INDEX, unused); 
    #undef VTIC_GET_INDEX
};

template <class C, typename F> int GetVtableIndex(C& object, F function)
{
    static VtableIndexCalculator calculator; 
    static void** vtable_new = *(void***)&calculator; 

    void*** pvptr = (void***)&object; 
    void** vtable_old = *pvptr; 
    *pvptr = vtable_new; 

    typedef int (C::*GetIndex)(); 
    GetIndex getIndex = (GetIndex)function;
    int index = (object.*getIndex)();

    *pvptr = vtable_old; 
    return index; 
}

缺点是要获取C :: F函数的vtable索引,需要一个C的实际实例(这个版本需要是一个非const实例,但我认为可以创建一个const版本),因此它不适用于抽象类。

另外,我刚做了一些快速测试,它似乎到目前为止工作,但我不确定它是否总是这样(可能取决于C使用什么类型的继承,如果它有任何继承的虚拟功能或所有虚拟在C中声明,甚至是否启用增量链接),所以要小心。

这是做什么的:

  • 创建一个名为VtableIndexCalculator的辅助类,它有许多GetIndex_n()函数,每个函数返回相应的n。
  • 如果C(object)和指向成员函数的指针(称为function),则获取一个实例。
  • 修补程序object的vptr,使其指向VtableIndexCalculator的vtable。
  • function转换为与VtableIndexCalculator中的GetIndex()函数的签名匹配的不同函数指针。
  • object上调用生成的指针指向成员。
  • 现在发生的事情(至少在理论上)是一个成员函数以相同的方式被调用(偏移计算,虚拟thunks,无论如何),因为正常的C :: F()调用将被完成,除了返回值和参数(这些来自指定的指向成员函数类型),以及最初指向C :: F的vtable条目#n现在指向VtableIndexCalculator :: GetIndex_n。
  • 因此该调用的结果是C :: F的vtable索引。