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 method | Description | Return value |
|---|---|---|
ebx = 0 | Get current break address | EAX = current break address |
ebx = 新地址 | Set new break address | EAX = new break address (returns original address on failure) |
Example
; 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
; 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
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:
| Rule | Description | Consequence of violation |
|---|---|---|
| No out-of-bounds access | Read/write no more than the size of the variable/buffer | Data corruption, segmentation fault |
| Don't read uninitialized memory | Values in the .bss section are undefined | Unpredictable results |
| Don't read/write at invalid addresses | Ensure the pointer points to valid memory | Segmentation fault |
| Don't write to read-only memory | The .text section and constant area are not writable | Segmentation fault |
| Address alignment | Word accesses use even addresses; doubleword accesses use multiples of 4 | Performance degradation or bus error |
Example
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
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
| Scheme | Flexibility | Complexity | Applicable scenario |
|---|---|---|---|
| Global variables (.data/.bss) | Low (fixed at compile time) | Lowest | Fixed-size data |
| Stack allocation (sub esp) | Medium (dynamic within function) | Low | Function local variables |
| brk/sbrk | Medium (can only grow/shrink) | Medium | Contiguous memory region |
| mmap | High (arbitrary allocation/freeing) | High | When flexible memory management is needed |