module_layout
Rev.2を表示中。最新版はこちら。
lkmをコンパイルしたら、WARNING: "module_layout" [/home/kitamura/lkm/euid.ko] undefined!のメッセージが表示されました。lkmは問題なく動作しましたが何故ということで調べてみた結果です。結論から言うと、母艦カーネルが CONFIG_MODVERSIONSで作成されていないのに、LKMのコンパイルではCONFIG_MODVERSIONSが設定されていますよ。との警告です。CONFIG_MODVERSIONSでカーネルをコンパイルすると、EXPORT_SYMBOLしているシンボルは、/usr/src/linux/Module.symversに書き出されます。
オリジナルなLKMのコンパイルも、カーネルがLKMをコンパイルするのとまったく同じで、通常はカーネルのMakefileへの橋渡するMakefileを通して行ったりします。そしてカーネルMakefileでは、scripts/Makefile.modpostへ橋渡ししています。この時CONFIG_MODVERSIONSが設定されていたら、オプション-mでmodpostを起動することになります。
modpostはmodpost.cから作成されていて、/usr/src/linux/Module.symversを参照することで、LKMで使用されているシンボルがカーネル内にあるかどうかをチェックします。
modpost -mで起動すると、LKMで使用しているシンボルにmodule_layout()を無理やり追加します。module_layout()は空の関数です。従ってCONFIG_MODVERSIONSが設定されていると、Module.symversにmodule_layoutが設定されていますが、そうでない故、module_layout" [/home/kitamura/lkm/euid.ko] undefined!とのメッセージが表示されるわけです。
#ifdef CONFIG_MODVERSIONS void module_layout(struct module *mod, struct modversion_info *ver, struct kernel_param *kp, struct kernel_symbol *ks, struct tracepoint * const *tp) { } EXPORT_SYMBOL(module_layout); #endif以前Kernel2でCONFIG_MODVERSIONSを調べていた時、MakefileにCONFIG_MODVERSIONSを設定してLKMをコンパイルしていました。現在セルフコンパイルしたKernel3にしており、前のCONFIG_MODVERSIONSを設定していたmakefileでLKMを作成したために表示されたようです。これもKernel3で追加された機能の1つでしょうが、ちょっと細かすぎはしないかと・・・。
通常、母艦のMakefileをそのまま使うので、このエラーメッセージが発生す事はないと思いますが、表示されるとマジック番号が違っている可能性があります。insmodできないようですと、マジック番号をチェックされたらいいかと思います。エラーメッセージのmodule_layoutとはまったく関係ありません。
scripts/Makefile.modpost
: modpost = scripts/mod/ \ $(if $(CONFIG_MODVERSIONS),-m) \ $(if $(CONFIG_MODULE_SRCVERSION_ALL),-a,) \ $(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \ $(if $(KBUILD_EXTMOD),-I $(modulesymfile)) \ $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \ $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ $(if $(cross_build),-c) :modpost.cのmainです。-mオプションの時、modversions = 1とし、read_symbols()でモジュールで使われているカーネルシンボルを取得します。この時、modversions = 1ならalloc_symbol()でmodule_layoutを追加し、check_exports()でModule.symversとの照合を行います。
int main(int argc, char **argv) { struct module *mod; struct buffer buf = { }; char *kernel_read = NULL, *module_read = NULL; char *dump_write = NULL; int opt; int err; struct ext_sym_list *extsym_iter; struct ext_sym_list *extsym_start = NULL; while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) { switch (opt) { : case 'e': external_module = 1; extsym_iter = NOFAIL(malloc(sizeof(*extsym_iter))); extsym_iter->next = extsym_start; extsym_iter->file = optarg; extsym_start = extsym_iter; break; case 'm': modversions = 1; break; case 'o': dump_write = optarg; break; : default: exit(1); } } if (kernel_read) read_dump(kernel_read, 1); if (module_read) read_dump(module_read, 0); while (extsym_start) { read_dump(extsym_start->file, 0); extsym_iter = extsym_start->next; free(extsym_start); extsym_start = extsym_iter; } while (optind < argc) read_symbols(argv[optind++]); for (mod = modules; mod; mod = mod->next) { if (mod->skip) continue; check_exports(mod); } : return err; } static void read_symbols(char *modname) { const char *symname; char *version; char *license; struct module *mod; struct elf_info info = { }; Elf_Sym *sym; : version = get_modinfo(info.modinfo, info.modinfo_len, "version"); if (version) maybe_frob_rcs_version(modname, version, info.modinfo, version - (char *)info.hdr); if (version || (all_versions && !is_vmlinux(modname))) get_src_version(modname, mod->srcversion, sizeof(mod->srcversion)-1); parse_elf_finish(&info); if (modversions) mod->unres = alloc_symbol("module_layout", 0, mod->unres); }