Programmierung von x86 Assembler unter Linux

Diese Seite habe ich ins Leben gerufen, weil es für die Technische Informatik 2 leider keine praktischen Assembler-Beispiele gibt. Vieles davon musste ich leider selber herausfinden, das hat ne Menge Zeit gekostet. Ich hoffe aber, dass sich es ein wenig gelohnt hat (ich hab auch viel dabei gelernt) - zumindest hat man jetzt eine Basis an Code, der wirklich funktioniert (sic!). Die Beispiele im TI2-Skript funktionieren leider nicht wirklich...

Alle Code-Beipsiele sind in GNU-Assembler abgefasst (als Flame nebenbei: Intel-ASM ist wirklich eine Krankheit u.a. wegen der fehlenden Kontrolle über die Opcodes, die daraus generiert werden, Stichwort Longjumps...). Jedes der Codestücke steht unter der GNU General Public License. Mit Download wird diese anerkannt. Blablabla. Wenn ich noch etwas Zeit habe stelle ich eventuell eigene Seiten dafür ins Netz oder mach noch einige Codebeispiele (wenn sich nette Einträge im Gästebuch finden, hehe). Ansonsten ist der Code wirklich gut kommentiert und in verschiedene Schwierigkeitsstufen eingeteilt.

Obwohl ich das ganze für die Informatiker mache, hier ein Paar Tipps, wie man das zum Laufen bringt:

Die Dateien sind heruntergeladen:
acer joe [~/tmp]: ls
insgesamt 12K
-rw-r--r--  1 joe users  14K   30.08.2004 02:44:40 x86asm-0.04.tar.bz2
Nun entpacken:
acer joe [~/tmp]: tar xfvj x86asm-0.01.tar.bz2
x86asm-0.01/
x86asm-0.01/01_plusminus.S
x86asm-0.01/02_aufsummieren.S
x86asm-0.01/03_aufsummieren_ungerade.S
x86asm-0.01/04_stack.S
x86asm-0.01/helper_functions.S
x86asm-0.01/Makefile
x86asm-0.01/05_funktion.S
x86asm-0.01/06_funktion_rekursion.S
x86asm-0.01/LICENSE
x86asm-0.01/README
x86asm-0.01/CHANGELOG
acer joe [~/tmp]: cd x86asm-0.01
acer joe [~/tmp/x86asm-0.01]: ls
insgesamt 60K
-rw-r--r--  1 joe users  556   19.08.2004 07:51:10 01_plusminus.S
-rw-r--r--  1 joe users  663   19.08.2004 07:51:05 02_aufsummieren.S
-rw-r--r--  1 joe users 1,1K   19.08.2004 07:51:52 03_aufsummieren_ungerade.S
-rw-r--r--  1 joe users 1,2K   19.08.2004 07:57:25 04_stack.S
-rw-r--r--  1 joe users 1,2K   19.08.2004 07:48:27 05_funktion.S
-rw-r--r--  1 joe users 1,9K   19.08.2004 08:14:01 06_funktion_rekursion.S
-rw-r--r--  1 joe users   65   19.08.2004 08:42:54 CHANGELOG
-rw-r--r--  1 joe users 1,4K   19.08.2004 07:48:14 helper_functions.S
-rw-r--r--  1 joe users  18K   19.08.2004 08:42:32 LICENSE
-rw-r--r--  1 joe users  388   19.08.2004 08:39:58 Makefile
-rw-r--r--  1 joe users  188   19.08.2004 08:42:11 README
Und dann "make" machen, das wars schon!
acer joe [~/tmp/x86asm-0.01]: make
gcc -c -o helper_functions.o helper_functions.S
gcc -c -o 01_plusminus.o 01_plusminus.S
gcc -c -o 02_aufsummieren.o 02_aufsummieren.S
gcc -c -o 03_aufsummieren_ungerade.o 03_aufsummieren_ungerade.S
gcc -c -o 04_stack.o 04_stack.S
gcc -c -o 05_funktion.o 05_funktion.S
gcc -c -o 06_funktion_rekursion.o 06_funktion_rekursion.S
gcc -Wall -O2 -o 01_plusminus 01_plusminus.o helper_functions.o
gcc -Wall -O2 -o 02_aufsummieren 02_aufsummieren.o helper_functions.o
gcc -Wall -O2 -o 03_aufsummieren_ungerade 03_aufsummieren_ungerade.o helper_functions.o
gcc -Wall -O2 -o 04_stack 04_stack.o helper_functions.o
gcc -Wall -O2 -o 05_funktion 05_funktion.o helper_functions.o
gcc -Wall -O2 -o 06_funktion_rekursion 06_funktion_rekursion.o helper_functions.o
acer joe [~/tmp/x86asm-0.01]:
Noch eine kleine Übersicht der mitgelieferten Programme:
Name Beschreibung
01_plusminus Löst die Aufgabe, die in den Übungen durchgesprochen wurde (17x-15y berechnen)
02_aufsummieren Ein Beispiel für Schleifen: Die Zahlen von X bis Y sollen aufsummiert werden
03_aufsummieren_ungerade Ein Beispiel für konditionierte Schleifen: Die ungeraden Zahlen von X bis Y sollen aufsummiert werden
04_stack Spielereien mit dem Stack: Zahlen werden ge-pushl-d und dann aufsummiert
05_funktion Einfache Funktion mit 3 Parametern, die über den Stack übergeben werden
06_funktion_rekursiv Rekursive Funktion, die die Zahlen von 1..n aufsummiert
07_matrixmultiplikation Multipliziert eine 2x3 mit einer 3x2-Matrix. Das funktioniert wirklich! Sehr cool.
08_malloc Holt Speicher aus dem Heap und macht diesen zugreifbar.
09_hints1 Vergleich zwischen "movl $0, %eax" und "xorl %eax, %eax"
10_hints2 Vergleich zwischen "cmp $0, %eax" und "test %eax, %eax"
11_hints3 Vergleich zwischen einer normalen Schleife und einer Schleife, die das "loop" Kommando verwendet. Auch Erklärung der Opcodes als Kommentar (relative Jumps und Calls)
12_float Zeigt, wie man die Flißkommaoperationen des Prozessors nutzen kann, um zwei Fließkommazahlen miteinander zu multiplizieren und gibt diese dann aus. Zur Berechnung der Fließkommazahlen selbst habe ich einen wirklich ausführlichen Kommentar mit in den Quellcode eingefügt (länger als der Code selber). Interessant für alle, die nocheinmal gerne wissen wollen, wie man Dezimalbrüche in IEEE-754 Floats umwandelt.
13_rorl Klausuraufgabe Juli 2003, Aufgabe 2b. Mit Erklärung des "rorl" Befehls.
14_call Wie ist ein "call" sauber zu implementieren? Wie kann man das "unsauber" machen?
15_callpop Klausuraufgabe, in der zuerst ein "call" gemacht wird, dann die Rücksprungadresse ge-popl-ed wird und damit ein bischen rumgerechnet.
16_cmp Beispiel, dass die verschiedenen Sprungarten ja, jb (unsigniert) mit jg, jl (signiert) vergleicht.
17_forloop Eine einfache for-Schleife
18_ggt Euklids Algorithmus zur Bestimmung des GGT (ehemalige Klausuraufgabe)
19_modulo Ehemalige Klausuraufgabe: Einen möglichst einfachen Modulo-10-Rechner in Assembler basteln. Meine Lösung als C-Source (modulo.c) und Assembler (19_modulo.S).
helper_functions Hilfsfunktionen, die ich benutze, und die sehr nützlich sein könnten, wenn man seinen eigenen Code debuggen muss. Im Übrigen wird hier auch gezeigt, wie man in Assembler verschiedene Programmteile in Module aufsplitten kann und wie diese dann zusammengelinkt werden. Ein Blick ins Makefile verrät mehr.

Zum Linken der Dateien sei folgendes angemerkt: ich verwende zum kompilieren des Assembler-Codes den gcc! Ursprünglich habe ich mit dem as und dem ld herumgespielt, habe es allerdings auch nach stundenlangen Versuchen (!) nicht hinbekommen. Auch wenn es im Skript so einfach toll drinsteht, bei mir ist immer folgendes passiert (und das ist wirklich merkwürdig):

acer joe [~/tmp]: ls
insgesamt 4,0K
-rw-r--r--  1 joe users 1,5K   19.08.2004 08:58:46 testprog.S
acer joe [~/tmp]: as -o testprog.o testprog.S
acer joe [~/tmp]: ld -lc -o testprog testprog.o
acer joe [~/tmp]: ls
insgesamt 16K
-rwxr-xr-x  1 joe users 5,7K   19.08.2004 08:59:05 testprog
-rw-r--r--  1 joe users 1,3K   19.08.2004 08:58:56 testprog.o
-rw-r--r--  1 joe users 1,5K   19.08.2004 08:58:46 testprog.S
acer joe [~/tmp]: ./testprog
-bash: ./testprog: Datei oder Verzeichnis nicht gefunden
acer joe [~/tmp]: ldd testprog
/usr/bin/ldd: line 1: ./testprog: Datei oder Verzeichnis nicht gefunden
acer joe [~/tmp]:

Kann das jemand nachvollziehen? Ja, es kann! Vor einigen Tagen erhielt ich von Max Wittgenstein folgende Mail, die ich mit seiner Genehmigung hier auszugsweise veröffentlichen darf:

Date: Thu, 5 May 2005 18:09:19 +0200 (CEST)
From: Max Wittgenstein <xxx>
To: Johannes Bauer <xxx>
Subject: Problem mit Assembler: "Datei nicht gefunden"

[...]
Hat ein Weilchen gedauert, diese wirklich komische Sache zu verstehen.

Meiner Meinung nach liegt das Problem am fehlenden "dynamic-linker". Bei mir führt nämlich die Art und Weise des "Linking", bei der dieses Problem auftritt dazu, daß ein falscher "Requesting program interpreter: /usr/lib/libc.so.1" "eingelinkt" wird und diese Datei gibt es bei mir nicht.

Richtig müsste dort eine "/lib/ld-linux.so.2" stehen.

Also muß das Teil per Parameter an den Linker übergeben werden. Für den Rest an Erklärung, schau mal in die Datei: "make.sh" im Attachment.


Ach ja, mit dem Aufruf "readelf -l testprog" kann man sich den Programmheader in menschenlesbarer Form anschauen. Da steht dann unter anderem sowas wie z.B.:

"Requesting programm interpreter: /lib/ld-linux.so.2"
[...]

Das Attachment, "make.sh", gibt es hier herunterzuladen. Vielen Dank nochmal an Max, der sich die Mühe gemacht hat, das Problem nachzuvollziehen.

Version Veränderungen
AsmReferenz.pdf Version 1 Download Eine x86-Assembler Referenz mit Codebeispielen
Funktionsaufrufe.pdf Version 3 Download Funktionsweise von Funktionsaufrufen und Verhalten des Stacks (geänderte Version)
Download 0.09 (27.9.2004) Klausuraufgabe: Möglichst effiziente Implementierung von "Modulo 10". Meine Lösung in C-Source (modulo.c) und in Assembler (19_modulo.S)
0.08 (27.9.2004) Klausuraufgabe: Implementierung einer rekursiven ggt-Funktion hinzugefügt
0.07 (26.9.2004) Beispiel, dass die verschiedenen Sprungarten ja, jb, jl, jg vergleicht
0.06 (25.9.2004) Das "call x; x: popl %eax" Beispiel ist nun mit im Paket drin, und ein Beispiel, wie man "calls" machen sollte, und wie nicht
0.05 (25.9.2004) Rorl-Klausurbeispiel hinzugefügt
0.04 (30.8.2004) Beispiel für Fließkommazahlen (nicht klausurrelevant!)
0.03 (25.8.2004) Malloc aus Assembler und drei Beispiele, wie man Code verkürzen/vereinfachen kann
0.02 (19.8.2004) Matrixmultiplikation hinzugefügt, das bisher umfangreichste Beispiel
0.01 (19.8.2004) Erster Versuch eines Releases