Assembler kommt überall da zum Einsatz, wo Performance und Speichereffizienz ein absolutes Muß sind. Typisch sind Einsätze in den Bereichen Spiele, Grafik, Meßtechnik und im innersten Teil von Betriebssystemen (z.B. Scheduler).
Assemblerprogrammierung ist nicht ganz trivial, da jede CPU eine eigene Maschinensprache besitzt. So lassen sich Assemblerprogramme nur schlecht portieren, was einen aber nicht davon abhalten sollte die Maschinensprache seines Rechners zumindest in den Grundzügen zu kennen.
Im Nachfolgenden werde ich auf die Grundzüge der Erstellung von Assemblerprogrammen auf i386-kompatiblen PC's unter Linux eingehen. Die Maschinensprache von i386-kompatiblen CPU´s ist nicht gerade umwerfend. Da sind CPU´s wie Motorola 680x0 und PowerPC wesentlich eleganter. Egal, wovon uns schlecht wird, hier geht es um i386-Assembler unter Linux.
Alle Programme, die existieren, sind im Endeffekt reine Maschinenprogramme. Der C-Compiler übersetzt den C-Quelltext für uns in die Maschinensprache. Die Codequalität ist in vielen Fällen nicht schlecht. Will man mehr, muß man in Assembler programmieren. Die Assembler-Befehle nennt man Mnemonics. Ein Assembler-Befehl entspricht genau einem echten Maschinenbefehl, also ist ein Assemblerprogramm immer eine Einszueins-Übersetzung in Maschinensprache.
Beispiel i386-Befehl: mov ax,4
Der Befehl lädt das 16-bit CPU-Register ax mit dem Wert 4. Ein Prozessor besitzt eine ganze Reihe von internen Speichervariablen, die man als Register bezeichnet. Als Assembler-Profi versucht man möglichst viele Operationen in den CPU-Registern auszuführen. Register-Operationen sind turboschnell, während jeder Speicherzugriff quälend langsam dagegen ist.
Die i386-Befehle werde ich Ihnen allerdings nicht erklären, die müssen Sie sich schon selbst beibringen. Empfehlenswert ist der Riesen-Wälzer 'Das Assembler-Buch' aus dem Addison-Wesley-Verlag. Leider kostet der Spaß fast 100 DM. Im nachfolgenden Teil setze ich grundlegende i386-Kenntnisse voraus.
Mit Asmutils 0.14 werden viele Unix-Kommandos in Assembler nachprogrammiert. Ziel ist es, Standard-Unix-Befehle durch extrem kleine und schnelle Maschinenprogramme zu ersetzen. Nebenbei kann jeder am Quellcode nachvollziehen, wie so ein Unix-Befehl mit Kernelaufrufen implementiert werden muß. Jeder, der Lust hat, kann diesen Code weiter optimieren. Der Autor freut sich über jede neue Optimierung und neue Unix-Befehle für die Asmutils. Nicht umsonst bezeichnet man Assembler-Freaks als 'Microsecond- und Memoryhunter'.
Die Asmutils 0.14 sind Freeware und stehen unter der GPL.
Asmutils 0.14 - Homepage: http://linuxassembly.org/asmutils.html
Konstantin Boldyshev <konst@voshod.com>
Beispiel: cat-Kommando
Das Unix-Kommando cat dient zum Ausgeben
von Dateiinhalten. Da cat normalerweise ohne zusätzliche Optionen
(s. man cat) verwendet wird, kann man ein ganz kurzes und schnelles cat
per Assembler bauen.
Unix-Kommando cat |
---|
Original cat Vers. 1.22 : 12840 Bytes |
Asmutils cat : 187 Bytes (68x kleiner !!) |
#include <stdio.h>
int main( void
)
{
printf("Hello,
world\n");
return 0;
}
Das Programm habe ich unter SuSE-Linux 6.1 mit
gcc -o hello hello.c übersetzt.
Wir benutzen allerdings nicht den gas (Gnu Assembler), da er die unangenehme A&T-Syntax verwendet. Wir verwenden den Assembler nasm (Netwide Assember), der stattdessen die übliche INTEL-Syntax verwendet. Der nasm -Assembler befindet sich bei der SuSE 6.1-Distribution in der Serie d.
Das folgende 'Hello world' in nasm-Assemblersyntax
stammt aus der Asmutils-Homepage:
section .text | |||
global _start | ; für den Linker (ld) notwendig | ||
msg | db | 'Hello world',0x0A | ; unser Text incl. Zeilenumbruch LF (0x0A) |
len | equ | $ - msg | ; Länge des Textes berechnen (12 Bytes) |
_start: | ; Programmstart für Linker | ||
mov | eax,4 | ; Systemaufruf Nr. 4 (sys_write) Textausgabe | |
mov | ebx,1 | ; Ausgabekanal Nr.1 = stdout | |
mov | ecx,msg | ; Adresse unseres Textes im Speicher | |
mov | edx,len | ; Länge des Textes in Bytes | |
int | 0x80 | ; Kernel aufrufen mit obigen System-Aufrufparametern | |
mov | eax,1 | ; Systemaufruf Nr. 1 (sys_exit) Programmende | |
int | 0x80 | ; Kernel aufrufen mit obigen System-Aufrufparametern |
Nachdem wir den Quelltext (hello.asm) mittels eines Texteditors eingegeben haben, muß er jetzt übersetzt assembliert werden.
nasm -f elf hello.asm
Die Option -f elf gibt an, daß der Assembler eine Objektdatei hello.o im ELF (Extended Linker Format) erzeugen soll (früher wurde das veraltete aout-Format in Unix verwendet).
Objektdateien sind vorübersetzte Assemblerdateien, die aber noch nicht ausführbar sind. Erst ein Linker erzeugt aus ihnen ausführbare Programmdateien.
Die Aufgabe übernimmt ld (GNU Linker). Das Programm ld ist Teil der binutils-Programme. Diese sind in jeder Distribution vorinstalliert.
ld -s -o hello hello.o
Der Linker erzeugt jetzt ein ausführbares
Programm hello. Die Option -o hello gibt den Namen des ausführbaren
Programms an. Die Option -s weist den Linker an keine Symboltabelle
an den Code anzuhängen. So eine Symboltabelle mit den Bezeichnern
(hier msg, len, _start) braucht man nur, wenn man den Code später
debuggen will.
Hello world in C und Assembler |
---|
C-Version : 33030 Bytes |
Asmutils Version : 400 Bytes |
Das CPU-Register eax (32-bit) enthält
dabei die Nummer des Systemaufrufs (0 - 190).
Die CPU-Register ebx, ecx, edx, esi und edi
enthalten je nach Systemaufruf entsprechende Vorgaben.
Im obigen Beispiel braucht der Systemaufruf sys_write 4 Parameter, während der Systemaufruf sys_exit nur die Funktionsnummer als Parameter braucht.
So finden Sie die Liste der Systemaufrufe, Informationen
zum ELF-Format, zum Aufbau des Stacks bei Parameterübergaben uvm.
in dieser Homepage.
Linux Assembly HOWTO -- (Online, im HTML-Format, sgml source)
Startup state of Linux/i386 ELF binary -- Erklärungen zum ELF-Dateiformat
List of Linux/i386 system calls -- Linux Systemaufrufe
A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux