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

Lehrmaterialien und Beispiele 11.5.2020

parent 47138b45
Branches
Tags
No related merge requests found
03.05.2020, 33:58:40
~~~~~~~~~~~~~~~~~~~~
:) letzte Woche: Bootvorgang
:) Ergänzung: VNC-Bot
:) letzte Woche: Hardware ansprechen: Kernel-Module
- Gerätedateien: Verzeichnis /dev
- /dev/null = "virtueller Mülleimer"
(Immer leer; was man hineinschreibt, ist weg.)
- /dev/pts/24 = Ausgabedatei für ein Bildschirmfenster
- /dev/pts/35 = Ausgabedatei für ein anderes Bildschirmfenster
- Das "c" in "ls -l" steht für "Character Device"
(zeichenorientierte Gerätedatei).
- selbst erzeugen: Kernel-Modul schreiben
- Struktur eines Kernel-Moduls
- "zwei Hauptprogramme": init_module(), cleanup_module()
- printk() (in den Kernel-Speicher schreiben)
statt printf() (zur Standardausgabe schreiben)
- Kernel-Modul compilieren
- make: siehe:
https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191212/hp-20191212.pdf
- standardisiertes Makefile: Makefile-2
- benötigte Werkzeuge: siehe Skript, Seite 10
- Modul laden: sudo insmod ./hellomod-1.ko
- Modul entladen: sudo rmmod ./hellomod-1.ko
- Output des Moduls: sudo tail /var/log/kern.log
- Lizenz des Moduls angeben. Wenn nicht GPL-kompatibel: Fehlermeldungen.
:) Ergänzung: Das Kernel-Modul legt die Gerätedatei selbst an.
* Die Software-Schichten dazwischen: Kernel, System-Bibliothek
von der Shell bis zur Hardware
#include <stdio.h>
int main (void)
{
char msg[10];
static int counter = 0;
sprintf (msg, "I already told you %d times Hello world!\n", counter++);
printf ("%s", msg);
return 0;
}
#include <stdio.h>
int main (void)
{
printf ("Hello, world!\n");
return 0;
}
.file "hello-0.c"
.text
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello, world!"
.text
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc # #include <stdio.h>
subq $8, %rsp #
.cfi_def_cfa_offset 16 # int main (void)
leaq .LC0(%rip), %rdi # {
call puts@PLT # printf ("Hello, world!\n");
movl $0, %eax # return 0;
addq $8, %rsp # }
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
.section .note.GNU-stack,"",@progbits
#include <stdio.h>
int main (void)
{
puts ("Hello, world!");
return 0;
}
.file "hello-1.c"
.text
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello, world!"
.text
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc # #include <stdio.h>
subq $8, %rsp #
.cfi_def_cfa_offset 16 # int main (void)
leaq .LC0(%rip), %rdi # {
call puts@PLT # printf ("Hello, world!\n");
movl $0, %eax # return 0;
addq $8, %rsp # }
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
.section .note.GNU-stack,"",@progbits
#include <unistd.h>
int main (void)
{
write (1, "Hello, world!\n", 14);
return 0;
}
printf ("Hello, world!\n") [hello-1.c]
Compiler --> puts ("Hello, world!") [libio/ioputs.c]
weak alias --> _IO_puts ("Hello, world!", 13) [libio/ioputs.c]
--> _IO_sputn ("Hello, world!", 13) [libio/libioP.h]
#define --> _IO_XSPUTN ("Hello, world!", 13) [libio/libioP.h]
#define --> JUMP2 (__xsputn, _IO_stdout, "Hello, world!", 13) [libio/libioP.h]
__xsputn (_IO_stdout, "Hello, world!", 13) [libio/libioP.h]
nur Deklaration: JUMP_FIELD(_IO_xsputn_t, __xsputn) [libio/libioP.h]
Zeigerfeld auf virtuelle Methode: _IO_default_xsputn (_IO_stdout, "Hello, world!", 13) [sysdeps/unix/sysv/linux/i386/libc.abilist]
_IO_default_xsputn (_IO_stdout, "Hello, world!", 13) [libio/genops.c]
Die Datei (f = _IO_stdout) enthält einen Pufferbereich zum Schreiben (f->_IO_write_ptr).
Dorthin wird der String kopiert und der Zeiger um die Länge versetzt.
Danach "weiß" f, was geschrieben werden soll.
Mit _IO_OVERFLOW (f, ...) veranlassen wir, daß tatsächlich geschrieben wird.
_IO_OVERFLOW --> JUMP1 (__overflow, FP, CH) [libioP.h]
Zeigerfeld auf virtuelle Methode: _IO_file_overflow [sysdeps/unix/sysv/linux/i386/libc.abilist]
libc_hidden_ver --> _IO_new_file_overflow [fileops.c]
--> _IO_do_write [fileops.c]
libc_hidden_ver --> _IO_new_do_write [fileops.c]
--> new_do_write (_IO_stdout, "Hello, world!", 13) [fileops.c]
--> _IO_SYSWRITE (_IO_stdout, "Hello, world!", 13) [fileops.c]
#define _IO_SYSWRITE(FP, DATA, LEN) JUMP2 (__write, FP, DATA, LEN) [libio/libioP.h]
Zeigerfeld auf virtuelle Methode: __write [io/write.c]
--> abstrakte Methode, liefert immer Fehlermeldung zurück
Abstrakte Methode wird durch systemabhängige Methode überschrieben:
Im Verzeichnis sysdeps/unix befindet sich ein Shell-Skript make-syscalls.sh.
Diese liest eine Datei syscalls.list
und erzeugt daraus automatisch Funktionen, die die System-Calls implementieren.
Diese Funktionen führen den Code aus sysdeps/unix/sysv/linux/x86_64/syscall.S aus:
.text
ENTRY (syscall)
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
cmpq $-4095, %rax /* Check %rax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
ret /* Return to caller. */
PSEUDO_END (syscall)
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.
../common/os-layers-1.jpg
\ No newline at end of file
../common/os-layers-2.jpg
\ No newline at end of file
../common/os-layers-3.jpg
\ No newline at end of file
../common/os-layers-4.jpg
\ No newline at end of file
../common/os-layers-5.jpg
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment