Linux カーネルモジュールのコンパイル

Linux カーネルモジュールのコンパイルを行った際に何をやっているか調べてみました。

# 簡単なカーネルモジュール

* まず簡単なカーネルモジュールのソースコードとMakefileを用意します。

```
/*  
 *  hello.c - The simplest kernel module.
 */
#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */

int init_module(void)
{
    printk(KERN_INFO "Hello world 1.\n");

    /* 
     * A non 0 return means init_module failed; module can't be loaded. 
     */
    return 0;
}

void cleanup_module(void)
{
    printk(KERN_INFO "Goodbye world 1.\n");
}
```

```
obj-m = hello.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
```

# makeすると、どんなコマンドが実行されるか

簡単なカーネルモージュルをコンパイルするとどんなコマンドが実行されるのか。

```
make
```
をすると、カーネルソースに含まれる３つのmakefileが実行されていました。


```
make -C /lib/modules/3.13.0-62-generic/build M=/home/eggman/tmp/driver/kernelmodule modules
```
```
make -f scripts/Makefile.build obj=/home/eggman/tmp/driver/kernelmodule
```
```
make -f /usr/src/linux-headers-3.13.0-62-generic/scripts/Makefile.modpost
```

# Makefile で適用されるルール

* 引数Mで指定した値をKBUILD_EXTMODに設定。
* KBUILD_EXTMODからmodule-dirsを設定
* ターゲット $(module-dirs)を生成後に、ターゲット modules を生成する。
* $(build) がどう設定されるかわかりませんが、　```-f scripts/Makefile.build obj``` が設定されてました。

```
ifeq ("$(origin M)", "command line")
  KBUILD_EXTMOD := $(M)
endif

module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
PHONY += $(module-dirs) modules
$(module-dirs): crmodverdir $(objtree)/Module.symvers
        $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)

modules: $(module-dirs)
        @$(kecho) '  Building modules, stage 2. ';
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
```

# scripts/Makefile.build で適用されるルール

* けっこう複雑でした。

* .cを.oを変換するのは このルールです。

```
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
        $(call cmd,force_checksrc)
        $(call if_changed_rule,cc_o_c)
```

* ```if_changed_rule```はscript/Kbuild.includeで定義されています。

```
if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ),                 \
        @set -e;                                                             \
        $(rule_$(1)))
```

* これは rule_cc_o_c を行います。

```
define rule_cc_o_c
        $(call echo-cmd,checksrc) $(cmd_checksrc)                         \
        $(call echo-cmd,cc_o_c) $(cmd_cc_o_c);                            \
        $(cmd_modversions)                                                \
        $(call echo-cmd,record_mcount)                                    \
        $(cmd_record_mcount)                                              \
        scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
                                                      $(dot-target).tmp;  \
        rm -f $(depfile);                                                 \
        mv -f $(dot-target).tmp $(dot-target).cmd
endef
```

* けっきょき cmd_cc_o_c を実行します。

```
cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
```

# scripts/Makefile.modpost で適用されるルール

* .koを生成します。

```
$(modules): %.ko :%.o %.mod.o FORCE
        $(call if_changed,ld_ko_o)```
```

リンカで.oを.koに変換します。

```
      cmd_ld_ko_o = $(LD) -r $(LDFLAGS)                                 \
                             $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
                             -o $@ $(filter-out FORCE,$^)
```

# 参考にしたページ
* http://stackoverflow.com/questions/29231876/how-does-kbuild-actually-work

ずっと下書きにあったけど、公開しとく。