When (as in temporally) does the vtable get set up?

TL;DR

At least for g++, as part of each constructor call. As a derived class object is instantiated the vtable pointer will be updated at each constructor called.

The Zero class

One of the common jobs you have to do in coding is to initialise memory  to sane defaults.  Sometimes the sane default is zero.  Recently a colleague (Brendan Babb) wrote some code to make setting member variables to zero a trivial task.  It went something like

#include 
#include 
template 
struct Zero
{
    Zero() { std::memset(this, 0, sizeof(T)); }
};

struct LotsOfData : public Zero
{
    int ii;
    char cc;
    long ll;
};

My first thoughts were cool!  My second thoughts were, uh oh, this is going to cause havoc in any object that has a vtable.  To be concrete, I was predicting that the virtual function call in the following code would fail to work properly.

struct Base
{
    char cc;

    Base() : cc('B'){}
    virtual ~Base() {};
    virtual const char value() const { return cc; }
};
struct Derived : public Zero, public Base
{
    long ll;

    Derived() : ll(0) { cc = 'D'; }
    virtual const char value() const { return 'E'; }
};

int main( int argc, char* argv[] )
{
    Base bb;
    Derived dd;

    std::cout << "size of char = " << sizeof(char) << "\n";
    std::cout << "size of long = " << sizeof(long) << "\n";
    std::cout << "Base=" << bb.value() << " sizeof(Base)=" << sizeof(Base) << "\n";
    std::cout << "Derived=" << dd.value() << " sizeof(Derived)=" << sizeof(Derived) << "\n";

    Base* pbase = ⅆ
    std::cout << "Ptr to Base=" << pbase->value() << " and " << pbase->cc << "\n";
}

Take that code, and compile it with gcc 4.6 or clang  3.0 (on linux) and you will find that the code actually works as you would expect and not fail as I expected.

Quick introduction to vtables and vtable pointers

I am not a compiler writer so take this section with a grain of salt.  I’ve picked up this knowledge over years by talking to more senior programmers, reading articles, and watching memory in a debugger. Also when you read text books on C++ you don’t usually get taught about vtables because strictly speaking they are not part of the language. vtables are a construct that compiler writers invented to help them implement the language.

So what are they? vtables are arrays of function pointers.  But let’s take a step back.  When a simple class like LotsOfData is instantiated as an object, all the compiler needs to do is set aside some memory for the data. Typically the object will take more space than the sum of its part as the compiler often puts in some padding space so that member variables start on word boundaries (which allows most cpus to access the data faster). Notice that there is no mention of vtables in this case.

vtables are only necessary for a class that has virtual functions.   In the code above, there is a pointer called pbase.  pbase can validly point to an object of type Base or an object of type Derived. When code like pbase->value() is called, the decision of whether Base::value or Derived::value is called must be determined at runtime.  As with all problems in computer science, compiler writers solve this problem by using a level of indirection. Specifically, they create one array with pointers to the functions for the Base class and another array with the pointers to the functions for the Derived class. These arrays are the vtables.  Then in each (and every) object of type Base and Derived, they insert a pointer to the appropriate vtable. So at runtime when pbase->value() is called the vtable pointer that is in the object is dereferenced to determine which vtable to look up which then in turn contains the pointer to the correct value function.  So finally after multiple indirections we have the correct value function to call. As an aside, the reason a pointer to the vtable, rather than simply inserting the whole vtable into every object, is to save memory.

Another point to note is that when sizeof(Derived) is called, the returned size is the size in bytes necessary for new (or malloc) to set aside to contain an object of type Derived. That is, the size returned includes enough space for the vtable pointer and padding.

Why did I think it would fail?

What I was worried about was that memset in the Zero constructor would overwrite the vtable pointer in the Derived object. If that vtable pointer has been overwritten to zero (i.e., the null pointer) then the implicit vtable pointer dereference that occurs in pbase->value() would cause a segfault.

So the question comes down to what is the temporal ordering of the creation of an object. Two weeks ago I naively thought that because the new call knows exactly the type of the object being created so it would insert the correct vtable pointer as the first step after memory allocation. The next step would be constructors called with the Zero constructor being called first and thus the Zero constructor would wipe out the vtable pointer.

What actually happens?

Now since the code doesn’t fail like I predicted, what is going on? For the investigation I used gcc 4.6 and clang 3.0 running on Ubuntu 12.04.  If you use a different compiler, and/or a different platform, you could end up with different results. I compiled up the code (remember the -g compiler flag) and then used kdbg to inspect the memory of the bb and dd variables in the above code.

What I found was that the vtable pointer updated as the construction process proceeded. As a C++ programmer you would know that as part of the construction process the base class is constructed first and then builds up to the most derived class. I was able to observe that both gcc and clang set the vtable pointer as one of the first actions of calling a constructor.  To be concrete, constructing dd calls Zero::Zero() and Base::Base() before calling Derived::Derived().  Zero does its job and wipes the memory (including the space for the vtable pointer) to zero. Next, the vtable pointer in dd is set to point to the Base vtable as the first action of the Base::Base() . Then the first action when Derived::Derived() is called is to update the vtable pointer to the Derived vtable.

Why the multiple updating of the vtable pointer when the final value is known?

As far as I can guess, the answer is to provide a measure of safety.  Imagine a compiler that just directly set the vtable pointer to the final value as the first part of constructing an object. In that case, if a base class called a virtual function as part of the constructor, the virtual call would resolve to the derived class function.  However the derived class data has not been set up yet and if the derived function relied on this unset data then bad joojoo would happen.

Summary

On one hand, the zero class is very cool. On the other hand, it will might wipe out the vtable. But on the gripping hand, most likely all is well.

Advertisements
Tagged with: ,
Posted in Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: