2016-08-27 11 views
4

가상 기본 클래스에서 상속 된 파생 클래스 개체의 메모리 레이아웃에 액세스하려고 할 때 문제가 발생했습니다.
프로그래밍 환경 : 3.19.0-32 제네릭 GNU/리눅스, x86_64에
컴파일러 :가상 기본 클래스에서 상속하는 가상 함수의 "가상 썽크"란 무엇입니까?

400c12 
Derived::f() 
400c3c 
segment fault 
: 나는이 코드를 실행하면 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; 
} 

, 나는 "세그먼트 오류"를 가지고

그래서 실행 파일을 분해합니다. 내 터미널에서 심볼 디맹 글링

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 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 :

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

     * Data of Base class (variable `x`) 

메모리에있는 Derived 객체가이 같은 든 모습이 예 봐 지금은 포인터에서 _ZTv0_n24_N7Derived1fEv을 호출하는 것이 왜 충돌하는지 분명해야합니다. 이 함수는 개체처럼 사용할 수있는 Derived 개체 내부를 가리키는 this 포인터가 지정된 경우에만 작동합니다.

0

질문에 불분명 한 것이 있습니다. "가상 썽크에서 :: f()", "추가 -0x18 (% r10), % rdi"이 포인터를 고칠 수 없다고 생각하기 때문에 파생 개체의 시작 부분과 그 하위 개체 (Base)는 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