Include Directive

This can be particularly useful for modularizing code, reusing macros and constants, and improving code organization. This article provides an in-depth look at the %INCLUDE directive, including its usage, variations, and best practices for managing include guards.

Understanding the %INCLUDE Directive

The %INCLUDE directive tells the NASM assembler to read and process another file as if its content were directly included in the current source file. This directive is particularly useful for breaking down large assembly programs into manageable modules or including commonly used macros and constants.

Basic Usage of %INCLUDE

The syntax for the %INCLUDE directive is straightforward:

%include 'filename.inc'

Here, filename.inc is the path to the file you want to include. The file extension .inc is a convention, but you can use any extension.

Example: Including a File

Suppose you have a file macros.inc that contains macro definitions:

; macros.inc
%macro PRINT_STRING 2
    mov eax, 4          ; syscall number for sys_write
    mov ebx, %1         ; file descriptor
    mov ecx, %2         ; address of string
    mov edx, %2_len     ; length of string
    int 0x80            ; call kernel
%endmacro

You can include and use these macros in your main assembly file main.asm:

; main.asm
%include 'macros.inc' ; Include macro definitions

section .data
hello db 'Hello, World!', 0xA
hello_len equ $ - hello

section .text
global _start

_start:
    PRINT_STRING 1, hello ; Use the PRINT_STRING macro to print the string

    ; Exit program
    mov eax, 1          ; syscall number for sys_exit
    xor ebx, ebx        ; exit code 0
    int 0x80            ; call kernel

Variations and Path Handling

NASM allows flexible handling of paths in %INCLUDE. You can specify relative or absolute paths, and the NASM assembler will search for the file accordingly. It's common to organize include files in a dedicated directory:

%include 'includes/macros.inc'

During Assembling, we can pass the include file path to the NASM:

NASM_INCLUDE_PATH := /path/to/includes

main.o: main.asm
    nasm -f elf32 -o $@ -I$(NASM_INCLUDE_PATH) $<

In this Makefile snippet, -I flag is used to specify the include path directly to NASM.

Include Guards

Include guards are essential for preventing multiple inclusions of the same file, which can lead to redefinition errors and other issues. In NASM, include guards can be implemented using conditional assembly directives.

Example of Include Guards

Here’s how you can implement include guards in NASM:

; macros.inc
%ifndef MACROS_INC
%define MACROS_INC

%macro PRINT_STRING 2
    mov eax, 4          ; syscall number for sys_write
    mov ebx, %1         ; file descriptor
    mov ecx, %2         ; address of string
    mov edx, %2_len     ; length of string
    int 0x80            ; call kernel
%endmacro

%endif ; MACROS_INC

The macros.inc file is wrapped with a %ifndef (if not defined) and %define pair, ensuring that its content is included only once. If MACROS_INC is already defined, the content between %ifndef and %endif will be ignored.

Advanced Usage

Including Assembly Code Conditionally

You can use conditional assembly directives with %INCLUDE to include files based on certain conditions:

%ifdef USE_MACROS
    %include 'macros.inc'
%endif

In this example, macros.inc is included only if USE_MACROS is defined.

Nested Includes

NASM supports nested includes, where an included file can itself include other files. Care must be taken to avoid circular dependencies:

; main.asm
%include 'macros.inc'
%include 'config.inc'

section .data
hello db 'Hello, World!', 0xA
hello_len equ $ - hello

section .text
global _start

_start:
    PRINT_STRING 1, hello ; Use the PRINT_STRING macro to print the string

    ; Use configuration settings
    mov eax, CONFIG_VALUE

    ; Exit program
    mov eax, 1          ; syscall number for sys_exit
    xor ebx, ebx        ; exit code 0
    int 0x80            ; call kernel

Best Practices

  1. Use Include Guards: Always use include guards to prevent multiple inclusions.
  2. Organize Include Files: Keep your include files in a dedicated directory to improve organization and readability.
  3. Use Relative Paths: Prefer relative paths for portability and ease of use across different development environments.
  4. Document Include Files: Provide comments and documentation in your include files to clarify their purpose and usage.