FLATフォーマットの動作テストをしようと、nasmでFLATバイナリを作成し実行すると、バイナリファイルを実行できません。のメッセージが表示されました。binfmt_flatのモジュールがinsmodされてないんだろう。とlsmodすると、
[root@localhost kitamura]# lsmod | grep binfmt
binfmt_misc 17207 1
binfmt_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()がコールされる事になります。
(と読み取れるのですが、 do_initcalls()の実装を見ると、.text.initセクションには初期関数(hogehoge)のアドレスが設定されているやにも見受けられるのですが・・・)
[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で表示されない事になります。(他にmodulesにリストしている処理は、ありませんでした。)
#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();
}
追記
make menuconfigで、<*>又は<M>で、built-inとするかmoduleとするかの設定を行っていました。また、カーネルver3.3.8ですが、ソースとしてbinfmt_flat.cは有るのですが、make menuconfigのecutable file formats / Emulationsセクションにflatの項目がありませんでした。独自にbinfmt_flat.cだけをモジュールとしてコンパイルしてみましたが、エラーで出て疲れがどどっど。って感じです。