Assembly Language - Macros
A macro is a code reuse mechanism provided by NASM that allows you to expand code templates at compile time, reducing the need to repeatedly write similar code.
What is a Macro
MacroIt is a compile-time text replacement mechanism that is expanded by the assembler's preprocessor before compilation.
Unlike procedures, macros have no CALL/RET overhead—each time a macro is used, the assembler directly copies the macro's content to the location of use.
A macro is not a function call and has no stack frame overhead; however, the executable file size becomes larger because a copy of the code is made on each expansion.
Single-line Macro: %define
%defineIt is a single-line macro with simple syntax:
Example
; Define a constant
%define MAX_SIZE 256
%define APP_NAME 'example'
; Define a macro with parameters (macro function)
%define mul_by_2(x) (x * 2)
%define sum3(a, b, c) ((a) + (b) + (c))
section .text
global _start
_start:
mov eax, MAX_SIZE ; eax = 256
mov eax, mul_by_2(10) ; eax = (10 * 2) = 20
mov eax, sum3(1, 2, 3) ; eax = ((1)+(2)+(3)) = 6
; Redefinition (%define can be changed)
%define MAX_SIZE 512
mov eax, MAX_SIZE ; eax = 512
; Undefine
%undef MAX_SIZE
; mov eax, MAX_SIZE ; Error: MAX_SIZE is not defined
mov eax, 1
mov ebx, 0
int 0x80
%defineMacro expansion is pure text replacement. For example,mul_by_2(2+3)expands to(2+3 * 2), due to operator precedence, the result is not 10 but 8. This is why macro parameters must be enclosed in parentheses.
Multi-line Macro: %macro / %endmacro
%macroUsed to define complex macros containing multiple instructions:
Example
; Multi-line macro example
; Macro definition: exit program
; Parameter argc: how many parameters the macro accepts
%macro exit_program 1
mov eax, 1 ; sys_exit
mov ebx, %1 ; The first parameter is used as the exit code
int 0x80
%endmacro
; Macro definition: print string
; Accepts 2 parameters: string address, length
%macro print_string 2
push eax
push ebx
push ecx
push edx
mov eax, 4 ; sys_write
mov ebx, 1 ; stdout
mov ecx, %1 ; String address
mov edx, %2 ; String length
int 0x80
pop edx
pop ecx
pop ebx
pop eax
%endmacro
section .data
msg db 'Hello, EXAMPLE!', 0xA
msg_len equ $ - msg
section .text
global _start
_start:
print_string msg, msg_len ; Use the macro to print a message
print_string msg, msg_len ; Call again
exit_program 0 ; Exit program
Advanced Usage of Macro Parameters
NASM macros support default parameters, parameter counting, and conditional expansion:
Example
; Macro with default parameters (parameter range 2-3)
%macro debug_print 2-3 1 ; At least 2 parameters, at most 3, the 3rd defaults to 1
%if %3 = 1 ; If the 3rd parameter = 1 (debug mode enabled)
push eax
push ebx
push ecx
push edx
mov eax, 4
mov ebx, 1
mov ecx, %1
mov edx, %2
int 0x80
pop edx
pop ecx
pop ebx
pop eax
%endif
%endmacro
; Macro with a variable number of parameters
%macro push_registers 1-* ; 1 to any number of parameters
%rep %0 ; %0 is the number of parameters
push %1 ; Expand the 1st
%rotate 1 ; Rotate the parameter list to the left
%endrep
%endmacro
section .text
global _start
_start:
; Use push_registers to save multiple registers
push_registers eax, ebx, ecx, edx
; Expands to:
; push eax
; push ebx
; push ecx
; push edx
; Correspondingly pop
pop edx
pop ecx
pop ebx
pop eax
mov eax, 1
mov ebx, 0
int 0x80
Local Labels in Macros
In macros, use%%labelto define local labels, avoiding label conflicts during multiple expansions:
Example
; Compare two values and set the minimum
%macro min_val 2
mov eax, %1
mov ebx, %2
cmp eax, ebx
jle %%skip ; ★ Local label, a unique name is generated for each expansion
mov eax, ebx
%%skip:
%endmacro
; If local labels are not used, duplicate skip labels appear after two expansions
section .text
global _start
_start:
min_val 10, 5 ; eax = 5
min_val eax, 3 ; eax = 3
mov eax, 1
mov ebx, 0
int 0x80
Expanding a normal label twice in a macro causes a label name collision error. Local labels
%%labelmake NASM generate a unique label name for each expansion (such as[email protected]), avoiding conflicts.
Comparison of Macros and Procedures
| Feature | Macro | Procedure |
|---|---|---|
| Implementation method | Compile-time text expansion | Runtime CALL/RET |
| Execution overhead | No call overhead (direct inlining) | Has CALL/RET overhead (approximately a few clock cycles) |
| Code size | Each expansion increases code size | One copy of code, called multiple times |
| Parameter types | Arbitrary text (registers, immediates, memory) | Runtime values |
| Debugging | Difficult (no trace after expansion) | Easy (has function call stack) |
| Applicable scenarios | Short, frequently called, and type-flexible code | Functions with complex logic and large code size |
Conditional Compilation: %if / %elif / %else / %endif
The macro preprocessor supports conditional compilation, which can select and generate different code based on symbol definitions:
Example
; Conditional compilation example: debug and release modes
%define DEBUG 1 ; 1=debug mode, 0=release mode
section .data
msg db 'Program running...', 0xA
msg_len equ $ - msg
debug_msg db '[DEBUG] Entering function', 0xA
debug_len equ $ - debug_msg
section .text
global _start
_start:
%if DEBUG = 1
; Debug mode: output debug information
mov eax, 4
mov ebx, 1
mov ecx, debug_msg
mov edx, debug_len
int 0x80
%endif
; Normal business logic
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, msg_len
int 0x80
mov eax, 1
mov ebx, 0
int 0x80
Common scenarios for conditional compilation:
| Scenario | Example |
|---|---|
| Debug/Release toggle | %if DEBUG / %else / %endif |
| Platform adaptation | %ifdef LINUX / %ifdef WINDOWS |
| Feature switches | Control features by whether a symbol is defined |
%rep Repeat Block
%repUsed to generate repeated code or data:
Example
section .data
; Generate a 256-byte lookup table
; 0, 1, 4, 9, 16, 25, ... (square number table)
%assign i 0
; Here %rep cannot be used to generate a square table for complex calculations
; Simple example: generate ASCII table for 0-9
digits: db '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
section .text
global _start
_start:
; %rep repeats instructions in code
mov eax, 0
%rep 5 ; Repeat 5 times
inc eax ; eax increments by 1 each time
%endrep
; eax = 5
mov ebx, eax
mov eax, 1
int 0x80
Other extensionsOverusing macros makes code difficult to read and debug. When the macro body exceeds 10 lines, consider changing to a procedure call. In performance-sensitive hot paths, the inlining effect of short macros has obvious benefits.