Define Symbols in NASM

When writing assembly code, we often need to adapt the program to different environments, architectures, or configurations. NASM (Netwide Assembler) provides a powerful feature called symbols that allows you to define and use constants, macros, and conditional compilation directives.

What are Symbols in NASM ?

Symbols in NASM are named constants or values that can be used throughout the assembly code. They are defined using directives like equ, %define, or the -d command-line flag. Symbols make the code more readable, reusable, and adaptable to different scenarios.

Why Use Symbols ?

1 Readability:

Symbols give meaningful names to values, making your code easier to understand.

mov eax, 10          ; Hardcoded value
mov eax, BUFFER_SIZE ; Symbolic value (more readable)

2 Portability:

Symbols allow you to write code that can be easily adapted to different platforms or architectures.

%ifdef LINUX
    ; Linux-specific code
%elifdef WINDOWS
    ; Windows-specific code
%endif

3 Maintainability:

By centralizing values in symbols, we can update them in one place instead of searching in one place instead of searching through your entire codebase.

How to Define Symbols in NASM

There are several ways to define symbols in NASM:

1 Using equ for Constants

The equ directive defines a constant symbol. Once defined, the symbol cannot be redefined.

BUFFER_SIZE equ 1024    ; Define a constant
mov eax, BUFFER_SIZE    ; Use the constant

2 Using %define for Macros

The %define directive defines a macro or a symbolic constant. Unlike equ, %define can be redefined.

%define DEBUG 1         ; Define a symbol
%if DEBUG == 1
    ; Debug-specific code
%endif

3 Using -d at the Command Line

The -d flag defines a symbol when invoking NASM. This is useful for conditional compilation.

nasm -f elf64 -dDEBUG myfile.asm -o myfile.o

In the code:

%ifdef DEBUG
    ; Debug-specific code
%endif

We can also define a symbol with a value:

nasm -f elf64 -dARCH=64 myfile.asm -o myfile.o

In the code:

%if ARCH == 64
    ; 64-bit code
%else
    ; 32-bit code
%endif

Practical Examples of Using Symbols

Example 1: Platform-Specific Code

We can use symbols to write code that works on multiple platforms.

Command Line

nasm -f elf64 -dLINUX myfile.asm -o myfile.o  # For Linux
nasm -f win64 -dWINDOWS myfile.asm -o myfile.o  # For Windows

Assembly Code

%ifdef LINUX
    ; Linux-specific code
    mov eax, 1  ; syscall: write
    mov edi, 1  ; file descriptor: stdout
    mov rsi, msg
    mov rdx, len
    syscall
%elifdef WINDOWS
    ; Windows-specific code
    extern MessageBoxA
    push 0
    lea rcx, [msg]
    lea rdx, [caption]
    xor r8, r8
    call MessageBoxA
%endif

Example 2: Debug Mode

We can use symbols to include or exclude debug-specific code.

Command Line

nasm -f elf64 -dDEBUG myfile.asm -o myfile.o

Assembly Code

%ifdef DEBUG
    ; Debug-specific code
    mov eax, 1
    mov edi, 1
    lea rsi, [debug_msg]
    mov rdx, debug_len
    syscall
%endif

Example 3: Architecture-Specific Code

We can use symbols to write code that works for both 32-bit and 64-bit architecture.

Command Line

nasm -f elf64 -dARCH=64 myfile.asm -o myfile.o  # For 64-bit
nasm -f elf32 -dARCH=32 myfile.asm -o myfile.o  # For 32-bit

Assembly Code

%if ARCH == 64
    ; 64-bit code
    mov rax, 1
%else
    ; 32-bit code
    mov eax, 1
%endif

Best Practices for Using Symbols

1 Using Meaningful Names:

Choose descriptive names for your symbols to improve code readability.

MAX_USERS equ 100  ; Good
X equ 100          ; Bad

2 Centralize Definitions:

Define symbols in a separate file or at the top of the code for easy maintenance.

%include "constants.asm"

3 Use Conditional Compilation Sparingly:

While conditional compilation is powerful, overusing it can make your code harder to read, and debug.

4 Document Your Symbols:

Add comments to explain the purpose of each symbol.

TIMEOUT equ 5000  ; Timeout in milliseconds