Double indirection in C++ vtables
assembly, c++, function-pointers, pointers, vtable
Solution
movq -24(%rbp), %rax # load 'foo_p' into %rax
movq (%rax), %rax # fetch VTABLE
movq (%rax), %rdx # fetch function `bar` from VTABLE.
You'd see it better if you added a `baz` (or `kerflunk`) function as a second function to your class, you'd see the second fetch be at `8` into the VTABLE.
You can see the structure inside the class as something like this (note this is "for illustration purposes, not intended to be reality")
struct VTABLE
{
void (*bar)();
};
struct Foo
{
VTABLE *vtable;
};
Inside the constructor for Foo [which exists even though you haven't declared one], there is a piece of code that does:
this->vtable = &VTABLE_Foo;
and somewhere the compiler has done (again, for illustration purposes, the names are certainly different):
VTABLE VTABLE_Foo = { foo::bar };
So to call `bar`, we would do:
foo_p->vtable->bar(foo_p);
What the compiler shows is:
void (*temp)() = foo_p->vtable->bar;
temp(foo_p);
This is almost certainly a consequence of using `-O0`, and if you compile with optimization, the compiler will do it more straight forward (including possibly realizing it doesn't need a vtable in this case and inline the function which doesn't do anything, thus eliminate the call completely).
Problem
I wrote this very simple C++ program, and I was wondering about why the compiler lays out the vtable across two pointer dereferences. Here's the C++ program: ``` class Foo { public: virtual void bar() { } }; int main(int argc, char *arv[]) { Foo foo; Foo *foo_p(&foo); foo_p->bar(); } ``` Now, I can look at the compiler-generated assembly: ``` $ g++ -ggdb -Wall -O0 -S test.cpp ``` Here are the relevant sections: ``` .loc 1 9 0 leaq -16(%rbp), %rax # put the address of 'foo' in %rax movq %rax, %rdi # use it as the first argument of the following function call _ZN3FooC1Ev # call the Foo constructor .loc 1 10 0 leaq -16(%rbp), %rax # put the address of 'foo' in %rax movq %rax, -24(%rbp) # create 'foo_p' on the stack .loc 1 11 0 movq -24(%rbp), %rax # load 'foo_p' into %rax movq (%rax), %rax # dereference the pointer, put it in %rax # %rax now holds the hidden pointer in 'foo', which is the vtable pointer movq (%rax), %rdx # dereference the pointer ::again:: (with an offset of 0), put it in %rdx # %rdx now holds a function pointer from the vtable movq -24(%rbp), %rax # create the 'this' pointer (== foo_p) and put it in %rax movq %rax, %rdi # use the 'this' pointer as the first argument to the following function call *%rdx # call Foo::bar (via the vtable) ``` Why the second pointer dereference is necessary? Why doesn't the 'hidden' vtable pointer in the object point directly into the vtable? edit: It ::is:: pointing directly to the vtable. I just got confused with my pointers :-P