C manually call function with stack and register

2.7k views Asked by At

i know this is the big deal to manipulate stack but i think it would be a great lesson for me. im searched the internet, and i found calling convention. I know how its working and why. I whant to simulate some of "Callee clean-up stack" maybe stdcall, fastcall its doesnt matter, important think is that who clean-up stack, then i will be have less work do to :)

for example. i have function in C

double __fastcall Add(int a, int b) {
    return a + b;
}

it will be Calee

and i have pointer to this function with type void*,

void* p = reinterpreted_cast<void*>(Add);

And i have function Caller

void Call(void* p, int a, int b) {

    //some code about push and pop arg
    //some code about save p into register
    //some code about move 'p' into CPU to call this function manually
    //some code about take of from stack/maybe register the output from function
}

And thats it, its helpful when i use calling convention "Calle clean-up" because i dont need

//some code about cleans-up this mess

I dont know how to do it, i know it can be done with assembler. but i afraid about it, and i never 'touch' this language. i would be greatful to simulate that calling with C, but when anyone can do it with ASM i will be haapy :)

I told also what i whant to do with it, when i will be know how to manually call function, i will be able to call function with several parameters(if i know the number and size of it) and any type of function. so i will be able to call any function in any language when that function is in the right calling convention.

I'm using Windows OS x64 and MinGw

1

There are 1 answers

8
Sergey L. On BEST ANSWER

First of all: C is intended to hide calling conventions and everything that is specific to how your code is executed from the programmer and provide an abstract layer above it.

The only condition when you need to (as you say) "manually" call a function is when you do it from asm.

C as a language has no direct control over the stack or the program counter.

To cite from GCC manual for fastcall for x86:

 On the Intel 386, the `fastcall' attribute causes the compiler to
 pass the first two arguments in the registers ECX and EDX.
 Subsequent arguments are passed on the stack. The called function
 will pop the arguments off the stack. If the number of arguments
 is variable all arguments are pushed on the stack.

Also as far as I remember return values are passed in EAX.

So in order to call a function in this way you need to provide the arguments in ECX, EDX and then invoke the call instruction on the function address

int __fastcall Add(int a, int b) {
    return a + b;
}

Please note I have changed the return type to int, because I do not remember how doubles are passed back.

int a, b;
// set a,b to something

void* p = reinterpreted_cast<void*>(Add);
int return_val;
asm (
    "call %3"
    : "=a" (return_val) // return value is passed in eax
    : "c" (a) // pass c in ecx
    , "d" (b) // pass b in edx
    , "r" (p) // pass p in a random free register
);

By calling convention it is up to the callee to clean up any used stack space. In this case we didn't use any, but if we did then your compiler will translate your function Add in such a way that it cleans up the stack automatically.

The code above is actually a hack in such a way that I use the GCC extended asm syntax to automatically put our variables into the appropriate registers. It will generate sufficient code around this asm call to make sure data is consistent.

If you wish to use a stack based calling convention then cdecl is the standard one

int __cdecl Add(int a, int b) {
    return a + b;
}

Then we need to push the arguments to the stack prior to calling

asm (
    "push %1\n" // push a to the stack
    "push %2\n" // push b to the stack
    "call %3"   // the callee will pop them from the stack and clean up
    : "=a" (return_val) // return value is passed in eax
    : "r" (a) // pass c in any register
    , "r" (b) // pass b in any register
    , "r" (p) // pass p in any register
);

One thing that I have not mentioned is that this asm call does not save any of our in-use registers, so I do not recommend putting this in a function that does anything else. In 32 bit x86 there is an instruction pushad that will push all general purpose registers to the stack and an equivalent (popad) to restore them. An equivalent for x86_64 is unavailable though. Normally when you compile C code the compiler will know which registers are in use and will save them in order for the callee not to overwrite them. Here it does not. If your callee uses registers that are in use by the caller - they will be overwritten!