解密vtable dump [英] deciphering vtable dumps

查看:381
本文介绍了解密vtable dump的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C ++中使用虚拟继承来玩,我想知道类对象是如何布局的。
我有这三个类:

I am "playing" with virtual inheritance in C++, and I want to know how a class object is laid out. I have those three classes:

class A {
private:
    int a;
public:
    A() {this->a = 47;}
    virtual void setInt(int x) {this->a = x;}
    virtual int getInt() {return this->a;}
    ~A() {this->a = 0;}
};

class B {
private:
    int b;
public:
    B() {b = 48;}
    virtual void setInt(int x) {this->b = x;}
    virtual int getInt() {return this->b;}
    ~B() {b = 0;}
};

class C : public A, public B {
private:
    int c;
public:
    C() {c = 49;}
    virtual void setInt(int x) {this->c = x;}
    virtual int getInt() {return this->c;}
    ~C() {c = 0;}
};

(我认为他们是正确的:p)

(I think they are correct :p)

我使用 -fdump-class-hierarchy 与g ++,我得到了

I used -fdump-class-hierarchy with g++, and I got this

Vtable for A
A::_ZTV1A: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1A)
16    A::setInt
24    A::getInt

Class A
   size=16 align=8
   base size=12 base align=8
A (0x10209fb60) 0
    vptr=((& A::_ZTV1A) + 16u)

Vtable for B
B::_ZTV1B: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1B)
16    B::setInt
24    B::getInt

Class B
   size=16 align=8
   base size=12 base align=8
B (0x1020eb230) 0
    vptr=((& B::_ZTV1B) + 16u)

Vtable for C
C::_ZTV1C: 8u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1C)
16    C::setInt
24    C::getInt
32    (int (*)(...))-0x00000000000000010
40    (int (*)(...))(& _ZTI1C)
48    C::_ZThn16_N1C6setIntEi
56    C::_ZThn16_N1C6getIntEv

Class C
   size=32 align=8
   base size=32 base align=8
C (0x1020f5080) 0
    vptr=((& C::_ZTV1C) + 16u)
  A (0x1020ebd90) 0
      primary-for C (0x1020f5080)
  B (0x1020ebe00) 16
      vptr=((& C::_ZTV1C) + 48u)

现在这些(int(*)(...)) - 0x00000000000000010 C :: _ ZThn16_N1C6setIntEi int(*)(...))0 ??
有人可以解释转储吗?

Now what the heck are those (int (*)(...))-0x00000000000000010 and C::_ZThn16_N1C6setIntEi and (int (*)(...))0?? Can someone explain the dump?

谢谢。

推荐答案

我不是100%肯定这个答案是正确的,但这是我最好的猜测。

I'm not 100% sure that this answer is correct, but here's my best guess.

当你有一个继承乘法和非虚拟的类,类的布局通常是第一个基本类型的完整对象,然后是第二个基本类型的完整对象,然后是对象本身的数据。如果你看看B,你可以看到A对象的vtable指针,如果你看看C,你可以看到有指向A和B对象的vtable的指针。

When you have a class that inherits multiply and non-virtually, the layout of the class is usually a complete object of the first base type, then a complete object of the second base type, then the data for the object itself. If you look at B, you can see the vtable pointer for the A object, and if you look at C you can see that there's pointers into the vtable for both the A and B objects.

因为对象是以这种方式布局的,这意味着如果你有一个 B * 指针指向 C 对象,指针实际上不会在对象的基础;而是指向中间某处。这意味着如果你需要将对象转换为 A * ,则需要调整 B * 指针一些量跳过它回到对象的开始。为了做到这一点,编译器需要编码某处你需要跳回到达到对象的开始的字节数。我认为第一个(int(*)(...))实际上只是一个原始的字节数,你需要看看到的开始的目的。如果你会注意到,对于 A vtable这个指针是0(因为A的vtable在对象的开始,并且对于<$但是,请注意 C vtable有两个部分 - 第一部分是 A 的vtable,它的第一个疯狂的条目也是零(因为如果你在 A vtable,你不需要做任何调整)。但是,在这个表的前半部分看起来是 B vtable之后,注意它的第一个条目是十六进制值 -0x10 。如果查看 C 对象布局, code> B vtable指针在 A vtable指针后面是16个字节,这个 -0x10 value可能是您需要跳过 B vtable指针以返回对象根目录的校正偏移。

Because the objects are laid out this way, it means that if you have a B* pointer pointing at a C object, the pointer will actually not be at the base of the object; rather it will be pointing somewhere in the middle. This means that if you ever need to cast the object to an A*, you'll need to adjust the B* pointer some amount to skip it back to the start of the object. In order to do this, the compiler needs to encode somewhere the number of bytes you need to skip back to get to the start of the object. I think that the very first (int(*)(...)) is actually just a raw number of bytes you need to look at to get to the start of the object. If you'll notice, for the A vtable this pointer is 0 (since the vtable for A is at the start of the object, and the same is true for the B vtable (since it also lives at the start of the object. However, notice that the C vtable has two parts - the first part is the vtable for A, and its first crazy entry is zero as well (since if you're at the A vtable, you don't need to do any adjustments). However, after the first half of this table is what appears to be the B vtable, and notice that its first entry is hex value -0x10. If you look at the C object layout, you'll notice that the B vtable pointer is 16 bytes after the A vtable pointer. This -0x10 value might be the corrective offset you need to skip back over the B vtable pointer to get back to the root of the object.

每个vtable的第二个疯狂的条目似乎是一个指向vtable本身的指针。注意,它总是等于vtable对象的地址(比较vtable的名称和它所指向的)。这是必要的,如果你想做任何类型的运行时类型标识,因为通常涉及到查看vtable的地址(或至少在它的前面附近的东西)。

The second crazy entry of each vtable seems to be a pointer to the vtable itself. Notice that it's always equal to the address of the vtable object (compare the name of the vtable and what it's pointing at). This would be necessary if you wanted to do any sort of runtime type identification, since that usually involves looking at the address of the vtable (or at least something near the front of it).

最后,至于为什么在 C vtable的结尾有隐藏的setInt和getInt函数,我很确定这是因为 C type继承两个不同的函数集 setInt getInt - one通过 A 和一到 B 。如果我不得不猜测,这里的修改是确保编译器内部可以区分两个虚拟函数。

Finally, as for why there's the cryptically-named setInt and getInt functions at the end of the C vtable, I'm pretty sure that's because the C type inherits two different sets of functions named setInt and getInt - one through A and one through B. If I had to guess, the mangling here is to ensure that the compiler internals can differentiate between the two virtual functions.

希望这有助于!

这篇关于解密vtable dump的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆