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