# 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`**: &emsp;&emsp;我對於 MODULE_LICENSE 的理解是,確認你所撰寫的模組是照著核心的 license 規範,但僅僅加上一行 `MODULE_LICENSE("GPL")` 不代表你真的有照著規範走,並沒有機制去驗證,因此這比較像是一個自我聲明,最後透過維護人員來確認。另外如果是 `Proprietary` 的類型,代表該模組使用、修改都需要付錢。 b. **`EXPORT_SYMBOL_GPL`**: &emsp;&emsp;