Calling Conventions
x86x86 (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, Nafter thecall). - Because the caller cleans up, cdecl supports variadic functions
(
printf) — the callee doesn't need to know the argument count.
push 3
push 2
push 1
call func ; func(1, 2, 3)
add esp, 12 ; caller pops 3 args → tells you this is cdeclstdcall — 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.). Theret imm16is the giveaway.
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
thispointer is passed inECX; remaining args on the stack; callee cleans up. - Seeing a
callwhereECXwas 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, Nafter the call → cdecl.ret Ninside 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 maybeEDX) before the call, with no matchingpush, means fastcall or thiscall — distinguish by whetherECXholds 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@Nis 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 inEAX,EDX,ECX.
See also: x64 Calling Conventions · ARM64 Calling Convention · The stack · CALL / RET.