2016-11-16 5 views
5

Die Codes sind wie folgt (C++ 11 Codes mit G ++ kompiliert - 5.4 auf Ubuntu 16.04):virtuelle Basis für virtuelle Vererbung in der virtuellen Funktionstabelle Offset

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

Ich versuche GDB zu verwenden Speicherlayout überprüfen des Objekts "obj_a" (zum einen setzen I "gesetzt Druckobjekt auf", "set-Druck ziemlich auf", "set-Druck vtbl on", "set-Druck 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 

Wir wissen, dass der Versatz zwischen dem Anfang von obj_a und seinem virtuellen Basisunterobjekt 16B (virtueller Basisoffset) ist. Und dann überprüfe ich virtuelle Funktionstabelle zeigte die A von 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 

Wie wir sehen können, gibt es zwei "virtuelle thunk xxx", nämlich "0x400b8f" und "0x400be1". Ich spähe in diese zwei Adressen.

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

Meine Fragen sind: Was "add -0x18 (% r10)% rdi" und "add -0x20 (% r10)% rdi" wirklich? Warum sind -24 (-0x18) und -32 (-0x20)? (Ich denke, sie sollten alle -16 sein)

+1

Ich denke, Sie würden die Antwort bekommen, indem Sie auf die ASM für die Funktionen Umsetzung – Rerito

+0

Hallo Rerito, danke für Ihre Antwort. Ich habe asm-Code für die Implementierung der zwei virtuellen Funktionen überprüft, ich hatte keine Antwort. Tatsächlich möchte ich wirklich wissen, wie man diesen Zeiger vor der Ausführung der Funktion ändert. – Jason

+0

Oh, das weiß ich. Hier muss der Offset indirekt erreicht werden. "vtbl - 0x18" zeigt den Offset-Wert in der virtuellen Funktionstabelle an. Und dann "das" durch Hinzufügen von Offset korrigieren. – Jason

Antwort

1

Danke Rerito, sorry darüber.

Mein Problem liegt darin, dass ich nicht mit Assemblercode vertraut bin.

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

Im Assembler-Code für "virtuelle thunk bis A :: show()", spart% rdi "diesen" Wert. "mov (% rdi),% r10" bedeutet, dass der "vptr" -Wert (seine Adresse ist das "Dies") in das r10-Register verschoben wird. "add -0x18 (% r10),% rdi" bedeutet, dass der Wert, dessen Adresse "vptr-24" ist (d. h. 0xfffffffffffffff0 in der virtuellen Tabelle), zu "this" addiert wird. Also kann dieser "Wert" als Adresse von A's Objekt korrigiert werden.