How does the virtual inheritance table work in g++?

北战南征 提交于 2021-01-28 08:25:38

问题


I'm trying to get a better understanding how virtual inheritance works in practice (that is, not according to the standard, but in an actual implementation like g++). The actual question is at the bottom, in bold face.

So, I built myself a inheritance graph, which has among other things, these simple types:

struct A {
  unsigned a;
  unsigned long long u;
  A() : a(0xAAAAAAAA), u(0x1111111111111111ull) {}
  virtual ~A() {}
};

struct B : virtual A {
  unsigned b;
  B() : b(0xBBBBBBBB) {
    a = 0xABABABAB;
  }
};

(In the whole hierarchy I also have a C: virtual A and BC: B,C, so that the virtual inheritance makes sense.)

I wrote a couple of functions to dump the layout of instances, taking the vtable pointer and printing the first 6 8-byte values (arbitrary to fit on screen), and then dump the actual memory of the object. This looks something like that:

Dumping an A object:

actual A object of size 24 at location 0x936010
vtable expected at 0x402310 {
          401036,          401068,          434232,               0,               0,               0,
}
1023400000000000aaaaaaaa000000001111111111111111
[--vtable ptr--]

Dumping a B object and where the A object is located, which is indicated by printing a lot As at the respective position.

actual B object of size 40 at location 0x936030
vtable expected at 0x4022b8 {
          4012d2,          40133c,        fffffff0,        fffffff0,          4023c0,          4012c8,
}
b822400000000000bbbbbbbb00000000e022400000000000abababab000000001111111111111111
                                AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  (offset: 16)

As you can see, the A part of B is located at an offset of 16 bytes to the start of the B object (which could be different if I had instantiated BC and dyn-casted it to a B*!).

I would have expected the 16 (or at least a 2, due to alignment) to show up somewhere in the table, because the program has to look up the actual location (offset) of A at runtime. So, how does the layout really look like?


Edit: The dump is done by calling dump and dumpPositions:

using std::cout;
using std::endl;

template <typename FROM, typename TO, typename STR> void dumpPositions(FROM const* x, STR name) {
  uintptr_t const offset {reinterpret_cast<uintptr_t>(dynamic_cast<TO const*>(x)) - reinterpret_cast<uintptr_t>(x)};
  for (unsigned i = 0; i < sizeof(FROM); i++) {
    if (offset <= i && i < offset+sizeof(TO))
      cout << name << name;
    else
      cout << "  ";
  }
  cout << "  (offset: " << offset << ")";
  cout << endl;
}
template <typename T> void hexDump(T const* x, size_t const length, bool const comma = false) {
  for (unsigned i = 0; i < length; i++) {
    T const& value {static_cast<T const&>(x[i])};
    cout.width(sizeof(T)*2);
    if (sizeof(T) > 1)
      cout.fill(' ');
    else
      cout.fill('0');
    cout << std::hex << std::right << (unsigned)value << std::dec;
    if (comma)
      cout << ",";
  }
  cout << endl;
}
template <typename FROM, typename STR> void dump(FROM const* x, STR name) {
  cout << name << " object of size " << sizeof(FROM) << " at location " << x << endl;
  uintptr_t const* const* const vtable {reinterpret_cast<uintptr_t const* const*>(x)};
  cout << "vtable expected at " << reinterpret_cast<void const*>(*vtable) << " {" << endl;
  hexDump(*vtable,6,true);
  cout << "}" << endl;
  hexDump(reinterpret_cast<unsigned char const*>(x),sizeof(FROM));
}

回答1:


The answer is actually documented here, in the Itanium ABI. In particular section 2.5 contains the layout of the virtual table.



来源:https://stackoverflow.com/questions/9947805/how-does-the-virtual-inheritance-table-work-in-g

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!