Try   HackMD

建構 User-Mode Linux 的實驗環境

主講人: jserv

直播錄影

User-Mode Linux 概況

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

User-Mode Linux (以下簡稱 UML) 顧名思義是將 Linux 核心移植到 user-space,如此一來,就可將這個修改的核心當作一般的 Linux process 來執行,技術分類來說屬於 para-virtualization,在 Kernel-based Virtual Machine (KVM) 問世前 (Intel/AMD 硬體虛擬化加速擴展普及之前,見 x86 virtualization),UML 是唯一 Linux 核心內建的虛擬化機制。UML 有什麼好處呢?至少有以下應用:

  • 對與硬體架構無關的一般性 Linux 程式作偵錯與快速測試
  • 檢驗 (客製化) 檔案系統的完整性與正確性,特別是 init scripts 相關的部份
  • 在單機建構虛擬網路環境,以多個網路單元進行模擬操作
  • 搭配 gdb 來追蹤 Linux 核心主體流程,快速測試新的演算法或引入改進
  • 易於部署的 Linux 教學環境

kernel unit-testing framework(KUnit)也採用 UML:

KUnit addresses the problem of being able to run tests without needing a virtual machine or actual hardware with User Mode Linux. User Mode Linux is a Linux architecture, like ARM or x86; however, unlike other architectures it compiles to a standalone program that can be run like any other program directly inside of a host operating system; to be clear, it does not require any virtualization support; it is just a regular program.

透過 UML,我們可以建構出一個極佳的測試環境。Android 5.0 以後,使用 UML 來測試核心和網路連線:

UML 所使用的檔案系統對宿主 Linux 來說也不過只是單純的檔案,一切都好比置身於保護的 sandbox (原文的意思就是「貓沙盒」,給調皮的貓咪一個自得其樂卻不傷害家具的器具,引申為受限的封閉測試機制),經由適當配置,我們大可放心對虛擬機器作任何更動,而不必擔憂損害到真實的硬體與系統。

相當重要的觀念是:UML 本身就是全功能的核心,具備專屬的虛擬環境,對硬體的支援僅仰賴於宿主 Linux 系統,

  • Virtual Labs with User Mode Linux (2004 年)
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

    UML = Linux-Kernel running as regular Linux user-process without root privileges

  • UML Status Report (2017 年)
  • Advanced testing with UserModeLinux (2019 年)
    • Early printk directly prints to the console
    • Not much kernel machinery needed (hence: early)
    • On UML earlyprintk is just fprintf()
    • fprintf() on the host side

    pcap - not much use for actual network connectivity, but great for monitoring traffic on the host

    • Userspace within UML wants to see a real ethernet device
    • Have a ethernet driver in UML: Instead of talking to real or emulated hardware just use libpcap
    • Call within the driver libpcap functions
    • Advanced example: Timetravel
    • Test framework took to long, Some test cases had to run into timeouts, Timeouts are bad for testing
    • CONFIG_UML_TIME_TRAVEL_SUPPORT
    • Kernel is built as regular program, links to libc
    • Has a real main() function
    • Every program within UML runs under ptrace()
    1. ptrace 系統呼叫用以實做 gdb 一類可斷點 (breakpoint) 的追蹤除錯,或作系統呼叫的追蹤分析
    2. ptrace 允許一個 parent process 去監控另一個 process 的執行,並得以檢驗 / 更改執行時期的系統 image (映射於虛擬記憶體) 和暫存器
    3. 使用情境可透過 fork 系統呼叫去建立 child process (搭配 exec 系統呼叫) 或者直接追蹤某個已執行的 process
    • UML makes every syscall a no-op on host side
    • Calls the syscall handler with UML kernel
    • Page faults via SIGSEGV
    • Interrupts (Timer, …) are signals

UML 預計會重用 LKL (Linux Kernel Library) 的成果。

延伸閱讀: LKL: 重用 Linux 核心的成果

建構 User-Mode Linux 和搭配的檔案系統

編譯 User-Mode Linux 所仰賴的套件,原則上如同編譯 Linux 核心,以 Ubuntu Linux 來說,需要事先安裝以下套件:

$ sudo apt install build-essential libncurses-dev flex bison
$ sudo apt install xz-utils wget ca-certificates bc

取得 Linux 核心原始程式碼,以 v5.12.0 為例:

$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.12.tar.xz
$ tar xvf linux-5.12.tar.xz

切換到 Linux 核心原始程式碼,欣賞史上最偉大的開放原始碼專案的面貌:

$ cd linux-5.12

若沒有特別說明,本文在開發端 (即編輯修改原始程式碼、編譯和準備相關工具等等動作) 的工作目錄皆位於上方解壓縮後的核心原始程式碼目錄。

為避免目錄切換導致的錯誤,可用環境變數保存: (此處 WS 指 "workspace",命名沒有特別意思)

export WS=`pwd`

設定核心組態,特別注意 ARCH=um 就是指定 UML:

$ make mrproper
$ make defconfig ARCH=um SUBARCH=x86_64
$ make linux ARCH=um SUBARCH=x86_64 -j `nproc`

如果編譯順利的話,預期會得到名為 linux 的執行檔:

$ file linux

參考輸出:

linux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0

為了說明 UML 真的就是一般的執行檔,我們也能這樣做:

$ ./linux --help

然後就會看到輸出中有 --showconfig, iomem, mem, debug 等等字樣。

光有 Linux 核心是不夠的,我們還要準備 root file system (簡稱 rootfs),本文採用 Alpine Linux,後者支援 x86, x86-64, ARMhf, AArch64 等硬體架構。

建立檔案系統時,可能會遇到權限問題,但我們又不想貿然用 sudo 來執行命令 (避免不小心毀壞開發環境),這時可安裝 fakeroot 套件:

$ sudo apt install fakeroot

以下從 Alpine Linux 的套件管理系統 APK (不要跟 Ubuntu/Debian 的 apt 搞混) 建立 rootfs:

$ export REPO=http://dl-cdn.alpinelinux.org/alpine/v3.13/main
$ mkdir -p rootfs
$ curl $REPO/x86_64/APKINDEX.tar.gz | tar -xz -C /tmp/
$ export APK_TOOL=`grep -A1 apk-tools-static /tmp/APKINDEX | cut -c3- | xargs printf "%s-%s.apk"`
$ curl $REPO/x86_64/$APK_TOOL | fakeroot tar -xz -C rootfs
$ fakeroot rootfs/sbin/apk.static \
    --repository $REPO --update-cache \
    --allow-untrusted \
    --root $PWD/rootfs --initdb add alpine-base
$ echo $REPO > rootfs/etc/apk/repositories
$ echo "LABEL=ALPINE_ROOT / auto defaults 1 1" >> rootfs/etc/fstab

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
上述命令執行過程中可能見到類似下方的錯誤訊息:

Operation not permitted
script exited with error 127

可先略過。

接著我們就能準備啟動 UML,為方便之後測試,我們先建立以下檔案,名為 UML.sh:

#!/bin/sh
./linux umid=uml0 \
        root=/dev/root rootfstype=hostfs hostfs=./rootfs \
        rw mem=64M init=/bin/sh quiet

注意,當指定 rootfs 型態為 hostfs 時,可搭配以下兩種參數: (擇一或組合):

  • hostfs=相對路徑
  • rootflags=絕對路徑

這裡採用 hostfs= 來指定相對路徑,即稍早建立的 rootfs 目錄。

隨即啟動 UML:

$ chmod +x UML.sh
$ ./UML.sh

命令一執行,就會看到類似以下的輸出:

Failed to initialize ubd device 0 :Couldn't determine size of device's file
/bin/sh: can't access tty; job control turned off
/ # 

別懷疑,這表示 UML 和第一個使用者層級的程式 (即 /bin/sh) 已啟動,很快!

可在 UML.sh 裡頭 ./linux 後方加上 ubd0=/dev/null 來抑制 Failed to initialize ubd device 0 :Couldn't determine size of device's file 這個錯誤訊息。

為了區隔命令不是在開發主機 (也稱為 host) 而是在 UML 的環境 (也稱為 guest) 中執行,我們用 UML # 的表示法來標註在 UML 環境中執行的命令。

稍早準備的檔案系統,已有 busybox,不過相關的 symbolic link 還未設定,我們需要執行以下: (只要做一次)

UML # /bin/busybox --install

由於我們沒有特別去撰寫 init scripts,像是 procfs 沒預先掛載,需要手動執行以下命令:

UML # mount -t proc none /proc

之後你可在 UML 執行 psuname -a 一類的命令。

還記得編譯 UML 時,我們在 Linux 核心程式碼指定 ARCH=um,這對於 UML 環境的影響是什麼呢?執行下列命令:

UML # cat /proc/cpuinfo

預期會得到類似的輸出:

processor	: 0
vendor_id	: User Mode Linux
model name	: UML
mode		: skas
host		: Linux node1 4.15.0-72-generic #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 x86_64
bogomips	: 7722.59

mode 那行可見是 skas,可對照 UML 的文件 skas mode,後者是 "Separate Kernel Address Space" 的縮寫。

在命令提示執行 exit 命令,就會讓 UML 停止運作,因為已經沒有使用者層級的程式:

UML # exit

你會看到類似的輸出:

Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
...
(core dumped) ./linux 

如果發現開發環境的終端機游標消失,可執行 reset (不要擔心,這是重置終端機,不是整台電腦) 命令。

若你嫌每次都要額外執行 $ reset,也可將下方命令加在 UML.sh 的最後一行:

stty sane ; echo

這樣下次啟動 UML 並離開後,終端機就會自行復原。

網路設定

以下命令針對開發環境:

$ sudo ip tuntap add tap0 mode tap 
$ sudo ip link set tap0 up
$ sudo ip address add 192.168.100.100/24 dev tap0

修改之前的 UML 啟動參數,在 umid=uml0 後面增加 hostname=uml1 eth0=tuntap,tap0 (記得前後有空白)

重新啟動 UML 後,執行以下命令:

UML # ip link set eth0 up
UML # ip address add 192.168.100.101/24 dev eth0
UML # ping -c 3 192.168.100.100

預期可見以下輸出:

PING 192.168.100.100 (192.168.100.100): 56 data bytes
64 bytes from 192.168.100.100: seq=0 ttl=64 time=0.343 ms
64 bytes from 192.168.100.100: seq=1 ttl=64 time=0.235 ms
64 bytes from 192.168.100.100: seq=2 ttl=64 time=0.281 ms

--- 192.168.100.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2054ms
rtt min/avg/max/mdev = 0.050/0.057/0.065/0.006 ms

客製化 UML 環境

tini 工具可避免 UML 一類的 guest 環境造成 zombie processes。取得並在 rootfs 做好準備:

$ wget -O rootfs/sbin/tini https://github.com/krallin/tini/releases/download/v0.19.0/tini-static
$ chmod +x rootfs/sbin/tini

建立 rootfs/init.sh 檔案,其內容如下:

#!/bin/sh

mount -t proc proc /proc
mount -t sysfs sys /sys

exec /sbin/tini /bin/sh +m

記得要變更檔案權限,使其可執行:

$ chmod +x rootfs/init.sh

rootfs/init.sh 中,我們在 /bin/sh 後方加上 +m 參數,目的是抑制 /bin/sh: can't access tty; job control turned off 這項錯誤訊息。

之前我們準備了 UML.sh 檔案來啟動 UML,因應上述這個 init script,修改 UML.sh 來指定 init:

#!/bin/sh
./linux umid=uml0 \
        root=/dev/root rootfstype=hostfs hostfs=./rootfs \
        rw mem=64M init=/init.sh quiet
stty sane ; echo

接著重新執行 UML.sh 即可在開機過程自動掛載 /proc/sys

我們可變更 UML 環境中命令提示訊息,使得裡頭包含 UML 字樣,例如:

UML # export PS1='UML:\w\ $ '

也可追加終端機色彩,使得 UML 命令提示更顯目:

UML # export PS1='\[\033[01;32mUML:\w\033[00m \$ '

這樣命令提示訊息就變成綠色。更多色彩的組合,可參見:

可在 host 端修改 rootfs/init.sh,將上述 export PS1 加在 exec /sbin/tini /bin/sh 的前一行,這樣下次啟動 UML 就會生效。

準備核心模組

編譯核心模組

$ make ARCH=um SUBARCH=x86_64 modules

預期將看到若干個以 .ko 結尾的檔案。隨後我們將安裝這些核心模組到 rootfs 所在的目錄,注意最終目錄名稱應為

/lib/modules/`uname -r`

這裡先用固定名稱,稍後切換到 UML 環境時再更名:

$ make modules_install MODLIB=`pwd`/rootfs/lib/modules/VER ARCH=um

As suggested by make help

Other generic targets:
  all             - Build all targets marked with [*]
* vmlinux         - Build the bare kernel
* modules         - Build all modules
  modules_install - Install all modules to INSTALL_MOD_PATH (default: /)
  ...

Nickchen Nick
Thu, Jun 16, 2022 10:32 AM

啟動 UML 並在環境中執行以下命令:

UML # cd /lib/modules
UML # mv VER `uname -r`
UML # depmod -ae `uname -r`

測試:

UML # modprobe isofs
UML # lsmod

預期輸出:

Module                  Size  Used by    Tainted: G  
isofs                  25330  0 

確認核心模組的功能正確運作後,接著我們可撰寫自己的核心模組。在 Linux 核心程式碼最上層建立一個名為 tests 的目錄:

$ mkdir -p tests

建立檔案 tests/hello.c,其內容為:

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");

static int hello_init(void)
{
    printk(KERN_ALERT "Hello World! - init\n");
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "Hello World! - exit\n");
}

module_init(hello_init);
module_exit(hello_exit);

還要有對應的 Makefile,內容如下:

obj-m += hello.o

PWD := $(shell pwd)
KDIR := $(PWD)/..

default:
    $(MAKE) -C $(KDIR) M=$(PWD) modules ARCH=um

clean:
    $(MAKE) -C $(KDIR) M=$(PWD) clean ARCH=um

注意在 default:$(MAKE) 之間不是空白字元,而是 Tab,同理,clean:$(MAKE) 之間也以 Tab 區隔。

編譯上述核心模組並複製到 rootfs 中:

$ make -C tests
$ cp tests/hello.ko rootfs/

再次啟動 UML,掛載 hello.ko:

UML # insmod hello.ko 

預期輸出為:

Hello World! - init

隨後卸載核心模組:

UML # rmmod hello

剛才的核心模組太單純,我們再挑戰稍微複雜的試驗。以下的核心模組嘗試將 Linux 內部的 jiffiesHZ 透過 sysfs 揭露給使用者層級。

先準備開發用目錄:

$ mkdir -p tests/ticks

建立 tests/ticks/ticks.c 檔案,內容如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/kernel.h>

static ssize_t jiffies_show(struct kobject *kobj, struct kobj_attribute *attr,
                            char *buf) {
        return sprintf(buf, "%lu", jiffies);
}

static ssize_t hz_show(struct kobject *kobj, struct kobj_attribute *attr,
                       char *buf) {
        return sprintf(buf, "%u", HZ);
}

static struct kobj_attribute jiffies_attr =
        __ATTR(jiffies, 0444, jiffies_show, NULL);
static struct kobj_attribute hz_attr = __ATTR(hz, 0444, hz_show, NULL);

static struct attribute *ticks_attrs[] = { &jiffies_attr.attr, &hz_attr.attr,
                                           NULL };

static struct attribute_group ticks_grp = { .attrs = ticks_attrs };

static struct kobject *ticks;

static int __init ticks_init(void) {
        int retval;

        ticks = kobject_create_and_add("ticks", NULL);
        if (!ticks)
                return -EEXIST;

        retval = sysfs_create_group(ticks, &ticks_grp);
        if (retval)
                kobject_put(ticks);

        return retval;
}
module_init(ticks_init);

static void __exit ticks_exit(void) {
        sysfs_remove_group(ticks, &ticks_grp);

        kobject_put(ticks);
}
module_exit(ticks_exit);

MODULE_LICENSE("GPL");

也準備對應的 tests/ticks/Makefile 檔案,內容如下:

obj-m := ticks.o

KDIR= ../../

all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules ARCH=um
clean:
        $(MAKE) -C $(KDIR) M=$(PWD) clean ARCH=um

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
$(MAKE) -C 之前的字元不是空白,而是 tab

編譯核心模組:

$ pushd tests/ticks
$ make
$ popd
$ cp tests/ticks/ticks.ko rootfs/

pushdpopdGNU Bash 提供的內建命令,可參見 Directory Stack Builtins,允許快速在目錄間切換,若你不使用 GNU Bash,該命令可能會無效,請自行切換目錄並記得回到 $WS 指向的目錄,即 Linux 核心原始程式碼所在目錄。

進入 UML 環境,並進行測試:

UML # insmod /ticks.ko
UML # cat /sys/ticks/jiffies 
UML # cat /sys/ticks/hz 

應可看到各自的數值,其中 HZ 設定為 100

搭配 GDB 進行核心追蹤和分析

Linux 核心提供 GDB script,讓分析和除錯更加便利,不過 GDB script 用 Python 撰寫,於是我們首先檢查 gdb 是否開啟 Python 的整合:

$ gdb -q -ex "python print(1+1)" -ex "quit"

若看到輸出 2 也就是 Python 執行 1 + 1 的結果,那表示 GDB 內建 Python 模組。

建構 GDB script:

$ echo "CONFIG_GDB_SCRIPTS=y" > .config-fragment
$ ARCH=um scripts/kconfig/merge_config.sh .config .config-fragment
$ make ARCH=um scripts_gdb

注意上方的 ARCH=um 不能省略,否則會使核心組態錯亂。

用下行命令來啟動 GDB,指定載入 Linux 核心提供的 GDB script:

$ gdb -ex "add-auto-load-safe-path scripts/gdb/vmlinux-gdb.py" \
      -ex "file vmlinux" \
      -ex "lx-version" -q

預期可見到:

Reading symbols from vmlinux...done.
Linux version 5.12.0 (jserv@node1) (gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1 Tue Apr 26 05:41:22 CST 2021

如果沒有見到 Linux version 5.12.0 這個訊息,就意味著 Linux 核心提供的 GDB script 沒有正確產生。GDB script 沒有正確載入的錯誤訊息如下:

Undefined command: "lx-version".  Try "help".

以下用 (gdb) 表示在 GDB 命令提示列裡頭輸入的命令

此時在 (gdb) 命令提示符號旁輸入 lx 但不要按下 Enter/Return 按鍵,而是按下 Tab 按鍵,就會發現 GDB 自動補完由 GDB script 提供的自行定義命令,像是 lx-dmesg, lx-symbols , lx-cpus 等等。詳細描述可透過執行以下命令取得:

(gdb) apropos lx

為了讓後續的操作更便利,我們準備名為 gdbinit 的檔案,內容如下:

python gdb.COMPLETE_EXPRESSION = gdb.COMPLETE_SYMBOL
add-auto-load-safe-path scripts/gdb/vmlinux-gdb.py
file vmlinux
lx-version
set args umid=uml0 root=/dev/root rootfstype=hostfs rootflags=FULLPATH/rootfs rw mem=64M init=/init.sh quiet
handle SIGSEGV nostop noprint
handle SIGUSR1 nopass stop print

再執行:

$ sed -i 's|FULLPATH|'"$PWD"'|' gdbinit 

用意是將 FULLPATH 換為目前目錄的完整路徑。

為何要設定 handle 呢?

  • UML 使用 SIGSEGV 作為內部通訊,而 GDB 會自動將此 signal 轉包給除錯器本身;
  • SIGUSR1 是我們要求停止 UML 執行所用的 signal;

接著可執行以下命令:

$ gdb -q -x gdbinit

預期會看到 UML 的核心版本號碼和 (gdb) 命令提示。此刻,我們終於可在 GDB 內啟動 UML:

(gdb) run

這時可見 UML 執行到 UML:/ # 命令提示訊息。不過很快就發現,就算按下 Ctrl-C 組合按鍵,我們仍身處 UML,那該如何喚醒 GDB 呢?再開啟另一個終端機視窗,然後執行:

$ pkill -SIGUSR1 -o vmlinux

再切換回原本執行 GDB 的終端機視窗,可見到以下訊息:

Thread 1 "vmlinux" received signal SIGUSR1, User defined signal 1.

我們即可在 GDB 執行命令,例如:

(gdb) lx-mounts 
(gdb) lx-cmdline
(gdb) lx-ps
(gdb) lx-dmesg
(gdb) lx-lsmod
(gdb) print $lx_module("hello")

(gdb) print $lx_module("hello") 應在 UML # insmod /hello.ko 之後執行。
注意到最後一個命令的 $ 符號,這是 GDB script 所定義的函式。我們可執行以下 GDB 命令:

(gdb) print $lx_task_by_pid(1).comm
(gdb) print $lx_task_by_pid(1).cred

上方函式的參數 1 就表示 PID (Process ID) = 1 即 init 程序。我們繼續做實驗:

(gdb) lx-ps 

預期得到以下輸出:

0x603f8aa0 <init_task> 0 swapper
0x63828040 1 tini
0x63832080 2 kthreadd
0x638380c0 3 kworker/0:0
0x6383a100 4 kworker/0:0H
0x63846140 5 kworker/u2:0
0x6384c180 6 mm_percpu_wq
0x6384e1c0 7 ksoftirqd/0
0x6385a200 8 kdevtmpfs
0x6386a240 9 netns
0x63888280 10 oom_reaper
0x638902c0 11 writeback
0x63892300 12 kworker/u2:1
0x6389c340 15 kblockd
0x638a2380 16 blkcg_punt_bio
0x638fc3c0 17 kworker/0:1
0x63916400 18 kswapd0
0x639c0480 21 sh

第一個欄位是地址,第二個欄位是 PID,不難發現 init 程序的地址是 0x63828040,於是我們可揭開 init 的內部資訊: (很長的列表,按下 Enter 繼續瀏覽)

(gdb) print *(struct task_struct *)0x63828040

也可檢驗 Linux 核心內部來得知:

(gdb) p $container_of(init_task.tasks.next, "struct task_struct", "tasks")

預期輸出就是 0x63828040。別忘了,Linux 核心用 linked list 來表示程序資訊,來做實驗:

(gdb) lx-list-check init_task.tasks

預期輸出:

Starting with: {next = 0x63828230, prev = 0x639c0670}
list is consistent: 18 node(s)

感受到 UML 搭配 GDB 的威力了吧!
延伸閱讀: Debugging kernel and modules via gdb

當然也能在 GDB 設定中斷點,比方說在掛載核心模組的實作程式碼:

(gdb) break do_init_module

參考輸出:

Breakpoint 1 at 0x600865ce: file kernel/module.c, line 3519.

注意到這個中斷點編號為 1,如果每次有核心模組掛載時,就會觸發中斷並且停留在命令提示列,說來有點惱人,於是我們可善用 GDB 裡頭的 command。先執行以下命令: (1 是數字一)

(gdb) command 1

然後會見到這樣的訊息:

Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>

在提示符號 > 後面,就是我們自訂的命令動作,繼續輸入以下:

py if str(gdb.parse_and_eval("mod->name")).find("hello") != 1: gdb.execute("continue", False, False)
end

end 輸入後,我們又見到熟悉的 (gdb) 命令提示列。

輸入 (gdb) continue 繼續執行,並參照上述掛載 hello.ko:

UML # insmod /hello.ko

這時就會觸發中斷點,參考輸出如下:

Thread 1 "vmlinux" hit Breakpoint 1, do_init_module (mod=0x6481d140) at kernel/module.c:3519
3519	{

(gdb) list 觀察,預期會得到類似下方輸出:

3514	 *
3515	 * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb
3516	 * helper command 'lx-symbols'.
3517	 */
3518	static noinline int do_init_module(struct module *mod)
3519	{
3520		int ret = 0;
3521		struct mod_initfree *freeinit;
3522	
3523		freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL)

TODO: