"Hello World" in Assembler unter Linux (32 bit, i386er)

Beispiel:

Nachfolgend ein "Hello World" Programm in Assembler unter Linux:

section .data			; Deklaration: Bereich Variablen

txt	db	'Hello World',0x0D,0x0A	; Text incl. Zeilenumbruch (= CR+LF, = ASCII 13+10)
len	equ	$ - txt		; Länge des Textes berechnen (13 Bytes)

section .text			; Deklaration: Bereich Quellcode
global _start			; Definition Start-Label

_start:				; Programmstart

	mov	edx,len		; 3. Parameter, Textlänge
	mov	ecx,txt		; 2. Parameter, Textvariable
	mov	ebx,1		; 1. Parameter, file handle, 1 = stdout
	mov	eax,4		; Systemaufruf Nr. 4 = sys_write (Systemausgabe)
	int	0x80		; Linux-Kernel aufrufen mit obigen Parametern
			
	mov	ebx,0		; 1. Parameter für sys_exit, 0 = Programm beendet ohne Fehler
	mov	eax,1		; Systemaufruf Nr. 1 = sys_exit (Programmende)
	int	0x80		; Linux-Kernel aufrufen mit obigen Parametern

Erklärung:

Es folgt eine kurze Einführung, wie dieser Code zu lesen ist:

Der Programmcode unterteilt sich in 2 Sektionen. Unterhalb von 'section .data' befinden sich die 2 Variablen (txt + len), die später im Programm verwendet werden. Unterhalb von 'section .text' schreibt man den eigentlichen Programmcode, der ausgeführt werden soll. 'global' ist die Position, die später vom Linux-System als Startpunkt für die Ausführung verwendet wird. 'global' ruft hier das Label '_start' auf, das gleich in der nächsten Zeile definiert wird und den Code für das "Hello World" Programm enthält.

Das Programm verwendet ansich nur 2 Assembler-Befehle (mov und int).

'mov': kommt vom englischen 'moven' = bewegen. Dieser Befehl schreibt einen Wert in ein Register der CPU, sprich in eine der Speicherstellen, mit denen die CPU intern arbeitet und die daher sehr schnell sind. Wir verwenden später beim Compilieren das Tool 'NASM'. Dieser Compiler verwendet die Intel Notation, bei welcher zuerst angegeben wird WOHIN geschrieben werden soll (= der Name des Registers) und dann WAS dorthin geschrieben werden soll, sprich der zu verwendende Wert. Ein 'mov eax,4' schreibt also den Wert 4 in das EAX Register.

'int' kommt vom englischen 'interrupt' = unterbrechen. Ein sog. Software-Interrupt unterbricht die Ausführung an der jeweiligen Stelle des Programms und ruft eine Funktion des Betriebssystems auf. Es wird hier 2x der Interrupt Nr. 80 (Befehl: 'int 0x80') aufgerufen, welches der typische Einsprungpunkt für Linux-Kernelfunktionen ist. (ähnlich dem Interrupt 21 unter DOS). Der 'int'-Befehl benötigt aber verschiedene Angaben beim Aufruf (Parameter) und diese werden daher vor dem 'int'-Aufruf mit den 'mov'-Befehlen in die Register geschrieben. Im sog. A-Register (bei 32bit Intel Systemen heisst dieses 'EAX') wird definiert, welcher Kernelbefehl ausgeführt werden soll. Beim 1. INT80 Aufruf wird also die Funktion Nr. 4 ausgeführt = Systemausgabe, beim 2. Aufruf wird die Funktion Nr. 1 aufgerufen = Programmende.

In der Regel schreibt man die Parameter von 'hinten' nach 'vorne' in die Register bzgl. der Reihenfolge im Alphabet, also von 'D', 'C', 'B' nach 'A'. Dies ist ua. bei dem ersten INT80 Aufruf gut sichtbar, wo zuerst das Register EDX und zuletzt das Register EAX beschreiben wird.

Erstellung:

Kompiliert wird das Programm mit folgenden Befehlen:

#> nasm -f elf hello.asm
#> ld -m elf_i386 -o hello hello.o

Anschließend können Sie es mit folgendem Befehl starten:

#> ./hello
#> Hello World

NASM: Das ist der hier verwendete Compiler für Assembler. NASM erstellt aus der Textdatei 'hello.asm', die den obigen Codetext enthält, die Objectcode-Datei 'hello.o' im ELF Format - sprich eine ausführbare Datei für Linux, ähnlich der EXE-Datei unter Windows.

LD: Das ist der sog. "Linker", der das endgültige Programm erstellt. Mit dem Parameter '-m elf_i386' wird angegeben, dass es sich um eine 32bit Programmdatei im ELF-Format handelt (auf 32bit Systemen kann dieser Parameter auch weggelassen werden, auf 64bit System ist er Pflicht, um den obigen Code kompilieren zu können). Mit dem Parameter '-o hello' wird der endgültige Name der Programmdatei angegeben.

Links: