Try   HackMD

2024q1 Homework6 (integration)

contributed by < padaray >

開發環境

$ 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

自我檢查清單

  • 研讀前述 Linux 效能分析 描述,在自己的實體電腦運作 GNU/Linux,做好必要的設定和準備工作
    從中也該理解為何不希望在虛擬機器中進行實驗;
  1. 檢查 Linux 核心版本 :( 確保版本 >= 5.4.0 )
$ uname -r
6.5.0-26-generic
  1. 安裝 linux-headers 套件,確認套件有被正常安裝
$ 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.
$ dpkg -L linux-headers-`uname -r` | grep "/lib/modules/.*/build"
/lib/modules/6.5.0-26-generic/build  //預期結果如左



  • 閱讀〈Linux 核心模組運作原理〉並對照 Linux 核心原始程式碼 (v6.1+),解釋 insmod 後,Linux 核心模組的符號 (symbol) 如何被 Linux 核心找到 (使用 List API)、MODULE_LICENSE 巨集指定的授權條款又對核心有什麼影響 (GPL 與否對於可用的符號列表有關),以及藉由 strace 追蹤 Linux 核心的掛載,涉及哪些系統呼叫和子系統?

運作基本的 linux 核心模組

1. 撰寫 hello.c 程式碼
透過 module_init 和 module_exit 函式,該模組初始化和卸載時,會執行該函式有連接函式

#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);

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 檔案視為核心的模組

$ make -C /lib/modules/`uname -r`/build M=`pwd` modules

但我測試結果顯示,沒有加 modules 也可以正常執行。

$ make -C /lib/modules/`uname -r`/build M=`pwd`

因此我進入 /lib/modules/6.5.0-26-generic/build,查看 Makefile :

# 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,讓程式執行更快


3. 掛載核心模組

insmod 會將指定的 .ko 檔載入到正在運行的核心中,為其分配一個記憶體空間,核心會呼叫模組的初始化函式 module_init,初始化完畢該模組功能即可正常使用

$ sudo insmod hello.ko

查看 message,確認模組是否有掛載成功

$ sudo dmesg

成功後結果如下:

[18562.323851] Hellllllo, worlllllld

4. 卸載核心模組

$ sudo rmmod hello

成功後結果如下:

[19481.559091] Gooooodbyeeeee, cruel world

遇到的問題

問題1 :尚未安裝 gcc-12,造成 not found error

$ 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'

解決方法如下:

$ 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

問題2 :module verification failed
在執行 $ sudo dmesg 後顯示以下模組驗證失敗,原因是 Secure Boot 被啟用,需要停止 Secure Boot 或添加簽章,我選擇第一種

[18562.323293] hello: module verification failed: signature and/or required key missing - tainting kernel
[18562.323851] Hellllllo, worlllllld

解決方法如下:

sudo apt install mokutil
sudo mokutil --disable-validation

MODULE_LICENSE 巨集指定的授權條款又對核心有什麼影響 (GPL 與否對於可用的符號列表有關)

  1. 進入 /lib/modules/6.5.0-26-generic/build/include/linux/module.h,我們可以看到 MODULE_LICENSE 這個巨集的說明。
/*
 * 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)

  1. 說明重點如下:
  • 有多種 license,如:GPL,Dual BSD/GPL,Dual MIT/GPL
  • 會有 GPL 和 GPL v2 這兩個的差別,是因為曾經想用 GPL v2 字串來當作 lisence 版本號,但失敗了,所以就先用 GPL
  • 而現在還存在的原因,是為了讓 Proprietary 可以作用,讓不是 free 的模組,EXPORT_SYMBOL_GPL 不會綁定 symbols ( 第三點進行說明 )
  1. 深入探討:
    a. MODULE_LICENSE
      我對於 MODULE_LICENSE 的理解是,確認你所撰寫的模組是照著核心的 license 規範,但僅僅加上一行 MODULE_LICENSE("GPL") 不代表你真的有照著規範走,並沒有機制去驗證,因此這比較像是一個自我聲明,最後透過維護人員來確認。另外如果是 Proprietary 的類型,代表該模組使用、修改都需要付錢。

    b. EXPORT_SYMBOL_GPL