2



I was messing around with vtables lately in order to better understand what a compiler/the process needs to do to implement classes and inheritance.

This is what I was trying to accomplish:
I wanted to write my own little vtable in order to force a static behaviour on an object:

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;
}

and here a dump of 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)


This did not work... so I researched a little.
I found this post: What is the first (int (*)(...))0 vtable entry in the output of g++ -fdump-class-hierarchy?
It explains the first two entries in a vtable. I understood what the first one does but all I know about the second entry is, that it's some sort of pointer to class information.
I guess that is the reason why it doesn't work.

The question that remains is:
What does the second entry in the vtable do that the funktionpointers underneath aren't read anymore???


Extra info: I am using g++ on openSuse 12.3

Community
  • 1
  • 1
Ch33f
  • 609
  • 8
  • 17
  • I am having problems understanding what the question is. Can you further explain what you mean to ask? – David Rodríguez - dribeas Nov 26 '13 at 17:04
  • That's horribly complicated. Why fight the language? Why not use normal C++ with overriding and function pointers, or lambdas? – curiousguy Nov 30 '18 at 08:22
  • This wasn't/isn't supposed to be used in production or anything. It was just an experiment in order to increase my understanding of the language and how it works. Just out of... curiousity ;) – Ch33f Nov 30 '18 at 14:31

1 Answers1

2

The vptr points to the third item in the vtable. You can see that from your class dump :

    vptr=((& A::_ZTV1A) + 16u)

or by comparing in-memory values with the member function addresses.

So, what you want to modify is the first two items :

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

Additionally, don't construct the new vtable with member function pointers, but rather with normal function pointers (or even void*). Eg. :

typedef void (*func)();

or :

typedef void* func;

The reason for that is that member function pointers already deal with virtual member functions, and hence are not appropriate as an entry in a vtable (for more info, refer to the question Why the size of a pointer to a function is different from the size of a pointer to a member function? eg.).

Community
  • 1
  • 1
Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40