Understanding the cdecl Calling Convention
The cdecl (short for “C declaration”) calling convention is one of the most widely used methods for passing arguments between functions in the C programming language and other languages like C++. While several different calling conventions exist, cdecl has been the default convention used by most C compilers for x86 architectures.
So what exactly is a calling convention? It’s a set of rules and standards that define how arguments are passed from the caller to the callee function, how return values are handled, and which registers and memory locations are used for these purposes. Having a standardized calling convention ensures that different functions can communicate and interoperate correctly.
Here are the key points about how cdecl works:
Argument Passing
Arguments are pushed onto the stack from right to left in the reverse order they appear in the function prototype
It is the caller’s responsibility to push the arguments onto the stack before transferring control to the callee function
Stack Maintenance
The callee function is responsible for cleaning up (removing) the arguments from the stack when it finishes executing
This is done by adjusting the stack pointer (ESP register on x86) by an amount equal to the total size of the arguments
Return Values
Return values of up to 32 bits are returned in the EAX register
Larger return values (like structs) have space allocated on the stack by the caller, with the return value then copied to this memory by the callee
Register Usage
Most registers are considered “caller-save” meaning the callee function must preserve their values if it modifies them
A few registers (EAX, ECX, EDX) are “callee-save” meaning the caller doesn’t need to preserve their original values
By following these conventions, any function compiled with cdecl calling conventions can correctly interface with other cdecl functions even across different compilation units or libraries. The alternative would require much more complex coordination of stack frames and registers between callers and callees.
One downside of cdecl is that it is relatively inefficient compared to some newer conventions. Each argument push/pop operation takes time, and larger arguments like structs must be copied on the stack. However, cdecl’s simplicity and legacy made it ubiquitous on Intel x86 architectures for decades.
Newer calling conventions like Microsoft’s __stdcall aim to be more efficient by having the caller clean up stack arguments. However,cdecl remains an important foundation for understanding how functions communicate under the hood on many systems. Grasping cdecl is essential for low-level systems programming, reverse engineering, and deeply understanding the x86 architecture.