4

Une erreur s'est produite lorsque j'essaie d'accéder à la disposition de la mémoire d'un objet de classe dérivé qui hérite d'une classe de base virtuelle.
environnement de programmation: GNU/Linux 3.19.0-32-générique, x86_64
compilateur: gcc 4.8.4Qu'est-ce qu'un "thunk virtuel" à une fonction virtuelle qui hérite d'une classe de base virtuelle?

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

Quand je lance le code, je me suis "faute de segment":

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

Donc je désassemble le fichier exécutable.
Je trouve la fonction < _ZTv0_n24_N7Derived1fEv> dans 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 le symbole dans mon terminal:

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

Alors ce qui est un thunk virtuel à Derived :: f() Pourquoi est-il Là?

+2

Votre question «qu'est-ce qu'un thunk virtuel» ou «pourquoi se sépare-t-elle? – xtofl

+0

@xtofl le premier. – linvoker

+2

Pourquoi essayez-vous de faire cela? –

Répondre

6

Le thunk virtuel d'une fonction est une fonction d'assistance qui corrige le paramètre this avant d'appeler la fonction actuelle. Regardez cet exemple:

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

Un objet Base en mémoire ressemble en quelque sorte comme ceci:

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

     * Data of Base class (variable `x`) 

Un objet Derived en mémoire ressemble en quelque sorte comme ceci:

|> * 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: Maintenant, il devrait également être clair pourquoi appelant _ZTv0_n24_N7Derived1fEv sur le pointeur d se bloque. Cette fonction ne fonctionne que si le pointeur this est pointé à l'intérieur de l'objet Derived - dans la partie qui peut être utilisée comme un objet Base.

0

Il y a une chose peu claire dans la question. Dans le "thunk virtuel à Derived :: f()", je pense que "add -0x18 (% r10),% rdi" ne peut pas fixer ce pointeur, car le décalage entre le début de l'objet Derive et son sous-objet (Base) n'est pas 24 (0x18).

+0

Oh, je le sais. Ici, il doit obtenir le décalage d'une manière indirecte. "vtbl - 0x18" pointe la valeur de décalage dans la table de fonction virtuelle. Et puis corriger "ceci" en ajoutant un décalage. – Jason

+0

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