Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

void (**vt)() = *(void (***)())ptr; a helper variable for virtual table in c++

Tags:

c++

I found this technique in the following link: http://www.codeproject.com/Tips/90875/Displaying-vtable-when-debugging

and there, he uses one helper variable

void (**vt)() = *(void (***)())ptr;

to help display the virtual function table.

But if I change it to

void (**vt)() = (void (**)())ptr;

it does not work as the previous one.

Could someone help me to explain what magic plays here, please?

like image 957
Alex Avatar asked Aug 15 '13 03:08

Alex


2 Answers

Let's introduce a typedef for clarity.

typedef void (**thing)();

Then the first code is

thing vt = *(thing*) ptr;

and the second

thing vt = (thing) ptr;

That is, the first says "treat ptr as a pointer to a thing, and give me what it points at" and the second "treat ptr as a thing".
The difference should be obvious.

The reason it works at all is very specific to both the Visual C++ compiler and the Visual Studio debugger.

If the debugger doesn't know what type the object really is, it displays what it knows, which is that it's an instance of the base class, and it only knows how many entries the vtable of the base class has.

(You left out a crucial part here, which is to add the expected number of table entries to the item in the watch window. This makes the debugger display that memory as an array of as many elements as you say.)

So the trick consists of inventing a name for the vtable, and telling the debugger how many elements to display from the table.

like image 138
molbdnilo Avatar answered Nov 14 '22 15:11

molbdnilo


The virtual table pointer in many C++ implementations is the first sizeof(void (**)()) bytes of the object. When you dereference that pointer you will get the starting address of the real virtual table. That is the meaning of the working code.

The cdecl program might be of bit of a help here:

cdecl> explain void (***foo)()
declare foo as pointer to pointer to pointer to function returning void
cdecl> explain void (**foo)()
declare foo as pointer to pointer to function returning void

The first code casts the pointer to your object as a properly derefereancable pointer (pointer to pointer to pointer to function, void (***)()), and then dereferences it to acquire the starting address to the virtual table, which is of a type void (**)() (pointer to pointer to function), which points to the beginning of the virtual table that is of type void (*[])() (array of pointer to function).

The second one just casts your pointer to object to a pointer to a pointer to a function returning void; the address stored in the variable vt is just the address of your object.

class Object {
public:
    virtual void foo() {};
};

Object x;

// is approximately equivalent to 

struct Object {
    void (**_vtable)();
};

void _Object_foo(Object this) {
}

// this does not really compile, however,
// because a pointer mismatch
void (*_vtable_for_Object[])() = {
    _Object_foo
};

Object y;
y._vtable = _vtable_for_Object;

Thus by having

Object *ptr = new Object();
// x now points to a block of memory,
// whose first bytes are void (**_vtable)()

// then
void (**vt)() = *(void (***)())ptr;

// is equivalent in our analogy to
void (**vt)() = ptr->_vtable;

// except that the C++ does not allow you to
// access the virtual table directly without
// the trickery of the former