TI2/Rechnerarchitektur-Tutorium, 26.01.2017

Assembler-Programmierung / 11. Übungszettel

Syscalls – Motivation

Früher:

  • Ein aktiver Prozess, der Zugriff auf alles hat (DOS, DOS-Spiele)

  • Direkter Hardware-Zugriff durch die Prozesse

Heute:

  • Mehrere Prozesse, die (quasi-)parallel arbeiten

  • Betriebssystem muss Hardware-Zugriff der Prozesse abstrahieren und verwalten

(Mehr Details in der Veranstaltung Betriebs- und Kommunikationssysteme, dem 2. Teil des Moduls)

Syscalls – Funktionalität

  1. Programmfluss des aktuellen Prozesses wird unterbrochen

  2. Betriebssystem wir mitgeteilt, dass ein Syscall des Prozesses ausgeführt wurde

  3. Betriebssystem führt die Funktionalität aus

  4. Prozess wird fortgesetzt

Ein minimales Programm, ohne C-Bibliothek (I)

  • Wollen ein kleines Programm schreiben, welches nicht mit dem gcc kompiliert werden muss

    section .text
    global _start   ; Wird ein Prozess gestartet, wird bei
    _start:         ; _start mit der Ausführung begonnen
        ret
  • Wir müssen weiterhin zuerst assemblieren

    nasm -f elf64 test.asm
  • Die entstehende o-Datei muss ausführbargemacht werden (Linken)

    ld -o test test.o

Ein minimales Programm, ohne C-Bibliothek (II)

  • Beim Ausführen stürzt das Programm ab

  • Wir gucken uns an, an welche Adresse im Code er denn springen möchte

    section .text
    global _start
    _start:
        pop rax
        ret
  • Angucken im Debugger:

    gdb test # Debugger mit unserem Programm starten
    break _start # Beim Erreichen der Marke _start stoppen
    run # Programm starten
    stepi # Nächste Instruktion (pop rax) ausführen
    print $rax # Wert von rax ausgeben
    quit # Debugger beenden

Ein minimales Programm, ohne C-Bibliothek (III)

  • Ergebnis: Er will an Adresse 1 Code ausführen, dort liegt aber kein Code

  • Dies ist offenbar nicht der richtige Weg das Programm zu beenden

  • Weglassen von ret führt auch zu Absturz

  • Lösung: Wir müssen dem Betriebssystem mitteilen, dass wir fertig sind, damit das Betriebssystem den Prozess entfernt

  • Dafür benötigen wir einen Syscall

Nutzung von Syscalls

  • Grundlegend ist die Benutzung nicht (viel) anders als bei normalen Funktionsaufrufen

  • Funktionsparameter werden in den Registern rdi, rsi, rdx, r10, r8 und r9 übergeben

  • Unterschied: Statt call funktionsname muss die Nummer des gewünschten Syscalls in rax geschrieben und danach der Befehl syscall verwendet werden

  • Hier findet sich eine Liste von Syscalls für 64-bit-Linux

  • Dort finden wir den Syscall sys_exit mit der Nummer 60 und einem Parameter

Syscall im Beispiel

section .text
global _start
_start:
    mov rax, 60 ; Syscall sys_exit verwenden
    mov rdi, 0  ; Programm war erfolgreich
    syscall     ; Betriebssystem Bescheid geben

Nützliche / Benötigte Syscalls

  • Zum lesen aus und schreiben in „Dateien“ gibt es sys_read und sys_write, entsprechend den C-Funktionen read und write

    ssize_t read(int fd, void *buf, size_t count);
    ssize_t write(int fd, const void *buf, size_t count);
  • Der erste Parameter ist ein sogenannter Dateideskriptor (englisch file descriptor), eine Zahl die eine geöffnete Datei eindeutig referenziert

  • Es gibt drei Standard-Deskriptoren:

    • 0 für die Standardeingabe (dort kann ich als Benutzer nach Start des Programms etwas eintippen)

    • 1 für die Standardausgabe (dort landet alles, was ihr z.b. auch per printf ausgegeben habt)

    • 2 für die Fehlerausgabe, für uns nicht so wichtig und fast das gleiche wie 1