kprobeの割込ハンドラ


カーネル初期化時、early_trap_init()がコールされ、割り込み番号3のトラップのテーブルが設定される。int 3が発生すると、int3にジャンプし、そこからdo_int3がコールされる事になる。
void __init early_trap_init(void)
{
       set_intr_gate_ist(1, &debug, DEBUG_STACK);
       set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
       set_intr_gate(14, &page_fault);
       load_idt(&idt_descr);
}

ENTRY(int3)
       RING0_INT_FRAME
       pushl_cfi $-1           # mark this as an int
       SAVE_ALL
       TRACE_IRQS_OFF
       xorl %edx,%edx          # zero error code
       movl %esp,%eax          # pt_regs pointer
       call do_int3
       jmp ret_from_exception
       CFI_ENDPROC
END(int3)
do_int3のnotify_die関数がkprobeの処理となる。以降は、デバッガーのようなユーザプロセスでint 3が発生した場合、そのプロセスにSIGTRAPを送信させる処理となる。
dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
{
   :
       if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
                       == NOTIFY_STOP)
               return;

       debug_stack_usage_inc();
       preempt_conditional_sti(regs);
       do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
       preempt_conditional_cli(regs);
       debug_stack_usage_dec();
}
notify_dieからatomic_notifier_call_chainがコールされ、最終的にnotifier_call_chainがコールされる。そこでdie_chainに登録されている、notifier_blockのコールバックを順次コールする。
int notrace __kprobes notify_die(enum die_val val, const char *str,
              struct pt_regs *regs, long err, int trap, int sig)
{
       struct die_args args = {
               .regs   = regs,
               .str    = str,
               .err    = err,
               .trapnr = trap,
               .signr  = sig,

       };
       return atomic_notifier_call_chain(&die_chain, val, &args);
}
notifier_call_chainの中でkprobeの処理が行われる。kprobeをリストしているstruct notifier_block **nlから、順にそのnb->notifier_callコールバック関数を呼び出すことで、kprobeのpre/postのハンドラをコールすることになる。
static int __kprobes notifier_call_chain(struct notifier_block **nl,
                                       unsigned long val, void *v,
                                       int nr_to_call, int *nr_calls)
{
       int ret = NOTIFY_DONE;
       struct notifier_block *nb, *next_nb;

       nb = rcu_dereference_raw(*nl);

       while (nb && nr_to_call) {
               next_nb = rcu_dereference_raw(nb->next);

#ifdef CONFIG_DEBUG_NOTIFIERS
               if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
                       WARN(1, "Invalid notifier called!");
                       nb = next_nb;
                       continue;
               }
#endif
               ret = nb->notifier_call(nb, val, v);

               if (nr_calls)
                       (*nr_calls)++;

               if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
                       break;
               nb = next_nb;
               nr_to_call--;
       }
       return ret;
}
kprobeモジュールの初期化の処理。register_die_notifierでkprobeの、割り込みが発生した時の情報を登録する。この時のハンドラはkprobe_exceptions_notifyとなる。
static struct notifier_block kprobe_exceptions_nb = {
       .notifier_call = kprobe_exceptions_notify,
       .priority = 0x7fffffff /* we need to be notified first */
};

static int __init init_kprobes(void)
{
   :
   :
       if (!err)
               err = register_die_notifier(&kprobe_exceptions_nb);
       if (!err)
               err = register_module_notifier(&kprobe_module_nb);

       kprobes_initialized = (err == 0);

       if (!err)
               init_test_probes();
       return err;
}
割り込みが発生した時のハンドラを、die_chainにリストしていく。
int register_die_notifier(struct notifier_block *nb)
{
       vmalloc_sync_all();
       return atomic_notifier_chain_register(&die_chain, nb);
}
kprobe_exceptions_notifyが割り込みが発生した時に、最終的によばれる、kprobeの実たる処理となる。DIE_INT3がpre_handore、DIE_DEBUGでpost_handerとコールされる。
int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
                                      unsigned long val, void *data)
{
       struct die_args *args = data;
       int ret = NOTIFY_DONE;

       if (args->regs && user_mode_vm(args->regs))
               return ret;

       switch (val) {
       case DIE_INT3:
               if (kprobe_handler(args->regs))
                       ret = NOTIFY_STOP;
               break;
       case DIE_DEBUG:
               if (post_kprobe_handler(args->regs)) {
                      (*(unsigned long *)ERR_PTR(args->err)) &= ~DR_STEP;
                       ret = NOTIFY_STOP;
               }
               break;
       case DIE_GPF:
               if (!preemptible() && kprobe_running() &&
                   kprobe_fault_handler(args->regs, args->trapnr))
                       ret = NOTIFY_STOP;
               break;
       default:
               break;
       }
       return ret;
}
処理の流れはこんな感じみたい。(嘘もあるかもしれません。)

int 3の処理は、die_chainのリスト管理としているため、int 3での割り込みを、kprobe以外で、新たな機能と、順次追加していくことが可能ということかな?そんな大それた事、億にひとつもありませんけど。

最終更新 2012/07/21 19:32:25 - north
(2012/07/21 19:30:39 作成)


検索

アクセス数
3586032
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。