100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Procedures and Calling Conventions

Learn how assembly functions are called, how arguments and return values are passed, and how the stack frame is built and torn down.

Control FlowIntermediate11 min readJul 10, 2026
Analogies

CALL, RET, and the Return Address

CALL is not a jump in disguise: before transferring control to the target label, it pushes the address of the very next instruction (the return address) onto the stack, so the callee knows where to resume the caller when it finishes. RET pops that same address off the stack and jumps to it. This pairing only works correctly if the stack pointer (ESP/RSP) is in exactly the state CALL left it in when RET executes — if the procedure pushes extra data onto the stack without popping it back off before RET, the CPU will pop garbage instead of the real return address and jump to an essentially random location, typically crashing the program.

🏏

Cricket analogy: CALL is like a batsman leaving a marker at the crease showing exactly where to resume after a mid-over consultation with the physio, and RET is walking back to that exact marker — if something else is left on the pitch shifting the marker, the batsman resumes in the wrong spot entirely.

The Standard Prologue and Epilogue

Most procedures that use local variables or call other functions establish a stack frame using a standard prologue: PUSH EBP (save the caller's base pointer), MOV EBP, ESP (establish a new base pointer at the current stack top), and SUB ESP, N (reserve N bytes for local variables). The matching epilogue reverses this: MOV ESP, EBP (deallocate locals by resetting the stack pointer), POP EBP (restore the caller's base pointer), and RET. This convention gives every local variable and parameter a fixed, predictable offset relative to EBP throughout the function body, regardless of how the stack pointer itself moves during execution — which is why debuggers can reliably unwind stack frames.

🏏

Cricket analogy: The prologue is like a substitute fielder taking the field and first noting exactly where the regular fielder was standing (saving EBP), then setting up their own position relative to that spot — the epilogue is restoring the original fielder to that exact noted position before leaving.

x86asm
; int add(int a, int b) using the cdecl calling convention
section .text
    global add

add:
    push ebp              ; prologue: save caller's base pointer
    mov ebp, esp          ; establish new base pointer

    mov eax, [ebp+8]      ; first argument (a)
    add eax, [ebp+12]     ; second argument (b); result in eax

    pop ebp               ; epilogue: restore caller's base pointer
    ret                    ; pop return address and jump back

; caller side:
;   push second_arg
;   push first_arg
;   call add
;   add esp, 8            ; cdecl: caller cleans up the pushed arguments

Calling Conventions: Who Cleans Up, and Where Do Arguments Go

A calling convention is a contract specifying how arguments are passed, which register holds the return value, which registers the callee must preserve, and who is responsible for cleaning the stack after the call. Under 32-bit cdecl, arguments are pushed right-to-left and the caller cleans up the stack afterward, which allows variadic functions like printf. Under stdcall, the callee cleans up instead, producing smaller call sites at the cost of not supporting variable argument counts. Modern 64-bit calling conventions (System V AMD64 for Linux/macOS, Microsoft x64 for Windows) pass the first several integer arguments in registers — RDI, RSI, RDX, RCX, R8, R9 on System V — falling back to the stack only when registers are exhausted, which is significantly faster than always pushing to memory.

🏏

Cricket analogy: cdecl is like a fielding captain who resets the field placement after every over regardless of who set it (caller cleans up), while stdcall is like the bowler themselves resetting the field before walking off (callee cleans up) — both valid conventions, but only one supports an unpredictable number of overs like a variadic function supports variable arguments.

The System V AMD64 ABI (Linux, macOS) and the Microsoft x64 calling convention differ not just in which registers hold arguments but also in details like the 128-byte 'red zone' below RSP that System V functions may use without adjusting the stack pointer — code written against one convention will not work correctly if linked against code expecting the other.

Forgetting to restore a callee-saved register (such as EBX, ESI, EDI, or EBP under the standard x86 conventions) before returning will corrupt the caller's state in ways that may not manifest until much later in the program, making this class of bug notoriously hard to trace.

  • CALL pushes the return address onto the stack before jumping; RET pops it and jumps back.
  • An unbalanced stack (extra pushes without matching pops) causes RET to jump to a garbage address.
  • The standard prologue (PUSH EBP; MOV EBP,ESP; SUB ESP,N) creates a stack frame with fixed offsets for locals and parameters.
  • The epilogue (MOV ESP,EBP; POP EBP; RET) tears down the frame in the reverse order it was built.
  • cdecl passes arguments right-to-left on the stack and lets the caller clean up, enabling variadic functions.
  • stdcall has the callee clean up the stack, producing smaller call sites but no variadic support.
  • 64-bit conventions (System V, Microsoft x64) pass the first several arguments in registers for speed, falling back to the stack only when registers run out.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#AssemblyLanguageStudyNotes#ProceduresAndCallingConventions#Procedures#Calling#Conventions#CALL#StudyNotes#SkillVeris#ExamPrep