Die Sprache des Prozessors
Assembler ist die lesbare Schreibweise für die Befehle, die ein Prozessor ausführt. In diesem Modul ist es x86-64-Assembler, wie ihn die meisten PCs und Server verstehen.
Wer wenige Zeilen davon lesen kann, sieht in einem Programm ohne Quelltext, wo es etwas vergleicht und wohin es springt. Mehr brauchen Sie dafür nicht.
Am Ende dieser Lektion
Notiz
Ich kann wenige Zeilen x86-64-Assembler lesen und sagen, was sie berechnen.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Register | ein winziger Speicher im Prozessor | eax |
| Befehl | eine Anweisung an den Prozessor | mov |
| Operand | die Zahl oder das Register, auf das ein Befehl wirkt | |
| Flag | ein Merkzettel im Prozessor, etwa ob der letzte Vergleich gleich war | |
| Sprung | ein Befehl, der an eine andere Stelle springt | jne |
| Rückgabewert | das Ergebnis einer Funktion, bei x86-64 im Register eax |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Eine Funktion mit einer Entscheidung
Ein Laden gibt Inhabern einer Kundenkarte 5 Euro Rabatt. Die Funktion rabatt bekommt den Preis und eine Zahl für die Karte. Ist die Karte 1, zieht sie 5 ab, sonst gibt sie den Preis unverändert zurück.
So sieht der Quelltext aus. Sie bauen daraus gleich ein Programm und löschen ihn wieder.
Der Quelltext von rabatt
int rabatt(int preis, int karte) {
if (karte == 1) {
return preis - 5;
}
return preis;
}
int main(void) {
return rabatt(40, 1) == 35 ? 0 : 1;
}Den Übungsordner anlegen, falls er fehlt
mkdir -p ~/uebungHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
In den Übungsordner wechseln
cd ~/uebungHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Den Quelltext von rabatt schreiben
printf '%s\n' 'int rabatt(int preis, int karte) {' ' if (karte == 1) {' ' return preis - 5;' ' }' ' return preis;' '}' 'int main(void) {' ' return rabatt(40, 1) == 35 ? 0 : 1;' '}' > rabatt.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Programm bauen
gcc -O0 -no-pie -fno-stack-protector -o rabatt rabatt.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Befehle von rabatt ansehen
objdump -d -M intel --no-show-raw-insn --disassemble=rabatt rabatt | grep '^ 'Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Jede Zeile hat einen Befehl und Operanden
Die Option -M intel schreibt das Ziel zuerst: mov eax, 5 legt die 5 in eax. Ohne die Option steht das Ziel hinten. --no-show-raw-insn lässt die Bytes weg, und grep behält die Zeilen mit Befehlen.
Die Zeichen edi und esi sind Register. Bei x86-64 stehen die ersten zwei Argumente einer Funktion darin. Das Ergebnis steht am Ende in eax.
Beispiel: die Zeilen von rabatt
| Befehl | Was er tut |
|---|---|
| mov DWORD PTR [rbp-0x4],edi | Merkt sich den Preis auf dem Stapel |
| mov DWORD PTR [rbp-0x8],esi | Merkt sich die Karte auf dem Stapel |
| cmp DWORD PTR [rbp-0x8],0x1 | Vergleicht die Karte mit 1 |
| jne | Springt weg, wenn beide nicht gleich sind |
| sub eax,0x5 | Zieht 5 vom Preis ab |
| ret | Kehrt zurück, das Ergebnis liegt in eax |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
cmp vergleicht, der Sprung entscheidet
Der Befehl cmp rechnet nichts in ein Register. Er setzt nur die Flags: Waren beide Werte gleich oder nicht? Erst der nächste Befehl, hier jne („jump if not equal“), liest dieses Flag.
So wird aus if (karte == 1) ein Paar aus cmp und jne. Das Diagramm zeigt die zwei Wege durch rabatt.
Beispiel: dieselben Zeilen, zusammenhängend
endbr64
push rbp
mov rbp,rsp
mov DWORD PTR [rbp-0x4],edi
mov DWORD PTR [rbp-0x8],esi
cmp DWORD PTR [rbp-0x8],0x1
jne Marke_A
mov eax,DWORD PTR [rbp-0x4]
sub eax,0x5
jmp Marke_B
Marke_A:
mov eax,DWORD PTR [rbp-0x4]
Marke_B:
pop rbp
retDas Programm starten
./rabattHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Den Rückgabewert von main lesen
echo $?Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ein häufiger Fehler
Warnung
Viele denken, cmp speichere ein Ergebnis in einem Register. Das tut es nicht. Der Befehl setzt nur Flags, und der nächste Sprung liest sie. Suchen Sie darum bei jedem Sprung den cmp oder test, der direkt davor steht.
Weiterführend
Ändern Sie karte == 1 in karte == 2, bauen Sie das Programm neu und lesen Sie die Zeile mit cmp. Welche Zahl steht jetzt am Ende?
Ändern Sie dann preis - 5 in preis - 7. Welche Zeile des Assemblers ändert sich? Prüfen Sie mit objdump, ob Sie recht hatten.