Impact of Stack Growth Direction on Embedded Development

In embedded software development, the stack is one of the most important memory areas during program execution. Understanding the direction of stack growth not only helps us write safer code but also provides critical clues when debugging memory-related issues.

Stack Growth Direction

What is Stack Growth Direction

The stack growth direction refers to the direction in which the stack pointer moves during function calls. This determines the storage order of local variables, function parameters, and return addresses in memory.

Stack Growth Direction
Downward growth: High address → Low address
Upward growth: Low address → High address
Most processors: ARM, x86, MIPS
Few processors: Some DSPs
Stack pointer decrement: SP = SP - size
Stack pointer increment: SP = SP + size

Stack Growth Direction in Different Architectures

Processor Architecture Stack Growth Direction Typical Application Scenarios
ARM Cortex-M Downward growth IoT devices, MCU
x86/x86-64 Downward growth PCs, servers
MIPS Downward growth Routers, embedded systems
RISC-V Downward growth Emerging embedded platforms
Some DSPs Upward growth Digital signal processing

Detecting Stack Growth Direction

Theoretical Detection Methods

Address Comparison Method

#include <stdio.h>
#include <stdint.h>

typedef struct {
    uintptr_t addr;
    int depth;
} stack_info_t;

void detect_stack_growth_recursive(stack_info_t* info, int depth) {
    int local_var = 0x12345678;
    uintptr_t current_addr = (uintptr_t)&local_var;
    
    if (depth == 0) {
        info->addr = current_addr;
        info->depth = depth;
        printf("Initial stack address: 0x%lx\n", current_addr);
    } else if (depth == 10) {
        printf("Stack address at level %d: 0x%lx\n", depth, current_addr);
        printf("Address difference: %ld\n", (long)(current_addr - info->addr));
        
        if (current_addr < info->addr) {
            printf("Conclusion: Stack grows downward (High address → Low address)\n");
        } else {
            printf("Conclusion: Stack grows upward (Low address → High address)\n");
        }
        return;
    }
    
    detect_stack_growth_recursive(info, depth + 1);
}

int main() {
    stack_info_t info = {0};
    detect_stack_growth_recursive(&info, 0);
    return 0;
}

Function Call Chain Analysis

#include <stdio.h>
#include <stdint.h>

#define MAX_DEPTH 20

typedef struct {
    uintptr_t addresses[MAX_DEPTH];
    int count;
} call_stack_t;

void analyze_call_stack(call_stack_t* stack, int depth) {
    int dummy = 0xDEADBEEF;
    uintptr_t addr = (uintptr_t)&dummy;
    
    if (depth < MAX_DEPTH) {
        stack->addresses[stack->count++] = addr;
    }
    
    if (depth < 5) {
        analyze_call_stack(stack, depth + 1);
    } else {
        printf("Call stack analysis result:\n");
        for (int i = 0; i < stack->count - 1; i++) {
            int64_t diff = (int64_t)(stack->addresses[i+1] - stack->addresses[i]);
            printf("Level %d -> %d: Address difference = %+ld\n", 
                   i, i+1, diff);
        }
        
        // Determine growth direction
        int64_t first_diff = stack->addresses[1] - stack->addresses[0];
        if (first_diff < 0) {
            printf("Stack grows downward (Address decreases with each call)\n");
        } else {
            printf("Stack grows upward (Address increases with each call)\n");
        }
    }
}

Practical Detection Tools

GDB Debugger Detection

# Compile with debug information
gcc -g -O0 stack_direction.c -o stack_direction

# Use GDB to analyze stack layout
gdb stack_direction
(gdb) break main
(gdb) run
(gdb) info registers sp
(gdb) step
(gdb) info registers sp

Inline Assembly Detection

#include <stdio.h>
#include <stdint.h>

uintptr_t get_stack_pointer(void) {
    uintptr_t sp;
    
    #if defined(__arm__) || defined(__aarch64__)
        asm volatile("mov %0, sp" : "=r" (sp));
    #elif defined(__x86_64__)
        asm volatile("mov %%rsp, %0" : "=r" (sp));
    #elif defined(__i386__)
        asm volatile("mov %%esp, %0" : "=r" (sp));
    #else
        #error "Unsupported architecture"
    #endif
    
    return sp;
}

void monitor_stack_usage(void) {
    uintptr_t sp1 = get_stack_pointer();
    
    // Allocate some local variables
    char buffer[1024];
    int dummy = 0x12345678;
    
    uintptr_t sp2 = get_stack_pointer();
    
    printf("Stack pointer change: 0x%lx -> 0x%lx\n", sp1, sp2);
    printf("Change amount: %+ld bytes\n", (int64_t)(sp2 - sp1));
    
    if (sp2 < sp1) {
        printf("Stack grows downward\n");
    } else {
        printf("Stack grows upward\n");
    }
}

Impact of Stack Growth Direction on Development

Memory Layout Optimization

Memory Layout Design
Downward growing stack
Upward growing stack
High address: Stack area address: Heap area Low address: Code/Data
Low address: Stack area address: Heap area High address: Code/Data
Stack overflow detection: Sentinel set at stack bottom
Stack overflow detection: Sentinel set at stack top
Memory protection: Prevent stack from overwriting heap
Memory protection: Prevent stack from overwriting code

Stack Overflow Detection Mechanism

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define STACK_SIZE 4096
#define GUARD_SIZE 64
#define GUARD_PATTERN 0xDEADBEEF

typedef struct {
    uint32_t guard_before[GUARD_SIZE / 4];
    uint8_t stack[STACK_SIZE];
    uint32_t guard_after[GUARD_SIZE / 4];
} protected_stack_t;

static protected_stack_t g_stack;

// Initialize protected stack
void init_protected_stack(void) {
    // Fill with sentinel pattern
    for (int i = 0; i < GUARD_SIZE / 4; i++) {
        g_stack.guard_before[i] = GUARD_PATTERN;
        g_stack.guard_after[i] = GUARD_PATTERN;
    }
    
    printf("Protected stack initialization complete\n");
    printf("Stack size: %d bytes\n", STACK_SIZE);
    printf("Guard size: %d bytes\n", GUARD_SIZE);
}

// Check for stack overflow
int check_stack_overflow(void) {
    for (int i = 0; i < GUARD_SIZE / 4; i++) {
        if (g_stack.guard_before[i] != GUARD_PATTERN) {
            printf("Stack underflow detected! Location: guard_before[%d]\n", i);
            return -1;
        }
        if (g_stack.guard_after[i] != GUARD_PATTERN) {
            printf("Stack overflow detected! Location: guard_after[%d]\n", i);
            return 1;
        }
    }
    return 0; // Normal
}

// Simulate stack overflow
void simulate_stack_overflow(void) {
    char buffer[STACK_SIZE + 100]; // Intentionally exceed stack size
    memset(buffer, 0xAA, sizeof(buffer));
    
    printf("Simulated stack overflow complete\n");
    if (check_stack_overflow() != 0) {
        printf("Stack overflow detection successful!\n");
    }
}

Recursive Depth Control

#include <stdio.h>
#include <stdint.h>

// Recursive depth control based on stack growth direction
typedef struct {
    uintptr_t initial_sp;
    uintptr_t current_sp;
    size_t max_stack_usage;
    int max_depth;
} recursion_monitor_t;

static recursion_monitor_t g_monitor = {0};

void init_recursion_monitor(void) {
    #if defined(__arm__) || defined(__aarch64__)
        asm volatile("mov %0, sp" : "=r" (g_monitor.initial_sp));
    #elif defined(__x86_64__)
        asm volatile("mov %%rsp, %0" : "=r" (g_monitor.initial_sp));
    #endif
    
    g_monitor.current_sp = g_monitor.initial_sp;
    g_monitor.max_stack_usage = 0;
    g_monitor.max_depth = 0;
    
    printf("Recursion monitor initialization complete\n");
    printf("Initial stack pointer: 0x%lx\n", g_monitor.initial_sp);
}

int check_recursion_depth(int current_depth) {
    // Get current stack pointer
    #if defined(__arm__) || defined(__aarch64__)
        asm volatile("mov %0, sp" : "=r" (g_monitor.current_sp));
    #elif defined(__x86_64__)
        asm volatile("mov %%rsp, %0" : "=r" (g_monitor.current_sp));
    #endif
    
    // Calculate stack usage
    size_t stack_usage;
    if (g_monitor.current_sp < g_monitor.initial_sp) {
        // Downward growth
        stack_usage = g_monitor.initial_sp - g_monitor.current_sp;
    } else {
        // Upward growth
        stack_usage = g_monitor.current_sp - g_monitor.initial_sp;
    }
    
    if (stack_usage > g_monitor.max_stack_usage) {
        g_monitor.max_stack_usage = stack_usage;
        g_monitor.max_depth = current_depth;
    }
    
    // Check if close to stack limit
    if (stack_usage > 1024 * 1024) { // 1MB limit
        printf("Warning: Stack usage too high (%zu bytes)\n", stack_usage);
        return -1;
    }
    
    return 0;
}

// Safe recursive function example
int safe_factorial(int n, int depth) {
    if (check_recursion_depth(depth) < 0) {
        printf("Recursion depth exceeded, stopping execution\n");
        return -1;
    }
    
    if (n <= 1) return 1;
    return n * safe_factorial(n - 1, depth + 1);
}

———— END ————Impact of Stack Growth Direction on Embedded Development

We invite you to visit the Embedded MCU/MPU Ecosystem Zone

Impact of Stack Growth Direction on Embedded Development

Implementation solutions for embedded memory monitoring systems

Impact of Stack Growth Direction on Embedded Development

Do you need to learn registers for MCU development?

Leave a Comment