みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

Yoctoターゲットのセルフコンパイル環境でカーネルモジュールをビルド

はじめに

Yoctoのセルフコンパイル環境でout-of-treeのカーネルモジュールをビルドする。

普通にビルドしようとエラーが発生するのでその回避方法をメモしておく。

hello-mod

今回はrecipe-skeleton/recipes-kernelにあるhello-modラズベリーパイ3上でビルドする。

Yoctoのイメージ

今回使用するセルフコンパイル環境を作成するには、local.confに次の内容を追加する。

# for raspberrypi3
MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# enable self-compile
EXTRA_IMAGE_FEATURES_append = " dev-pkgs tools-debug tools-sdk"

# add kernel soruce
IMAGE_INSTALL_append = "kernel-devsrc"

カーネルソースを含むパッケージkernel-devsrcを追加する。

作成するモジュール

~/helloにソースとMakefileを作成する。

$ mkdir ~/hello
$ cd hello

~/hello/hello.cを次の内容で作成する。

#include <linux/module.h>

int init_module(void)
{
    printk("Hello World!\n");
    return 0;
}

void cleanup_module(void)
{
    printk("Goodbye Cruel World!\n");
}

MODULE_LICENSE("GPL");

~/hello/Makefileを次の内容で作成する。

obj-m := hello.o

SRC := $(shell pwd)

all:
    $(MAKE) -C $(KERNEL_SRC) M=$(SRC)

modules_install:
    $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

clean:
    rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
    rm -f Module.markers Module.symvers modules.order
    rm -rf .tmp_versions Modules.symvers

このMakefileではKERNEL_SRCを変数にしているので、make実行に次のようにするか

# cd ~/hello
# make KERNEL_SRC=/lib/modules/`uname -r`/build

予め環境変数に登録しておく。

# export KERNEL_SRC=/lib/modules/`uname -r`/build
# make

発生するエラーの内容

初期状態でモジュールをビルドすると次のようなエラーが発生する。

# make KERNEL_SRC=/lib/modules/`uname -r`/build
make -C /lib/modules/4.14.79/build M=/home/root/hello
make[1]: Entering directory '/lib/modules/4.14.79/build'
make[1]: Warning: File 'scripts/Makefile.ubsan' has modification time 601 s in the future
make[2]: Warning: File 'scripts/Makefile.lib' has modification time 601 s in the future
  AR      /home/root/hello/built-in.o
  CC [M]  /home/root/hello/hello.o
In file included from ./include/asm-generic/int-ll64.h:11,
                 from ./arch/arm/include/uapi/asm/types.h:5,
                 from ./include/uapi/linux/types.h:5,
                 from ./include/linux/types.h:6,
                 from ./include/linux/list.h:5,
                 from ./include/linux/module.h:9,
                 from /home/root/hello/hello.c:1:
./include/uapi/asm-generic/int-ll64.h:12:10: fatal error: asm/bitsperlong.h: No such file or directory
 #include <asm/bitsperlong.h>
          ^~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [scripts/Makefile.build:335: /home/root/hello/hello.o] Error 1
make[1]: *** [Makefile:1527: _module_/home/root/hello] Error 2
make[1]: Leaving directory '/lib/modules/4.14.79/build'
make: *** [Makefile:6: all] Error 2

fatal error: asm/bitsperlong.h: No such file or directorybitsperlong.hが見つからないというエラーが出る。

エラー回避の方法

まずは、ラズベリーパイの時刻が狂っているので現在時刻に設定する。

$ date -s "MM/DD hh:mm YYYY"

日付を合わせておかないとwarning: Clock skew detected. Your build may be incomplete.という警告が出る。 実害はなさそうだが気持ち悪いので対策しておく。

次にカーネルソースのscriptsをビルドする。

$ cd /lib/modules/`uname -r`/build
$ make scripts

bitsperlong.hのエラーはこちらで対策できる。

再びビルド

再度モジュールをビルドしてみる。

# cd ~/hello
# make KERNEL_SRC=/lib/modules/`uname -r`/build
make -C /lib/modules/4.14.79/build M=/home/root/hello
make[1]: Entering directory '/lib/modules/4.14.79/build'
  AR      /home/root/hello/built-in.o
  CC [M]  /home/root/hello/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/root/hello/hello.mod.o
  LD [M]  /home/root/hello/hello.ko
make[1]: Leaving directory '/lib/modules/4.14.79/build'

hello.koが生成される。

ほかのヘッダファイルでエラー

たとえば、hello.cの先頭の方にに次の行を追加する。

#include <linux/module.h>
#include <linux/unistd.h>

...(snip)...

すると、makeで次のエラーが発生する。

# make KERNEL_SRC=/lib/modules/`uname -r`/build
make -C /lib/modules/4.14.79/build M=/home/root/hello
make[1]: Entering directory '/lib/modules/4.14.79/build'
  CC [M]  /home/root/hello/hello.o
In file included from ./arch/arm/include/asm/unistd.h:16,
                 from ./include/uapi/linux/unistd.h:8,
                 from /home/root/hello/hello.c:2:
./arch/arm/include/uapi/asm/unistd.h:21:10: fatal error: asm/unistd-eabi.h: No such file or directory
 #include <asm/unistd-eabi.h>
          ^~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [scripts/Makefile.build:335: /home/root/hello/hello.o] Error 1
make[1]: *** [Makefile:1527: _module_/home/root/hello] Error 2
make[1]: Leaving directory '/lib/modules/4.14.79/build'
make: *** [Makefile:6: all] Error 2

これは次のコマンドで回避できる。

# cd /lib/modules/`uname -r`/build
# make prepare

最終的な回避策

Yoctoで作成したセルフコンパイル環境でout-of-treeのカーネルモジュールをビルドするためには、 カーネルソースで予めscriptsprepareをmakeしておく必要がある。

# cd /lib/modules/`uname -r`/build
# make scripts
# make prepare

hello.cに次のようなincludeを追加したが、とりあえずのところエラーは発生していない。

#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#include <asm/io.h>

...(snip)...