Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get "direct" function pointer to a virtual member function?

Tags:

c++

pointers

I am working on an embedded platform which doesn't cope very well with dynamic code (no speculative / OOO execution at all). On this platform I call a virtual member function on the same object quite often, however the compiler fails to optimize the vtable-lookup away, as it doesn't seem to recognize the lookup is only required for the first invocation.

Therefore I wonder: Is there a manual way to devirtualize a virtual member function of a C++ class in order to get a function-pointer which points directly to the resolved address? I had a look at C++ function pointers, but since they seem to require a type specified, I guess this won`t work out.

Thank you in advance

like image 822
user2923748 Avatar asked Oct 21 '22 19:10

user2923748


2 Answers

There's no general standard-C++-only way to find the address of a virtual function, given only a reference to a base class object. Furthermore there's no reasonable type for that, because the this needs not be passed as an ordinary argument, following a general convention (e.g. it can be passed in a register, with the other args on stack).

If you do not need portability, however, you can always do whatever works for your given compiler. E.g., with Microsoft's COM (I know, that's not your platform) there is a known memory layout with vtable pointers, so as to access the functionality from C.

If you do need portability then I suggest to design in the optimization. For example, instead of

class Foo_base
{
public:
    virtual void bar() = 0;
};

do like

class Foo_base
{
public:
    typedef (*Bar_func)(Foo_base&);

    virtual Bar_func bar_func() const = 0;
    void bar() { bar_func()( *this ); }
};

supporting the same public interface as before, but now exposing the innards, so to speak, thus allowing manual optimization of repeated calls to bar.

like image 52
Cheers and hth. - Alf Avatar answered Nov 01 '22 11:11

Cheers and hth. - Alf


Regarding gcc I have seen the following while debuggging the assembly code compiled. I have seen that a generic method pointer holds two data: a) a "pointer" to the method b) an offset to add eventually to the class instance starting address ( the offset is used when multiple inheritance is involved and for methods of the second and further parent class that if applied to their objects would have their data at different starting points).

The "pointer" to the method is as follows: 1) if the "pointer" is even it is interpreted as a normal (non virtual) function pointer. 2) If the "pointer" is odd then 1 should be subtracted and the remaining value should be 0 or 4 or 8 or 12 ( supposing a pointer size of 4 bytes). The previous codification supposes obviously that all normal methods start at even addresses (so the compiler should align them at even addresses). So that offset is the offset into the vtable where to fetch the address of the "real" non virual method pointer.

So the correct idea in order to devirtualize the call is to convert a virtual method pointer to a non virtual method pointer and use it aftewards in order to apply it to the "subject" that is our class instance.

The code bellow does what described.

#include <stdio.h>
#include <string.h>
#include <typeinfo>
#include <typeindex>
#include <cstdint>

struct Animal{
    int weight=0x11111111;
    virtual int mm(){printf("Animal1 mm\n");return 0x77;};
    virtual int nn(){printf("Animal1 nn\n");return 0x99;};
};

struct Tiger:Animal{
    int weight=0x22222222,height=0x33333333; 
    virtual int mm(){printf("Tigerxx\n");return 0xCC;}
    virtual int nn(){printf("Tigerxx\n");return 0x99;};
};

typedef int (Animal::*methodPointerT)();

typedef struct {
    void** functionPtr;
    size_t offset;
} MP;

void devirtualize(methodPointerT& mp0,const Animal& a){
    MP& t=*(MP*)&mp0;
    if((intptr_t)t.functionPtr & 1){
        size_t index=(t.functionPtr-(void**)1); // there is obviously a more
        void** vTable=(void**)(*(void**)&a);        // efficient way. Just for clearness !
        t.functionPtr=(void**)vTable[index];
    }
};

int main()  
{
    int (Animal::*mp1)()=&Animal::nn;
    MP& mp1MP=*(MP*)&mp1;

  Animal x;Tiger y;

    (x.*mp1)();(y.*mp1)();

    devirtualize(mp1,x);

    (x.*mp1)();(y.*mp1)();

}
like image 44
George Kourtis Avatar answered Nov 01 '22 10:11

George Kourtis