FUNCTION CALLS
the stack and the call instruction are used for function calls and appear differently in assembly code. the three most common calling conventions encountered are cdecl, std-call and fastcall.
BASIC FUNCTION CALL

CDECL
with this convention, the parameters are pushed onto the stack from right-to-left, the caller cleans up the stack when the function is complete, and the return value is stored in EAX
#C CODE SNIPPPET - PSEUDO CODE
int test(int x, int y, int z);
int a, b, c, ret;
ret = test(a, b, c);
#ASSEMBLY CODE SNIPPET: CDECL
push c
push b
push a
call test
add esp, 12 //this represents that the stack is cleaned up by the caller
mov ret, eax
* in this example the cdecl pushed the parameters onto the stack from right-to-left, beginning with c
STDCALL
this convention is similar to cdecl, except stdcall requires the callee to clean up the stack when the function is complete
#C CODE SNIPPPET - PSEUDO CODE
int test(int x, int y, int z);
int a, b, c, ret;
ret = test(a, b, c);
#ASSEMBLY CODE SNIPPET: CDECL
push c
push b
push a
call test
//the add esp, 12 is not required with stdcall since the function called would be the one responsible for cleaning up the stack
//the epilogue would be responsible for cleaning up the stack
mov ret, eax
* in this example the cdecl pushed the parameters onto the stack from right-to-left, beginning with c
FASTCALL
in this convention, the first few arguments (typically two) are passed in registers, with the most commonly used registers being EDX and ECX (the MS fastcall convention). additional arguments are loaded from right-to-left, and the calling function is usually responsible for cleaning up the stack, if necessary. it is often more efficient to use fastcall than other conventions, because the code doesn't need to involve the stack as much.
PUSH VS MOVE
the compiler determines whether to use the PUSH instruction or an alternative approach, such as MOV with stack pointer adjustment (SUB), based on optimization strategies and architectural considerations when storing values on the stack. the MOV + SUB is sometimes preferred for manual stack management (e.g., aligning the stack for function calls or reducing dependencies in instruction execution pipelines)
#C CODE SNIPPET
int adder(int a, int b)
{
return a+b;
}
void main()
{
int x = 1;
int y = 2;
printf("the function returned the number %d\n", adder(x,y));
}
#ASSEMBLY CODE SNIPPET
00401730 push ebp
00401731 mov ebp, esp
00401733 mov eax, [ebp+arg_0]
00401736 add eax, [ebp+arg_4]
00401739 pop ebp
0040173A retn
* eax stores the return value
FUNCTION CALL WITH TWO DIFFERENT CALLING CONVENTIONS
#Visual Studio version
00401746 mov [ebp+var_4], 1
0040174D mov [ebp+var_8], 2
00401754 mov eax, [ebp+var_8]
00401757 push eax
00401758 mov ecx, [ebp+var_4]
0040175B push ecx
0040175C call adder
00401761 add esp, 8
00401764 push eax
00401765 push offset TheFunctionRet
0040176A call ds:printf
#GCC Version
00401085 mov [ebp+var_4], 1
0040108C mov [ebp+var_8], 2
00401093 mov eax, [ebp+var_8]
00401096 mov [esp+4], eax
0040109A mov eax, [ebp+var_4]
0040109D mov [esp], eax
004010A0 call adder
//no add esp, 8 and push eax
004010A5 mov [esp+4], eax
004010A9 mov [esp], offset TheFunctionRet
004010B0 call printf
Last updated