j Stack Operations - CodeToLive

CodeToLive

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:

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:

+-------------------+
|     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:

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:

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:

Next Steps

Now that you understand stack operations, you can learn about: