Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
B
bs
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Peter Gerwinski
bs
Commits
af417384
Commit
af417384
authored
May 18, 2020
by
Peter Gerwinski
Browse files
Options
Downloads
Patches
Plain Diff
Notizen 18.5.2020: vom Systemaufruf bis zum Treiber-Modul
parent
f0a7c6f9
No related branches found
No related tags found
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
20200511/hello.log
+0
-40
0 additions, 40 deletions
20200511/hello.log
20200518/bs-20200518.txt
+5
-0
5 additions, 0 deletions
20200518/bs-20200518.txt
20200518/hello.log
+185
-0
185 additions, 0 deletions
20200518/hello.log
with
190 additions
and
40 deletions
20200511/hello.log
+
0
−
40
View file @
af417384
...
...
@@ -51,43 +51,3 @@ Diese Funktionen führen den Code aus sysdeps/unix/sysv/linux/x86_64/syscall.S a
Der Assembler-Befehl syscall bildet die Schnittstelle zwischen dem Programm im User-Space
und dem Kernel. An dieser Stelle gewinnt das Programm die zusätzlichen Rechte, die nötig
sind, um z.B. direkt in den Bildschrimspeicher schreiben zu können.
Kernel, 25.05.2017, 17:53:23
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suchbegriff: sys_call_table
/usr/src/linux-headers-4.9.0-2-amd64/arch/x86/include/generated/asm/syscalls_64.h
arch/x86/entry/syscall_64.c: Defnition sys_call_table
arch/x86/entry/entry_64.S: Verwendung sys_call_table
arch/x86/kernel/cpu/common.c: wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
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) [...]"
--> 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->di, regs->si, regs->dx,
regs->r10, regs->r8, regs->r9);
Wenn man lange genug sucht, findet man: fs/read_write.c
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count)
ret = vfs_write(f.file, buf, count, &pos);
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
ret = __vfs_write(file, buf, count, pos);
ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, loff_t *pos)
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?
include/linux/fs.h: Definition "inode"
include/linux/cdev.h: Char-Device-spezifische Felder des inode
Major und Minor sind gemeinsam in einer 16-Bit-Integer hinterlegt.
This diff is collapsed.
Click to expand it.
20200518/bs-20200518.txt
0 → 100644
+
5
−
0
View file @
af417384
18.05.2020, 13:33:02
~~~~~~~~~~~~~~~~~~~~
:) letzte Woche: von der Anwendung durch die Systembibliothek bis zum Kernel
:) im Kernel: vom Systemaufruf bis zum Callback des Treiber-Moduls
This diff is collapsed.
Click to expand it.
20200518/hello.log
0 → 100644
+
185
−
0
View file @
af417384
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->di, regs->si, regs->dx,
regs->r10, regs->r8, regs->r9);
Wo wird die Funktion __x64_sys_write definiert?
Wenn man lange genug sucht, findet man: arch/x86/include/asm/syscall_wrapper.h
Dort steht:
#define __SYSCALL_DEFINEx(x, name, ...) \
... \
asmlinkage long __x64_sys##name(const struct pt_regs *regs) \
{ \
return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\
} \
...
Dies ist 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?
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment