Something went wrong when I try to access the memory layout of a derived class object which inherits from a virtual base class.
Programming environment: GNU/Linux 3.19.0-32-generic, x86_64
Compiler: gcc 4.8.4
//virtual base class
class Base {
public :
virtual void f() {
cout << "Base::f()" << endl;
}
private:
long x;
};
//derived class
class Derived : public virtual Base {
public:
virtual void f() {
cout << "Derived::f()" << endl;
}
private:
long y;
};
int main() {
typedef void (*FUNC)(void);
Derived d;
//In my machine, sizeof(long) == sizeof(pointers). My code below is neither portable nor concise. You can just read the annotation.
//dereference the first element of the first virtual function table(equals to *(vptr1->slot[0]))
cout << hex << *((long*)*((long*)(&d) + 0) + 0) << endl;
((FUNC)*((long*)*((long*)(&d) + 0) + 0))();//invoke Derived::f()
//dereference the first element of the second virtual function table(equals to *(vptr2->slot[0]))
cout << hex << *((long*)*((long*)(&d) + 2) + 0) << endl;
((FUNC)*((long*)*((long*)(&d) + 2) + 0))();//maybe Derived::f()?
return 0;
}
When I run the code, I got "segment fault":
400c12
Derived::f()
400c3c
segment fault
So I disassembly the executable file.
I found the function <_ZTv0_n24_N7Derived1fEv> in 0x400c3c:
0000000000400c3c <_ZTv0_n24_N7Derived1fEv>:
400c3c: 4c 8b 17 mov (%rdi),%r10
400c3f: 49 03 7a e8 add -0x18(%r10),%rdi
400c43: eb cd jmp 400c12 <_ZN7Derived1fEv>
400c45: 90 nop
Demangle the symbol in my terminal:
> c++filt _ZTv0_n24_N7Derived1fEv
virtual thunk to Derived::f()
Then what is a virtual thunk to Derived::f()?Why is it there?
Virtual thunk of some function is a helper function that fixes the this
parameter before calling the actual function. Look at this example:
Derived *d = new Derived();
// d now points to some address, e.g. 0x6eac40
d->f(); // This calls _ZN7Derived1fEv (Derived::f() directly)
Base *b = d;
// b now points to some other address (!), e.g. 0x6eac50
b->f(); // This calls _ZTv0_n24_N7Derived1fEv (the virtual thunk
// of Derived::f()), which subtracts some amount from `this`
// and then jumps to the _ZN7Derived1fEv (Derived::f())
A Base
object in memory looks somehow like this:
* Pointer to part of Base vtable with Base's virtual functions.
This vtable contains Base::f()
* Data of Base class (variable `x`)
A Derived
object in memory looks somehow like this:
|> * Pointer to part of Derived vtable with Derived's virtual functions.
|> This vtable contains the Derived::f()
|>
|> |> * Pointer to part of Derived vtable with the same layout as Base vtable.
|> |> This vtable contains the thunk of Derived::f()
|> |>
|> |> * Data of Base class (variable `x`)
| |>
| |> * Data of Derived class (variable `y`)
| |
| \ This is complete Derived object.
| The `d` pointer points at the beginning of this.
|
\ This is the part of Derived object that can act as a Base object.
The `b` pointer points at beginning of this.
PS: Now it should be also clear why calling _ZTv0_n24_N7Derived1fEv
on the d
pointer crashes. That function works only when given this
pointer that is pointing inside the Derived
object - into the part of it that can be used like a Base
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