ちょっと早い夏休みだ自由研究をしよう(3日目)
TL;DR
- kernel objectのinsmod処理は、kernel/module.cの中で行われる。
- syscall(init_module)が起点となる。
- 最終的にはkernelへのnotifierが呼ばれる。
はじめに
insmodってよく使いますよね! でも、その裏側は見た記憶があまりない… ここでは順番にその流れを確認していきたい。
busybox
組込エンジニアなので、busyboxのinsmodを起点とする。
- ユーザーからの要求は、busybox/tree/modutils/insmod.cで受け取る。
- kernelへの要求は、busybox/tree/modutils/modutils.cで行われる。
Kernel
kernel側のソースコードは、大部分がkernel/module.cにまとまっている。
point
Q. moduleの未定義シンボルはどこで解決されるの?
A. simplify_symbols()から呼ばれた、resolve_symbol()で。
Q. moduleのアドレス再割り当てはどこで行われる?
A. apply_relocations() で補正される。
static int apply_relocations(struct module *mod, const struct load_info *info)
{
unsigned int i;
int err = 0;
/* Now do relocations. */
for (i = 1; i < info->hdr->e_shnum; i++) {
unsigned int infosec = info->sechdrs[i].sh_info;
/* Not a valid relocation section? */
if (infosec >= info->hdr->e_shnum)
continue;
/* Don't bother with non-allocated sections */
if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
continue;
if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH)
err = klp_apply_section_relocs(mod, info->sechdrs,
info->secstrings,
info->strtab,
info->index.sym, i,
NULL);
else if (info->sechdrs[i].sh_type == SHT_REL)
err = apply_relocate(info->sechdrs, info->strtab,
info->index.sym, i, mod);
else if (info->sechdrs[i].sh_type == SHT_RELA)
err = apply_relocate_add(info->sechdrs, info->strtab,
info->index.sym, i, mod);
if (err < 0)
break;
}
return err;
}
この関数内の、apply_relocate()
は、例えば、ARMの場合ならば、arm/kernel/modules.c内に定義されている。
Q. kernelはどうやってinsmodされたことを知覚するの?
A. do_init_module()内の関数が、kernelへの通知処理。
/*
* This is where the real work happens.
*
* Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb
* helper command 'lx-symbols'.
*/
static noinline int do_init_module(struct module *mod)
{
int ret = 0;
struct mod_initfree *freeinit;
freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
if (!freeinit) {
ret = -ENOMEM;
goto fail;
}
freeinit->module_init = mod->init_layout.base;
/*
* We want to find out whether @mod uses async during init. Clear
* PF_USED_ASYNC. async_schedule*() will set it.
*/
current->flags &= ~PF_USED_ASYNC;
do_mod_ctors(mod);
/* Start the module */
if (mod->init != NULL)
ret = do_one_initcall(mod->init);
if (ret < 0) {
goto fail_free_freeinit;
}
if (ret > 0) {
pr_warn("%s: '%s'->init suspiciously returned %d, it should "
"follow 0/-E convention\n"
"%s: loading module anyway...\n",
__func__, mod->name, ret, __func__);
dump_stack();
}
/* Now it's a first class citizen! */
mod->state = MODULE_STATE_LIVE;
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_LIVE, mod);
/* Delay uevent until module has finished its init routine */
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
まとめ
- kernel objectのinsmod処理は、kernel/module.cの中で行われる。
- syscall(init_module)が起点となる。
- 最終的にはkernelへのnotifierが呼ばれる。