Monday, 20 November 2006

システムコールはどこで定義されている?



ってな問題がOSの課題にあるんだけど...

実は、毎年答えが違う。使っているOSとかによって全然違うので。BSD系は割と簡単で、write() とかのソースがあるんだよね。

Linux だと、kernel 側は割と簡単に見つかる。find で sys_write かなんかをgrepすれば良い。ただ、.c でないってところが味噌だが。.S なんだよね。この部分が「自動生成でない」ってのが、Linux の7不思議の一つだと思う。

問題はユーザ側。gdb で、write をtraceすれば、割と簡単に int 0x80 に行き付くのだが、問題は「ソースがどこか」ってこと。「libc に入っていて、ソースは、SPRMから取って来る」と言ってあって、apt-get source glibc 位で、一発で取れる。でも、そこまでやった学生はいないらしい?!

まぁ、そこからは簡単には話は進まないんだけど、

glibc-2.3.3-200312301455/sysdeps/unix/sysv/linux/i386/sysdep.h



  #define SYS_ify(syscall_name)  __NR_##syscall_name

  #define DO_CALL(syscall_name, args)      \
    PUSHARGS_##args              \
    DOARGS_##args               \
    movl $SYS_ify (syscall_name), %eax;    \
    ENTER_KERNEL               \
    POPARGS_##args

  #define PSEUDO(name, syscall_name, args)   \
   .text;                   \
   ENTRY (name)                \
    DO_CALL (syscall_name, args);      \
    cmpl $-4095, %eax;           \
    jae SYSCALL_ERROR_LABEL;        \
 L(pseudo_end):

あたりだってのは、glibc をmake して見ればわかる。(深いので見つけにくい...)

make してみないとわからないのは、実は、write.c みたいなソースは、存在してなくて、

(echo '#include <sysdep-cancel.h>'; \
echo 'PSEUDO (__libc_write, write, 3)'; \
echo ' ret'; \
echo 'PSEUDO_END(__libc_write)'; \
echo 'libc_hidden_def (__libc_write)'; \
echo 'weak_alias (__libc_write, __write)'; \
echo 'libc_hidden_weak (__write)'; \
echo 'weak_alias (__libc_write, write)'; \
echo 'libc_hidden_weak (write)'; \
) | gcc -c -I../include -I /lib/modules/2.4.26-0vl15/build/include -D_LIBC_REENTRANT -include ../include/libc-symbols.h -DPROF -DASSEMBLER -DGAS_SYNTAX -Wa,--noexecstack -x assembler-with-cpp -o /root/rpm/SOURCES/build/io/write.op -

とかやっているから。ここに出て来る3は、system call の番号ではありません。引数が三つっていう意味。

このコマンドは、

  sysd-syscalls

というscript にあって、これ自体は、
  sysdeps/unix/make-syscalls.sh
から作られるらしい。

 x*)
 echo "\
    \$(make-target-directory)
    (echo '#include <sysdep$cancellable.h>'; \\
     echo 'PSEUDO$noerrno ($strong, $syscall, $nargs)'; \\
     echo ' ret$noerrno'; \\
     echo 'PSEUDO_END$noerrno($strong)'; \\
     echo 'libc_hidden_def ($strong)'; \\"
 ;;
 esac

こんな感じ。二重三重に生成されているわけだね。kernel 側の「手動でなんとかしてね」に比べて、何で、こんなに凝ってるんだ?

昔は... っていうか BSD 系では、もっと話が簡単なんだけど、Linux には話を難しくするのが好きな人が多いらしい。

No comments: