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ファイルイメージだと言うことでした。
#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(&notify_mutex);
       notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);
       up(&notify_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;
}

最終更新 2013/05/19 19:34:37 - north
(2013/05/19 19:34:37 作成)


検索

アクセス数
3689293
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。