I am pretty sure you already know this, but I am going to go over it anyway. One of the problem is that changing flags for GCC can cause enormous differences in emitted machine instructions. Pretty much right there you have a problem of having the correct preamble and prolog that GCC expects. Mainly, you have no _easy_ way to generate the code it wants due to a flick of the hat of what might be going from
-O0 to
-O3. Using
-O3 will allow the compiler to start reordering instructions and this includes moving something from the preamble and prolog of the function somewhere else into the guts of it if need be. Using -fomit-frame-pointer you can see:
Code: Select all
08048430 <_ZN2cA1fEv>:
8048430: 8b 44 24 04 mov 0x4(%esp),%eax
8048434: c7 00 01 00 00 00 movl $0x1,(%eax)
804843a: c3 ret
804843b: 90 nop
804843c: 8d 74 26 00 lea 0x0(%esi),%esi
Code: Select all
0804841c <_ZN2cA1fEv>:
804841c: 55 push %ebp
804841d: 89 e5 mov %esp,%ebp
804841f: 8b 45 08 mov 0x8(%ebp),%eax
8048422: c7 00 01 00 00 00 movl $0x1,(%eax)
8048428: 5d pop %ebp
8048429: c3 ret
Now I am about to suggestion a sludgy work around since I am assuming that the code is generates is not the actual problem, but instead the code that is executed first is the problem. I am deducing my assumption from your noting of system calls which can derive from interrupts or a interrupt like mechanism provided by the special system call instructions.
This sludgy work around is really just a method to wrap an all virtual methods in a class with your own
prolog and
epilog code, while preserving the code GCC emits. The only pitfall is if the C++ ABI changes for GCC and it starts passing the
this pointer with another mechanism besides the first argument on the stack, or something similar so this is at your own risk.
This code creates a new virtual table for the class instance, and modifies the specified virtual functions to call the wrapper first.
example.cc
Code: Select all
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <malloc.h>
class cA
{
public:
uint32_t val;
cA()
{
val = 10;
}
~cA()
{
val = 20;
}
virtual void fa(){val = 1; printf("called me\n");}
virtual void fb(){val = 1;}
virtual void fc(){val = 1;}
virtual void fd(){val = 1;}
};
extern uint8_t class_vfunc_wrapper_start;
extern uint8_t class_vfunc_wrapper_end;
extern uint8_t class_vfunc_wrapper_loadreg_prologcall;
extern uint8_t class_vfunc_wrapper_loadreg_membercall;
extern uint8_t class_vfunc_wrapper_loadreg_epilogcall;
void wrap_class_vfuncs(uintptr_t instance, uintptr_t prolog, uintptr_t epilog, uint32_t vfc, uint8_t count, ...)
{
uintptr_t *vte = (uintptr_t*)((uintptr_t*)instance)[0];
va_list ap;
va_start(ap, count);
// create a new virtual table.
uint32_t *nvt = new uint32_t[vfc];
for(uint32_t x = 0; x < vfc; ++x)
{
nvt[x] = vte[x];
}
// set new virtual table in instance.
((uintptr_t*)instance)[0] = (uintptr_t)nvt;
for(; count > 0; --count)
{
// yeild thirty-two bit index from byte index.
uint32_t index = (va_arg(ap, uintptr_t) >> 2);
// we found function's address in the vtable, create a wrapper instance.
uint8_t *wrapperCode = (uint8_t*)memalign(32, (uintptr_t)&class_vfunc_wrapper_end - (uintptr_t)&class_vfunc_wrapper_start);
// copy wrapper code into wrapper instance.
memcpy(wrapperCode, &class_vfunc_wrapper_start, ((uintptr_t)&class_vfunc_wrapper_end) - ((uintptr_t)&class_vfunc_wrapper_start));
printf("wrapper code size:%x\n", ((uintptr_t)&class_vfunc_wrapper_end) - ((uintptr_t)&class_vfunc_wrapper_start));
// set wrapper instance call values (very hacky)
*(uint32_t*)(((uintptr_t)&class_vfunc_wrapper_loadreg_prologcall - (uintptr_t)&class_vfunc_wrapper_start) + (uintptr_t)wrapperCode + 1) = prolog;
*(uint32_t*)(((uintptr_t)&class_vfunc_wrapper_loadreg_epilogcall - (uintptr_t)&class_vfunc_wrapper_start) + (uintptr_t)wrapperCode + 1) = epilog;
*(uint32_t*)(((uintptr_t)&class_vfunc_wrapper_loadreg_membercall - (uintptr_t)&class_vfunc_wrapper_start) + (uintptr_t)wrapperCode + 1) = vte[index];
printf("vte:%x\n", vte[index]);
nvt[index] = (uintptr_t)wrapperCode;
printf("wrapped class member function, wrapper function address %x\n", wrapperCode);
}
return;
}
typedef void (*pmf)(void);
extern uint8_t example_prolog;
extern uint8_t example_epilog;
int main()
{
cA *a = new cA();
wrap_class_vfuncs((uintptr_t)a, (uintptr_t)&example_prolog, (uintptr_t)&example_epilog, 4, 1, &cA::fa);
a->fa();
a->fa();
return 1;
}
This is the wrapper code, and it includes a seperate function for the prolog and epilog. You do not have to use a seperate function, but I included them just for show.
example.s
Code: Select all
.global class_vfunc_wrapper_start
.global class_vfunc_wrapper_end
.global class_vfunc_wrapper_loadreg_prologcall
.global class_vfunc_wrapper_loadreg_membercall
.global class_vfunc_wrapper_loadreg_epilogcall
class_vfunc_wrapper_start:
class_vfunc_wrapper_loadreg_prologcall:
movl $0, %ebx
call *%ebx
class_vfunc_wrapper_loadreg_membercall:
movl $0, %ebx
movl 4(%esp), %eax
push %eax
call *%ebx
pop %edx
push %eax
class_vfunc_wrapper_loadreg_epilogcall:
movl $0, %ebx
call *%ebx
pop %eax
ret
class_vfunc_wrapper_end:
.global example_prolog
.global example_epilog
example_prolog:
ret
example_epilog:
ret
g++ example.cc example.s -o example