Systemaufrufe: Unterschied zwischen den Versionen
KKeine Bearbeitungszusammenfassung |
|||
| (9 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
In diesem Kapitel werden wir uns mit Systemaufrufen unter Linux beschäftigen. Insbesondere werden wir: | In diesem Kapitel werden wir uns mit Systemaufrufen unter Linux beschäftigen. Insbesondere werden wir: | ||
| Zeile 6: | Zeile 4: | ||
Grundlegende Systemaufrufe wie exit, read, write, open und close kennenlernen und | Grundlegende Systemaufrufe wie exit, read, write, open und close kennenlernen und | ||
Erklären, wie Parameter übergeben und Rückgabewerte entgegengenommen werden. | Erklären, wie Parameter übergeben und Rückgabewerte entgegengenommen werden. | ||
Einführung in Linux-Systemaufrufe | == Einführung in Linux-Systemaufrufe == | ||
Systemaufrufe sind Schnittstellen, die es Programmen ermöglichen, Dienste und Funktionen des Betriebssystems in Anspruch zu nehmen. Diese können niedrigere Eingabe-/Ausgabe-Funktionen, Prozesssteuerung, Speicherverwaltung und mehr umfassen. In ARM64-Assembler verwenden wir den svc-Befehl, um Systemaufrufe auszuführen. Dabei tragen wir die Nummer des spezifischen Systemaufrufs in Register x8 ein und die Parameter in Registers x0 bis x5. | Systemaufrufe sind Schnittstellen, die es Programmen ermöglichen, Dienste und Funktionen des Betriebssystems in Anspruch zu nehmen. Diese können niedrigere Eingabe-/Ausgabe-Funktionen, Prozesssteuerung, Speicherverwaltung und mehr umfassen. In ARM64-Assembler verwenden wir den svc-Befehl, um Systemaufrufe auszuführen. Dabei tragen wir die Nummer des spezifischen Systemaufrufs in Register x8 ein und die Parameter in Registers x0 bis x5. | ||
Grundlegende Systemaufrufe | == Grundlegende Systemaufrufe == | ||
exit | === exit === | ||
Dieser Systemaufruf beendet das Programm. | Dieser Systemaufruf beendet das Programm. | ||
Syntax: | Syntax: | ||
<syntaxhighlight lang="asm"> | |||
mov x8, #93 // syscall number for exit | mov x8, #93 // syscall number for exit | ||
mov x0, #0 // exit code (0 for success) | mov x0, #0 // exit code (0 for success) | ||
svc 0 | svc 0 | ||
</syntaxhighlight> | |||
=== read === | |||
read | |||
Dieser Systemaufruf liest Daten von einer Datei. | Dieser Systemaufruf liest Daten von einer Datei. | ||
Syntax: | Syntax: | ||
<syntaxhighlight lang="asm"> | |||
mov x8, #63 // syscall number for read | mov x8, #63 // syscall number for read | ||
mov x0, #file_desc // file descriptor (0 for stdin) | mov x0, #file_desc // file descriptor (0 for stdin) | ||
| Zeile 34: | Zeile 32: | ||
mov x2, #size // number of bytes to read | mov x2, #size // number of bytes to read | ||
svc 0 | svc 0 | ||
</syntaxhighlight> | |||
=== write === | |||
write | |||
Dieser Systemaufruf schreibt Daten in eine Datei. | Dieser Systemaufruf schreibt Daten in eine Datei. | ||
Syntax: | Syntax: | ||
<syntaxhighlight lang="asm"> | |||
mov x8, #64 // syscall number for write | mov x8, #64 // syscall number for write | ||
mov x0, #file_desc // file descriptor (1 for stdout) | mov x0, #file_desc // file descriptor (1 for stdout) | ||
| Zeile 47: | Zeile 45: | ||
mov x2, #size // number of bytes to write | mov x2, #size // number of bytes to write | ||
svc 0 | svc 0 | ||
</syntaxhighlight> | |||
=== open === | |||
open | |||
Dieser Systemaufruf öffnet eine Datei und gibt einen Datei-Deskriptor zurück. | Dieser Systemaufruf öffnet eine Datei und gibt einen Datei-Deskriptor zurück. | ||
Syntax: | Syntax: | ||
<syntaxhighlight lang="asm"> | |||
mov x8, #56 // syscall number for openat | mov x8, #56 // syscall number for openat | ||
mov x0, #-100 // AT_FDCWD (current working directory) | mov x0, #-100 // AT_FDCWD (current working directory) | ||
| Zeile 60: | Zeile 58: | ||
mov x2, #flags // flags (e.g., O_RDONLY for read only) | mov x2, #flags // flags (e.g., O_RDONLY for read only) | ||
svc 0 | svc 0 | ||
</syntaxhighlight> | |||
=== close === | |||
close | |||
Dieser Systemaufruf schließt eine Datei. | Dieser Systemaufruf schließt eine Datei. | ||
Syntax: | Syntax: | ||
<syntaxhighlight lang="asm"> | |||
mov x8, #57 // syscall number for close | mov x8, #57 // syscall number for close | ||
mov x0, #file_desc // file descriptor | mov x0, #file_desc // file descriptor | ||
svc 0 | svc 0 | ||
</syntaxhighlight> | |||
Übergabe von Parametern und Entgegennahme von Rückgabewerten | == Übergabe von Parametern und Entgegennahme von Rückgabewerten == | ||
Die Übergabe von Parametern erfolgt über bestimmte Register: | Die Übergabe von Parametern erfolgt über bestimmte Register: | ||
x0 bis x5 für die ersten sechs Argumente | * x0 bis x5 für die ersten sechs Argumente | ||
x8 für die Systemaufrufnummer | * x8 für die Systemaufrufnummer | ||
Rückgabewerte werden in x0 zurückgegeben. | Rückgabewerte werden in x0 zurückgegeben. | ||
Beispiel: | Beispiel: | ||
<syntaxhighlight lang="asm"> | |||
.global _start | .global _start | ||
| Zeile 100: | Zeile 99: | ||
mov x8, #93 // syscall number for exit | mov x8, #93 // syscall number for exit | ||
svc 0 | svc 0 | ||
</syntaxhighlight> | |||
In diesem Beispiel wird die Zeichenkette "Hello, World!\n" auf die Standardausgabe geschrieben und danach das Programm mit dem Rückgabewert 0 beendet. | In diesem Beispiel wird die Zeichenkette "Hello, World!\n" auf die Standardausgabe geschrieben und danach das Programm mit dem Rückgabewert 0 beendet. | ||
Verweis auf alle verfügbaren Linux-Systemaufrufe | == Verweis auf alle verfügbaren Linux-Systemaufrufe == | ||
Eine vollständige Liste der verfügbaren Linux-Systemaufrufe und deren Nummern finden Sie in den entsprechenden Header-Dateien des Systems, wie z.B. in /usr/include/asm/unistd.h oder online in den offiziellen Linux-Kernel-Dokumentationen. | Eine vollständige Liste der verfügbaren Linux-Systemaufrufe und deren Nummern finden Sie in den entsprechenden Header-Dateien des Systems, wie z.B. in /usr/include/asm/unistd.h oder online in den offiziellen Linux-Kernel-Dokumentationen. | ||
Eine häufig verwendete Referenz für Systemaufrufe ist die Man-Seite (man 2 syscall). Eine umfassendere Liste spezifisch für den ARM64-Architektur | Eine häufig verwendete Referenz für Systemaufrufe ist die Man-Seite ([https://man7.org/linux/man-pages/man2/syscalls.2.html man 2 syscall]). Eine umfassendere Liste spezifisch für den ARM64-Architektur habe ich hier erstellt: [[Übersicht der Linux ARM64 Systemaufrufen]] | ||
== Fehlercodes == | |||
Wenn ein Systemcall ausgeführt wird (z. B. write, open, read), prüft der Kernel, ob die Anfrage gültig ist, und führt sie aus – oder gibt einen Fehlercode zurück, wenn etwas schiefgeht. | |||
Viele Systemcalls geben in x0 ein Ergebnis wie zum Beispiel die Anzahl von Bytes, ein Dateideskriptor, PID oder anderes zurück, wenn alles geklappt hat. Sollte allerdings ein negatives Ergebnis (errno) zurück kommen, so hat in der Regel etwas nicht funktioniert. | |||
Beispiel: | |||
<syntaxhighlight lang="asm"> | |||
// Datei öffnen (openat) | |||
mov x0, AT_FDCWD // aktuelles Verzeichnis | |||
ldr x1, =filename // Dateiname | |||
mov x2, #0 // O_RDONLY | |||
mov x8, #56 // syscall number für openat | |||
svc #0 | |||
// Rückgabewert ist jetzt in x0 | |||
cmp x0, #0 | |||
b.ge open_success // x0 >= 0 → Erfolg | |||
// Fehlerbehandlung | |||
neg x1, x0 // Fehlercode positiv machen | |||
// Jetzt enthält x1 z.B. 2 → ENOENT (Datei nicht gefunden) | |||
open_success: | |||
// normal weiter... | |||
</syntaxhighlight> | |||
[[Übersicht der Fehlercodes]] | |||
== Strukturen bei Systemcalls == | |||
einige Systemcalls in Linux (auch auf ARM64) erwarten Strukturen als Argumente – also zusammengesetzte Daten, die im Speicher bereitgestellt werden müssen und dann per Zeiger übergeben werden. | |||
=== Strukturen an Systemcalls übergeben (ARM64/Linux) === | |||
Einige Systemcalls (z. B. stat, gettimeofday, timespec, uname, poll, etc.) brauchen als Argument einen Zeiger auf eine Struktur, die im Speicher angelegt ist. | |||
Auf folgendes ist zu achten: | |||
* Speicher für die Struktur bereitstellen (z. B. über .skip oder .space) | |||
* Die Struktur ggf. initialisieren (z. B. Null setzen oder Werte eintragen) | |||
* Den Zeiger zur Struktur ins richtige Register laden (z. B. x1) | |||
=== Beispiel 1: uname-Systemcall === | |||
Dieser Systemcall füllt eine struct utsname mit Informationen über das System (wie uname -a in der Shell). | |||
<syntaxhighlight lang="C"> | |||
// C-Definition: | |||
struct utsname { | |||
char sysname[65]; | |||
char nodename[65]; | |||
char release[65]; | |||
char version[65]; | |||
char machine[65]; | |||
char domainname[65]; // oft ignoriert | |||
}; | |||
</syntaxhighlight> | |||
Gesamtgröße: mindestens 390 Bytes | |||
Assembler-Beispiel: uname aufrufen und sysname (z. B. „Linux“) ausgeben | |||
<syntaxhighlight lang="asm"> | |||
.section .bss | |||
utsbuf: .skip 390 // Speicher für struct utsname | |||
outbuf: .skip 65 // Puffer zum Ausgeben von sysname | |||
.section .text | |||
.global _start | |||
_start: | |||
// Systemcall: uname(struct utsname *buf) | |||
// syscall number: 160 | |||
ldr x0, =utsbuf // Zeiger auf struct utsname | |||
mov x8, #160 // syscall number | |||
svc #0 | |||
// Rückgabewert in x0: 0 bei Erfolg, sonst -errno | |||
cmp x0, #0 | |||
b.ne uname_fail | |||
// Jetzt sysname (erstes Feld der Struktur) ausgeben: | |||
// Es ist eine nullterminierte Zeichenkette | |||
ldr x1, =utsbuf // sysname ist am Anfang | |||
bl print_string | |||
b done | |||
uname_fail: | |||
// Fehlerausgabe (optional) | |||
ldr x1, =failmsg | |||
bl print_string | |||
done: | |||
// exit(0) | |||
mov x0, #0 | |||
mov x8, #93 | |||
svc #0 | |||
// Ausgabe-Funktion (x1 = Zeiger auf 0-terminierten String) | |||
print_string: | |||
mov x2, #0 | |||
count_loop: | |||
ldrb w3, [x1, x2] | |||
cbz w3, print_now | |||
add x2, x2, #1 | |||
b count_loop | |||
print_now: | |||
mov x0, #1 // stdout | |||
mov x8, #64 // write | |||
svc #0 | |||
ret | |||
.section .data | |||
failmsg: .asciz "uname failed\n" | |||
</syntaxhighlight> | |||
Kompilieren & Ausführen | |||
<syntaxhighlight lang="shell"> | |||
as -o uname.o uname.s | |||
ld -o uname uname.o | |||
./uname | |||
</syntaxhighlight> | |||
Erwartete Ausgabe: | |||
<syntaxhighlight lang="shell"> | |||
Linux | |||
</syntaxhighlight> | |||
=== Weitere Beispiele für strukturbasierte Syscalls === | |||
{| class="wikitable" | |||
|- | |||
! Systemcall !! Struktur !! Zweck | |||
|- | |||
| uname || struct utsname || Systeminfo | |||
|- | |||
| statx || struct statx || Dateiinformation | |||
|- | |||
| gettimeofday || struct timeval || aktuelle Uhrzeit | |||
|- | |||
| nanosleep || struct timespec || schlafen für Zeitspanne | |||
|- | |||
| poll || struct pollfd || Warten auf Dateideskriptor | |||
|} | |||
=== Beispiel 2: nanosleep mit struct timespec === | |||
<syntaxhighlight lang="C"> | |||
// struct timespec { | |||
long tv_sec; // ganze Sekunden | |||
long tv_nsec; // Nanosekunden | |||
// }; | |||
</syntaxhighlight> | |||
Schlafen für 2 Sekunden: | |||
<syntaxhighlight lang="asm"> | |||
.section .data | |||
timespec: | |||
.quad 2 // tv_sec = 2 | |||
.quad 0 // tv_nsec = 0 | |||
.section .text | |||
.global _start | |||
_start: | |||
ldr x0, =timespec // Zeiger auf timespec | |||
mov x1, #0 // NULL für "remaining" | |||
mov x8, #101 // syscall: nanosleep | |||
svc #0 | |||
// done | |||
mov x0, #0 | |||
mov x8, #93 | |||
svc #0 | |||
</syntaxhighlight> | |||
== Arbeiten mit Dateien == | |||
Siehe hier auch das Incude an: https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h | |||
=== Datei öffnen (openat) === | |||
<syntaxhighlight lang="asm"> | |||
mov x0, #-100 // AT_FDCWD (aktuelles Verzeichnis) | |||
ldr x1, =Dateiname // char *pathname | |||
mov x2, #0 // flags z. B. O_RDONLY (0), O_WRONLY, O_CREAT usw. | |||
mov x3, #0 // Modus (z. B. 0666) – nur relevant bei O_CREAT | |||
mov x8, #56 // openat | |||
svc #0 | |||
// Rückgabewert (File Descriptor) in x0 | |||
adds x10, x0, #0 // File Descriptor in x10 speichern | |||
bpl ok // Wenn >= 0, dann erfolgreich | |||
// Fehlerbehandlung | |||
</syntaxhighlight> | |||
=== Datei schließen (close) === | |||
<syntaxhighlight lang="asm"> | |||
mov x0, x10 // File Descriptor | |||
mov x8, #57 // __NR_close | |||
svc #0 | |||
</syntaxhighlight> | |||
=== Lesen aus einer Datei (read) === | |||
<syntaxhighlight lang="asm"> | |||
mov x0, x10 // File Descriptor | |||
ldr x1, =buffer // Zielpuffer | |||
mov x2, #256 // Anzahl der Bytes | |||
mov x8, #63 // __NR_read | |||
svc #0 | |||
</syntaxhighlight> | |||
=== In Datei schreiben (write) === | |||
<syntaxhighlight lang="asm"> | |||
mov x0, x10 // File Descriptor | |||
ldr x1, =buffer // Quelle | |||
mov x2, #256 // Anzahl der Bytes | |||
mov x8, #64 // __NR_write | |||
svc #0 | |||
</syntaxhighlight> | |||
=== Beispiel === | |||
Ein Beispiel das zeigt, wie Sie eine Datei öffnen, die Daten lesen, bearbeiten und anschließend die bearbeiteten Daten in einer anderen Datei speichern können. Ich werde dafür Systemaufrufe (syscalls) verwenden. | |||
* Überblick der Schritte: | |||
# Datei öffnen (open). | |||
# Daten aus der Datei lesen (read). | |||
# Daten bearbeiten (In diesem Beispiel ändern wir die Daten nicht, sie könnten aber leicht modifiziert werden). | |||
# Neue Datei erstellen und öffnen (open). | |||
# Daten in die neue Datei schreiben (write). | |||
# Beide Dateien schließen (close). | |||
* Vollständiges Beispiel in ARM64-Assembler: | |||
<syntaxhighlight lang="asm"> | |||
.section .data | |||
filename: .asciz "input.txt" | |||
newfilename: .asciz "output.txt" | |||
buffer: .space 1024 | |||
.section .text | |||
.global _start | |||
_start: | |||
// input.txt öffnen | |||
mov x0, #-100 // AT_FDCWD | |||
ldr x1, =filename | |||
mov x2, #0 // O_RDONLY | |||
mov x3, #0 // mode (nicht benötigt) | |||
mov x8, #56 // openat | |||
svc #0 | |||
mov x19, x0 // FD in x19 | |||
// Lesen | |||
mov x0, x19 | |||
ldr x1, =buffer | |||
mov x2, #1024 | |||
mov x8, #63 // read | |||
svc #0 | |||
mov x20, x0 // Gelesene Bytes | |||
// output.txt öffnen (schreibend, erstellen, ggf. überschreiben) | |||
mov x0, #-100 | |||
ldr x1, =newfilename | |||
mov x2, #577 // O_WRONLY | O_CREAT | O_TRUNC (0x241) | |||
mov x3, #0o644 // rw-r--r-- | |||
mov x8, #56 // openat | |||
svc #0 | |||
mov x21, x0 // FD für Ausgabe | |||
// Schreiben | |||
mov x0, x21 | |||
ldr x1, =buffer | |||
mov x2, x20 | |||
mov x8, #64 // write | |||
svc #0 | |||
// Eingabedatei schließen | |||
mov x0, x19 | |||
mov x8, #57 // close | |||
svc #0 | |||
// Ausgabedatei schließen | |||
mov x0, x21 | |||
mov x8, #57 | |||
svc #0 | |||
// Programm beenden | |||
mov x0, #0 | |||
mov x8, #93 // exit | |||
svc #0 | |||
</syntaxhighlight> | |||
==== Erklärung wichtiger Flags ==== | |||
Die Flags findest du unter: | |||
[https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h fcntl.h] auf GitHub | |||
Beispiele: | |||
<syntaxhighlight lang="C"> | |||
#define O_RDONLY 00000000 | |||
#define O_WRONLY 00000001 | |||
#define O_RDWR 00000002 | |||
#define O_CREAT 00000100 | |||
#define O_TRUNC 00001000 | |||
</syntaxhighlight> | |||
Kombination z. B.: | |||
<syntaxhighlight lang="C"> | |||
O_WRONLY | O_CREAT | O_TRUNC = 0x1 | 0x100 | 0x1000 = 0x1101 = 577 dezimal | |||
</syntaxhighlight> | |||
Aktuelle Version vom 10. April 2025, 07:30 Uhr
In diesem Kapitel werden wir uns mit Systemaufrufen unter Linux beschäftigen. Insbesondere werden wir:
Eine Einführung in Linux-Systemaufrufe geben, Grundlegende Systemaufrufe wie exit, read, write, open und close kennenlernen und Erklären, wie Parameter übergeben und Rückgabewerte entgegengenommen werden.
Einführung in Linux-Systemaufrufe
Systemaufrufe sind Schnittstellen, die es Programmen ermöglichen, Dienste und Funktionen des Betriebssystems in Anspruch zu nehmen. Diese können niedrigere Eingabe-/Ausgabe-Funktionen, Prozesssteuerung, Speicherverwaltung und mehr umfassen. In ARM64-Assembler verwenden wir den svc-Befehl, um Systemaufrufe auszuführen. Dabei tragen wir die Nummer des spezifischen Systemaufrufs in Register x8 ein und die Parameter in Registers x0 bis x5.
Grundlegende Systemaufrufe
exit
Dieser Systemaufruf beendet das Programm.
Syntax:
mov x8, #93 // syscall number for exit
mov x0, #0 // exit code (0 for success)
svc 0
read
Dieser Systemaufruf liest Daten von einer Datei.
Syntax:
mov x8, #63 // syscall number for read
mov x0, #file_desc // file descriptor (0 for stdin)
mov x1, #buffer // buffer to store the read data
mov x2, #size // number of bytes to read
svc 0
write
Dieser Systemaufruf schreibt Daten in eine Datei.
Syntax:
mov x8, #64 // syscall number for write
mov x0, #file_desc // file descriptor (1 for stdout)
mov x1, #buffer // buffer with data to write
mov x2, #size // number of bytes to write
svc 0
open
Dieser Systemaufruf öffnet eine Datei und gibt einen Datei-Deskriptor zurück.
Syntax:
mov x8, #56 // syscall number for openat
mov x0, #-100 // AT_FDCWD (current working directory)
mov x1, #filename // pointer to the filename string
mov x2, #flags // flags (e.g., O_RDONLY for read only)
svc 0
close
Dieser Systemaufruf schließt eine Datei.
Syntax:
mov x8, #57 // syscall number for close
mov x0, #file_desc // file descriptor
svc 0
Übergabe von Parametern und Entgegennahme von Rückgabewerten
Die Übergabe von Parametern erfolgt über bestimmte Register:
- x0 bis x5 für die ersten sechs Argumente
- x8 für die Systemaufrufnummer
Rückgabewerte werden in x0 zurückgegeben.
Beispiel:
.global _start
.section .data
hello_msg: .asciz "Hello, World!\n"
_start:
// write system call
mov x0, #1 // file descriptor (1 for stdout)
ldr x1, =hello_msg // buffer
mov x2, #13 // size
mov x8, #64 // syscall number for write
svc 0
// exit system call
mov x0, #0 // exit code
mov x8, #93 // syscall number for exit
svc 0
In diesem Beispiel wird die Zeichenkette "Hello, World!\n" auf die Standardausgabe geschrieben und danach das Programm mit dem Rückgabewert 0 beendet.
Verweis auf alle verfügbaren Linux-Systemaufrufe
Eine vollständige Liste der verfügbaren Linux-Systemaufrufe und deren Nummern finden Sie in den entsprechenden Header-Dateien des Systems, wie z.B. in /usr/include/asm/unistd.h oder online in den offiziellen Linux-Kernel-Dokumentationen.
Eine häufig verwendete Referenz für Systemaufrufe ist die Man-Seite (man 2 syscall). Eine umfassendere Liste spezifisch für den ARM64-Architektur habe ich hier erstellt: Übersicht der Linux ARM64 Systemaufrufen
Fehlercodes
Wenn ein Systemcall ausgeführt wird (z. B. write, open, read), prüft der Kernel, ob die Anfrage gültig ist, und führt sie aus – oder gibt einen Fehlercode zurück, wenn etwas schiefgeht.
Viele Systemcalls geben in x0 ein Ergebnis wie zum Beispiel die Anzahl von Bytes, ein Dateideskriptor, PID oder anderes zurück, wenn alles geklappt hat. Sollte allerdings ein negatives Ergebnis (errno) zurück kommen, so hat in der Regel etwas nicht funktioniert.
Beispiel:
// Datei öffnen (openat)
mov x0, AT_FDCWD // aktuelles Verzeichnis
ldr x1, =filename // Dateiname
mov x2, #0 // O_RDONLY
mov x8, #56 // syscall number für openat
svc #0
// Rückgabewert ist jetzt in x0
cmp x0, #0
b.ge open_success // x0 >= 0 → Erfolg
// Fehlerbehandlung
neg x1, x0 // Fehlercode positiv machen
// Jetzt enthält x1 z.B. 2 → ENOENT (Datei nicht gefunden)
open_success:
// normal weiter...
Strukturen bei Systemcalls
einige Systemcalls in Linux (auch auf ARM64) erwarten Strukturen als Argumente – also zusammengesetzte Daten, die im Speicher bereitgestellt werden müssen und dann per Zeiger übergeben werden.
Strukturen an Systemcalls übergeben (ARM64/Linux)
Einige Systemcalls (z. B. stat, gettimeofday, timespec, uname, poll, etc.) brauchen als Argument einen Zeiger auf eine Struktur, die im Speicher angelegt ist.
Auf folgendes ist zu achten:
- Speicher für die Struktur bereitstellen (z. B. über .skip oder .space)
- Die Struktur ggf. initialisieren (z. B. Null setzen oder Werte eintragen)
- Den Zeiger zur Struktur ins richtige Register laden (z. B. x1)
Beispiel 1: uname-Systemcall
Dieser Systemcall füllt eine struct utsname mit Informationen über das System (wie uname -a in der Shell).
// C-Definition:
struct utsname {
char sysname[65];
char nodename[65];
char release[65];
char version[65];
char machine[65];
char domainname[65]; // oft ignoriert
};
Gesamtgröße: mindestens 390 Bytes
Assembler-Beispiel: uname aufrufen und sysname (z. B. „Linux“) ausgeben
.section .bss
utsbuf: .skip 390 // Speicher für struct utsname
outbuf: .skip 65 // Puffer zum Ausgeben von sysname
.section .text
.global _start
_start:
// Systemcall: uname(struct utsname *buf)
// syscall number: 160
ldr x0, =utsbuf // Zeiger auf struct utsname
mov x8, #160 // syscall number
svc #0
// Rückgabewert in x0: 0 bei Erfolg, sonst -errno
cmp x0, #0
b.ne uname_fail
// Jetzt sysname (erstes Feld der Struktur) ausgeben:
// Es ist eine nullterminierte Zeichenkette
ldr x1, =utsbuf // sysname ist am Anfang
bl print_string
b done
uname_fail:
// Fehlerausgabe (optional)
ldr x1, =failmsg
bl print_string
done:
// exit(0)
mov x0, #0
mov x8, #93
svc #0
// Ausgabe-Funktion (x1 = Zeiger auf 0-terminierten String)
print_string:
mov x2, #0
count_loop:
ldrb w3, [x1, x2]
cbz w3, print_now
add x2, x2, #1
b count_loop
print_now:
mov x0, #1 // stdout
mov x8, #64 // write
svc #0
ret
.section .data
failmsg: .asciz "uname failed\n"
Kompilieren & Ausführen
as -o uname.o uname.s
ld -o uname uname.o
./uname
Erwartete Ausgabe:
Linux
Weitere Beispiele für strukturbasierte Syscalls
| Systemcall | Struktur | Zweck |
|---|---|---|
| uname | struct utsname | Systeminfo |
| statx | struct statx | Dateiinformation |
| gettimeofday | struct timeval | aktuelle Uhrzeit |
| nanosleep | struct timespec | schlafen für Zeitspanne |
| poll | struct pollfd | Warten auf Dateideskriptor |
Beispiel 2: nanosleep mit struct timespec
// struct timespec {
long tv_sec; // ganze Sekunden
long tv_nsec; // Nanosekunden
// };
Schlafen für 2 Sekunden:
.section .data
timespec:
.quad 2 // tv_sec = 2
.quad 0 // tv_nsec = 0
.section .text
.global _start
_start:
ldr x0, =timespec // Zeiger auf timespec
mov x1, #0 // NULL für "remaining"
mov x8, #101 // syscall: nanosleep
svc #0
// done
mov x0, #0
mov x8, #93
svc #0
Arbeiten mit Dateien
Siehe hier auch das Incude an: https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h
Datei öffnen (openat)
mov x0, #-100 // AT_FDCWD (aktuelles Verzeichnis)
ldr x1, =Dateiname // char *pathname
mov x2, #0 // flags z. B. O_RDONLY (0), O_WRONLY, O_CREAT usw.
mov x3, #0 // Modus (z. B. 0666) – nur relevant bei O_CREAT
mov x8, #56 // openat
svc #0
// Rückgabewert (File Descriptor) in x0
adds x10, x0, #0 // File Descriptor in x10 speichern
bpl ok // Wenn >= 0, dann erfolgreich
// Fehlerbehandlung
Datei schließen (close)
mov x0, x10 // File Descriptor
mov x8, #57 // __NR_close
svc #0
Lesen aus einer Datei (read)
mov x0, x10 // File Descriptor
ldr x1, =buffer // Zielpuffer
mov x2, #256 // Anzahl der Bytes
mov x8, #63 // __NR_read
svc #0
In Datei schreiben (write)
mov x0, x10 // File Descriptor
ldr x1, =buffer // Quelle
mov x2, #256 // Anzahl der Bytes
mov x8, #64 // __NR_write
svc #0
Beispiel
Ein Beispiel das zeigt, wie Sie eine Datei öffnen, die Daten lesen, bearbeiten und anschließend die bearbeiteten Daten in einer anderen Datei speichern können. Ich werde dafür Systemaufrufe (syscalls) verwenden.
- Überblick der Schritte:
- Datei öffnen (open).
- Daten aus der Datei lesen (read).
- Daten bearbeiten (In diesem Beispiel ändern wir die Daten nicht, sie könnten aber leicht modifiziert werden).
- Neue Datei erstellen und öffnen (open).
- Daten in die neue Datei schreiben (write).
- Beide Dateien schließen (close).
- Vollständiges Beispiel in ARM64-Assembler:
.section .data
filename: .asciz "input.txt"
newfilename: .asciz "output.txt"
buffer: .space 1024
.section .text
.global _start
_start:
// input.txt öffnen
mov x0, #-100 // AT_FDCWD
ldr x1, =filename
mov x2, #0 // O_RDONLY
mov x3, #0 // mode (nicht benötigt)
mov x8, #56 // openat
svc #0
mov x19, x0 // FD in x19
// Lesen
mov x0, x19
ldr x1, =buffer
mov x2, #1024
mov x8, #63 // read
svc #0
mov x20, x0 // Gelesene Bytes
// output.txt öffnen (schreibend, erstellen, ggf. überschreiben)
mov x0, #-100
ldr x1, =newfilename
mov x2, #577 // O_WRONLY | O_CREAT | O_TRUNC (0x241)
mov x3, #0o644 // rw-r--r--
mov x8, #56 // openat
svc #0
mov x21, x0 // FD für Ausgabe
// Schreiben
mov x0, x21
ldr x1, =buffer
mov x2, x20
mov x8, #64 // write
svc #0
// Eingabedatei schließen
mov x0, x19
mov x8, #57 // close
svc #0
// Ausgabedatei schließen
mov x0, x21
mov x8, #57
svc #0
// Programm beenden
mov x0, #0
mov x8, #93 // exit
svc #0
Erklärung wichtiger Flags
Die Flags findest du unter:
fcntl.h auf GitHub
Beispiele:
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
#define O_CREAT 00000100
#define O_TRUNC 00001000
Kombination z. B.:
O_WRONLY | O_CREAT | O_TRUNC = 0x1 | 0x100 | 0x1000 = 0x1101 = 577 dezimal