The codes are as follows (C++11 codes compiled with G++-5.4 on Ubuntu 16.04):
#include <iostream>
using namespace std;
class Base
{
public:
virtual void show()
{
cout << "Base" << endl;
}
virtual void func()
{
cout << "func in Base" << endl;
}
protected:
int base = 15;
};
class A: public virtual Base
{
public:
void show() override
{
cout << this << endl;
cout << 'A' << endl;
}
void func() override
{
cout << this << endl;
cout << "func in A" << endl;
}
protected:
int a = 31;
};
int main(int argc, const char *argv[])
{
A obj_a;
return 0;
}
I try to use GDB to check memory layout of object "obj_a" (firstly, I set "set print object on", "set print pretty on", "set print vtbl on", "set print asm-demangle on" in GDB):
(gdb) p sizeof(obj_a)
$1 = 32
(gdb) x/8aw &obj_a
0x7fffffffe320: 0x400d20 <vtable for A+24> 0x0 0x1f 0x0
0x7fffffffe330: 0x400d50 <vtable for A+72> 0x0 0xf 0x0
We can know that the offset between the beginning of obj_a and its virtual base subobject is 16B (virtual base offset). And then I check A's virtual function table pointed by 0x400d08 (0x400d20 - 24):
(gdb) x/14ag 0x400d08
0x400d08 <vtable for A>: 0x10 0x0
0x400d18 <vtable for A+16>: 0x400d90 <typeinfo for A> 0x400b46 <A::show()>
0x400d28 <vtable for A+32>: 0x400b98 <A::func()> 0xfffffffffffffff0
0x400d38 <vtable for A+48>: 0xfffffffffffffff0 0xfffffffffffffff0
0x400d48 <vtable for A+64>: 0x400d90 <typeinfo for A> 0x400b8f <virtual thunk to A::show()>
0x400d58 <vtable for A+80>: 0x400be1 <virtual thunk to A::func()> 0x400d20 <vtable for A+24>
0x400d68 <VTT for A+8>: 0x400d50 <vtable for A+72> 0x0
As we can see, there are two "virtual thunk to xxx", namely "0x400b8f " and "0x400be1 ". I peer into these two addresses.
(gdb) x/3i 0x400b8f
0x400b8f <virtual thunk to A::show()>: mov (%rdi),%r10
0x400b92 <virtual thunk to A::show()+3>: add -0x18(%r10),%rdi
0x400b96 <virtual thunk to A::show()+7>: jmp 0x400b46 <A::show()>
(gdb) x/3i 0x400be1
0x400be1 <virtual thunk to A::func()>: mov (%rdi),%r10
0x400be4 <virtual thunk to A::func()+3>: add -0x20(%r10),%rdi
0x400be8 <virtual thunk to A::func()+7>: jmp 0x400b98 <A::func()>
My questions are: what do "add -0x18(%r10),%rdi" and "add -0x20(%r10),%rdi" really mean? why are -24 (-0x18) and -32 (-0x20)? (I think they should all be -16)
Thank Rerito, sorry about that.
My problem lies that I am not familiar with assembly code.
(gdb) x/3i 0x400b8f
0x400b8f <virtual thunk to A::show()>: mov (%rdi),%r10
0x400b92 <virtual thunk to A::show()+3>: add -0x18(%r10),%rdi
0x400b96 <virtual thunk to A::show()+7>: jmp 0x400b46 <A::show()>
In the assembly code for "virtual thunk to A::show()", %rdi saves "this" value. "mov (%rdi),%r10" means moving "vptr" value (its address is the "this") to r10 register. "add -0x18(%r10),%rdi" means that adding the value whose address is "vptr - 24" (i.e. 0xfffffffffffffff0 in the virtual table) to "this". So "this" value can be corrected as address of A's object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With