Stack Operations in Assembly
The stack is a fundamental data structure in assembly programming used for function calls, local variables, and temporary storage. In x86 architecture, the stack grows downward in memory, and the ESP register points to the top of the stack.
Stack Basics
The stack operates on a Last-In-First-Out (LIFO) principle. Key characteristics:
- Grows downward in memory (from high addresses to low addresses)
- ESP (Stack Pointer) register points to the top of the stack
- EBP (Base Pointer) often used to reference local variables and parameters
- PUSH decreases ESP and stores data
- POP retrieves data and increases ESP
High Memory Addresses +-------------------+ | | | Stack | Grows downward | | +-------------------+ | | ESP points here +-------------------+ Low Memory Addresses
Basic Stack Instructions
Instruction | Description | Effect on ESP |
---|---|---|
PUSH reg/mem/imm | Push value onto stack | ESP = ESP - 4 |
POP reg/mem | Pop value from stack | ESP = ESP + 4 |
PUSHAD | Push all general-purpose registers | ESP = ESP - 32 |
POPAD | Pop all general-purpose registers | ESP = ESP + 32 |
PUSHFD | Push EFLAGS register | ESP = ESP - 4 |
POPFD | Pop EFLAGS register | ESP = ESP + 4 |
Simple Stack Example
section .text
global _start
_start:
mov eax, 42
push eax ; Push 42 onto stack (ESP decreases by 4)
push 100 ; Push 100 onto stack (ESP decreases by 4)
pop ebx ; Pop top value (100) into EBX (ESP increases by 4)
pop ecx ; Pop next value (42) into ECX (ESP increases by 4)
Stack Frame
A stack frame is created for each function call, containing:
- Function parameters (pushed by caller)
- Return address (pushed by CALL instruction)
- Saved EBP (previous frame pointer)
- Local variables
+-------------------+ | Parameter 3 | EBP + 16 +-------------------+ | Parameter 2 | EBP + 12 +-------------------+ | Parameter 1 | EBP + 8 +-------------------+ | Return Address | EBP + 4 +-------------------+ | Saved EBP | EBP points here +-------------------+ | Local Variable | EBP - 4 +-------------------+ | Local Variable | EBP - 8 +-------------------+
Function Prologue and Epilogue
Standard function setup and cleanup sequences:
Prologue
push ebp ; Save old base pointer
mov ebp, esp ; Set new base pointer
sub esp, N ; Allocate space for locals (N bytes)
Epilogue
mov esp, ebp ; Deallocate locals
pop ebp ; Restore old base pointer
ret ; Return to caller
Function Call Example
section .text
global _start
; Function to add two numbers
add_numbers:
push ebp ; Prologue
mov ebp, esp
mov eax, [ebp+8] ; First parameter
add eax, [ebp+12] ; Second parameter
mov esp, ebp ; Epilogue
pop ebp
ret
_start:
push 20 ; Push second parameter
push 10 ; Push first parameter
call add_numbers ; Call function
add esp, 8 ; Clean up parameters
; EAX now contains 30
Stack Alignment
Modern systems often require the stack to be aligned on 16-byte boundaries when making function calls. This is especially important when calling library functions or system calls.
section .text
global _start
_start:
and esp, -16 ; Align stack to 16 bytes
; ... rest of code ...
Stack Overflow
A stack overflow occurs when the stack grows beyond its allocated memory space. This can happen with:
- Deep or infinite recursion
- Allocating large local arrays on the stack
- Excessive pushing without popping
Stack and Local Variables
Local variables are allocated on the stack by adjusting ESP:
section .text
global _start
my_function:
push ebp
mov ebp, esp
sub esp, 12 ; Allocate space for 3 integers (12 bytes)
; Access local variables
mov dword [ebp-4], 10 ; First local
mov dword [ebp-8], 20 ; Second local
mov dword [ebp-12], 30 ; Third local
; ... use locals ...
mov esp, ebp ; Clean up locals
pop ebp
ret
Stack and Parameters
Function parameters are typically passed on the stack in reverse order (right to left):
section .text
global _start
; Function with parameters
print_sum:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; First parameter
add eax, [ebp+12] ; Second parameter
; ... print or use the sum ...
mov esp, ebp
pop ebp
ret
_start:
push 30 ; Second parameter
push 40 ; First parameter
call print_sum
add esp, 8 ; Clean up parameters
Stack in Interrupt Handling
When an interrupt occurs, the processor pushes the following onto the stack:
- EFLAGS register
- CS (Code Segment)
- EIP (Instruction Pointer)
For error codes, additional information may be pushed.
Stack and System Calls
System calls often use the stack to pass parameters. In Linux:
section .data
message db 'Hello, World!', 0xA
len equ $ - message
section .text
global _start
_start:
; Write system call
mov eax, 4 ; sys_write
mov ebx, 1 ; stdout
mov ecx, message ; message pointer
mov edx, len ; message length
int 0x80 ; invoke kernel
; Exit system call
mov eax, 1 ; sys_exit
xor ebx, ebx ; exit code 0
int 0x80
Stack Frame Pointer Omission
In optimized code, EBP may not be used as a frame pointer, freeing it up as a general-purpose register. Local variables are then referenced relative to ESP.
optimized_function:
sub esp, 8 ; Allocate space for locals
mov [esp+4], eax ; Store in first local
mov [esp], ebx ; Store in second local
; ... use locals ...
add esp, 8 ; Clean up
ret
Stack Safety
Important stack safety considerations:
- Always balance PUSH and POP operations
- Clean up parameters after function calls
- Be careful with stack alignment in mixed environments
- Avoid buffer overflows that can overwrite return addresses
Next Steps
Now that you understand stack operations, you can learn about: