2016-11-16 24 views
5

Các mã được như sau (C++ 11 mã biên dịch với G ++ - 5.4 trên Ubuntu 16,04):cơ sở ảo bù đắp trong bảng chức năng ảo cho thừa kế ảo

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

tôi cố gắng sử dụng GDB để kiểm tra bố trí bộ nhớ của đối tượng "obj_a" (trước hết, tôi đặt "đối tượng thiết lập in trên", "thiết lập in đẹp trên", "thiết lập vtbl in trên", "thiết lập in asm-demangle trên" trong 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 

Chúng ta có thể biết rằng sự bù đắp giữa đầu obj_a và subobject cơ sở ảo của nó là 16B (offset cơ sở ảo). Và sau đó tôi kiểm tra của một bảng chức năng ảo chỉ bằng 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 

Như chúng ta có thể thấy, có hai "thunk ảo để xxx", cụ thể là "0x400b8f" và "0x400be1". Tôi nhìn vào hai địa chỉ này.

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

Câu hỏi của tôi là: "thêm -0x18 (% r10),% rdi" và "thêm -0x20 (% r10),% rdi" thực sự có ý nghĩa? tại sao -24 (-0x18) và -32 (-0x20)? (Tôi nghĩ tất cả họ nên là -16)

+1

Tôi nghĩ bạn sẽ nhận được câu trả lời bằng cách xem xét asm để triển khai chức năng – Rerito

+0

Xin chào Rerito, cảm ơn bạn đã trả lời. Tôi đã kiểm tra mã asm cho việc triển khai hai chức năng ảo, tôi không có bất kỳ câu trả lời nào. Trong thực tế, những gì tôi thực sự muốn biết là làm thế nào để thay đổi con trỏ này trước khi thực hiện chức năng. – Jason

+0

Ồ, tôi biết điều đó. Ở đây nó cần để có được bù đắp một cách gián tiếp. "vtbl - 0x18" chỉ giá trị offset trong bảng chức năng ảo. Và sau đó sửa "this" bằng cách thêm offset. – Jason

Trả lời

1

Cảm ơn Rerito, xin lỗi về điều đó.

Vấn đề của tôi nằm ở chỗ tôi không quen với mã lắp ráp.

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

Trong mã lắp ráp cho "giá trị thunk ảo để A :: show()",% rdi lưu "giá trị này". "mov (% rdi),% r10" có nghĩa là di chuyển "vptr" giá trị (địa chỉ của nó là "this") thành r10 register. "thêm -0x18 (% r10),% rdi" có nghĩa là thêm giá trị có địa chỉ là "vptr - 24" (tức là 0xfffffffffffffff0 trong bảng ảo) thành "this". Vì vậy, giá trị "này" có thể được sửa chữa như là địa chỉ của đối tượng A.

Các vấn đề liên quan