The Stack
The stack is an area of memory that is reserved for stacking temporary items.
The operating system preallocates space for the stack and then puts the pointer to this memory in the stack pointer (%rsp).
While pointers generally refer to the beginning of a memory region at the beginning of a program %rsp points to the end of the memory region containing the stack.
You can add things to your stack using the push family of instructions
push does two things:
- decrements %rsp to point to the next location on the stack.
- copies the value to the location specified by
%rsp
You can then get the values back using the pop family of instructions, which does the reverse
Note
pushandpoparen’t the only way to touch the stack. Instead you can use%rspas your starting location and use offsetsmovq -8(%rsp), %raxthis loads the value from offset 8 from the current top of the stack. This is what compiled code does for locals.
Importance of the stack
Rather than having to memorize which variables are in use at which time, and making sure you don’t actually clobber something (like a register), you can store register values (and other values) on the stack before jumping out to another part of the program
Reserving Space on the Stack
All you have to do in order to do that is simply subtract 16 from the stack pointer using subq $16, %rsp. You just have to remember to add it back when you are done with it with addq $16, %rsp
Important
Everything done to the stack pointer has to be done in exactly the reverse order that it occurred when creating it.
Stack structure
Definition
stack frame: the section of the stack the belongs to a function. The stack frame consists of all of the local temporary storage needed for your function. Since each thread only has one stack, stack frames a way to break up the stack for each function to locally use.
Reminder of what memory looks like:
high 0x7fff... ┌──────────────┐
│ stack │ grows down ↓
├──────────────┤
│ (mmap region)│ shared libs, big mallocs
├──────────────┤
│ heap │ grows up ↑ (brk)
├──────────────┤
│ .bss / .data │
│ .text │
low 0x400000 └──────────────┘
Setting up the stack
- starting a function save the value of
%rbp(base of the stack frame) - after pushing the value of %rbp onto the stack, %rsp should be copied to %rbp. This makes %rbp point to the previous version of itself
# Save the pointer to the previous stack frame
pushq %rbp
# Copy the stack pointer to the base pointer for a fixed reference point
movq %rsp, %rbp
# Reserve however much memory on the stack I need
subq $NUMBYTES, %rsp# Restore the stack pointer
movq %rbp, %rsp
# Restore the base pointer
popq %rbpQuestion
What’s going on why do we need to perserve
%rbp?
Answer
The caller function had it’s own stack frame and same as you the caller needs to know where the base is, so it can use it’s offsets to locate values on the stack. So be nice to them (not that it’s optional) and save
%rbp.
These operations are common instructions were added to do both in a single instruction enter and leave. Note - the enter instruction is actually much slower than the equivalent set of instructions given before. Compilers will often opt for manual setup and use leave to clean up the stack.