module_layout
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!とのメッセージが表示されるわけです。
通常、母艦のMakefileをそのまま使うので、このエラーメッセージが発生す事はないと思いますが、表示されるとマジック番号が違っている可能性があります。insmodできないようですと、マジック番号をチェックされたらいいかと思います。エラーメッセージのmodule_layoutとはまったく関係ありません。
scripts/Makefile.modpost
結論から言うと、母艦カーネルが 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);
}
カーネルがCONFIG_MODVERSIONSでコンパイルされているかどうかは、/usr/src/linux/Module.symversにmodule_layoutの有無を調べることが可能でもあるわけです。




