Ahnihmuhs
Ahnihmuhs

Reputation: 107

How to correctly invoke member methods using the virtual table?

Short Version

I'm using the entries from the vtable of a specific object to invoke virtual methods inherited from an interface. In the end I'm searching for a way to get the exact offset each address to a virtual method has in the vtable of a specific object.

Detailed Version


Disclaimer

I know that this topic is implementation dependant and that one should not try to do this manually because the compiler does the (correct) work and a vtable is not considered to be a standard (let alone the dataformat). I hereby testify that I already read dozens of "Don't do it... just, don't do it!"'s and am clear about the outrageous consequences my actions could have.

Therefore (and to favor a contructive discussion) I'll be using g++ (4.x.x) for the compiler on a Linux x64 Platform as my reference. Any software compiled using the below presented code will use the same setup, so it should be platform-indepandant as far as this is concerned.


Okay, this whole issue of mine is completely experimental, I don't want to use it in production code but to educate myself and my fellow students (my professor asked me if I could write a quick paper about this topic).

What I'm trying to do is basically the automatic invocation of methods using offsets to determine which method to invoke. The basic outline of the classes is as follows (this is a simplification but shows the composition of my current attempts):

class IMethods
{
    virtual double action1(double) = 0;
    virtual double action2(double) = 0;
};

As you can see just a class with pure virtual methods which share the same signature.

enum Actions
{
    actionID1,
    actionID2
};

The enum-items are used to invoke the appropriate method.

class MethodProcessor : public IMethods
{
    public:
        
        double action1(double);
        double action2(double);
};

Purposely omitted ctor/dtor of above class. We can safely assume that these are the only virtual methods inherited from the interface and that polymorphy is of no concern.


This concludes the basic outline. Now to the real topic:

Is there a safely way to get the mapping of addresses in the vtable to the inherited virtual methods?

What I'm trying to do is something like this:

MethodProcessor proc;
size_t * vTable = *(size_t**) &proc;
double ret = ((double(*)(MethodProcessor*,double))vTable[actionID2])(&proc, 3.14159265359);

This is working out fine and is invoking action2, but I'm assuming that the address pointing to action2 is equal to index 1 and this part bugs me: What if there is some sort of offset added into the vtable before the address of action1 is defined?

In a book about the data object model of C++ I read that normally the first address in the vtable is leading to the RTTI (runtime type information), which in return I couldn't confirm because vTable[0] is legitimately pointing to action1.

The compiler knows the exact index of every virtual method pointer because, yeah, the compiler is building them and replacing every member invocation of the virtual methods with augmented code which equals the one I used above - but knows the index to use. I, for once, am taking the educated guess that there is no offset before my defined virtual methods.

Can't I use some C++-Hack to let the compiler compute the correct index on compile (or even run)-time? I could then use this information to add some offset to my enum-items and woulnd't have to worry about casting wrong addresses...

Upvotes: 2

Views: 405

Answers (2)

Yves Lhuillier
Yves Lhuillier

Reputation: 123

I have nearly the same problem, even worse, in my case, this is for procuction code... don't ask me why (just tell me: "Don't do it... just, don't do it", or at least use an existing dynamic compiler such as LLC...).

Of course, this "torture" could be smoothed if compiler designers were asked to follow some generic C++ ABI specifications (or at least specify their own). Apparently, there is a growing consensus on complying with "Generic C++ ABI" (also often called "ITANIUM C++ ABI", mentioned by Dietmar Kühl).

Because you are using G++ and because this is for educationnal purposes, I suggest you have a look at what the "-fdump-class-hierarchy" g++ switch does (you will find vtable layouts); this is what I personally use to be sure that I'm not casting wrong addresses.


Note: using a x86 (ia32) MinGW g++-4.7.2 compiler,

In double MethodProcessor::action( double ), the hidden "(MethodProcessor*)this" argument will be passed in a register (%ecx).

Whereas in ((double(*)(MethodProcessor*,double)), the first explicit "this" argument will be passed on stack.

Upvotes: 0

Dietmar Kühl
Dietmar Kühl

Reputation: 154015

The ABI used by Linux is known: you should look at the section on virtual function table layout of the Itanium ABI. The document also specifies the object layout to find the vtable. I don't know if the approach you outlined works, however.

Note, that despite pointing at the document, I do not recommend to use the information to play tricks based on it! You unfortunately didn't explain what your actual goal is but it seems that using a pointer to member of the irtual functions is a more reliable approach to run-time dispatch to a virtual function:

double (IMethods::*method)(double)
    = flag? &IMethods:: action1: &INethods::actions2;
MethodProcessor mp;
double rc = (mp.*method)(3.14);

Upvotes: 1

Related Questions