虚继承中虚函数表中的虚基类偏移量

6
以下是代码 (C++11 代码,使用 G++-5.4 在 Ubuntu 16.04 上编译):
#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;
}

我试图使用GDB检查对象"obj_a"的内存布局(首先,在GDB中设置"set print object on","set print pretty on","set print vtbl on","set print asm-demangle on"):

(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

我们可以知道,obj_a的起始位置和其虚基类子对象之间的偏移量为16B(虚基类偏移量)。然后我检查了由0x400d08指向的A的虚函数表(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

我们可以看到有两个“virtual thunk to xxx”,分别是“0x400b8f”和“0x400be1”。我查看了这两个地址。
(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()>

我的问题是:"add -0x18(%r10),%rdi" 和 "add -0x20(%r10),%rdi" 究竟是什么意思?为什么是-24(-0x18)和-32(-0x20)?(我认为它们应该都是-16)

1
我认为你可以通过查看函数实现的汇编代码来得到答案。 - Rerito
嗨Rerito,谢谢你的回复。我查看了这两个虚函数实现的汇编代码,但是没有找到答案。事实上,我真正想知道的是如何在执行函数之前更改此指针。 - Jason
哦,我知道。这里需要以间接的方式获取偏移量。“vtbl - 0x18”指向虚函数表中的偏移值。然后通过加上偏移量来纠正“this”。 - Jason
1
如果你找到了你想要的答案,不要犹豫,回答自己的问题吧! :) - Rerito
我想指出的是,这是特定于编译器的,不同的编译器可能会以不同的方式实现它们的虚函数表。例如,MSVC保留了不同的虚函数表和虚基表,至少在我上次检查时是这样。我从未检查过生成的汇编代码,但由于这个原因它很可能是不同的。 - Justin Time - Reinstate Monica
@JustinTime 感谢您的提醒。是的,您是对的。 - Jason
1个回答

1

感谢Rerito,很抱歉。

我的问题在于我不熟悉汇编代码。

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

在“virtual thunk to A::show()”的汇编代码中,%rdi保存了“this”的值。“mov (%rdi),%r10”表示将“vptr”值(其地址为“this”)移动到r10寄存器。“add -0x18(%r10),%rdi”表示将地址为“vptr - 24”(即虚拟表中的0xfffffffffffffff0)的值添加到“this”。因此,“this”值可以被纠正为A对象的地址。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接