====== Kernel-Funktionen (Syscalls) über einen Interrupt aufrufen ====== Der Linux-Kernel hat einige grundlegende Funktionen, die wir in unseren Programmen nutzen können. Diese Funktionen werden "Syscalls" genannt und durch eine eindeutige und konstanten Nummer zwischen 0 und 255 identifiziert. Syscalls bilden die Schnittstelle zwischen Programmen und dem Linux-Kernel.\\ \\ Details zu Syscalls finden sich in der Manpage: $ man syscalls ===== Linux-Callgate ===== Diese vom Kernel bereitgestellte Funktionen können aber nicht direkt aufgerufen werden. Sie müssen über das sogenannte "Callgate" angesprochen werden. Dabei wird die Nummer der jeweiligen Funktion im ''rax''-Register gespeichert und ein Software-Interrupt ausgelöst. Dieser bestimmte Interrupt hat den hexadezimalen Wert 80. Das ist gleichzeitig auch der einzige in NASM verfügbare Interrupt.\\ Den Kernel-Funktionen können je nach Bedarf noch Werte übergeben werden, die in anderen Registern platziert werden.\\ Sehen wir uns das folgende Beispiel an: mov rax, 4 mov rbx, 1 mov rcx, MyString mov rdx, MyStringLength int 80h Dabei wird über ''rax'' festgelegt, dass die System-Funktion mit der eindeutigen Nummer 4 aufgerufen werden soll. In diesem Fall ist das ein Aufruf der Funktion ''write''. Diese Funktion schreibt Daten einer bestimmten Länge an ein Ziel. C-Programmierer können mit dieser Signatur etwas anfangen: ssize_t write( int fd, const void *buf, size_t count ); Die anderen 3 Register sind Parameter für die Funktion. In ''rbx'' befindet sich das Ziel des Schreibens. Dabei steht die Konstante 1 für die Standardausgabe, was im Normalfall die Konsole ist. ''rcx'' enthält die Daten und ''rdx'' die Anzahl der Bytes die geschrieben werden sollen.\\ Tatsächlich aufgerufen wird diese Funktion erst durch das mit ''int'' gekennzeichnete Interrupt mit dem hexadezimalen Wert 80.\\ Nach dem Aufruf wird die Ausführung nach der Zeile ''int 80h'' fortgesetzt. \\ \\ Ein weiteres Beispiel: mov rax, 1 mov rbx, 0 int 80h Die Funktion mit der Nummer 1 nennt sich ''exit'' und wird zum Beenden eines Programmes verwendet. Zum Vergleich die Signatur der Funktion in der Programmiersprache C: void exit( int status ); Diese Funktion nimmt nur einen Parameter, nämlich einen Status-Code. Dieser Code gibt an, ob das Programm ordnungsgemäß beendet wurde. Dabei steht 0 für Erfolg, jeder andere Wert für einen Fehler. Der Status-Code befindet sich im Register ''rbx''. ===== Ablauf des Aufrufs ===== Das war eine grobe Beschreibung des Ablaufs eines Syscall-Interrupts. Im Hintergrund geschehen jedoch noch einige Dinge, um die wir uns nicht kümmern müssen.\\ Kernel-Funktionen befinden sich an einer ganz anderen Stelle im Speicher als unser Assembler-Code. Um eine sichere Rückkehr zu dieser Stelle in der Ausführung zu garantieren, muss die Position gespeichert werden. Deshalb speichert ''int 80h'' noch vor dem Aufruf der Kernel-Funktion die Adresse der nachfolgenden Anweisung am Stack.\\ Ist die Rückkehr gesichert, wird die Nummer der System-Funktion in eine Adresse umgesetzt. Weiß das Programm nun an welcher Stelle die Funktion im Speicher liegt, kann sie vom Kernel ausgeführt werden.\\ Nach der vollständigen Ausführung der Funktion wird die von ''int 80h'' gespeicherte Adresse wieder vom Stack genommen und zu ihr zurückgekehrt. Die Anweisung dafür heißt ''IRET'' (**I**nterrupt **RET**urn) und wird automatisch aufgerufen.\\ Nun ist der Aufruf der System-Funktion wirklich komplett und es folgen die nächsten Anweisungen in unserem Programm. \\ \\ FIXME Grafik ===== Liste der Linux-Syscalls ===== FIXME /usr/include/asm/unistd_64.h