Cursul 2
2
Apeluri de sistem
Apeluri de sistem
Apeluri de sistem
Read (fd, buffer, nbytes)
Apelurile de sistem sunt modalitatea prin care aplicaţiile accesează serviciile puse la dispoziţie de kernel.
Exemple
Ce este un apel de sistem?
Ce se întâmplă la un apel de sistem?
Un apel de sistem determină comutarea contextului de la aplicaţie la kernel. Procesul curent îşi continuă execuţia în kernel mode şi rulează rutina asociată apelului de sistem.
Ce se întâmplă la un apel de sistem? (2)
Parametrii apelurilor de sistem
Pointeri către kernel-space
Pointeri către zone invalide
Soluţia eficientă
Page fault-uri
Exemplu ambiguitate
int *i=NULL;
if (*i != 0)...
/* ubuf vine din user-space si e NULL */
memcpy(kbuf, ubuf, len);
Rezolvarea ambiguităţii
Analiză
Accesul la spaţiul utilizator
64/32bit user/kernel
OS | char | short | int | long | void* |
Unix 32bit | 1 byte | 2 bytes | 4 bytes | 4 bytes | 4 bytes |
Windows 32bits | 1 byte | 2 bytes | 4 bytes | 4 bytes | 4 bytes |
Unix 64bits | 1 byte | 2 bytes | 4 bytes | 8 bytes | 8 bytes |
Windows 64bits | 1 byte | 2 bytes | 4 bytes | 4 bytes | 8 bytes |
Exemplu
User-space 32 biţi:
{ .a = 1, .b = 2 };
Kernel space 64 biţi:
{ .a = 8589934593,
.b = -1075374248 };
struct my_struct {
long a;
int b;
}
1
1
2
?
?
?
?
Little endian
2
Apeluri de sistem în Linux
Exemplu de invocare
NAME� dup, dup2, dup3 - duplicate a file descriptor��SYNOPSIS� #include <unistd.h>�� int dup(int oldfd);� int dup2(int oldfd, int newfd);�� #define _GNU_SOURCE� #include <unistd.h>�� int dup3(int oldfd, int newfd, int flags);�
Exemplu de invocare (2)
000c7590 <__dup2>:
...
c7592: movl 0x8(%esp),%ecx
c7596: movl 0x4(%esp),%ebx
c759a: movl $0x3f,%eax
c759f: int $0x80
...
Rutina de dispatch
ENTRY(system_call)
...
SAVE_ALL
...
cmpl $(nr_syscalls),%eax
jae badsys
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp)
...
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
Rutina de dispatch (2)
#define SAVE_ALL \
...
push %eax \
push %ebp \
push %edi \
push %esi \
push %edx \
push %ecx \
push %ebx
Implementare apel de sistem
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
asmlinkage
long sys_open(const char * filename, int flags, int mode)
Exerciţiu
Scrieţi secvenţa de cod ce interceptează apelul de sistem open şi face astfel încât la fiecare invocare să se afişeze numărul şi rezultatul apelului de sistem.
Rezolvare
struct syscall_params {
long ebx, ecx, edx, esi, edi, ebp, eax;
};
asmlinkage long interceptor(struct syscall_params sp)
{
int syscall=sp.eax, r=f(sp);
printk(„%d: %d\n”, syscall, r);
return r;
}
f=sys_call_table[__NR_open];
sys_call_table[__NR_open]=interceptor;
Rezolvare (2)
VDSO
VDSO (2)
$ cat /proc/$$/maps
...
bfac5000-bfada000 rw-p bffeb000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
$ dd if=/proc/self/mem of=linux-gate.so bs=4096 \
skip=$[0xffffe] count=1
VDSO (3)
$ objdump -d linux-gate.so –no-show-raw-insn
ffffe400 <__kernel_vsyscall>:
ffffe400: push %ecx
ffffe401: push %edx
ffffe402: push %ebp
ffffe403: mov %esp,%ebp
ffffe405: sysenter
ffffe407: nop
Vsyscalls (virtual system calls)
Accesarea spaţiului utilizator
/* Dacă s-a generat un page fault datorită unui access
* invalid primitivele întorc o valoare diferită de zero */
if (copy_from_user(kernel_buffer, user_buffer, size))
return -EFAULT;
get_user
#define get_user(x,ptr) \
({ \
int __ret_gu; \
unsigned long __val_gu; \
switch(sizeof (*(ptr))) { \
case 1:
__get_user_x(1,__ret_gu,__val_gu,ptr); \
break; \
case 2: \
__get_user_x(2,__ret_gu,__val_gu,ptr); \
break; \
...
__ret_gu; \
})
__get_user_x
#define __get_user_x(size,ret,x,ptr) \
__asm__ __volatile__( \
"call __get_user_" #size \
:"=a" (ret),"=d" (x) :"0" (ptr) \
)
ENTRY(__get_user_1)
GET_THREAD_INFO(%edx)
cmpl TI_addr_limit(%edx),%eax
jae bad_get_user
1: movzbl (%eax),%edx
xorl %eax,%eax
ret
ENDPROC(__get_user_1)
__get_user_x
#define __get_user_x(size,ret,x,ptr) \
__asm__ __volatile__( \
"call __get_user_" #size \
:"=a" (ret),"=d" (x) :"0" (ptr) \
)
ENTRY(__get_user_1)
GET_THREAD_INFO(%edx)
cmpl TI_addr_limit(%edx),%eax
jae bad_get_user
1: movzbl (%eax),%edx
xorl %eax,%eax
ret
ENDPROC(__get_user_1)
Este un pointer către userspace?
Instrucţiunea de copiere şi adresa ei
__get_user_x (2)
bad_get_user:
xorl %edx,%edx
movl $-14,%eax
ret
END(bad_get_user)
.section __ex_table,"a"
.long 1b,bad_get_user
.long 2b,bad_get_user
.long 3b,bad_get_user
.previous
Adresa instrucţiunii ce face accesul în userspace din cadrul __get_user_1
Tabela de excepţii
Accesul la datele din secţiunile speciale
. = ALIGN(16); /* Exception table */
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
}
fixup_exception
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
fixup = search_exception_tables(regs->eip);
if (fixup) {
regs->eip = fixup->fixup;
return 1;
}
return 0;
}
Setăm instrucţiunea de la care
se continuă execuţia după ce ieşim
din handler-ul de tratare al
page-fault-ului
Întrebări
?