# 2024q1 Homework6 (integration)
contributed by < `padaray` >
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 20
On-line CPU(s) list: 0-19
Vendor ID: GenuineIntel
Model name: 12th Gen Intel(R) Core(TM) i7-12700H
CPU family: 6
Model: 154
Thread(s) per core: 2
Core(s) per socket: 14
Socket(s): 1
Stepping: 3
CPU max MHz: 4700.0000
CPU min MHz: 400.0000
BogoMIPS: 5376.00
```
## 自我檢查清單
- [x] 研讀前述 ==Linux 效能分析== 描述,在自己的實體電腦運作 GNU/Linux,做好必要的設定和準備工作 $\to$ 從中也該理解為何不希望在虛擬機器中進行實驗;
1. 檢查 Linux 核心版本 :( 確保版本 >= 5.4.0 )
```shell
$ uname -r
6.5.0-26-generic
```
2. 安裝 linux-headers 套件,確認套件有被正常安裝
```shell
$ sudo apt install linux-headers-`uname -r`
[sudo] password for ray:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
linux-headers-6.5.0-26-generic is already the newest version (6.5.0-26.26~22.04.1).
linux-headers-6.5.0-26-generic set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 28 not upgraded.
```
```shell
$ dpkg -L linux-headers-`uname -r` | grep "/lib/modules/.*/build"
/lib/modules/6.5.0-26-generic/build //預期結果如左
```
</br></br>
- [ ] 閱讀〈[Linux 核心模組運作原理](https://hackmd.io/@sysprog/linux-kernel-module)〉並對照 Linux 核心原始程式碼 (v6.1+),解釋 insmod 後,Linux 核心模組的符號 (symbol) 如何被 Linux 核心找到 (使用 List API)、MODULE_LICENSE 巨集指定的授權條款又對核心有什麼影響 (GPL 與否對於可用的符號列表有關),以及藉由 strace 追蹤 Linux 核心的掛載,涉及哪些系統呼叫和子系統?
### 運作基本的 linux 核心模組
**1. 撰寫 hello.c 程式碼**
透過 module_init 和 module_exit 函式,該模組初始化和卸載時,會執行該函式有連接函式
```c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void) {
printk(KERN_INFO "Hellllllo, worlllllld\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_INFO "Gooooodbyeeeee, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
```
</br>
**2. 撰寫 Makefile 後執行**
obj-m 告知生成一個 hello.o 檔,並且將其視為模組,clean 只是方便刪除檔案所使用
```
obj-m := hello.o
clean:
rm -rf *.o *.ko *.mod.* *.symvers *.order *.mod.cmd *.mod
```
指定在 `/lib/modules/uname -r/build` 目錄進行編譯,`M=pwd` 在當前目錄尋找 module 的 code,`modules` 會將生成的 .ko 檔案視為核心的模組
```shell
$ make -C /lib/modules/`uname -r`/build M=`pwd` modules
```
:::info
但我測試結果顯示,沒有加 modules 也可以正常執行。
```shell
$ make -C /lib/modules/`uname -r`/build M=`pwd`
```
因此我進入 `/lib/modules/6.5.0-26-generic/build`,查看 Makefile :
```shell
# Modules
ifdef CONFIG_MODULES
# By default, build modules as well
all: modules
# When we're building modules with modversions, we need to consider
# the built-in objects during the descend as well, in order to
# make sure the checksums are up to date before we record them.
ifdef CONFIG_MODVERSIONS
KBUILD_BUILTIN := 1
endif
# Build modules
#
# *.ko are usually independent of vmlinux, but CONFIG_DEBUG_INFO_BTF_MODULES
# is an exception.
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
KBUILD_BUILTIN := 1
modules: vmlinux
endif
modules: modules_prepare
# Target to prepare building external modules
modules_prepare: prepare
$(Q)$(MAKE) $(build)=scripts scripts/module.lds
export modules_sign_only :=
ifeq ($(CONFIG_MODULE_SIG),y)
PHONY += modules_sign
modules_sign: modules_install
@:
```
執行 make 後,會吃到 all:modules。若是下 modules 指令則會進到 modules: modules_prepare,執行的程式碼會限縮在 modules_prepare,讓程式執行更快
:::
</br>
**3. 掛載核心模組**
insmod 會將指定的 .ko 檔載入到正在運行的核心中,為其分配一個記憶體空間,核心會呼叫模組的初始化函式 `module_init`,初始化完畢該模組功能即可正常使用
```shell
$ sudo insmod hello.ko
```
查看 message,確認模組是否有掛載成功
```shell
$ sudo dmesg
```
成功後結果如下:
```shell
[18562.323851] Hellllllo, worlllllld
```
</br>
**4. 卸載核心模組**
```shell
$ sudo rmmod hello
```
成功後結果如下:
```
[19481.559091] Gooooodbyeeeee, cruel world
```
#### 遇到的問題
**問題1** :尚未安裝 gcc-12,造成 not found error
```shell
$ make -C /lib/modules/`uname -r`/build M=`pwd` modules
make: Entering directory '/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:
CC [M] /home/ray/linux2024/hello/hello.o
/bin/sh: 1: gcc-12: not found
make[2]: *** [scripts/Makefile.build:251: /home/ray/linux2024/hello/hello.o] Error 127
make[1]: *** [/usr/src/linux-headers-6.5.0-26-generic/Makefile:2039: /home/ray/linux2024/hello] Error 2
make: *** [Makefile:234: __sub-make] Error 2
make: Leaving directory '/usr/src/linux-headers-6.5.0-26-generic'
```
解決方法如下:
```shell
$ sudo apt-get install gcc-12
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100
$ make -C /lib/modules/`uname -r`/build M=`pwd` modules
```
</br>
**問題2** :module verification failed
在執行 `$ sudo dmesg` 後顯示以下模組驗證失敗,原因是 Secure Boot 被啟用,需要停止 Secure Boot 或添加簽章,我選擇第一種
```shell
[18562.323293] hello: module verification failed: signature and/or required key missing - tainting kernel
[18562.323851] Hellllllo, worlllllld
```
解決方法如下:
```shell
sudo apt install mokutil
sudo mokutil --disable-validation
```
</br>
### MODULE_LICENSE 巨集指定的授權條款又對核心有什麼影響 (GPL 與否對於可用的符號列表有關)
1. 進入 [/lib/modules/6.5.0-26-generic/build/include/linux/module.h](https://elixir.bootlin.com/linux/latest/source/include/linux/module.h),我們可以看到 `MODULE_LICENSE` 這個巨集的說明。
```c
/*
* The following license idents are currently accepted as indicating free
* software modules
*
* "GPL" [GNU Public License v2]
* "GPL v2" [GNU Public License v2]
* "GPL and additional rights" [GNU Public License v2 rights and more]
* "Dual BSD/GPL" [GNU Public License v2
* or BSD license choice]
* "Dual MIT/GPL" [GNU Public License v2
* or MIT license choice]
* "Dual MPL/GPL" [GNU Public License v2
* or Mozilla license choice]
*
* The following other idents are available
*
* "Proprietary" [Non free products]
*
* Both "GPL v2" and "GPL" (the latter also in dual licensed strings) are
* merely stating that the module is licensed under the GPL v2, but are not
* telling whether "GPL v2 only" or "GPL v2 or later". The reason why there
* are two variants is a historic and failed attempt to convey more
* information in the MODULE_LICENSE string. For module loading the
* "only/or later" distinction is completely irrelevant and does neither
* replace the proper license identifiers in the corresponding source file
* nor amends them in any way. The sole purpose is to make the
* 'Proprietary' flagging work and to refuse to bind symbols which are
* exported with EXPORT_SYMBOL_GPL when a non free module is loaded.
*
* In the same way "BSD" is not a clear license information. It merely
* states, that the module is licensed under one of the compatible BSD
* license variants. The detailed and correct license information is again
* to be found in the corresponding source files.
*
* There are dual licensed components, but when running with Linux it is the
* GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL
* is a GPL combined work.
*
* This exists for several reasons
* 1. So modinfo can show license info for users wanting to vet their setup
* is free
* 2. So the community can ignore bug reports including proprietary modules
* 3. So vendors can do likewise based on their own policies
*/
#define MODULE_LICENSE(_license) MODULE_FILE MODULE_INFO(license, _license)
```
2. 說明重點如下:
- 有多種 license,如:GPL,Dual BSD/GPL,Dual MIT/GPL...等
- 會有 GPL 和 GPL v2 這兩個的差別,是因為曾經想用 GPL v2 字串來當作 lisence 版本號,但失敗了,所以就先用 GPL
- 而現在還存在的原因,是為了讓 Proprietary 可以作用,讓不是 free 的模組,`EXPORT_SYMBOL_GPL` 不會綁定 symbols ( **第三點進行說明** )
3. 深入探討:
a. **`MODULE_LICENSE`**:
  我對於 MODULE_LICENSE 的理解是,確認你所撰寫的模組是照著核心的 license 規範,但僅僅加上一行 `MODULE_LICENSE("GPL")` 不代表你真的有照著規範走,並沒有機制去驗證,因此這比較像是一個自我聲明,最後透過維護人員來確認。另外如果是 `Proprietary` 的類型,代表該模組使用、修改都需要付錢。
b. **`EXPORT_SYMBOL_GPL`**: