10: CALL f
11:
+-------------------+ <- RSP (9920)
| Variablen (80 B) |
+-------------------+ <- RBP (1000)
| Rücksprungadresse |
+-------------------+
=> 10: CALL f
11:
+-------------------+ <- RSP (9912)
| Rückspr.-Addr: 11 |
+-------------------+
| Variablen (80 B) |
+-------------------+ <- RBP (1000)
| Rücksprungadresse |
+-------------------+
f:
=> 20: PUSH RBP 23: MOV RSP, RBP
21: MOV RBP, RSP 24: POP RBP
22: Beliebige PUSHs 25: RET
+-------------------+ <- RSP (9904)
| alter RBP: 10000 |
+-------------------+
| Rückspr.-Addr: 11 |
+-------------------+
| Variablen (80 B) |
+-------------------+ <- RBP (1000)
| Rücksprungadresse |
+-------------------+
f:
20: PUSH RBP 23: MOV RSP, RBP
=> 21: MOV RBP, RSP 24: POP RBP
22: Beliebige PUSHs 25: RET
+-------------------+ <- RSP (9904), RBP (9904)
| alter RBP: 10000 |
+-------------------+
| Rückspr.-Addr: 11 |
+-------------------+
| Variablen (80 B) |
+-------------------+
| Rücksprungadresse |
+-------------------+
f:
20: PUSH RBP 23: MOV RSP, RBP
21: MOV RBP, RSP 24: POP RBP
=> 22: Beliebige PUSHs 25: RET
+-------------------+ <- RSP (9744)
| Variablen (160 B) |
+-------------------+ <- RBP (9904)
| alter RBP: 10000 |
+-------------------+
| Rückspr.-Addr: 11 |
+-------------------+
| Variablen (80 B) |
+-------------------+
| Rücksprungadresse |
+-------------------+
f:
20: PUSH RBP => 23: MOV RSP, RBP
21: MOV RBP, RSP 24: POP RBP
22: Beliebige PUSHs 25: RET
+-------------------+
| Variablen (160 B) | > Nicht mehr auf dem Stack
+-------------------+ <- RSP (9904), RBP (9904)
| alter RBP: 10000 |
+-------------------+
| Rückspr.-Addr: 11 |
+-------------------+
| Variablen (80 B) |
+-------------------+
| Rücksprungadresse |
+-------------------+
f:
20: PUSH RBP 23: MOV RSP, RBP
21: MOV RBP, RSP => 24: POP RBP
22: Beliebige PUSHs 25: RET
+-------------------+
| Variablen (160 B) | \
+-------------------+ > Nicht mehr auf dem Stack
| alter RBP: 10000 | /
+-------------------+ <- RSP (9904)
| Rückspr.-Addr: 11 |
+-------------------+
| Variablen (80 B) |
+-------------------+ <- RBP (1000)
| Rücksprungadresse |
+-------------------+
f:
20: PUSH RBP 23: MOV RSP, RBP
21: MOV RBP, RSP 24: POP RBP
22: Beliebige PUSHs => 25: RET
+-------------------+
| Variablen (160 B) | \
+-------------------+ |
| alter RBP: 10000 | > Nicht mehr auf dem Stack
+-------------------+ |
| Rückspr.-Addr: 11 | /
+-------------------+ <- RSP (9904)
| Variablen (80 B) |
+-------------------+ <- RBP (1000)
| Rücksprungadresse |
+-------------------+
Wenn wir ein Assembler-Programm schreiben, mit einer main
-Funktion, kann ich die nach dem assemblieren enstehende o
-Datei mit dem gcc
kompilieren und bekomme ein lauffähiges Programm.
global main
section .text
main:
mov rax, 0
ret
nasm -f elf64 test.asm # Erzeugt test.o
gcc -o test test.o # Erzeugt test
./test # Führt es aus
Die Signatur der main
-Funktion ist
int main(int argc, char *argv[])
Man bekommt also im ersten Parameter (rdi
) die Anzahl der Parameter und im zweiten (rsi
) ein Array von Adressen, an denen die Parameter im Speicher stehen. Der 0. Parameter ist immer der Name des Programms, es gibt also immer einen Parameter mehr, als man übergibt.
Die Adressen(!) der einzelnen Parameter-Strings(!) erhält man entsprechend per [rsi]
, [rsi+8]
, [rsi+16]
, etc.
Externe Bibliotheksfunktionen kann man aufrufen, wenn man dem Assembler sagt, dass die Funktion extern
zur Verfügung gestellt wird. Um einen String zu einer Zahl umzuwandeln gibt es beispielsweise die Funktion strtoul
, deren Signatur wie folgt aussieht:
unsigned long int strtoul(const char *nptr, char **endptr, int base);
Wir müssen ihr also drei Parameter übergeben (wie bei der von euch implementierten strToInt
-Funktion). Hier hatte sich im ersten Tutorium der Fehler versteckt, da ich nur einen Parameter übergeben hatte. Der zweite Parameter kann einfach auf 0
gesetzt werden.
global main
extern strtoul
section .text
main:
; Hier die Parameter korrekt belegen
call strtoul
; Jetzt steht das Ergebnis von strtoul in rax
ret
rand
mit Zufallswerten befüllenHier verwenden wir wie oben eine Bibliotheksfunktion, nämlich rand
. Wenn rand
nicht initialisiert wird (siehe dazu srand
), gibt es bei jedem Programmaufruf immer die gleiche Folge von Werten zurück.
Um Text und/oder Zahlen auszugeben, gibt es die Funktion printf
(deutsche Wikipedia). Der erste Parameter ist eine Adresse auf einen String. Sind in dem String Platzhalter enthalten (etwas, das mit %
beginnt), so werden weitere Parameter erwartet. Diese werden ganz normal entsprechend der Calling Convention übergeben. Für euch wichtig ist eigentlich hauptsächlich der Platzhalter %lu
für eine 64bit-Zahl ohne Vorzeichen.
Wichtig: Damit euer Programm nicht abstürzt, müsst ihr vor Aufruf von printf
das Register rax
auf 0
setzen. In rax
erwartet printf
die Anzahl der verwendeten xmm
-Register. Da wir nicht mit Kommazahlen arbeiten, muss da eine 0
drin stehen.
Texte können/müssen im Assembler-Programm im .data
-Bereich abgelegt werden.
global main
extern printf
section .data
; db = Speichere eine Folge von Bytes
; 10 = Zeilenumbruch
; NIE die 0 am Ende vergessen!
text:
db "Hallo!", 10, 0
mit_param:
db "Die Zahl ist %lu.", 10, 0
section .text
main:
; printf ohne zusätzlichen Parameter
mov rdi, text
call printf
; printf mit zusätzlichem Parameter
mov rdi, mit_param
mov rsi, 5345
call printf
ret
Das sollte eigentlich alle offenen Fragen soweit klären, dass ihr an der Aufgabe arbeiten könnt.