Assembly Language - Memory Management

Memory management is an important part of system programming. In assembly language, you can dynamically allocate and free memory through system calls, directly manipulate memory addresses, and achieve efficient memory management.


Memory Layout of a Program

A running program is distributed in memory as follows:

程序内存布局图

The memory layout of a Linux process is as follows:

高地址 (0xFFFFFFFF)
+----------------------+
|    内核空间           |  用户程序不可访问
+----------------------+  (0xC0000000)
|    栈 (Stack)        |  <-- ESP 指向栈顶
|    向下增长           |
+----------------------+
|                      |
|    内存映射区域        |  mmap 分配的区域
|                      |
+----------------------+
|    堆 (Heap)         |  <-- brk/sbrk 管理
|    向上增长           |
+----------------------+
|    BSS 段 (.bss)     |  未初始化全局变量
+----------------------+
|    数据段 (.data)     |  已初始化全局变量
+----------------------+
|    代码段 (.text)     |  程序指令(只读)
+----------------------+
低地址 (0x08048000)

Dynamic Memory Allocation: brk System Call

sys_brk(System call number 45) by adjusting the program'sdata segment boundary (program break)to allocate/free memory.

The program break is the end position of the data segment (including .data, .bss, and the heap). The program cannot access memory above it.

Calling methodDescriptionReturn value
ebx = 0Get current break addressEAX = current break address
ebx = 新地址Set new break addressEAX = new break address (returns original address on failure)

Example

; File path: brk_demo.asm
; Use sys_brk to dynamically allocate memory

section .data
    alloc_msg db 'Memory allocated successfully at: 0x'
    alloc_len equ $ - alloc_msg
    newline db 0xA

section .bss
    orig_break resd 1            ; Save the original break address

section .text
    global _start

_start:
    ; 1. Get the current program break
    mov eax, 45                  ; sys_brk
    mov ebx, 0                   ; ebx=0 means query the current break
    int 0x80
    mov [orig_break], eax        ; Save the original break address

    ; 2. Allocate 4096 bytes (4KB) of memory
    mov ebx, eax                 ; Current break address
    add ebx, 4096                ; Increase by 4096 bytes
    mov eax, 45                  ; sys_brk
    int 0x80
    ; EAX = new break address (i.e., the end of the allocated region)

    ; 3. The newly allocated memory is between orig_break and eax-1
    ; You can safely read and write this memory region
    mov ebx, [orig_break]        ; Get the starting address of the allocated region
    mov dword [ebx], 42          ; Write 42 into the new memory
    mov eax, [ebx]               ; Read it back

    ; 4. Free memory: restore the break to its original position
    mov eax, 45                  ; sys_brk
    mov ebx, [orig_break]        ; Restore to the original break
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

sys_brk can only adjust the continuous data segment boundary and cannot free memory in the middle. To free a block of allocated memory, you must first free all memory allocated after it. This is why modern programs more often use mmap or the malloc library functions.


Using mmap to Allocate Memory (Recommended)

sys_mmap2(System call number 192) is more flexible; it can independently allocate and free multiple memory blocks:

Example

; File path: mmap_demo.asm
; Use mmap to allocate independently freeable memory

section .data
    ; mmap-related constants
    PROT_READ equ 1
    PROT_WRITE equ 2
    MAP_PRIVATE equ 2
    MAP_ANONYMOUS equ 0x20

section .bss
    mem_block resd 1             ; Save the allocated memory address

section .text
    global _start

_start:
    ; mmap call: allocate 4096 bytes of anonymous memory
    mov eax, 192                 ; sys_mmap2
    mov ebx, 0                   ; Let the kernel choose the address
    mov ecx, 4096                ; Allocation size: 4KB
    mov edx, PROT_READ | PROT_WRITE  ; Readable and writable
    mov esi, MAP_PRIVATE | MAP_ANONYMOUS  ; Private anonymous mapping
    mov edi, -1                  ; File descriptor (use -1 for anonymous mapping)
    mov ebp, 0                   ; Offset (use 0 for anonymous mapping)
    int 0x80
    ; EAX = allocated memory address (returns a negative value on failure)

    cmp eax, -4096               ; Check if it failed
    ja  mmap_failed              ; If it is between -4095 and -1, it failed

    mov [mem_block], eax         ; Save the memory address

    ; Use the allocated memory: write data
    mov ebx, [mem_block]
    mov dword [ebx], 0x12345678  ; Write 4 bytes
    mov dword [ebx + 4], 'runo'  ; Write "runo"
    mov dword [ebx + 8], 'ob!!'  ; Write "ob!!"

    ; Free memory: munmap
    mov eax, 91                  ; sys_munmap
    mov ebx, [mem_block]         ; Memory address
    mov ecx, 4096                ; Release size
    int 0x80

    jmp exit

mmap_failed:
    ; Handle errors...

exit:
    mov eax, 1
    mov ebx, 0
    int 0x80

Memory Read/Write Operations

In assembly language, all memory accesses are done throughmovinstructions combined with square brackets:

Example

; Basic memory read/write operations

section .data
    var1 db 0x55                ; 1 byte
    var2 dw 0x1234              ; 2 bytes
    var3 dd 0x12345678          ; 4 bytes

section .text
    global _start

_start:
    ; Reading memory of different sizes
    mov al, [var1]              ; Read 1 byte: al = 0x55
    mov ax, [var2]              ; Read 2 bytes: ax = 0x1234
    mov eax, [var3]             ; Read 4 bytes: eax = 0x12345678

    ; Writing memory of different sizes
    mov byte [var1], 0xAA       ; Write 1 byte
    mov word [var2], 0xABCD     ; Write 2 bytes
    mov dword [var3], 0xDEADBEEF ; Write 4 bytes

    ; Accessing memory via pointers
    mov ebx, var3               ; ebx points to var3
    mov eax, [ebx]              ; Indirectly read the value of var3
    add dword [ebx], 1          ; var3 = var3 + 1

    mov eax, 1
    mov ebx, 0
    int 0x80

Safety Guidelines for Memory Operations

There is no "safety net" when operating on memory in assembly; keep the following rules in mind:

RuleDescriptionConsequence of violation
No out-of-bounds accessRead/write no more than the size of the variable/bufferData corruption, segmentation fault
Don't read uninitialized memoryValues in the .bss section are undefinedUnpredictable results
Don't read/write at invalid addressesEnsure the pointer points to valid memorySegmentation fault
Don't write to read-only memoryThe .text section and constant area are not writableSegmentation fault
Address alignmentWord accesses use even addresses; doubleword accesses use multiples of 4Performance degradation or bus error

Example

; Examples of common memory errors (warning: will cause crashes!)

section .data
    small_buf db 0, 0, 0, 0     ; Only 4 bytes

section .text
    global _start

_start:
    ; Dangerous operation 1: out-of-bounds write
    ; mov dword [small_buf + 3], 0x12345678
    ; This overwrites 3 bytes of data after small_buf!

    ; Dangerous operation 2: writing to the code section (read-only)
    ; mov dword [_start], 0x90 ; Attempting to modify code will cause a segmentation fault

    ; Dangerous operation 3: null pointer / invalid address
    ; mov eax,
    ; mov

    ; Safe approach: always operate within the allocated memory range
    mov dword [small_buf], 'runo'  ; Correct: operate within 4 bytes

    mov eax, 1
    mov ebx, 0
    int 0x80

Stack Memory Management

The stack is another important memory area, managed by the PUSH/POP instructions and the ESP register:

Example

; Comprehensive example of stack operations

section .data
    original_esp dd 0

section .text
    global _start

_start:
    mov [original_esp], esp      ; Save the original stack pointer

    ; PUSH: push onto stack (ESP -= 4, write data)
    push dword 100               ; Push 100
    ; ESP = ESP - 4, [ESP] = 100

    push dword 200               ; Push 200
    push dword 300               ; Push 300

    ; Stack layout:
    ; ESP+0: 300
    ; ESP+4: 200
    ; ESP+8: 100

    ; POP: pop off stack (read data, ESP += 4)
    pop eax                      ; eax = 300, ESP += 4
    pop ebx                      ; ebx = 200, ESP += 4
    pop ecx                      ; ecx = 100, ESP += 4

    ; Allocate local space on the stack
    sub esp, 256                 ; Allocate 256 bytes on the stack
    ; Now the 256 bytes from ESP to ESP+255 can be safely used
    mov dword [esp], 42          ; Write 42 to the local space
    add esp, 256                 ; Free the local space

    ; Make sure ESP returns to its original position!
    mov eax, 1
    mov ebx, 0
    int 0x80

The most important rule for stack operations:PUSH and POP must be paired. Before a function returns, you must ensure ESP returns to the correct position; otherwise RET will pop the wrong return address, causing the program to crash or execute arbitrary code. This is one of the most fatal bugs in assembly programming.


Comparison of Memory Management Methods

SchemeFlexibilityComplexityApplicable scenario
Global variables (.data/.bss)Low (fixed at compile time)LowestFixed-size data
Stack allocation (sub esp)Medium (dynamic within function)LowFunction local variables
brk/sbrkMedium (can only grow/shrink)MediumContiguous memory region
mmapHigh (arbitrary allocation/freeing)HighWhen flexible memory management is needed
Other extensions