understanding c++ vtables and RTTI

understanding c++ vtables and RTTI

本文关键字:RTTI and vtables c++ understanding      更新时间:2023-10-16



为了更好地理解编译器/进程需要做些什么来实现类和继承,我最近一直在摆弄vtables

这就是我努力实现的目标:
我想写我自己的小vtable,以便在对象上强制执行静态行为:

class A {
public:
    virtual void foo() { cout << "A.foo()" << endl; }
    virtual void bar() { cout << "A.bar()" << endl; }
};
class B : public A {
public:
    void foo() { cout << "B.foo()" << endl; }
    void bar() { cout << "B.bar()" << endl; }
};
typedef void (A::*func)();
int main() {
    A& b_as_a = *(new B());
    long* p = (long*)(&b_as_a);
    func* vtab = (func*)(p[0]);
    b_as_a.foo();
    b_as_a.bar();
    func* my_vtab = new func[4];
    my_vtab[0] = vtab[0]; //   I added these lines in step two after i got an
    my_vtab[1] = vtab[1]; // /  access violation
    my_vtab[2] = &A::bar;
    my_vtab[3] = &A::foo;
    p[0] = (long)(my_vtab);
    b_as_a.foo();
    b_as_a.bar();
    delete[] my_vtab;
    delete &b_as_a;
    return EXIT_SUCCESS;
}

这里是g++ -std=c++11 -fdump-class-hierarchy 的转储

Vtable for A
A::_ZTV1A: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1A)
16    (int (*)(...))A::foo
24    (int (*)(...))A::bar
Class A
   size=8 align=8
   base size=8 base align=8
A (0x0x7f40b60fe000) 0 nearly-empty
    vptr=((& A::_ZTV1A) + 16u)
Vtable for B
B::_ZTV1B: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1B)
16    (int (*)(...))B::foo
24    (int (*)(...))B::bar
Class B
   size=8 align=8
   base size=8 base align=8
B (0x0x7f40b60dfbc8) 0 nearly-empty
    vptr=((& B::_ZTV1B) + 16u)
  A (0x0x7f40b60fe060) 0 nearly-empty
      primary-for B (0x0x7f40b60dfbc8)


这不起作用。。。所以我研究了一下
我发现了这篇文章:g++-fdump类层次结构输出中的第一个(int(*)(…))0 vtable条目是什么
它解释了vtable中的前两个条目。我理解第一个条目的作用,但我对第二个条目的了解是,它是指向类信息的某种指针
我想这就是它不起作用的原因

剩下的问题是:
vtable中的第二个条目做了什么,下面的放克指针不再被读取


额外信息:我在openSuse 12.3 上使用g++

vptr指向vtable中的第三项。你可以从你的类转储中看到:
    vptr=((& A::_ZTV1A) + 16u)

或者通过将存储器中的值与成员函数地址进行比较。

因此,您要修改的是前两项:

my_vtab[0] = &A::bar;
my_vtab[1] = &A::foo;

此外,不要用成员函数指针构造新的vtable,而是用普通函数指针(甚至void*)构造。例如:

typedef void (*func)();

或:

typedef void* func;

原因是成员函数指针已经处理虚拟成员函数,因此不适合作为vtable中的条目(有关更多信息,请参阅为什么指向函数的指针大小与指向成员函数的指针的大小不同?例如)