Skip to content
Snippets Groups Projects
Commit a480e790 authored by Peter Gerwinski's avatar Peter Gerwinski
Browse files

Notizen und Beispiele 22.5.2023

parent 14f4f6e5
No related branches found
No related tags found
No related merge requests found
Von "Hello, world!n" bis zum Kernel, 15.05.2023, 16:20:31 Von "Hello, world!\n" bis zum Kernel, 15.05.2023, 16:20:31
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
printf ("Hello, world!\n"); wird zu puts ("Hello, world!"); optimiert. printf ("Hello, world!\n"); wird zu puts ("Hello, world!"); optimiert.
Quelltext von puts(): apt-get source glibc Quelltext von puts(): apt-get source glibc
......
Vom syscall-Befehl bis zum Treiber-Modul, 22.05.2023, 16:11:55
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/x86/entry/entry_64.S:
Assembler-Code für den Einsprung, nachdem das Benutzerprogramm
den syscall-Befehl aufgerufen hat
(einschließlich "trampoline": Code, der ermöglicht, ein Callback
auch mit einer anderen als der vorgesehenen Anzahl von Parametern
aufzurufen)
--> Funktionsaufruf do_syscall_64 (unsigned long a, struct pt_regs *b)
a = Nummer des Syscall-Aufrufs
b = Zeiger auf eine Datenstruktur auf dem CPU-Stack,
die die Registerinhalte des Benutzerprogramms enthält:
Stack-Segment, Stack-Pointer, CPU-Flags, Code-Segment, Instruction Pointer
(wichtig für den Zugriff auf Variable des aufrufenden Programms
sowie für den Rücksprung)
arch/x86/entry/common.c:
if (unlikely(nr >= NR_syscalls))
goto bad;
--> Hinweis an den Compiler, welcher Zweig einer if-Verzweigung
wahrscheinlicher ist, mit dem Ziel, Pipelining möglichst effizient
zu unterstützen
nr = array_index_nospec(nr, NR_syscalls)
--> Mache aus dem Übergebenen Parameter (Nr. des Syscalls)
einen Index für ein Array.
Wenn alles funktioniert hat:
regs->ax = sys_call_table[nr](regs);
--> Aufruf der Funktion, die den Syscall durchführt.
Das Ergebnis des Funktionsaufrufs speichern wir in der Struktur, die
die Registerinhalte des Aufrufers enthält. Der Aufrufer bekommt somit
den Funktionsrückgabewert in seinem ax-Register (genauer: %rax).
sys_call_table ist eine Tabelle mit Zeigern auf Funktionen.
Worauf zeigen diese?
arch/x86/entry/syscalls/syscall_64.tbl:
0 common read __x64_sys_read
1 common write __x64_sys_write
2 common open __x64_sys_open
...
arch/x86/entry/syscalls/syscalltbl.sh:
Dieses Shell-Skript macht aus der o.a. Tabelle eine Datei "syscalls_64.h"
mit Aufrufen eines Präprozessor-Macros:
__SYSCALL_64(0, __x64_sys_read, )
__SYSCALL_64(0, sys_read, )
__SYSCALL_64(0, __x64_sys_read, )
__SYSCALL_64(0, sys_read, )
__SYSCALL_64(1, __x64_sys_write, )
__SYSCALL_64(1, sys_write, )
__SYSCALL_64(2, __x64_sys_open, )
__SYSCALL_64(2, sys_open, )
...
arch/x86/entry/syscall_64.c:
#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);
#include <asm/syscalls_64.h>
#undef __SYSCALL_64
--> Extern-Deklarationen aller SysCall-Funktionen (Prototypen)
Dadurch möglich: Zeiger auf diese Funktionen zeigen lassen
#define __SYSCALL_64(nr, sym, qual) [nr] = sym,
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>
};
--> Dies ist die Tabelle, ein initialisiertes Array.
Bei jedem Index "nr" steht ein Zeiger auf die Funktion "sym".
Vorher initialisieren wir das ganze Array auf "&sys_ni_syscall"
("not implemented syscall").
Wie sorgen wir nun dafür, daß unser Einsprungpunkt "entry_SYSCALL_64_trampoline"
tatsächlich aufgerufen wird, wenn ein Benutzerprogramm den syscall-Befehl aufruft?
arch/x86/kernel/cpu/common.c:
void syscall_init(void)
{
...
wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
if (static_cpu_has(X86_FEATURE_PTI))
wrmsrl(MSR_LSTAR, SYSCALL64_entry_trampoline);
else
wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
...
}
--> Hier hinterlegen wir die Adresse von SYSCALL64_entry_trampoline
in einem internen Prozessorregister, das genau für diesen Aufruf
zuständig ist.
wrmsrl: In ein solches Register schreiben
MSR_STAR: Nummer des Registers, das dafür zuständig ist, sich die Segmente zu merken
(__KERNEL_CS = Code-Segment des Kernels)
MSR_LSTAR: Nummer des Registers, das dafür zuständig ist, sich das Offset zu merken
Wo ist die Funktion sys_write() denn nun definiert?
include/linux/syscalls.h:
/* fs/read_write.c */
...
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
size_t count);
fs/read_write.c:
ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_write(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
fdput_pos(f);
}
return ret;
}
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
return ksys_write(fd, buf, count);
}
ebenfalls in fs/read_write.c:
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);
else if (file->f_op->write_iter)
return new_sync_write(file, p, count, pos);
else
return -EINVAL;
}
Damit sind wir eigentlich am Ziel.
Noch offen: Wie gelangen die "fops"-Callbacks aus dem Kernel-Modul
in die "struct file"-Datenstruktur?
Dies führt uns zu dem Konzept der inodes (index nodes).
include/linux/fs.h:
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
enum rw_hint f_write_hint;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
errseq_t f_wb_err;
} __randomize_layout
__attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
...
struct file_operations {
...
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
...
} __randomize_layout;
Dieselbe "struct file_operations" übergeben wir in Kernel-Modulen beim Aufruf
von register_chrdev().
ebenfalls in include/linux/fs.h:
struct inode {
...
dev_t i_rdev;
...
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
...
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
char *i_link;
unsigned i_dir_seq;
};
...
} __randomize_layout;
--> Ein inode repräsentiert entweder eine Pipe oder ein Block Device oder ein Char Device
oder einen Symlink oder ein Verzeichnis oder eine normale Datei.
include/linux/cdev.h:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
} __randomize_layout;
include/linux/types.h:
typedef u32 __kernel_dev_t;
...
typedef __kernel_dev_t dev_t;
include/linux/kdev_t.h:
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
Damit haben wir auch gefunden, wie die Datei ihre file_operations
aus dem Kernel-Modul bekommt (nämlich über den inode).
program Trampoline;
procedure MacheWas (procedure Callback (a: Integer));
begin
Callback (42)
end;
procedure Answer (a: Integer);
begin
WriteLn ('The answer is: ', a)
end;
begin
MacheWas (Answer)
end.
program Trampoline;
procedure MacheWas (procedure Callback (a: Integer));
begin
Callback (42)
end;
procedure Philosophy;
var
Msg: String[42];
procedure Answer (a: Integer);
begin
WriteLn (Msg, a)
end;
begin
Msg := 'The answer is: ';
MacheWas (Answer)
end;
begin
Philosophy
end.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment