Assemblersprache - Einrichtung der Umgebung
Dieses Kapitel führt Sie durch die Einrichtung der NASM-Assembler-Entwicklungsumgebung auf einem Linux-System und das Abschließen Ihres ersten Assemblerprogramms.
Linux-Umgebungsvorbereitung
Wenn Sie Windows verwenden, wird empfohlen,WSL (Windows Subsystem for Linux)zu installieren oder eine virtuelle Maschine zu verwenden, um eine Linux-Umgebung zu erhalten.
macOS-Benutzer können Linux in einer virtuellen Maschine installieren oder Docker-Container verwenden.
Alle Beispiele in diesem Tutorial basieren auf demUbuntu/Debian-System. Für andere Distributionen passen Sie bitte die Paketmanager-Befehle entsprechend an.
NASM-Assembler installieren
Installieren Sie NASM auf Ubuntu/Debian-Systemen mit dem apt-Paketmanager:
$ sudo apt update $ sudo apt install nasm
Überprüfen Sie nach der Installation, ob NASM erfolgreich installiert wurde:
$ nasm -version NASM version 2.16.01
Wenn Sie die Versionsnummernausgabe sehen, bedeutet dies, dass NASM erfolgreich installiert wurde.
Linker installieren
Der Assembler generiertObjektdateien (.o-Dateien), die nicht direkt ausgeführt werden können.
Sie benötigen einen Linker, um die Objektdatei in eine ausführbare Datei zu konvertieren. Wir verwenden den mit GCC gelieferten Linker:
$ sudo apt install gcc
Alternativ können Sie direkt den GNU-Linker ld verwenden:
$ ld --version
In diesem Tutorial verwenden wir hauptsächlichgcczum linken, da dieser automatisch einige Low-Level-Details verarbeitet und besser für Anfänger geeignet ist.
Debugging-Tool GDB installieren
GDB (GNU Debugger)ist der am häufigsten verwendete Debugger unter Linux, mit dem Sie Programme schrittweise ausführen, Registerzustände anzeigen und Speicherinhalte untersuchen können.
$ sudo apt install gdb
GDB-Installation überprüfen:
$ gdb --version GNU gdb (Ubuntu 12.1-0ubuntu1) 12.1
Erstes Assemblerprogramm: Hello, example
Erstellen Sie eine neue Dateihello.asmund geben Sie den folgenden Inhalt ein:
Beispiel
; Erstes Assemblerprogramm: "Hello, example!" ausgeben
; NASM-Syntax, Linux 32-Bit
section .data ; Datenabschnitt: speichert initialisierte Daten
msg db 'Hello, example!', 0xA ; String msg definieren, 0xA ist das Zeilenumbruchzeichen
len equ $ - msg ; Stringlänge berechnen: aktuelle Adresse - Startadresse von msg
section .text ; Codeabschnitt: speichert ausführbare Anweisungen
global _start ; _start als Programmeinstiegspunkt deklarieren
_start: ; Programmeinstieg
; Systemaufruf sys_write (4): String an stdout ausgeben
mov eax, 4 ; Systemaufruf-Nummer 4 = sys_write
mov ebx, 1 ; Dateideskriptor 1 = stdout (Standardausgabe)
mov ecx, msg ; Adresse des auszugebenden Strings
mov edx, len ; Stringlänge
int 0x80 ; Interrupt auslösen, um den Systemaufruf auszuführen
; Systemaufruf sys_exit (1): Programm normal beenden
mov eax, 1 ; Systemaufruf-Nummer 1 = sys_exit
mov ebx, 0 ; Rückgabewert 0 = normaler Beendigungsstatus
int 0x80 ; Interrupt auslösen, um den Systemaufruf auszuführen
Kompilieren und Ausführen
Der gesamte Kompilierungs- und Ausführungsablauf ist im Diagramm dargestellt:
Verwenden Sie die folgenden Befehle, um dieses Programm zu kompilieren und auszuführen:
$ nasm -f elf32 hello.asm -o hello.o # 汇编:将 .asm 转为 .o 目标文件 $ ld -m elf_i386 hello.o -o hello # 链接:将 .o 转为可执行文件 $ ./hello # 运行程序 Hello, example!
Der Zweck der drei obigen Befehle jeweils:
| Schritt | Befehl | Beschreibung |
|---|---|---|
| 1. Assemblieren | nasm -f elf32 hello.asm -o hello.o | -f elf32 legt das Ausgabeformat als 32-Bit-ELF fest, -o gibt den Namen der Ausgabedatei an |
| 2. Linken | ld -m elf_i386 hello.o -o hello | -m elf_i386 legt den 32-Bit-Linkmodus fest und generiert die ausführbare Datei hello |
| 3. Ausführen | ./hello | Führen Sie das Programm im aktuellen Verzeichnis aus |
Mit GCC linken (Empfohlen)
Wenn die ld-Konfiguration in Ihrer Umgebung komplex ist, können Sie gcc zum Linken verwenden, wodurch Details wie die C-Laufzeitumgebung automatisch verarbeitet werden:
$ nasm -f elf32 hello.asm -o hello.o $ gcc -m32 hello.o -o hello -nostartfiles $ ./hello Hello, example!
-nostartfilesDie Option weist GCC an, keinen Standard-Startcode hinzuzufügen, da wir unseren eigenen_startEinstiegspunkt definieren.
Analyse der Codestruktur
Gehen wir das obige Programm Schritt für Schritt durch:
| Teil | Code | Beschreibung |
|---|---|---|
| Deklaration des Datensegments | section .data | Deklariert das Datensegment, in dem Variablen und Konstanten gespeichert werden |
| String-Definition | msg db 'Hello, example!', 0xA | db definiert eine Byte-Sequenz; 0xA ist der ASCII-Code für einen Zeilenumbruch |
| Längenberechnung | len equ $ - msg | $ repräsentiert die aktuelle Adresse; das Subtrahieren der msg-Adresse ergibt die String-Länge |
| Deklaration des Codesegments | section .text | Deklariert das Codesegment, in dem Anweisungen gespeichert werden |
| Einstiegsdeklaration | global _start | Exportiert _start als globales Symbol; der Linker benötigt dies |
| Einstiegs-Label | _start: | Hier beginnt die Programmausführung |
int 0x80Dies ist die 32-Bit-Systemaufruf-Anweisung von Linux. Sie löst einen Software-Interrupt aus, der es dem Kernel ermöglicht, den von uns über eax angegebenen Systemaufruf auszuführen. Dies ist die Brücke zwischen Benutzerprogrammen und dem Betriebssystemkernel.
Debugging mit GDB
Fügen Sie während der Kompilierung Debugging-Informationen hinzu und gehen Sie dann mit GDB Schritt für Schritt durch:
$ nasm -f elf32 -g hello.asm -o hello.o # -g 添加调试信息 $ ld -m elf_i386 hello.o -o hello $ gdb ./hello (gdb) break _start # 在 _start 处设置断点 (gdb) run # 运行程序 (gdb) stepi # 单步执行一条指令 (gdb) info registers # 查看所有寄存器 (gdb) x/s msg # 查看 msg 字符串内容 (gdb) quit # 退出 GDB
| GDB-Befehl | Funktion |
|---|---|
break _start | Einen Haltepunkt am Label _start setzen |
run | Das Programm ausführen, am Haltepunkt anhalten |
stepi | Eine Maschinenanweisung nach der anderen ausführen |
info registers | Aktuelle Werte aller Register anzeigen |
x/s 地址 | Inhalt an der angegebenen Adresse im String-Format anzeigen |
Häufig gestellte Fragen
Wenn Sie 32-Bit-Assembly auf einem 64-Bit-System kompilieren, müssen Sie 32-Bit-Kompatibilitätsbibliotheken installieren:
sudo apt install gcc-multilib
Andere ErweiterungenWenn ld beim Linken den Fehler "cannot find entry symbol _start" meldet, überprüfen Sie, ob Sie es in Ihrem Code richtig geschrieben haben
global _startund ob der Label-Name genau übereinstimmt (achten Sie auf den Unterstrich).