lkm起動について(2)
insmodは引数のlkmモジュールを組み込みます。これはシステムコール__NR_init_moduleをコールする事で行っています。sys_init_module()は、引数のvoid __user *umodから、load_module()でstruct module *modを作成し、静的変数modulesをヘッドとするリストに登録して、lkmのスタート関数のmod->init()を呼び出すことにあります。
引数のvoid __user *umodは、漠然とlkmのファイル名だと思っていましたが、改めて読み返すと、lkmファイルイメージだと言うことでした。
最後に、改めて複写した領域のイメージ内の.rel.gnu.linkonce.this_moduleを、modとして、ユーザ引数として割り当てた、カーネル空間メモリを開放しています。
引数のvoid __user *umodは、漠然とlkmのファイル名だと思っていましたが、改めて読み返すと、lkmファイルイメージだと言うことでした。
#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);
up(&module_mutex);
down(¬ify_mutex);
notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);
up(¬ify_mutex);
ret = mod->init();
if (ret < 0) {
/* Init routine failed: abort. Try to protect us from
buggy refcounters. */
mod->state = MODULE_STATE_GOING;
synchronize_kernel();
if (mod->unsafe)
printk(KERN_ERR "%s: module is now stuck!\n",
mod->name);
else {
module_put(mod);
down(&module_mutex);
free_module(mod);
up(&module_mutex);
}
return ret;
}
down(&module_mutex);
mod->state = MODULE_STATE_LIVE;
/* Drop initial reference. */
module_put(mod);
module_free(mod, mod->module_init);
mod->module_init = NULL;
mod->init_size = 0;
mod->init_text_size = 0;
up(&module_mutex);
return 0;
}
hogehog.cのlkmをmakeすると、動的にhogehoge.mod.cが作成されます。ここにセクション.gnu.linkonce.this_moduleに、メンバー.name=lkm名/.init=初期関数/・・・等が設定された、struct module構造体が配置されます。
[root@localhost lkm]# cat hogehoge.mod.c
#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>
MODULE_INFO(vermagic, VERMAGIC_STRING);
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
static const char __module_depends[]
__used
__attribute__((section(".modinfo"))) =
"depends=";
実際のlkmモジュールのセクションです。注目すべき所は、.gnu.linkonce.this_moduleです。[root@localhost lkm]# readelf -WS hogehoge.ko There are 26 section headers, starting at offset 0xc9a4: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .note.gnu.build-id NOTE 00000000 000034 000024 00 A 0 0 4 [ 2] .text PROGBITS 00000000 000060 00001c 00 AX 0 0 16 [ 3] .rel.text REL 00000000 00cdb4 000010 08 24 2 4 [ 4] .modinfo PROGBITS 00000000 00007c 000072 00 A 0 0 1 [ 5] __mcount_loc PROGBITS 00000000 0000f0 000008 00 A 0 0 4 [ 6] .rel__mcount_loc REL 00000000 00cdc4 000010 08 24 5 4 [ 7] .data PROGBITS 00000000 0000f8 000000 00 WA 0 0 4 [ 8] .gnu.linkonce.this_module PROGBITS 00000000 000100 00017c 00 WA 0 0 32 [ 9] .rel.gnu.linkonce.this_module REL 00000000 00cdd4 000010 08 24 8 4 [10] .bss NOBITS 00000000 00027c 000000 00 WA 0 0 4 [11] .debug_info PROGBITS 00000000 00027c 006cec 00 0 0 1 [12] .rel.debug_info REL 00000000 00cde4 003338 08 24 11 4 [13] .debug_abbrev PROGBITS 00000000 006f68 000562 00 0 0 1 [14] .debug_aranges PROGBITS 00000000 0074ca 000020 00 0 0 1 [15] .rel.debug_aranges REL 00000000 01011c 000010 08 24 14 4 [16] .debug_line PROGBITS 00000000 0074ea 000737 00 0 0 1 [17] .rel.debug_line REL 00000000 01012c 000008 08 24 16 4 [18] .debug_str PROGBITS 00000000 007c21 004be1 01 MS 0 0 1 [19] .comment PROGBITS 00000000 00c802 00005a 01 MS 0 0 1 [20] .note.GNU-stack PROGBITS 00000000 00c85c 000000 00 0 0 1 [21] .debug_frame PROGBITS 00000000 00c85c 000054 00 0 0 4 [22] .rel.debug_frame REL 00000000 010134 000020 08 24 21 4 [23] .shstrtab STRTAB 00000000 00c8b0 0000f1 00 0 0 1 [24] .symtab SYMTAB 00000000 010154 0001d0 10 25 25 4 [25] .strtab STRTAB 00000000 010324 0000c9 00 0 0 1.gnu.linkonce.this_moduleの内容(32ビット)は、hogehogeの.nameだけが設定されています。順にenum module_state/struct list_head となります。.init/.exitについては、ここでのアドレスは0として、.rel.gnu.linkonce.this_moduleのリロケーションセクションとして設定されていると思います。
[root@localhost lkm]# objdump -s -j .gnu.linkonce.this_module hogehoge.ko
0000 00000000 00000000 00000000 686f6765 ............hoge
0010 686f6765 00000000 00000000 00000000 hoge............
0020 00000000 00000000 00000000 00000000 ................
0030 00000000 00000000 00000000 00000000 ................
:
:
0140 00000000 00000000 00000000 00000000 ................
0150 00000000 00000000 00000000 00000000 ................
0160 00000000 00000000 00000000 00000000 ................
0170 00000000 00000000 00000000 ............
ちなみにstruct moduleは、以下のようになっています。struct list_headは*next/*prevの2つのアドレスを持つメンバーです。
struct module
{
enum module_state state;
struct list_head list;
char name[MODULE_NAME_LEN];
:
:
}
load_module()の処理の概要は、lkmイメージのvoid __user *umodを、カーネル空間メモリに複写して、ELFセクションの.rel.gnu.linkonce.this_moduleのアドレスを、struct module *modといたします。このmodに各セクションからの必要な情報を設定した後、改めて実行イメージとして配置するため、各セクションが連続するようにアドレスをサイズを考慮して、再設定していきます。lkmの最終サイズが決定すると、改めてメモリーをアロケートし、SHF_ALLOC属性のセクションをそこに複写していきます。最後に、改めて複写した領域のイメージ内の.rel.gnu.linkonce.this_moduleを、modとして、ユーザ引数として割り当てた、カーネル空間メモリを開放しています。
static struct module *load_module(void __user *umod,
unsigned long len,
const char __user *uargs)
{
Elf_Ehdr *hdr;
Elf_Shdr *sechdrs;
char *secstrings, *args, *modmagic, *strtab = NULL;
unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
exportindex, modindex, obsparmindex, infoindex, gplindex,
crcindex, gplcrcindex, versindex, pcpuindex;
long arglen;
struct module *mod;
long err = 0;
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
umod, len, uargs);
if (len < sizeof(*hdr))
return ERR_PTR(-ENOEXEC);
lkmサイズは64Mを超えてはならない。 OKならそのサイズをカーネル空間メモリを取得し、
ELFイメージのまま、その空間に転送する。
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
return ERR_PTR(-ENOMEM);
if (copy_from_user(hdr, umod, len) != 0) {
err = -EFAULT;
goto free_hdr;
}
イメージがELFフォーマットとか、ELFイメージそのもにチェック
if (memcmp(hdr->e_ident, ELFMAG, 4) != 0
|| hdr->e_type != ET_REL
|| !elf_check_arch(hdr)
|| hdr->e_shentsize != sizeof(*sechdrs)) {
err = -ENOEXEC;
goto free_hdr;
}
hdr->e_shnum個のセクション情報をsechdrs[i]に取得
sechdrs = (void *)hdr + hdr->e_shoff;
secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
sechdrs[0].sh_addr = 0;
symindex = strindex = 0;
for (i = 1; i < hdr->e_shnum; i++) {
/* Mark all sections sh_addr with their address in the
temporary image. */
sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;
/* Internal symbols and strings. */
if (sechdrs[i].sh_type == SHT_SYMTAB) {
symindex = i;
strindex = sechdrs[i].sh_link;
strtab = (char *)hdr + sechdrs[strindex].sh_offset;
}
#ifndef CONFIG_MODULE_UNLOAD
/* Don't load .exit sections */
if (strncmp(secstrings+sechdrs[i].sh_name, ".exit", 5) == 0)
sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
#endif
}
セクション.gnu.linkonce.this_moduleのアドレスを、modとする
modindex = find_sec(hdr, sechdrs, secstrings,
".gnu.linkonce.this_module");
if (!modindex) {
printk(KERN_WARNING "No module found in object\n");
err = -ENOEXEC;
goto free_hdr;
}
mod = (void *)sechdrs[modindex].sh_addr;
各セクションのインデックスの取得
exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");
gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");
crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");
gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");
setupindex = find_sec(hdr, sechdrs, secstrings, "__param");
exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");
obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
.modinfoセクションは、実行してしまえば必要ないため、SHF_ALLOCを解除
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
#ifdef CONFIG_KALLSYMS
CONFIG_KALLSYMSなら、シンボル/文字列は保持
sechdrs[symindex].sh_flags |= SHF_ALLOC;
sechdrs[strindex].sh_flags |= SHF_ALLOC;
#endif
モジュールバージョン等のチェック
if (!check_modstruct_version(sechdrs, versindex, mod)) {
err = -ENOEXEC;
goto free_hdr;
}
modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
if (!modmagic) {
tainted |= TAINT_FORCED_MODULE;
printk(KERN_WARNING "%s: no version magic, tainting kernel.\n",
mod->name);
} else if (!same_magic(modmagic, vermagic)) {
printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
mod->name, modmagic, vermagic);
err = -ENOEXEC;
goto free_hdr;
}
モジュールの引数の設定
arglen = strlen_user(uargs);
if (!arglen) {
err = -EFAULT;
goto free_hdr;
}
args = kmalloc(arglen, GFP_KERNEL);
if (!args) {
err = -ENOMEM;
goto free_hdr;
}
if (copy_from_user(args, uargs, arglen) != 0) {
err = -EFAULT;
goto free_mod;
}
if (find_module(mod->name)) {
err = -EEXIST;
goto free_mod;
}
mod->state = MODULE_STATE_COMING;
err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
if (err < 0)
goto free_mod;
if (pcpuindex) {
/* We have a special allocation for this section. */
percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
sechdrs[pcpuindex].sh_addralign);
if (!percpu) {
err = -ENOMEM;
goto free_mod;
}
sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
mod->percpu = percpu;
}
イメージを転送するための、トータルサイズおよび、各セクションのアドレスを再設定し、
トータルサイズのメモリを取得し、それをptrとする。ここにイメージが転送される。
layout_sections(mod, hdr, sechdrs, secstrings);
ptr = module_alloc(mod->core_size);
if (!ptr) {
err = -ENOMEM;
goto free_percpu;
}
memset(ptr, 0, mod->core_size);
mod->module_core = ptr;
ptr = module_alloc(mod->init_size);
if (!ptr && mod->init_size) {
err = -ENOMEM;
goto free_core;
}
memset(ptr, 0, mod->init_size);
mod->module_init = ptr;
SHF_ALLOC属性の各セクションの内容を、ptr以下にに転送してゆく。
for (i = 0; i < hdr->e_shnum; i++) {
void *dest;
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
continue;
if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
dest = mod->module_init
+ (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
else
dest = mod->module_core + sechdrs[i].sh_entsize;
if (sechdrs[i].sh_type != SHT_NOBITS)
memcpy(dest, (void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size);
sechdrs[i].sh_addr = (unsigned long)dest;
}
セクション.gnu.linkonce.this_moduleも転送されたので、modのアドレスを転送先のアドレスに再設定
mod = (void *)sechdrs[modindex].sh_addr;
色々と各種設定をやっています・・・、そして最後に必要なくなったhdrのメモリを解放します。
module_unload_init(mod);
set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
mod);
if (err < 0)
goto cleanup;
mod->num_syms = sechdrs[exportindex].sh_size / sizeof(*mod->syms);
mod->syms = (void *)sechdrs[exportindex].sh_addr;
if (crcindex)
mod->crcs = (void *)sechdrs[crcindex].sh_addr;
mod->num_gpl_syms = sechdrs[gplindex].sh_size / sizeof(*mod->gpl_syms);
mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr;
if (gplcrcindex)
mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr;
#ifdef CONFIG_MODVERSIONS
if ((mod->num_syms && !crcindex) ||
(mod->num_gpl_syms && !gplcrcindex)) {
printk(KERN_WARNING "%s: No versions for exported symbols."
" Tainting kernel.\n", mod->name);
tainted |= TAINT_FORCED_MODULE;
}
#endif
mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
mod->extable = (void *)sechdrs[exindex].sh_addr;
for (i = 1; i < hdr->e_shnum; i++) {
const char *strtab = (char *)sechdrs[strindex].sh_addr;
if (sechdrs[i].sh_type == SHT_REL)
err = apply_relocate(sechdrs, strtab, symindex, i,mod);
else if (sechdrs[i].sh_type == SHT_RELA)
err = apply_relocate_add(sechdrs, strtab, symindex, i,
mod);
if (err < 0)
goto cleanup;
}
percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
sechdrs[pcpuindex].sh_size);
err = module_finalize(hdr, sechdrs, mod);
if (err < 0)
goto cleanup;
#ifdef CONFIG_KALLSYMS
add_kallsyms(mod, sechdrs, symindex, strindex, secstrings);
#endif
mod->args = args;
if (obsparmindex) {
err = obsolete_params(mod->name, mod->args,
(struct obsolete_modparm *)
sechdrs[obsparmindex].sh_addr,
sechdrs[obsparmindex].sh_size
/ sizeof(struct obsolete_modparm),
sechdrs, symindex,
(char *)sechdrs[strindex].sh_addr);
} else {
err = parse_args(mod->name, mod->args,
(struct kernel_param *)
sechdrs[setupindex].sh_addr,
sechdrs[setupindex].sh_size
/ sizeof(struct kernel_param),
NULL);
}
if (err < 0)
goto arch_cleanup;
vfree(hdr);
return mod;
arch_cleanup:
module_arch_cleanup(mod);
cleanup:
module_unload_free(mod);
module_free(mod, mod->module_init);
free_core:
module_free(mod, mod->module_core);
free_percpu:
if (percpu)
percpu_modfree(percpu);
free_mod:
kfree(args);
free_hdr:
vfree(hdr);
if (err < 0) return ERR_PTR(err);
else return ptr;
}





