久々にやってみようと思って、2.6.33でシステムコールの追加をしてみました。
やってみたら新しい発見があったり面白いですね。
copy_buffer.cというファイルは新規に作ったのと、それようにMakefileを弄ってます。
他のファイルはシステムコールを追加するために弄っているので、もし新規にシステムコールを追加するならここは変更する必要があります。
ファイルを新規に作らずに、既存のファイル内に関数を追加するなら、Makefileを弄る必要はありません。
テスト環境は仮想環境(kvm)でホスト・ゲストともにアーキテクチャはx86_64です。
システムコールの追加方法は、lwnの記事で最近のカーネルに対してシステムコールの追加に関する記事があったので、
それを参考に弄るファイルとかを調べました。
多分、最近のカーネルと昔のカーネルでは弄るファイルが多少変わってます。
最近はinclude/asm/unistd.hはなくてinclude/asm-generic/unistd.hだったり変化があります。
カーネルのdiffstatはこんな感じです。
masami@lisa:~$ diffstat copy.diff arch/x86/ia32/ia32entry.S | 1 + arch/x86/include/asm/unistd_32.h | 3 ++- arch/x86/include/asm/unistd_64.h | 3 +++ arch/x86/kernel/syscall_table_32.S | 2 ++ include/asm-generic/unistd.h | 5 ++++- include/linux/syscalls.h | 3 +++ kernel/Makefile | 3 ++- kernel/copy_buffer.c | 26 ++++++++++++++++++++++++++ 8 files changed, 43 insertions(+), 3 deletions(-)
実際のdiffはこちらです。
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 53147ad..d6f8e65 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -842,4 +842,5 @@ ia32_sys_call_table: .quad compat_sys_rt_tgsigqueueinfo /* 335 */ .quad sys_perf_event_open .quad compat_sys_recvmmsg + .quad sys_copy_buffer ia32_syscall_end: diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h index 3baf379..2fc10f0 100644 --- a/arch/x86/include/asm/unistd_32.h +++ b/arch/x86/include/asm/unistd_32.h @@ -343,10 +343,11 @@ #define __NR_rt_tgsigqueueinfo 335 #define __NR_perf_event_open 336 #define __NR_recvmmsg 337 +#define __NR_copy_buffer 338 #ifdef __KERNEL__ -#define NR_syscalls 338 +#define NR_syscalls 339 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h index 4843f7b..447c3dd 100644 --- a/arch/x86/include/asm/unistd_64.h +++ b/arch/x86/include/asm/unistd_64.h @@ -664,6 +664,9 @@ __SYSCALL(__NR_perf_event_open, sys_perf_event_open) #define __NR_recvmmsg 299 __SYSCALL(__NR_recvmmsg, sys_recvmmsg) +#define __NR_copy_buffer 300 +__SYSCALL(__NR_copy_buffer, sys_copy_buffer) + #ifndef __NO_STUBS #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 15228b5..bbe07f9 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -337,3 +337,5 @@ ENTRY(sys_call_table) .long sys_rt_tgsigqueueinfo /* 335 */ .long sys_perf_event_open .long sys_recvmmsg + .long sys_copy_buffer + \ No newline at end of file diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index 6a0b30f..da9efe6 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h @@ -627,8 +627,11 @@ __SYSCALL(__NR_accept4, sys_accept4) #define __NR_recvmmsg 243 __SYSCALL(__NR_recvmmsg, sys_recvmmsg) +#define __NR_copy_buffer 244 +__SYSCALL(__NR_copy_buffer, sys_copy_buffer) + #undef __NR_syscalls -#define __NR_syscalls 244 +#define __NR_syscalls 245 /* * All syscalls below here should go away really, diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 207466a..3455589 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -836,4 +836,7 @@ asmlinkage long sys_perf_event_open( asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff); + +asmlinkage long sys_copy_buffer(char *to, char *from, unsigned long len); + #endif diff --git a/kernel/Makefile b/kernel/Makefile index 864ff75..b69d76e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -10,7 +10,8 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \ - async.o + async.o copy_buffer.o + obj-y += groups.o ifdef CONFIG_FUNCTION_TRACER diff --git a/kernel/copy_buffer.c b/kernel/copy_buffer.c new file mode 100644 index 0000000..01586e4 --- /dev/null +++ b/kernel/copy_buffer.c @@ -0,0 +1,26 @@ +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/unistd.h> +#include <linux/syscalls.h> +#include <asm/uaccess.h> + +SYSCALL_DEFINE3(copy_buffer, char *, to, char *, from, unsigned long, len) +{ + char buf[256] = { 0 }; + + printk(KERN_INFO "This is %s()\n", __FUNCTION__); + + if (!from) + return -EINVAL; + + if (len > 256) + return -EINVAL; + + if (copy_from_user(buf, from, len)) + return -EFAULT; + + if (copy_to_user(to, buf, len)) + return -EFAULT; + + return len; +}
copy_buffer.c以外のファイルに付いては、システムコール番号の追加とか、システムコールの総数に1足してあげる程度です。
今回始めて知ったのは、下の関数の定義方法でした。
SYSCALL_DEFINE3(copy_buffer, char *, to, char *, from, unsigned long, len)
SYSCALL_DEFINEはマクロで、定義はinclude/linux/syscalls.hにあります。
こいつはSYSCALL_DEFINE0からSYSCALL_DEFINE6まであって、数字は何個引数を取るかを意味します。
今回作ったcopy_buffer()は3つの引数を取るので、SYSCALL_DEFINE3を使ってます。
書式は、以下の用になってて、関数名の後に、型と引数の名前が続きます。
SYSCALL_DEFINE3(関数名, 型, 引数名, 型, 引数名, 型, 引数名)
システムコールを実装している関数だからと言って、これを必ず使ってるわけでも無いみたいです。sys_fork()とか・・
テスト用のプログラムはこれです。
masami@lisa:~$ cat copy_buffer_test.c #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include <string.h> #include <stdio.h> #include <errno.h> #define __NR_copy_buffer 300 static inline long execute_syscall(char *to, char *from, unsigned long len) { return syscall(__NR_copy_buffer, to, from, len); } static void test_003(void) { long ret = 0; char from[256] = { 0 }; strcpy(from, "Hello, World"); ret = execute_syscall(NULL, from, sizeof(from)); printf("%s\n", strerror(errno)); } static void test_002(void) { long ret = 0; char to[256] = { 0 }; ret = execute_syscall(to, NULL, sizeof(to)); printf("%s\n", strerror(errno)); } static void test_001(void) { long ret = 0; char to[256] = { 0 }; char from[256] = { 0 }; strcpy(from, "Hello, World"); ret = execute_syscall(to, from, sizeof(to)); if (ret < 0) printf("%s\n", strerror(errno)); else printf("the to buffer is [%s] and its length is %d\n", to, ret); } int main(int argc, char **argv) { test_001(); test_002(); test_003(); return 0; }
_syscall[0-6]()は最近のカーネルでは使えないので、syscall(2)を使います。
これの使い方は簡単で、1番目の引数はシステムコールの番号、後は、実行したいシステムコールの引数を渡します。
syscall(__NR_copy_buffer, to, from, sizeof(from) - 1);
これを実行するとこんな感じです。
masami@lisa:~$ gcc copy_buffer_test.c masami@lisa:~$ ./a.out the to buffer is [Hello, World] and its length is 256 Invalid argument Bad address