Overview

The x86-64 stack grows towards lower addresses. When a procedure is invoked, more spack on the top of the stack is allocated for that procedure to make use of. This portion of the stack is called a frame. The general shape of the stack looks as follows:

Note parts of this diagram are omitted when possible. For instance, a stack frame may not exist at all if all arguments to a leaf procedure can be passed through registers. A leaf procedure is a function that does not call another function.

Local Variables

x86-64 employs 6 registers for passing integral (i.e. integer and pointer) arguments between caller and callee.

BitsArg 1Arg2Arg3Arg4Arg5Arg6
64%rdi%rsi%rdx%rcx%r8%r9
32%edi%esi%edx%ecx%r8d%r9d
16%di%si%dx%cx%r8w%r9w
8%dil%sil%dl%cl%r8b%r9b

If more than 6 integral arguments are specified to a procedure, the surplus are placed onto the stack in the caller’s frame. The 7th argument is placed closer to the top of the stack (i.e. with lower address) than subsequent arguments.

In some cases, integral values still need to be placed onto the stack. For example, operator & is applied to a local variable and hence we must be able to generate an address for it.

Callee-Saved Registers

x86-64 employs 6 registers as callee-saved registers. If procedure P calls procedure Q, Q must preserve the values of these registers. That is, if Q were to modify these registers, Q is also responsible for restoring these values before returning back to P.

Bits
64%rbx%rbp%r12%r13%r14%r15
32%ebx%ebp%r12d%r13d%r14d%r15d
16%bx%bp%r12w%r13w%r14w%r15w
8%bl%bpl%r12b%r13b%r14b%15b

Frame Pointer

The frame pointer, also known as the base pointer, marks the start of a variable-sized frame. %rbp is pushed onto the stack (since it is caller-saved) and its value updated to the new value of %rsp. Relative offsets are then computed according to it.

Bibliography

  • Bryant, Randal E., and David O’Hallaron. Computer Systems: A Programmer’s Perspective. Third edition, Global edition. Always Learning. Pearson, 2016.