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).