web-dev-qa-db-ja.com

ロード可能なカーネルモジュールを使用してLinux 3.2.xに新しいシステムコールを追加する

Linuxカーネル3.2.xに特定の新しいシステムコールを追加したいが、ロード可能なカーネルモジュールとして(カーネルを何度も再コンパイルしたくないため)

私はインターネットやSOに関する多くの投稿を読みましたが、ローダブルモジュールとしてシステムコールを実装することは不可能であると主張する場所もあれば、可能であると主張する場所もあります。

どっち?可能であればどのように行われますか?

11
abhi

システムコールテーブル(_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を既知のシステムコール関数へのポインタと比較しながら、カーネルメモリを反復処理します。テーブル内のこの既知のシステムコールのオフセットがわかっているので、テーブルの開始アドレスを計算できます。

14