x86 Assembly: Calling Conventions
CS 301 Lecture, Dr. Lawlor, 2005/10/03
Example of Local Variables
Here's an example where we set up a
stack frame (using %ebp as a frame pointer) with room for 4 integers,
and use two of the local variable slots to store two integers read in
from a file. Note that because calling a subroutine may
trash %eax, %ebx (if not position-independent), %ecx, and %edx, you
can't store the value in any of these registers!
push %ebp # Prologue
mov %esp,%ebp
sub $16,%esp # Make room for 4 4-byte ints
call read_input # Read A
mov %eax,-4(%ebp) # Store A into -4(%ebp)
call read_input # Read B
mov %eax,-8(%ebp) # Store B into -8(%ebp)
add -4(%ebp),%eax # A+B
push %eax # Print the value in %eax
call print_int
pop %ebx # Clean stack
mov %ebp,%esp # Epilogue
pop %ebp # (these two lines=="leave")
Calling Conventions
SysV ABI (Page 35-43) / win32 "cdecl"
- Subroutine return value in %eax
- Subroutine arguments are pushed onto the stack in right-to-left
order (rightmost is pushed first, so it's the deepest in the
stack). Before you call a subroutine, you push the arguments;
after the call, you pop them.
- ALL CODE must preserve %esp, %ebp, %edi, %esi. You can
still use these registers, but you've got to save and restore them
first!
- ALL CODE can trash (use without saving) %eax, %ecx, %edx.
- Position-independent code (shared libraries) MUST preserve %ebx; other code can trash %ebx.
Win32 "stdcall":
- As above, but now the called subroutine pops its own arguments!
Win32 "fastcall":
- As above, but first two arguments are passed in %ecx and %edx. Remaining arguments are passed on the stack.
Non-Lecture Bonus Material
Raw machine code: .byte
With UNIX assembly, you can add plain machine code bytes with the ".byte" command, like this:
.byte 0xb8, 0x0d, 0xd0, 0,0 # == mov <const>, %eax
.byte 0xc3 # == ret
You can get the machine code from the disassembly. Try it!
Enter Instruction
The x86 "enter stack_size, nesting" instruction can be used at the start of a subroutine to set up a stack frame. "stack_size" is the number of bytes of stack space to leave for local variables. "nesting"
is used for nested procedures (which don't exist in C!), so it's
normally 0. So an "enter $16,$0" call is exactly equivalent to
the standard three-line function prologue above:
push %ebp # Prologue
mov %esp,%ebp
sub $16,%esp # Make room for 4 4-byte ints
This means "enter" is the exact analogue of "leave". I think I'll
keep using the push/mov/sub, just because that way it's clearer exactly
what happens.