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

; Single-line macro 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

; File path: macro_multi.asm
; 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

; Advanced macro parameters 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

; Local labels in macros

; 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..@0001.skip), avoiding conflicts.


Comparison of Macros and Procedures

FeatureMacroProcedure
Implementation methodCompile-time text expansionRuntime CALL/RET
Execution overheadNo call overhead (direct inlining)Has CALL/RET overhead (approximately a few clock cycles)
Code sizeEach expansion increases code sizeOne copy of code, called multiple times
Parameter typesArbitrary text (registers, immediates, memory)Runtime values
DebuggingDifficult (no trace after expansion)Easy (has function call stack)
Applicable scenariosShort, frequently called, and type-flexible codeFunctions 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

; File path: cond_compile.asm
; 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:

ScenarioExample
Debug/Release toggle%if DEBUG / %else / %endif
Platform adaptation%ifdef LINUX / %ifdef WINDOWS
Feature switchesControl features by whether a symbol is defined

%rep Repeat Block

%repUsed to generate repeated code or data:

Example

; %rep repeat block 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

Overusing 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.

Other extensions