【2024年03月版】Linuxカーネルモジュールで Hello World【初心者向け】

# はじめに

Linuxのカーネルモジュールで Hello World を書く機会があったのでメモ

# 環境

- Ubuntu22.04

```terminal
$ uname -a
Linux dev 6.5.0-26-generic #26~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Mar 12 10:22:43 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
```

```terminal
$ cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
```

```terminal
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.4 LTS"
```

# 手順

- 開発ツールのインストール
- コーディング（hello.c、Makefile）
- ビルド（make）
- 実行（insmod, rmmod）
    - 作成したカーネルモジュールのロード
    - 作成したカーネルモジュールのアンロード

# 開発ツールのインストール

```terminal
$ sudo apt install build-essential linux-headers-$(uname -r) gcc-12
```

# コーディング（hello.c、Makefile）

エディタで以下の通り作成する

```terminal
$ cat hello.c
#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");
    // 0 を返すと、モジュールのロードに成功したことを意味します
    return 0;
}

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

MODULE_LICENSE("GPL");

$ cat Makefile
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）

```terminal
$ make
make -C /lib/modules/6.5.0-26-generic/build M=/home/dev/devel/kp modules
make[1]: ディレクトリ '/usr/src/linux-headers-6.5.0-26-generic' に入ります
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
  You are using:           gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
  CC [M]  /home/dev/devel/kp/hello.o
  MODPOST /home/dev/devel/kp/Module.symvers
  CC [M]  /home/dev/devel/kp/hello.mod.o
  LD [M]  /home/dev/devel/kp/hello.ko
  BTF [M] /home/dev/devel/kp/hello.ko
Skipping BTF generation for /home/dev/devel/kp/hello.ko due to unavailability of vmlinux
make[1]: ディレクトリ '/usr/src/linux-headers-6.5.0-26-generic' から出ます
p$ ll
合計 352
drwxrwxr-x 2 dev dev   4096  3月 25 23:55 ./
drwxrwxr-x 3 dev dev   4096  3月 25 23:52 ../
-rw-rw-r-- 1 dev dev    173  3月 25 23:55 .Module.symvers.cmd
-rw-rw-r-- 1 dev dev    270  3月 25 23:55 .hello.ko.cmd
-rw-rw-r-- 1 dev dev    150  3月 25 23:55 .hello.mod.cmd
-rw-rw-r-- 1 dev dev  38494  3月 25 23:55 .hello.mod.o.cmd
-rw-rw-r-- 1 dev dev  37332  3月 25 23:55 .hello.o.cmd
-rw-rw-r-- 1 dev dev    122  3月 25 23:55 .modules.order.cmd
-rw-rw-r-- 1 dev dev    156  3月 25 23:53 Makefile
-rw-rw-r-- 1 dev dev      0  3月 25 23:55 Module.symvers
-rw-rw-r-- 1 dev dev    387  3月 25 23:52 hello.c
-rw-rw-r-- 1 dev dev 111400  3月 25 23:55 hello.ko
-rw-rw-r-- 1 dev dev     27  3月 25 23:55 hello.mod
-rw-rw-r-- 1 dev dev    968  3月 25 23:55 hello.mod.c
-rw-rw-r-- 1 dev dev  96304  3月 25 23:55 hello.mod.o
-rw-rw-r-- 1 dev dev  16512  3月 25 23:55 hello.o
-rw-rw-r-- 1 dev dev     27  3月 25 23:55 modules.order
```

# 実行（insmod, rmmod）

別のターミナルで `journalctl` でカーネルの出力を見ておく

```terminal
$ journalctl -r
```

## 作成したカーネルモジュールのロード

```terminal
$ sudo insmod hello.ko
```

## 作成したカーネルモジュールのアンロード

```terminal
$ sudo rmmod hello
```

## 出力

```terminal
$ journalctl -r
  :
 3月 25 23:55:54 dev kernel: Hello world 1.
 3月 25 23:56:29 dev kernel: Goodbye world 1. 
  :
```

# エラーについて

journalctl で出力されたエラーについて、以下の通り。

```
dev kernel: hello: loading out-of-tree module taints kernel.
```

:::note info
`loading out-of-tree module taints kernel`

このメッセージは、カーネル外部からのモジュールをロードすることによってカーネルが「汚染」されたことを意味する。ここで言う「汚染」とは、そのモジュールがオープンソースではないか、または公式のLinuxカーネルソースツリーの一部ではないことを意味し、カーネルの安定性やセキュリティに潜在的な影響を与える可能性があるという警告。ただし、自分で開発中のモジュールのテストなど、特定の開発目的で行っている場合は問題ない。
:::

```
dev kernel: hello: module verification failed: signature and/or required key missing - tainting kernel
```

:::note info
`module verification failed: signature and/or required key missing - tainting kernel`

このメッセージは、ロードしようとしたモジュールが署名されていないか、必要な鍵が見つからないことを意味する。多くのLinuxディストリビューションでは、セキュリティのためにカーネルモジュールの署名を要求していて、署名されていないモジュールをロードすると、このような警告が表示される。開発やテスト目的であれば問題ないが、公開されたシステムやセキュリティが重要な環境では、信頼できる署名されたモジュールのみを使用することが推奨される。
:::

# さいごに

かんたんでしたね