53

For this code:

class B1{
public:  
  virtual void f1() {}  
};

class D : public B1 {
public:
  void f1() {}
};

int main () {
    B1 *b1 = new B1();
    D  *d  = new D();

    return 0;
}

After compilation, the vtable I get with g++ -fdump-class-hierarchy is:

Vtable for B1
B1::_ZTV2B1: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI2B1)
16    B1::f1


Vtable for D
D::_ZTV1D: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1D)
16    D::f1

I failed to understand what do the entries like (int ()(...))0* correspond to. Of course it means something like, it is a function which returns an int and takes unlimited number of arguments, I don't understand anything further. To which function does this function pointer correspond to? and how do you know that? Mine is a 64 bit machine.

The second function pointer has an address associated at end?? To whom does that correspond to?

EDIT

The compiler, I use is g++:

g++ -v
Using built-in specs.
Target: x86_64-suse-linux
Configured with: ../configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 --enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.4 --enable-ssp --disable-libssp --with-bugurl=http://bugs.opensuse.org/ --with-pkgversion='SUSE Linux' --disable-libgcj --disable-libmudflap --with-slibdir=/lib64 --with-system-zlib --enable-__cxa_atexit --enable-libstdcxx-allocator=new --disable-libstdcxx-pch --enable-version-specific-runtime-libs --program-suffix=-4.4 --enable-linux-futex --without-system-libunwind --with-arch-32=i586 --with-tune=generic --build=x86_64-suse-linux
Thread model: posix
*gcc version 4.4.1 [gcc-4_4-branch revision 150839] (SUSE Linux)*
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411

3 Answers3

56

Those are the offset-to-top (needed for multiple inheritence) and typeinfo (RTTI) pointers.

From the Itanium ABI (you are not using the Itanium compiler, but their description of this is really good):

The offset to top holds the displacement to the top of the object from the location within the object of the virtual table pointer that addresses this virtual table, as a ptrdiff_t. It is always present. The offset provides a way to find the top of the object from any base subobject with a virtual table pointer. This is necessary for dynamic_cast in particular.
(In a complete object virtual table, and therefore in all of its primary base virtual tables, the value of this offset will be zero. [...])

The typeinfo pointer points to the typeinfo object used for RTTI. It is always present. All entries in each of the virtual tables for a given class must point to the same typeinfo object. A correct implementation of typeinfo equality is to check pointer equality, except for pointers (directly or indirectly) to incomplete types. The typeinfo pointer is a valid pointer for polymorphic classes, i.e. those with virtual functions, and is zero for non-polymorphic classes.


Offset-to-top in more detail (by request)

Let's say you have a derived class D that derives from a base class, B1. What happens when you try to cast a D instance to type B1? Since functions that take a B1 object don't know anything about D, part of the D vtable must also be a valid B1 vtable. This is easy enough - just make the start of the D vtable look like a B1 vtable, and add on any additional entries we need after that. Functions expecting a B1 will be happy, because they won't use any part of the vtable beyond what they're expecting for a B1.

However, what happens if D now also derives from B2? The pointer to the D vtable can't be both a valid B1 vtable and a valid B2 vtable! The compiler solves this by appending a separate B2 vtable to the end of our combined D/B1 vtable, and adjusts the vtable-pointer manually when we try to cast from a D to a B2.

However, this leads to a new problem - what happens when we try to cast back from a B2 to a D? The compiler can't just adjust the vtable-pointer backwards by the same amount it adjusted the pointer previously, because it doesn't actually know for sure that the B2 object we're giving it is of type D! In particular, dynamic_cast<D>() must be able to tell if our object is or isn't of type D. For that, it needs to access the object's RTTI, and for that, it needs to know where the start of the original object's vtable is. This is the purpose of the offset-to-top value - it gives us the offset to the start of the original object's vtable, we get our object's RTTI, and the vengeful god of C++ allows our crops to grow for another season.

This page has some good examples of vtable layouts (under Table 1c). Note that they are slightly more complicated due to the use of virtual inheritance, which adds an extra offset to the vtable of each child class.

nowox
  • 25,978
  • 39
  • 143
  • 293
BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • I haven't understood this quote _"The offset to top holds the displacement to the top of the object from the location within the object of the virtual table pointer that addresses this virtual table"_ – Aquarius_Girl Apr 19 '11 at 07:53
  • 1
    Pressed enter by mistake ^, I meant, where in that vtable is a pointer to the function f1 () ? The explaination shown by you is still not clear for me, please see if you can explain in a layman's language. – Aquarius_Girl Apr 19 '11 at 07:54
  • 3
    @Anisha Kaul: Well, it pretty much says in your question: the pointer to `f1` is stored as the third element in the vtable in that particular implementation: `16 B1::f1`. – David Rodríguez - dribeas Apr 19 '11 at 08:11
  • 1
    As to the explanation for the two pointers, the `typeinfo` pointer is a pointer to an instance of `typeinfo` (constant) that contains the information about what this particular object is. This is where you will get the `typeinfo const &` when you call `typeid` on a pointer to an object with runtime type information. – David Rodríguez - dribeas Apr 19 '11 at 08:13
  • 2
    The offset to top is a little less trivial to put in layman's words... basically in the event of multiple inheritance casts to the different bases will result in pointers to the different subobjects which are offsetted from the pointer to the most derived object. That offset allows you (well, `dynamic_cast`) to revert that offset and obtain a pointer to the beginning of the object. – David Rodríguez - dribeas Apr 19 '11 at 08:17
  • 2
    `struct A { int x; }; struct B { int y; virtual void foo() {} }; struct C : A, B {}; int main() { C c; C *cp = &c; A *ap = &c; B *bp = &c; cout << cp << "," << ap << "," << bp << std::endl; }` This test shows how upcasting affects the pointers. Now intuitively to be able to downcast from `B*` to `C*` the compiler needs the `typeinfo` object to know that the cast is possible, and the offset to base to be able to move the pointer back to the original position. (In fact that is not strictly needed for that case, but in the event of `virtual` inheritance it is, but that is much more complex. – David Rodríguez - dribeas Apr 19 '11 at 08:22
  • 1
    @David Thanks so much for explaining, but please explain this: **"which are offsetted from the pointer to the most derived object."** What did you mean here? – Aquarius_Girl Apr 19 '11 at 10:24
  • @David You typeinfo explanation told me about the new key word named typeid, That explanation was related to the second pointer, i.e. `(int (*)(...))(& _ZTI2B1)`? Now what does (& _ZTI2B1) represent? – Aquarius_Girl Apr 19 '11 at 10:42
  • @Anisha Kaul: the text in bold in your last comment can be easily understood with the sample code above: the address stored in `cp` and `bp` will differ by a small offset (`sizeof(A)`). That is the offset that appears in the discussion above. As of `&_ZTI2B1` is the address of `_ZTI2B1` which is a symbol generated by the compiler (a constant `typeinfo` object), but the detail is not as important as the fact that it is *stored type information* – David Rodríguez - dribeas Apr 19 '11 at 13:32
  • @Anisha - see edit. `_ZTI2B1` and `_ZTI1D` are just internal identifiers generated by the compiler - I don't know what they stand for, but it doesn't really matter. – BlueRaja - Danny Pflughoeft Apr 19 '11 at 15:59
  • 1
    "_adjusts the vtable-pointer manually_" the **object** pointer – curiousguy Jul 15 '13 at 22:50
4

Maybe the first entry is for a virtual destructor and the second one is for RTTI support? But that's only a guess.

Michael
  • 53,859
  • 22
  • 133
  • 139
  • 1
    The second entry does actually look like RTTI support, but the destructor is not virtual for these classes. – Gorpik Apr 19 '11 at 07:22
1

I believe the answers quoting the Itanium ABI are too cumbersome to understand.

I think this paper by Ruhr-Universität Bochum and others (https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2019/10/02/ACSAC19-VPS.pdf) describes RTTI and Offset-To-Top in a friendlier way.

Extracted from the paper itself:

enter image description here

RTTI holds a pointer to type information about the class. Among other things, this type information contains the name of the class and its base classes. However, RTTI is optional and often omitted by the compiler. It is only needed when the programmer uses, e.g.,dynamic_cast or type_info. Hence, a reliable static analysis cannot rely on this information. Classes that do not contain RTTI have the RTTI field set to zero.

Offset-to-Top is needed when a class uses multiple inheritance (hence has a base vtable and one or more sub-vtables) as class C does. Offset-to-Top specifies the distance between a sub-vtable’s own vtblptr and the base vtblptr at the beginning of the object. In our example, the vtblptr to class C’s sub-vtable resides at offset 0x10 in the object,while the vtblptr to the base vtable resides at offset 0x0. Hence, the distance between the two, as stored in the Offset-to-Top field in sub-vtable C, is-0x10. Offset-to-Top is 0 if the vtable is the base vtable of the class or no multiple inheritance is used.

daniglezad
  • 91
  • 2
  • 9