2016-08-27 10 views
4

Что-то пошло не так, когда я пытаюсь получить доступ к макету памяти объекта производного класса, который наследуется от виртуального базового класса.
Среда программирования: GNU/Linux 3.19.0-32-родовое, x86_64
Компилятор: GCC 4.8.4Что такое «виртуальный thunk» для виртуальной функции, которая наследуется от виртуального базового класса?

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

Когда я запускаю код, я получил "вина сегмента":

400c12 
Derived::f() 
400c3c 
segment fault 

Итак, я разобрал исполняемый файл.
Я нашел функцию < _ZTv0_n24_N7Derived1fEv> в 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 символа в моем терминале:

> c++filt _ZTv0_n24_N7Derived1fEv 
virtual thunk to Derived::f() 

Тогда что такое виртуальный преобразователь для Derived :: f() Почему там?

+2

Ваш вопрос: «Что такое виртуальный кусок» или «почему это происходит»? – xtofl

+0

@xtofl the first. – linvoker

+2

Почему вы пытаетесь это сделать? –

ответ

6

Виртуальный кусок некоторой функции является вспомогательной функцией, которая фиксирует параметр this перед вызовом фактической функции. Посмотрите на этот пример:

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

Base объект в памяти выглядит как-то так:

 * Pointer to part of Base vtable with Base's virtual functions. 
      This vtable contains Base::f() 

     * Data of Base class (variable `x`) 

Derived объект в памяти выглядит как-то так:

|> * 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: Теперь также должно быть понятно, почему при вызове _ZTv0_n24_N7Derived1fEv об ошибке указателя d. Эта функция работает только при указании this указателя, указывающего внутри объекта Derived - в его часть, которая может использоваться как объект Base.

0

В вопросе есть неясная вещь. В «virtual thunk to Derived :: f()» я думаю, что «add -0x18 (% r10),% rdi» не может исправить этот указатель, поскольку смещение между началом объекта Derive и его подобъектом (Base) не 24 (0x18).

+0

О, я это знаю. Здесь он должен получить смещение косвенным образом. «vtbl - 0x18» указывает значение смещения в таблице виртуальных функций. И затем исправление «этого» путем добавления смещения. – Jason

+0

см. В http://stackoverflow.com/questions/40627476/virtual-base-offset-in-virtual-function-table-for-virtual-inheritance/40649781#40649781 – Jason