Assemblersprache - Makros

Ein Makro ist ein von NASM bereitgestellter Mechanismus zur Wiederverwendung von Code, mit dem Sie Codevorlagen zur Kompilierzeit erweitern können, wodurch das wiederholte Schreiben ähnlichen Codes reduziert wird.


Was ist ein Makro

MakroEs ist ein Textersetzungsmechanismus zur Kompilierzeit, der vom Präprozessor des Assemblers vor der Kompilierung erweitert wird.

Im Gegensatz zu Prozeduren haben Makros keinen CALL/RET-Overhead – jedes Mal, wenn ein Makro verwendet wird, kopiert der Assembler den Inhalt des Makros direkt an den Verwendungsort.

Ein Makro ist kein Funktionsaufruf und hat keinen Stapelrahmen-Overhead; jedoch wird die Größe der ausführbaren Datei größer, da bei jeder Erweiterung eine Kopie des Codes erstellt wird.


Einzeiliges Makro: %define

%defineEs ist ein einzeiliges Makro mit einfacher Syntax:

Beispiel

; Einzeiliges Makrobeispiel

; Eine Konstante definieren
%define MAX_SIZE 256
%define APP_NAME 'example'

; Ein Makro mit Parametern definieren (Makrofunktion)
%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

    ; Neudefinition (%define kann geändert werden)
    %define MAX_SIZE 512
    mov eax, MAX_SIZE            ; eax = 512

    ; Definition aufheben
    %undef MAX_SIZE
    ; mov eax, MAX_SIZE ; Fehler: MAX_SIZE ist nicht definiert

    mov eax, 1
    mov ebx, 0
    int 0x80

%defineDie Makroerweiterung ist eine reine Textersetzung. Zum Beispiel,mul_by_2(2+3)wird erweitert zu(2+3 * 2)ist das Ergebnis aufgrund der Operatorenrangfolge nicht 10, sondern 8. Aus diesem Grund müssen Makroparameter in Klammern eingeschlossen werden.


Mehrzeiliges Makro: %macro / %endmacro

%macroWird verwendet, um komplexe Makros mit mehreren Anweisungen zu definieren:

Beispiel

; Dateipfad: macro_multi.asm
; Mehrzeiliges Makrobeispiel

; Makrodefinition: Programm beenden
; Parameter argc: wie viele Parameter das Makro akzeptiert
%macro exit_program 1
    mov eax, 1                   ; sys_exit
    mov ebx, %1                  ; Der erste Parameter wird als Exit-Code verwendet
    int 0x80
%endmacro

; Makrodefinition: Zeichenkette drucken
; Akzeptiert 2 Parameter: Zeichenkettenadresse, Länge
%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-Adresse
    mov edx, %2                  ; String-Länge
    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     ; Verwenden Sie das Makro, um eine Nachricht auszugeben
    print_string msg, msg_len     ; Erneut aufrufen
    exit_program 0                ; Programm beenden

Erweiterte Verwendung von Makro-Parametern

NASM-Makros unterstützen Standardparameter, Parameterzählung und bedingte Expansion:

Beispiel

; Beispiel für erweiterte Makro-Parameter

; Makro mit Standardparametern (Parameterbereich 2-3)
%macro debug_print 2-3 1        ; Mindestens 2 Parameter, höchstens 3, der 3. hat den Standardwert 1
    %if %3 = 1                   ; Wenn der 3. Parameter = 1 ist (Debug-Modus aktiviert)
        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

; Makro mit einer variablen Anzahl von Parametern
%macro push_registers 1-*        ; 1 bis zu einer beliebigen Anzahl von Parametern
    %rep %0                      ; %0 ist die Anzahl der Parameter
        push %1                  ; Den 1. expandieren
        %rotate 1                ; Die Parameterliste nach links rotieren
    %endrep
%endmacro

section .text
    global _start

_start:
    ; push_registers verwenden, um mehrere Register zu sichern
    push_registers eax, ebx, ecx, edx
    ; Wird expandiert zu:
    ; push eax
    ; push ebx
    ; push ecx
    ; push edx

    ; Entsprechendes pop
    pop edx
    pop ecx
    pop ebx
    pop eax

    mov eax, 1
    mov ebx, 0
    int 0x80

Lokale Labels in Makros

Verwenden Sie in Makros%%labelum lokale Labels zu definieren und Label-Konflikte bei mehrfachen Expansionen zu vermeiden:

Beispiel

; Lokale Labels in Makros

; Zwei Werte vergleichen und das Minimum setzen
%macro min_val 2
    mov eax, %1
    mov ebx, %2
    cmp eax, ebx
    jle %%skip                   ; ★ Lokales Label, für jede Expansion wird ein eindeutiger Name generiert
    mov eax, ebx
%%skip:
%endmacro

; Wenn keine lokalen Labels verwendet werden, treten nach zwei Expansionen doppelte skip-Labels auf
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

Das zweimalige Expandieren eines normalen Labels in einem Makro führt zu einem Label-Namenskonfliktfehler. Lokale Labels%%labelsorgen dafür, dass NASM für jede Expansion einen eindeutigen Labelnamen generiert (wie[email protected]), wodurch Konflikte vermieden werden.


Vergleich von Makros und Prozeduren

MerkmalMakroProzedur
ImplementierungsmethodeTextexpansion zur Compile-ZeitLaufzeit-CALL/RET
Ausführender OverheadKein Call-Overhead (direktes Inlining)Hat CALL/RET-Overhead (ungefähr ein paar Taktzyklen)
CodegrößeJede Expansion vergrößert die CodegrößeEine Kopie des Codes, die mehrmals aufgerufen wird
ParametertypenBeliebiger Text (Register, Konstanten, Speicher)Laufzeitwerte
DebuggingSchwierig (keine Ablaufverfolgung nach der Expansion)Einfach (hat Funktionsaufruf-Stack)
Anwendbare SzenarienKurzer, häufig aufgerufener und typflexibler CodeFunktionen mit komplexer Logik und großer Codegröße

Bedingte Kompilierung: %if / %elif / %else / %endif

Der Makro-Präprozessor unterstützt bedingte Kompilierung, mit der basierend auf Symboldefinitionen verschiedener Code ausgewählt und generiert werden kann:

Beispiel

; Dateipfad: cond_compile.asm
; Beispiel für bedingte Kompilierung: Debug- und Release-Modus

%define DEBUG 1                  ; 1=Debug-Modus, 0=Release-Modus

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-Modus: Debug-Informationen ausgeben
        mov eax, 4
        mov ebx, 1
        mov ecx, debug_msg
        mov edx, debug_len
        int 0x80
    %endif

    ; Normale Geschäftslogik
    mov eax, 4
    mov ebx, 1
    mov ecx, msg
    mov edx, msg_len
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

Häufige Szenarien für die bedingte Kompilierung:

SzenarioBeispiel
Umschalten zwischen Debug und Release%if DEBUG / %else / %endif
Plattformanpassung%ifdef LINUX / %ifdef WINDOWS
FunktionsschalterSteuerung von Funktionen danach, ob ein Symbol definiert ist

%rep Wiederholungsblock

%repWird verwendet, um wiederholten Code oder Daten zu generieren:

Beispiel

; Beispiel für %rep-Wiederholungsblock

section .data
    ; Generieren einer 256-Byte-Nachschlagetabelle
    ; 0, 1, 4, 9, 16, 25, ... (Quadratzahlen-Tabelle)
    %assign i 0
    ; Hier kann %rep nicht verwendet werden, um eine Quadratzahlentabelle für komplexe Berechnungen zu generieren
    ; Einfaches Beispiel: ASCII-Tabelle für 0-9 generieren
    digits: db '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'

section .text
    global _start

_start:
    ; %rep wiederholt Anweisungen im Code
    mov eax, 0
    %rep 5                      ; 5 mal wiederholen
        inc eax                  ; eax wird jedes Mal um 1 erhöht
    %endrep
    ; eax = 5

    mov ebx, eax
    mov eax, 1
    int 0x80

Das Übertreiben mit Makros macht den Code schwer lesbar und zu debuggen. Wenn der Makrokörper 10 Zeilen überschreitet, sollten Sie einen Prozeduraufruf in Betracht ziehen. In leistungskritischen Hot-Pfaden hat der Inlining-Effekt kurzer Makros offensichtliche Vorteile.

Weitere Erweiterungen