diff --git a/20220425/bs-20220425.txt b/20220425/bs-20220425.txt
index ba7537fce07fd14c553030fdd850ea464a8276a0..3841aab1f5dd71438f3e18f23115c91f559ab684 100644
--- a/20220425/bs-20220425.txt
+++ b/20220425/bs-20220425.txt
@@ -62,3 +62,219 @@ Datei fileops.c, Zeile 791
 --> __libc_write()
 Datei sysdeeps/unix/sysv/linux/write.c, Zeile 26
 --> return SYSCALL_CANCEL (write, fd, buf, nbytes);
+
+Systemaufrufe, 21.05.2021, 12:08:00
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+hello-2.c
+...
+--> _IO_new_do_write (f, ...)
+--> new_do_write (f, ...)
+--> _IO_SYSWRITE (fp, ...)
+--> __libc_write()
+Datei sysdeeps/unix/sysv/linux/write.c, Zeile 26
+--> return SYSCALL_CANCEL (write, fd, buf, nbytes);
+
+b+ │0x7ffff7eb54f0 <__GI___libc_write>      lea    0xd61f9(%rip),%rax        # 0x7ffff7f8b6f0 │
+  >│0x7ffff7eb54f7 <__GI___libc_write+7>    mov    (%rax),%eax                                │
+   │0x7ffff7eb54f9 <__GI___libc_write+9>    test   %eax,%eax                                  │
+   │0x7ffff7eb54fb <__GI___libc_write+11>   jne    0x7ffff7eb5510 <__GI___libc_write+32>      │
+   │0x7ffff7eb54fd <__GI___libc_write+13>   mov    $0x1,%eax                                  │
+   │0x7ffff7eb5502 <__GI___libc_write+18>   syscall                                           │
+   │0x7ffff7eb5504 <__GI___libc_write+20>   cmp    $0xfffffffffffff000,%rax                   │
+   │0x7ffff7eb550a <__GI___libc_write+26>   ja     0x7ffff7eb5560 <__GI___libc_write+112>     │
+   │0x7ffff7eb550c <__GI___libc_write+28>   retq                                              │
+   │0x7ffff7eb550d <__GI___libc_write+29>   nopl   (%rax)                                     │
+   │0x7ffff7eb5510 <__GI___libc_write+32>   push   %r12                                       │
+   │0x7ffff7eb5512 <__GI___libc_write+34>   mov    %rdx,%r12                                  │
+   │0x7ffff7eb5515 <__GI___libc_write+37>   push   %rbp                                    
+
+lea: Lade einen Zeiger aus einer konstanten Tabelle in das rax-Register
+mov: Lade das, worauf der Zeiger zeigt, in das eax-Register
+test: Prüfe, welchen Wert eax hat
+jne: Jump if Not Equal = bedingter Sprung, falls ungleich 0 (Vermutung: Fehler)
+mov: Lade die Zahl 1 als Parameter für syscall in das eax-Register. 1 steht für "write".
+syscall: übergebe an den Kernel --> schreibt "Hello, world!\n" auf den Bildschirm
+cmp: Vergleiche den Wert von eax mit einer Zahl
+ja: Jump if Above (Prüfen auf Fehler)
+retq: Beende die Funktion
+
+syscall erwartet Parameter in den Prozessor-Registern:
+  eax: Nummer der Funktion, die aufgerufen werden soll
+  rsi: Zeiger auf die auszugebenden Daten
+       (siehe: https://0xax.gitbooks.io/linux-insides/content/SysCall/linux-syscall-1.html)
+  ebx oder edx: Länge der auszugebenden Daten
+  (ggf. weitere Register)
+
+Kernel, 25.05.2017, 17:53:23
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Fragestellung: Wir haben in der glibc beim Maschinenbefehl "syscall"
+aufgehört. Wie geht es nun im Kernel weiter? Wer nimmt den Funktions-
+aufruf entgegen?
+
+Suchbegriff: sys_call_table
+
+--> Es gibt eine Tabelle, die für jeden der durchnumerierten Systemaufrufe
+    einen Zeiger auf die aufzurufende Funktion enthält.
+
+--> Der Maschinenbefehl "syscall" ruft eine Funktion innerhalb des
+    Kernels auf. Wo steht, welche Funktion das ist?
+
+arch/x86/entry/syscall_64.c: Defnition des Arrays sys_call_table
+
+Initialisiertes Array von Zeigern auf Funktionen.
+Zunächst zeigen alle Zeiger auf die Funktion sys_ni_syscall(),
+die lediglich eine Fehlermeldung zurückgibt ("nicht implementiert").
+Danach werden auf diejenigen Funktionen, die es tatsächlich gibt
+Zeiger initialisiert. Dies erfolgt durch ein #include der Datei
+/usr/src/linux-headers-4.19.0-8-amd64/arch/x86/include/generated/asm/syscalls_64.h
+mit vorheriger Definition eines Präprozessor-Macros __SYSCALL().
+
+  asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
+          /*
+           * Smells like a compiler bug -- it doesn't work
+           * when the & below is removed.
+           */
+          [0 ... __NR_syscall_max] = &sys_ni_syscall,
+  #include <asm/syscalls_64.h>
+  -->     [0] = __x64_sys_read,
+          [1] = __x64_sys_write,
+          [2] = __x64_sys_open,
+          ...
+  };
+
+Vorher wurden mit demselben Trick alle Funktionen vorwärts-deklariert:
+
+  #define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);
+  #include <asm/syscalls_64.h>
+  -->  extern asmlinkage long __x64_sys_read(const struct pt_regs *);
+       extern asmlinkage long __x64_sys_write(const struct pt_regs *);
+       extern asmlinkage long __x64_sys_open(const struct pt_regs *);
+       ...
+  #undef __SYSCALL_64
+
+Wie können wur nun dafür sorgen, daß der Befehl syscall dieses Array
+auch benutzt?
+
+arch/x86/entry/entry_64.S enthält eine Funktion entry_SYSCALL_64,
+die eine Funktion "do_syscall_64()" aufruft
+und sich über "USERGS_SYSRET64" beendet.
+Dies ist ein Präprozessor-Makro, der zu "swapgs; sysretq;" expandiert
+(Definition in include/asm/irqflags.h).
+
+Woher kommt die Include-Datei in "generated"?
+arch/x86/entry/syscalls/Makefile ruft das Shell-Skript syscalltbl.sh auf.
+Dieses liest syscall_64.tbl und erzeugt daraus syscall_64.h.
+
+Damit haben wir die Funktion gefunden, die den Syscall entgegennehmen soll.
+Daraus ergeben sich neue Fragen:
+1. Wie sorge ich dafür, daß sie tatsächlich bei "syscall" aufgerufen wird?
+2. Wie geht es von dort aus weiter bis zu der Tabelle "sys_call_table"?
+
+zu 1.:
+Suche nach "entry_SYSCALL_64" liefert:
+arch/x86/kernel/cpu/common.c: wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
+
+Was bedeutet "wrmsrl"?
+https://sites.google.com/site/masumzh/articles/hypervisor-based-virtualization/compute-virtualization
+"As shown below, the content of the IA32_LSTAR MSR (Model Specific Register)
+is copied to the instruction pointer register (RIP) [...]"
+
+Demnach ist das Prozessorregister "IA32_LSTAR MSR" die Einsprungadresse,
+zu der der "syscall"-Aufruf springen soll.
+Um das Register "IA32_LSTAR MSR" zu setzen, gibt es den Befehl "wrmsrl"
+(Write MSR (long = 32 Bit)).
+Offensichtlich ist das MSR ein Array, und "IA32_LSTAR" ist der Index.
+
+zu 2.:
+arch/x86/entry/common.c enthält die C-Funcktion do_syscall_64().
+Diese enthält die Zeile "regs->ax = sys_call_table[nr](regs);", also den
+Aufruf der Funktion, auf die ein Zeiger in der tabelle "sys_call_table"
+an der Stelle "nr" gespeichert ist.
+
+--> Weiter geht's mit dem Eintrag in sys_call_table,
+    also mit der Implementation von sys_write().
+
+Die Tabelle wird benutzt in der Funktion do_syscall_64():
+
+  regs->ax = sys_call_table[nr](regs);
+
+Wo wird die Funktion __x64_sys_write definiert?
+Wenn man lange genug sucht, findet man: arch/x86/include/asm/syscall_wrapper.h
+Dort steht eine kleine Funktion __x64_sys_write(), die ihrerseits eine Funktion
+__se_sys_write() aufruft.
+
+Der Präprozessor-Makro __SYSCALL_DEFINEx() wird über einen anderen Präprozessor-Makro
+SYSCALL_DEFINEx() aufgerufen, der in include/linux/syscalls.h definiert wird
+und seinerseits über SYSCALL_DEFINE3() aufgerufen wird, der ebenfalls in
+include/linux/syscalls.h definiert wird.
+
+Wenn man lange genug sucht, findet man: fs/read_write.c
+SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count)
+
+Dort wird eine Funktion definiert, die ihrerseits eine Funktion ksys_write() aufruft.
+Diese enthält:
+
+  ret = vfs_write(f.file, buf, count, &pos);
+
+Hier passiert das eigentliche Schreiben.
+
+Ebenfalls in fs/read_write.c:
+
+  ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
+
+Diese ruft auf:
+
+  ret = __vfs_write(file, buf, count, pos);
+
+Ebenfalls in fs/read_write.c:
+
+  ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, loff_t *pos)
+
+Diese ruft auf:
+
+  if (file->f_op->write) return file->f_op->write(file, p, count, pos);
+
+:-)
+
+Jetzt fehlt nur noch: Wie bekommt f_op->write den Wert, den das Modul hinterlegt hat?
+
+Der Funktion wird ein Parameter "file" übergeben.
+Dies ist eine "struct file", die u.a. ein Feld "f_op" enthält,
+das auf ein weiteres struct mit Callback-Funktionen zeigt.
+
+Wie kommen die "richtigen" Callback-Funktionen dort hinein?
+Eigentlich müßte dies bei register_chrdev() passieren.
+Schauen wir uns daher an, was register_chrdev() eigentlich macht.
+
+Dies ist definiert in include/linux/fs.h und ruft die Funktion
+__register_chrdev() auf.
+
+Diese wiederum ist definiert in fs/char_dev.c.
+Sie alloziert eine cdev-Struktur und speichert darin die fops.
+Die cdev-Struktur wird dann mittels cdev_add() in eine Liste eingefügt.
+Hierfür ruft cdev_add() die Funktion kobj_map() auf.
+Diese Funktion wird in drivers/base/map.c definiert
+und legt die cdev-Struktur in einer Hash-Tabelle ab.
+Der letzte Parameter "data" ist dabei der Zeiger auf die cdev-Strutur,
+die auch die fops enthält.
+
+Wie kommen nun die fops von der Hash-Tabelle aus in die file-Struktur?
+
+Vermutung: Dies geschieht beim Öffnen der Datei.
+
+In fs/open.c gibt es eine Funktion vfs_open().
+Diese sucht den zur Datei gehörenden inode heraus und ruft damit do_dentry_open() auf.
+
+Theorie der Dateisysteme: Dateien werden durch "inodes" repräsentiert.
+
+include/linux/fs.h: Definition "inode"
+
+  Die "struct inode" enthält ein Datenfeld "i_rdev", in dem die Major- und Minor-Nr.
+  der Gerätedatei gemeinsam gespeichert sind.
+
+  --> Sobald wir einen inode kennen, kennen wir Major- und Minor-Nr. der Gerätedatei.
+
+do_dentry_open() schreibt den inode in die file-Struktur,
+holt die fops aus dem inode und legt sie direkt in der file-Struktur ab.
+
+# Das einzige, was jetzt noch fehlt: Wie kommen die fops in den inode?