Kprobesの使い方
Kprobesは、レジスタ情報ダンプなど任意の処理を、アセンブラの命令単位で実行できる機能です。
利用するにはカーネルモジュールを作成する必要はありますが、
カーネルの再ビルド無しで、カーネル内のすべての命令をデバッグできます。
なお以降の説明は、Linux 3.14.51で確認したものです。
また、debugfsのマウントポイントを
◆ 参考: debugfsのマウント方法
(マウントポイント /sys/kernel/debug の場合)
sudo mount -t debugfs none /sys/kernel/debug
カーネルコンフィギュレーション
以下を有効化してください。
- General setup
- [*] Kprobes <- 有効化
また、カーネルソース内にKprobesのサンプルコードがあります。
これもカーネルビルド時にビルドされるよう、有効化します。
- Kernel hacking
- [*] Sample kernel code <- 有効化
-
Build kprobes examples -- loadable modules only <- 有効化
-
- [*] Sample kernel code <- 有効化
Kprobesの使い方
Kprobesを使用するために行うことは以下の2つです。
1. kprobe構造体の変数を定義して値を設定
2. 1.で定義した変数をregister_kprobe関数でKprobesの枠組みに登録
1. で以下の情報をkprobe構造体に登録することで、任意の命令で任意の処理を行わせることができます。
- デバッグ対象の命令
- 「シンボル名(+オフセット)」か、「アドレス」で指定
- デバッグ対象の命令に到達した時、呼ばれるハンドラのアドレス
- 「命令実行前」・「命令実行後」・「fault時」の3つを指定できる
上述したとおり、サンプルコードがありますので、
ここではサンプルコードを参照しながら説明します。
◆ サンプルコードの場所
まず、サンプルコードが何をするものかというと、
システムコール"fork"の処理を行うdo_fork関数の最初の命令をデバッグ対象として、
この命令の実行前・実行後・fault時に、レジスタ情報とアドレスをダンプさせています。
サンプルコードでkprobeの定義と登録を行っている箇所は以下のとおりです。
/* For each probe you need to allocate a kprobe structure */ static struct kprobe kp = { .symbol_name = "do_fork", }; static int __init kprobe_init(void) { int ret; kp.pre_handler = handler_pre; kp.post_handler = handler_post; kp.fault_handler = handler_fault; ret = register_kprobe(&kp); if (ret < 0) { printk(KERN_INFO "register_kprobe failed, returned %d\n", ret); return ret; } printk(KERN_INFO "Planted kprobe at %p\n", kp.addr); return 0; }
まず、kprobe構造体のグローバル変数kpの定義で、
symbol_nameに初期値として"do_fork"を設定しており、
オフセット(offsetメンバー)には何も設定していないので、
do_fork関数の最初の命令がKprobeの対象となります。
そして、モジュールのinit処理(kprobe_init関数)で、
- pre_handler : 命令実行前ハンドラ
- post_handler : 命令実行後ハンドラ
- fault_handler : faultハンドラ
に関数ポインタを設定しています。
"handler_pre"、"handler_post"、"handler_fault"は関数として
サンプルコード内で定義されています。
サンプルコードでのこれらのハンドラ内の処理は、
どれも「printk()でアドレスとレジスタ情報ダンプ」のみなので、
命令実行前ハンドラの"handler_pre"のみ紹介します。
/* kprobe pre_handler: called just before the probed instruction is executed */ static int handler_pre(struct kprobe *p, struct pt_regs *regs) { printk(KERN_INFO "pre_handler: p->addr = 0x%p, ip = %lx," " flags = 0x%lx\n", p->addr, regs->ip, regs->flags); /* A dump_stack() here will give a stack backtrace */ return 0; }
※ アーキテクチャ判別のifdefと、x86以外の処理は削除しました
ハンドラには
- kprobe構造体
- pr_regs構造体(レジスタ情報)
が引数で渡されます。
サンプルコードでは、これらの情報をダンプしています。
カーネルのビルド時にサンプルコードのカーネルモジュールは、
以下の場所に生成されます。
このカーネルモジュールをinsmodでカーネルにインストールすれば、
今回のKprobesを使用したサンプルの動作を確認できます。
$ sudo insmod kprobe_example.ko