THIS_MODULEマクロ


THIS_MODULEマクロはstruct module __this_moduleとなります。しかしexternとして宣言して、その実態はカーネルに有しておりません。
#ifdef MODULE
extern struct module __this_module;
#define THIS_MODULE (&__this_module)
#else
#define THIS_MODULE ((struct module *)0)
#endif
__this_module変数は、lkmコンパイルのカーネル本体makefileから呼び出されるscripts/Makefile.modpost。そこから呼び出されるscripts/mod/modpostで以下の様に動的に作成され、elfセクションの.gnu.linkonce.this_moduleに埋め込まれる変数ということです。
#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=";
THIS_MODULEはカーネルが管理する構造体となり、file_system_type等のコールバック関数の.ownerに設定されます。このようなコールバック関数は他のカーネルパスからコールされることもあり、コールするに当たって、lkmは任意にrmmodで削除されるため、該当するLKMのstruct moduleの参照カウンタをインクリメントする必要があります。また既にlkmその物が削除されているケースもあるわけです。
static struct file_system_type ext3_fs_type = {
       .owner          = THIS_MODULE,
       .name           = "ext3",
       .mount          = ext3_mount,
       .kill_sb        = kill_block_super,
       .fs_flags       = FS_REQUIRES_DEV,
};

register_filesystem(&ext3_fs_type);
lkmをinsmodするとload_module()からlayout_and_allocate()/setup_load_info()とコールされ、THIS_MODULEのセグメントをアロケイトします。find_sec()でelfの.gnu.linkonce.this_moduleのセクション番号からstruct moduleを取得します。なおsetup_load_info()から復帰後、このstruct module以降にlkmのイメージが配置されます。
static struct module *setup_load_info(struct load_info *info)
{
       unsigned int i;
       int err;
       struct module *mod;

       info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
       info->secstrings = (void *)info->hdr
               + info->sechdrs[info->hdr->e_shstrndx].sh_offset;

       err = rewrite_section_headers(info);
       if (err)
               return ERR_PTR(err);

       for (i = 1; i < info->hdr->e_shnum; i++) {
               if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
                       info->index.sym = i;
                       info->index.str = info->sechdrs[i].sh_link;
                       info->strtab = (char *)info->hdr
                               + info->sechdrs[info->index.str].sh_offset;
                       break;
               }
       }

       info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
       if (!info->index.mod) {
               printk(KERN_WARNING "No module found in object\n");
               return ERR_PTR(-ENOEXEC);
       }

       mod = (void *)info->sechdrs[info->index.mod].sh_addr;

       if (info->index.sym == 0) {
               printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
                      mod->name);
               return ERR_PTR(-ENOEXEC);
       }

       info->index.pcpu = find_pcpusec(info);

       if (!check_modstruct_version(info->sechdrs, info->index.vers, mod))
               return ERR_PTR(-ENOEXEC);

       return mod;
}
struct moduleをカーネル内でkmallocでダイナミックに取得するのでなく、lkmサイドでも参照するため、lkm内に静的に配置する訳ですが、通常の静的変数でなく、何故、あえて複雑なelfのセクションとして割り当てているか。と言うことです。

思うに、セクションに割り当てる事で、通常変数と別に処理ができ、struct module以降にlkmイメージを配置することで、lkm内の静的変数のオーバライトでstruct module内のメンバを書き換えられるという脆弱性の危険性回避するためでは。と。

追記

LKMの関数をコールする時に、参照カウンターとしてmodule->refptr->incsをインクリメントするため、以下の2つの関数が実装されています。自LKMパスからLKMの関数をコールする場合は、LKMはMODULE_STATE_LIVEであり__module_get()を、他のパスからコールする場合、try_module_get()を使用することになります。

LKMパスから自LKMの関数をコールする場合で、その処理でスリープする場合もあり、その時lkmがrmmodされないようするため、自パスからコールする場合でも__module_get()を呼び出す必要があります。
static inline void __module_get(struct module *module)
{
       if (module) {
               preempt_disable();
               __this_cpu_inc(module->refptr->incs);
               trace_module_get(module, _THIS_IP_);
               preempt_enable();
       }
}

static inline int try_module_get(struct module *module)
{
       int ret = 1;

       if (module) {
               preempt_disable();

               if (likely(module_is_live(module))) {
                       __this_cpu_inc(module->refptr->incs);
                       trace_module_get(module, _THIS_IP_);
               } else
                       ret = 0;

               preempt_enable();
       }
       return ret;
}
なお、try_module_get()は、module->state != MODULE_STATE_GOINGとするだけで、使う分にはtry_module_get()を使っても__module_get()と比べて小差すらなく、あえて__module_get()を実装する必要はないのでは。と思いますが・・・。

自分の能力はさて置いて、なかなか読み進められない歯がゆさ故、このような実装に出くわす度に、苦々しく思ったりしています・・・。


最終更新 2014/05/23 16:17:49 - north
(2014/05/23 16:17:49 作成)


検索

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