システムコールの流れ
- アプリケーションはシステムコールラッパーを呼び出す
- システムコールラッパーはレジスタに引数を設定し、システムコール命令を実行する
- SAVE_ALLでレジスタを保存し、*sys_call_table(, %eax, 4)でドライバやファイルシステムのコードを実行する。RESTORE_REGSでレジスタを復元し、システムコールの戻り値をeaxで返す
- システムコールラッパーはシステムコールの戻り値をアプリケーションに返すが、戻り値が負の場合は符号反転した値をerrnoに設定して-1を返す
カーネルがerrnoを変更しない理由
- errnoは標準Cライブラリのグローバル変数であり、ユーザ空間に存在する
- errnoのアドレスはアプリケーション毎に変動する
システムコールの引数の制限
システムコールの引数はレジスタ渡しの為、個数に制限がある。
システムコールラッパーは構造体のポインタを渡すことで対処する。
システムコールの追加
システムコールを追加する時、2つの選択肢がある。
- 既存のシステムコールは変更せず、新たな番号と名前を用意する
- 既存のシステムコールの名前を変更して、新たな番号だけ用意する
システムコールラッパーの呼び出し先(eax)を修正する必要はあるが、既存のシステムコールの番号は変更されない為、前方互換性を持つ。
システムコールの負の戻り値
システムコールラッパーは負の戻り値をエラーと認識する為、技巧を凝らす。
- システムコールはx(-20<=x<=19)を返したいが、20-x(1<=20-x<=40)を返す
- システムコールラッパーは20-(20-x)を計算し、本来の戻り値x(-20<=x<=19)を復元できる
システムコールの任意の戻り値
ptrace()のPTRACE_PEEKDATAは任意の値を返す。
- PTRACE_PEEKDATAを使用しない場合、成功時は0、失敗時は-1が返る
- PTRACE_PEEKDATAを使用する場合、成功時は任意の値、失敗時は-1が返る
errnoが0なら成功、それ以外は失敗と判断するのが良い。
POSIX
LinuxカーネルはPOSIXの機能を提供し、glibcがPOSIXのインターフェースを実現する。
負の戻り値を要求するAPIに対し、システムコールだけでは実現できないが、システムコールラッパーを使用すると可能である。