2016-08-27 11 views
4

仮想基本クラスから継承した派生クラスオブジェクトのメモリレイアウトにアクセスしようとしたときに何か問題が発生しました。
プログラミング環境:GNU/Linuxの3.19.0-32ジェネリック、x86_64版
コンパイラ:私は、コードを実行すると、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; 
} 

は、私は、 "セグメント・フォルト" を得た:

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

実行ファイルを逆アセンブルしました。私のターミナル内のシンボルを復号化し

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 


私は0x400c3cで機能< _ZTv0_n24_N7Derived1fEv>を見つけ?

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

次に派生:: F()への仮想サンクは何か、なぜそれがありますそこ?

+2

あなたの質問は「仮想サンクは何ですか?」または「なぜそれはセグメンテーションですか」ですか? – xtofl

+0

@xtofl前者。 – 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:今それdポインターで_ZTv0_n24_N7Derived1fEvを呼び出すとクラッシュする理由も明らかになります。この関数は、オブジェクト内を指しているthisポインターがBaseオブジェクトのように使用できる部分にある場合にのみ機能します。

0

質問に不明な点があります。 Derivedオブジェクトの開始点とそのサブオブジェクト(Base)の間にオフセットがあるため、 "Add -0x18(%r10)、%rdi"はこのポインタを修正できません。 )は24(0x18)ではありません。

+0

ああ、私はそれを知っています。ここでは間接的にオフセットを取得する必要があります。 "vtbl - 0x18"は、仮想関数テーブルのオフセット値を指します。そして、オフセットを追加して "this"を修正してください。 – Jason

+0

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