
The offset to top has a value equal to the number of bytes that must be added to the this pointer to get the start of the object from some subobject. However, because we are assuming RTTI is disabled, it will always be 0. The RTTI pointer will typically point to an RTTI struct (that is also described by the Itanium spec). Itanium vtable layout (without virtual inheritance) A virtual table does not consist of just function pointers. If we look more closely at the Itanium specification, we can see why. However, if we examine the binary we don't see any place with 6 consecutive function pointers in the. Recall from part 1 that there will be two destructors, and because Bat does not override walk, we would expect the walk from Mammal to appear in Bat's table. With this in mind, we would expect the vtable group for Bat to be something like: Offset These tables will be adjacent in the binary, in the order the parent types were declared in the source. Virtual Table GroupsĪ virtual table group consists of a primary table for the first parent type, and an arbitrary number of secondary tables, one for each parent type after the first. The Itanium ABI refers to these as a "virtual table group". So a type with multiple parents has a vtable in the binary for each one. These pointers point to different tables. Notice that Bat includes a complete instance (called subobjects) of Bird and Mammal as well as a vptr for each. It becomes clear why this happens when you consider the layout of an object with multiple inheritance. In fact there are 6 regions in the binary that meet the above criteria. But as you might have guessed, things aren't that simple. We would expect there to be 5 vtables, one for Mammal, Cat, Dog, Bird, and Bat. It turns out that this is a vtable, so maybe this heuristic is good enough? If we were to compile the following code: It is an array of 3 function pointers in the '.rodata' segment, and only the pointer at 0x08048D48 is referenced. This is a section from a binary that seems to fit that definition. We can also say that the array should only be referenced by its first element, because the other elements will be accessed as offsets in to this array.rodata:08048D48 off_8048D48 dd offset sub_8048B6E Unfortunately, discussing this would dramatically increase the complexity of this topic, and because virtual inheritance is somewhat uncommon I didn't think it was worth it.īefore we move forward, recall that in part 1, we described a vtable as a contiguous collection of function pointers in a data segment of the binary. The program does not contain occurrences of virtual inheritance.RTTI is disabled (if it were on, this would be much easier).

So in essence, I am describing the highlighted section:Īdditionally, the following assumptions are made: So I will further specify that I'm describing GCC's particular brand of Itanium. For example, it does not state what segments should be used to store vtables.

The Itanium ABI is also still ambiguous in some areas. The descriptions I give will be applicable these compilers. This standard is implemented by GCC, clang, ICC, and many other compilers (but notably, not Visual Studio). The most common of these is the Itanium C++ ABI. However, because programs produced by different compilers frequently need to interact (most notable, for dynamic linking), compiler developers agreed to a kind of supplemental specification for things like vtable layout, exception implementation and others. The compiler developers for that architecture would be required to choose between performance and compliance (more than they already are). It would be unfortunate if the spec required some specific vtable layout that was inefficient on some architecture. This is because the C++ standard needs to be applicable regardless of the underlying architecture. In the first part I mentioned that most things related to vtable layout were not specified in the standard, and so tended to vary from compiler to compiler. I will also show how we can sometimes recover relationships between these vtables (and therefore, between the types they are associated with).īut first I need to describe the set of binaries this is applicable to. So, in this part I will go through a more precise description of the layout of vtables and how we can find them programmatically. If the target binary contains thousands of vtables, it is not practical to manually locate the tables and create these structures and relationships. Naturally there were several limitations to that approach, namely that it is very manual.

In the previous part I described one approach to 'devirtualize' function calls in a small C++ program. Menu Reversing C++ Virtual Functions: Part 2 24 January 2017
