ADVERTISEMENT
Responsive Ad Unit • Systems Architecture & Low-Level Engineering

Module 5 — Stack Pointer (SP)

Learning Objectives

After completing this module, you should be able to answer:

  • What is a stack?
  • Why does a CPU need a stack?
  • What is the Stack Pointer?
  • Why does the stack usually grow downward?
  • How are function calls implemented?
  • How are local variables stored?
  • What happens during recursion?
  • What is a stack frame?
  • What is stack overflow?
  • How does the OS use the stack?
  • How does an RTOS use separate stacks?

1. What is a Stack?

A stack is a special memory area that follows the LIFO (Last In, First Out) principle.

Think of a stack of plates.

Plate D ← Top
Plate C
Plate B
Plate A

If you want Plate A, you must first remove D, C, and B.

Computers use exactly the same idea.

2. Why Do We Need a Stack?

Every program repeatedly calls functions.

Example:

main()
{
    fun1();
}
fun1()
{
    fun2();
}
fun2()
{
}

Question:
How does the CPU know where to return after fun2() finishes?

Answer:
The stack stores the return address.

Without a stack, the CPU would not know where execution should continue.

3. What is the Stack Pointer (SP)?

The Stack Pointer is a special CPU register.

It always points to the top of the stack.

Example:

Memory
0x20001000
---------------
0x20000FFC
---------------
0x20000FF8 <--- SP
---------------

Whenever something is pushed,
SP changes.

Whenever something is popped,
SP changes again.

4. Push Operation

Suppose
SP = 1000

Push value
10

After PUSH

AddressValue
99610 ← SP
1000

Notice
SP moved.

Diagram

Before

SP
↓
1000

After PUSH

996
↓
10
SP

5. Pop Operation

Current stack

996
↓
10
POP

Result
SP =1000

Value
10
removed.

Push/Pop Animation

PUSH

SP
↓
1000
↓
996
↓
992
↓
988

POP

988
↓
992
↓
996
↓
1000

6. Why Does the Stack Grow Downward?

Most CPUs use descending stacks.

Example

High Address
0xFFFFFFFF
↓
↓
↓
Stack
↓
↓
0x00000000
Low Address

Reasons include:

  • Efficient coexistence with the heap (which typically grows upward).
  • Simpler addressing modes on many architectures.
  • Historical CPU design choices.

Note: While downward-growing stacks are common (ARM, x86, many others), they are not universal. Some architectures use upward-growing stacks.

7. Function Call Example

void add()
{
    int x=10;
}
int main()
{
    add();
}

When main() calls add():

CPU performs:

Call add
↓
Push Return Address
↓
Jump to add()
↓
Execute
↓
Return

Stack During Function Call

Initially

High Address
---------------
SP
Low Address

After CALL

High Address
Return Address
↓
SP

After Local Variable

High Address
Return Address
Local Variable x
↓
SP

8. Function Parameters

Example

add(10,20);

Depending on the calling convention:

Arguments may be

  • Registers (preferred)
  • Stack (when registers are exhausted or required)

Example Stack

20
10
Return Address
Local Variables
SP

On ARM, the first few integer arguments are usually passed in registers (R0–R3 in AArch32, X0–X7 in AArch64), with additional arguments placed on the stack.

9. Local Variables

Example

void fun()
{
    int a;
    int b;
}

The compiler reserves stack space.

High Address
Return Address
a
b
↓
SP

When the function returns,
this memory is released automatically by adjusting the SP.

10. Nested Function Calls

Example

main()
↓
A()
↓
B()
↓
C()

Stack

Return C
Local C
--------------
Return B
Local B
--------------
Return A
Local A
--------------
Return Main
SP

Each function gets its own stack frame.

11. Stack Frame

A stack frame contains everything needed for one function invocation.

------------------
Function Arguments
------------------
Return Address
------------------
Saved Registers
------------------
Local Variables
------------------
Temporary Variables
------------------
SP

The OS and debugger use stack frames to unwind function calls.

12. Recursion

Example

factorial(5)

Calls

factorial(4)
↓
factorial(3)
↓
factorial(2)
↓
factorial(1)

Each call creates another stack frame.

factorial(1)
--------------
factorial(2)
--------------
factorial(3)
--------------
factorial(4)
--------------
factorial(5)

This is why deep recursion can exhaust stack space.

13. Stack Overflow

Suppose
Stack Size
8 KB

Recursive function

fun()
{
   fun();
}

Never returns.

Eventually

Stack
↓
↓
↓
↓
Memory End
×
Crash

This is a stack overflow.

In embedded systems, stack size is often fixed, making this especially important.

14. RTOS Example

RTOS

Task1
↓
Own Stack
Task2
↓
Own Stack
Task3
↓
Own Stack

Each task has its own Stack Pointer.

Context switch

Save SP
↓
Load Next Task SP
↓
Continue

This allows each task to resume exactly where it left off.

15. Interrupt Stack

Suppose
Image Processing running.
Camera interrupt occurs.

CPU

Push Registers
↓
Push PC
↓
Push Status Register
↓
SP changes
↓
ISR
↓
Restore
↓
Return

Many CPUs perform part of this automatically on exception entry.

Embedded Example (ADAS)

Suppose

Camera Frame Ready
↓
Interrupt
↓
CPU
↓
Save Registers
↓
Run ISR
↓
DMA Completed
↓
Restore Stack
↓
Continue Algorithm

The interrupted code resumes because the CPU preserved the execution state on the stack.

ARM Stack Example

main()
↓
BL add
↓
LR Saved
↓
SP Reduced
↓
Local Variables
↓
Return
↓
SP Restored

BL (Branch with Link) stores the return address in the Link Register. If needed (for nested calls), the compiler saves the Link Register on the stack.

x86 Stack Example

CALL add
↓
Push Return Address
↓
Jump
↓
RET
↓
Pop Return Address
↓
Continue

GDB Example

When you execute:

backtrace

GDB walks through stack frames to produce something like:

main()
↓
A()
↓
B()
↓
C()

This is how crash reports identify the call sequence.

Interview Questions

Beginner

  • What is a stack?
  • What is the Stack Pointer?
  • What is LIFO?
  • Why is the stack needed?

Intermediate

  • Explain PUSH and POP.
  • Why does the stack usually grow downward?
  • What is a stack frame?
  • How are local variables stored?
  • How are return addresses stored?

Senior

  • Explain a function call at the assembly level.
  • How does recursion affect the stack?
  • Explain stack overflow.
  • How does an RTOS use multiple stacks?
  • How does the CPU use the stack during an interrupt?
  • Why does a debugger rely on stack frames?

Common Mistakes

  • ❌ Thinking the stack stores the entire program. It mainly stores call-related data such as return addresses, local variables, saved registers, and sometimes function arguments.
  • ❌ Assuming all function parameters are passed on the stack. Modern ABIs pass many arguments in registers.
  • ❌ Believing local variables always live on the stack. Compilers may keep them in registers or optimize them away.
  • ❌ Assuming every architecture grows the stack downward. Most do, but not all.

Summary

The stack is a LIFO memory region that supports function calls, local variables, recursion, interrupts, and multitasking. The Stack Pointer always identifies the top of the current stack. During execution, the CPU and operating system continuously push and pop data as functions are called, interrupts occur, and tasks are switched. A solid understanding of the stack is essential for debugging, embedded development, and operating system internals.

Mini Quiz

  1. Why is the stack required for function calls?
  2. What does the Stack Pointer contain?
  3. Why does the stack usually grow downward?
  4. What information is stored in a stack frame?
  5. Why does recursion consume more stack memory?
  6. How does an RTOS allow multiple tasks to run independently using stacks?
  7. Why does a stack overflow occur?
Knowledge Check

Concept Mastery Assessment — Module 05

Verify your conceptual understanding with 12 targeted technical assessment questions. Instant feedback and explanations are provided for every choice.

Question 1 of 12
In x86 architectures, which direction does the call stack grow in virtual memory as items are pushed?
Explanation
On x86 and x86-64 architectures, the call stack grows downward from higher virtual addresses toward lower virtual addresses.
Question 2 of 12
What arithmetic operation is performed on the Stack Pointer (RSP) when an 8-byte value is pushed onto the stack via 'push rax' in x86-64?
Explanation
Because the stack grows toward lower addresses, pushing an 8-byte value decrements RSP by 8 and writes the operand at the new RSP address.
Question 3 of 12
What is the primary function of the Base Pointer (RBP / EBP) register in standard function prologues?
Explanation
The base pointer provides a stable reference point for the active stack frame, allowing local variables and function parameters to be accessed via fixed offsets.
Question 4 of 12
What instructions conventionally form the standard function prologue in x86-64 when a frame pointer is enabled?
Explanation
The prologue saves the caller's frame pointer ('push rbp'), establishes the new frame pointer ('mov rbp, rsp'), and reserves stack space for locals ('sub rsp, N').
Question 5 of 12
What is the 'red zone' in the System V AMD64 ABI specification for x86-64?
Explanation
The 128-byte red zone below RSP can be used by leaf functions without modifying RSP, as signal handlers and OS interrupts will not modify it.
Question 6 of 12
What security mechanism inserts a randomized canary value between local buffers and the saved base pointer/return address on the stack?
Explanation
Stack canaries (e.g., GCC -fstack-protector) place a secret sentinel value before the return address; before returning, the canary is checked, aborting if tampered.
Question 7 of 12
What fatal condition occurs if a recursive function fails to hit its terminating base case, allocating stack frames indefinitely?
Explanation
Unbounded recursion exhausts the thread stack limit, triggering a page fault when accessing the kernel's unmapped guard page at the bottom of the stack.
Question 8 of 12
What is the consequence of compiling C code with '-fomit-frame-pointer' in GCC?
Explanation
Omitting the frame pointer frees RBP/EBP as a general register, reducing prologue/epilogue overhead and addressing locals using RSP offsets.
Question 9 of 12
What is an 'activation record' on the call stack?
Explanation
An activation record (or stack frame) encapsulates all runtime execution context for an individual function invocation.
Question 10 of 12
How does the x86 'leave' instruction function during function epilogue?
Explanation
'leave' restores RSP to RBP (reclaiming local variable space) and pops the previous RBP off the stack, preparing for the 'ret' instruction.
Question 11 of 12
Why is returning a pointer to a local stack-allocated variable considered undefined behavior in C?
Explanation
Stack frames cease to be valid once a function returns. Pointers to stack locals become dangling pointers that point to reused stack memory.
Question 12 of 12
How are variable-length arrays (VLAs) or alloca() allocations managed on the runtime stack?
Explanation
alloca() and VLAs adjust RSP directly at runtime, expanding the current stack frame dynamically without heap allocator overhead.
0 / 12 (0%)
Great job! Review the concepts above and proceed when ready.
ADVERTISEMENT
Responsive Ad Unit • Technical Courses & Certifications