Skip to content

Calling Conventions

x86

x86 (32-bit) Calling Conventions

cdecl, stdcall, fastcall and thiscall — the stack-based 32-bit ABIs you still meet in legacy Windows binaries, malware and DLLs. Who passes args where, and who cleans up.

Before x86-64 unified things around System V / Microsoft x64, 32-bit code used several competing conventions. They mostly differ in who cleans the arguments off the stack and whether the first few arguments go in registers. You still meet all of them in legacy Windows software, drivers and malware.

In every 32-bit convention below, arguments pushed on the stack go right-to-left, and the integer/pointer return value comes back in EAX (64-bit returns use EDX:EAX).

cdecl — the C default

  • Arguments: all on the stack.
  • Cleanup: the caller removes them (add esp, N after the call).
  • Because the caller cleans up, cdecl supports variadic functions (printf) — the callee doesn't need to know the argument count.
asm
push 3
push 2
push 1
call func          ; func(1, 2, 3)
add  esp, 12       ; caller pops 3 args  → tells you this is cdecl

stdcall — the Win32 API convention

  • Arguments: all on the stack.
  • Cleanup: the callee removes them with ret N (e.g. ret 0Ch).
  • Used by virtually the entire Win32 API (CreateFileW, etc.). The ret imm16 is the giveaway.
asm
push 2
push 1
call func          ; func(1, 2)
                   ; no stack fixup here — the callee did `ret 8`

fastcall — first two args in registers

  • Arguments: first two integer/pointer args in ECX, EDX; the rest on the stack.
  • Cleanup: callee (like stdcall). Microsoft spells it __fastcall.

thiscall — C++ non-static methods (MSVC)

  • The this pointer is passed in ECX; remaining args on the stack; callee cleans up.
  • Seeing a call where ECX was just loaded with an object pointer is the fingerprint of a C++ method call under MSVC — invaluable for reconstructing classes and vtables.

Reverse-engineering notes

  • Read the cleanup to read the convention. add esp, N after the call → cdecl. ret N inside the callee → stdcall/fastcall/thiscall. No fixup and no register args → likely cdecl with zero args, or the result is discarded.
  • A function loading ECX (and maybe EDX) before the call, with no matching push, means fastcall or thiscall — distinguish by whether ECX holds an object pointer (thiscall) or a plain argument (fastcall).
  • Name decoration encodes the convention in MSVC symbols: _func (cdecl), _func@N (stdcall), @func@N (fastcall). The trailing @N is the argument byte count.
  • GCC/Clang expose these via __attribute__((cdecl/stdcall/fastcall)); the modern, register-heavy __attribute__((regparm(3))) Linux variant passes up to three args in EAX, EDX, ECX.

See also: x64 Calling Conventions · ARM64 Calling Convention · The stack · CALL / RET.