Linuxカーネル3.2.xに特定の新しいシステムコールを追加したいが、ロード可能なカーネルモジュールとして(カーネルを何度も再コンパイルしたくないため)
私はインターネットやSOに関する多くの投稿を読みましたが、ローダブルモジュールとしてシステムコールを実装することは不可能であると主張する場所もあれば、可能であると主張する場所もあります。
どっち?可能であればどのように行われますか?
システムコールテーブル(_sys_call_table
_と呼ばれる)は静的サイズの配列であるため、これは不可能です。また、そのサイズは、コンパイル時に登録されたシステムコールの数によって決定されます。これは、別のスペースがないことを意味します。
_Arch/x86/kernel/syscall_64.c
_が定義されている_sys_call_table
_ファイルで、x86アーキテクチャの実装を確認できます。そのサイズは正確に___NR_syscall_max+1
_です。 ___NR_syscall_max
_は_Arch/x86/kernel/asm-offsets_64.c
_でsizeof(syscalls) - 1
として定義されています(これは最後のシステムコールの数です)。ここで、syscall
はすべてのシステムコールを含むテーブルです。
考えられる解決策の1つは、既存の(または、アーキテクチャに廃止されたものがある場合は、_sys_setaltroot
_を参照)syscall番号を再利用することです。これにより、メモリにそれ以上のスペースが必要なくなります。一部のアーキテクチャでは、syscallテーブルに穴が開いている場合があるため(64ビットバージョンのx86など)、これも使用できます。
新しいシステムコールを開発していて、実験中に再起動を避けたい場合は、この手法を使用できます。新しいシステムコールを定義し、syscallテーブルで既存のエントリを見つけて、モジュールから置き換える必要があります。
カーネルはバージョン2.6の時点で_sys_call_table
_をモジュールにエクスポートしないため、カーネルモジュールからこれを行うのは簡単ではありません(このシンボルがエクスポートされた最後のカーネルバージョンは_2.5.41
_でした)。
これを回避する1つの方法は、_sys_call_table
_シンボルをモジュールにエクスポートするようにカーネルを変更することです。これを行うには、次の2行を_kernel/kallsyms.c
_に追加する必要があります(本番マシンではこれを行わないでください):
_extern void *sys_call_table;
EXPORT_SYMBOL(sys_call_table);
_
別の手法は、syscallテーブルを動的に見つけることです。各Wordを既知のシステムコール関数へのポインタと比較しながら、カーネルメモリを反復処理します。テーブル内のこの既知のシステムコールのオフセットがわかっているので、テーブルの開始アドレスを計算できます。