lkm取扱いについて再考
FLATフォーマットの動作テストをしようと、nasmでFLATバイナリを作成し実行すると、バイナリファイルを実行できません。のメッセージが表示されました。binfmt_flatのモジュールがinsmodされてないんだろう。とlsmodすると、
下記はmodule_initマクロ(hogehoge.c)が、どのように展開されるか見たものです。module_initマクロは、__inittest()とinit_module()の2つの関数を定義し、__inittest()は定義した関数アドレスを、init_module()は定義した関数(aliasで設定)となります。そして、__inittest()はinitcall_tで宣言して、.text.initセクションに配置されます。従って.text.initセクションに設定されているアドレスをコールする事で、モジュールのinit()がコールされる事になります。
start_kernael()で低レベル実行環境が整った後、最後にinit()がコールされます。そこのdo_basic_setup()/ do_initcalls()で、__initcall_start/__initcall_endは、.text.initセクションの開始/終了アドレス(たぶん)で、その中のアドレスを順にコールする事で、binfmt_elf等の起動が行われるようです。
[root@localhost kitamura]# lsmod | grep binfmt binfmt_misc 17207 1binfmt_miscしか表示されていません。(wineをインストールした時に、勝手にインストールされました。)。確かにbinfmt_flatが組み込まれていません。でも、「なんでbinfmt_elfがインストールされてないの。」
下記はmodule_initマクロ(hogehoge.c)が、どのように展開されるか見たものです。module_initマクロは、__inittest()とinit_module()の2つの関数を定義し、__inittest()は定義した関数アドレスを、init_module()は定義した関数(aliasで設定)となります。そして、__inittest()はinitcall_tで宣言して、.text.initセクションに配置されます。従って.text.initセクションに設定されているアドレスをコールする事で、モジュールのinit()がコールされる事になります。
[root@localhost test]# cat hogehoge.c typedef int (*initcall_t)(void); #define __init __attribute__ ((__section__ (".text.init"))) #define module_init(initfn) \ static inline initcall_t __inittest(void) \ { return initfn; } \ int init_module(void) __attribute__((alias(#initfn))); static int __init hogehoge(void) { } module_init(hogehoge) [root@localhost test]# gcc -E hogehoge.c # 1 "hogehoge.c" # 1 "<組み込み>" # 1 "<コマンドライン>" # 1 "hogehoge.c" typedef int (*initcall_t)(void); # 10 "hogehoge.c" static int __attribute__ ((__section__ (".text.init"))) hogehoge(void) { } static inline initcall_t __inittest(void) { return hogehoge; } int init_module(void) __attribute__((alias("hogehoge"))); insmodすると、システムコールsys_init_moduleがコールされ、load_module()をコールする事で、lkmをロード/実行します。そしてstruct module *modをlist_add(&mod->list, &modules)で、modulesにリストします。このリスト化がlsmodコマンドの表示対象となるわけです。従って、sys_init_module()を介さないでモジュールをロードすると、lsmodで表示されない事になります。 #define __NR_init_module 175 __SYSCALL(__NR_init_module, sys_init_module) asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs) { struct module *mod; int ret; if (!capable(CAP_SYS_MODULE)) return -EPERM; if (down_interruptible(&module_mutex) != 0) return -EINTR; mod = load_module(umod, len, uargs); if (IS_ERR(mod)) { up(&module_mutex); return PTR_ERR(mod); } if (mod->module_init) flush_icache_range((unsigned long)mod->module_init, (unsigned long)mod->module_init + mod->init_size); flush_icache_range((unsigned long)mod->module_core, (unsigned long)mod->module_core + mod->core_size); spin_lock_irq(&modlist_lock); list_add(&mod->list, &modules); spin_unlock_irq(&modlist_lock); : }binfmt_elfのケースだと、カーネルコンパイル時にローダブルモジュールとするか、カーネルその物にバンドリングするかの設定があるやに思います。(このような事意識してコンパイルした事なかったので、定かではありません。)で、binfmt_elfはカーネル本体の一部として組み込まれ、この.text.initセクションに、初期化関数のアドレスが配置されるわけです。
start_kernael()で低レベル実行環境が整った後、最後にinit()がコールされます。そこのdo_basic_setup()/ do_initcalls()で、__initcall_start/__initcall_endは、.text.initセクションの開始/終了アドレス(たぶん)で、その中のアドレスを順にコールする事で、binfmt_elf等の起動が行われるようです。
static void __init do_basic_setup(void) { driver_init(); #ifdef CONFIG_SYSCTL sysctl_init(); #endif sock_init(); init_workqueues(); do_initcalls(); } static void __init do_initcalls(void) { initcall_t *call; int count = preempt_count(); for (call = &__initcall_start; call < &__initcall_end; call++) { char *msg; if (initcall_debug) printk("calling initcall 0x%p\n", *call); (*call)(); msg = NULL; if (preempt_count() != count) { msg = "preemption imbalance"; preempt_count() = count; } if (irqs_disabled()) { msg = "disabled interrupts"; local_irq_enable(); } if (msg) { printk("error in initcall at 0x%p: " "returned with %s\n", *call, msg); } } flush_scheduled_work(); }