system call의 동작 내부구조에 대해서 알아보자..
일단 기본적인 시스템 콜 동작은 다음과 같다.
kernel mode에서 처리하는 과정..?
일단 system call table이 존재. (/arch/x86/um/sys_call_table_64.c)
asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h>
};
사실 배열 중간에 #include
가 들어간다는 것이 잘 이해가 되지 않기는 하다.
어쨌든 각 entry들은 sys_ni_syscall로 초기화가 되는데, 그러면 sys_ni_syscall은 무엇인가?
asmlinkage long sys_ni_syscall(void)
{
return -ENOSYS;
}
그냥 error상태를 반환하는데, syscall이 존재하지 않는다는 뜻임.
기본적으로 모든 entry를 error함수로 채워놓고, 그 이후에 채운다.
배열을 어떤 식으로 채울까?
#include <asm/syscalls_64.h>
로 채우게 된다. __SYSCALL_COMMON(0, sys_read, sys_read)
__SYSCALL_COMMON(1, sys_write, sys_write)
__SYSCALL_COMMON(2, sys_open, sys_open)
__SYSCALL_COMMON(3, sys_close, sys_close)
__SYSCALL_COMMON(5, sys_newfstat, sys_newfstat)
...
...
...
/arch/x86/um/sys_call_table_64.c에 아래의 내용이 define되어 있다. #define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
#define __SYSCALL_64(nr, sym, compat) [nr] = sym,
그러므로, 결론적으로 asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
[0 ... __NR_syscall_max] = &sys_ni_syscall,
[0] = sys_read,
[1] = sys_write,
[2] = sys_open,
...
...
...
};
이런 꼴이 되어, 각 배열의 인덱스마다 알맞은 syscall함수가 배정되게 되고, 나머지 entry들은 예상대로 error를 리턴하는 함수가 채워짐을 알 수 있다.시스템 콜 호출하는 함수
__visible noinstr void do_syscall_64(struct pt_regs *regs, int nr)
{
add_random_kstack_offset(); //커널 스택 ASLR
nr = syscall_enter_from_user_mode(regs, nr); //user mode -> kernel mode
instrumentation_begin();
// 64비트 시스템 콜 실행 -> 실패 시 32비트 시스템 콜 실행.
if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
/* Invalid system call, but still a system call. */
regs->ax = __x64_sys_ni_syscall(regs);
}
instrumentation_end();
syscall_exit_to_user_mode(regs); //kernel mode -> user mode
}
64비트 시스템 콜 호출하는 함수
static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
{
/*
* Convert negative numbers to very high and thus out of range
* numbers for comparisons.
*/
unsigned int unr = nr;
if (likely(unr < NR_syscalls)) {
unr = array_index_nospec(unr, NR_syscalls);
regs->ax = sys_call_table[unr](regs);
return true;
}
return false;
}
32비트 시스템 콜 호출하는 함수
static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
{
/*
* Adjust the starting offset of the table, and convert numbers
* < __X32_SYSCALL_BIT to very high and thus out of range
* numbers for comparisons.
*/
unsigned int xnr = nr - __X32_SYSCALL_BIT;
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
xnr = array_index_nospec(xnr, X32_NR_syscalls);
regs->ax = x32_sys_call_table[xnr](regs);
return true;
}
return false;
}
출처