---
# System prepended metadata

title: 測試 Linux 核心的虛擬化環境
tags: [' LKI', LINUX KERNEL]

---

---
tags: LINUX KERNEL, LKI
---

# 測試 Linux 核心的虛擬化環境

[virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 是 Linux 核心開發者利用 QEMU 所建立一個輕量級的 Linux 核心測試環境，和 Linux 核心原始程式碼有很好的整合。
:::warning
[virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 已不再維護，請改用 [virtme-ng](https://github.com/arighi/virtme-ng)
:::

## 預先安裝套件

以 Ubuntu Linux 22.04 來說，需要安裝以下套件:
```shell
$ sudo apt -y -q install       \
  bc                           \
  flex                         \
  bison                        \
  build-essential              \
  expect                       \
  git                          \
  libncurses-dev               \
  libssl-dev                   \
  libelf-dev                   \
  u-boot-tools                 \
  wget                         \
  xz-utils                     \
  qemu-kvm                     \
  iproute2                     \
  python3                      \
  python3-pip
```

## 安裝 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git)
[virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 以 Python3 撰寫，可用 pip 安裝，注意 Python 版本需要大於 `3.3`
```shell
$ pip3 install --user \
    git+https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git
```

:::success
:warning: `$PATH` 環境變數需要一併更新:
```shell
$ export PATH=$HOME/.local/bin:$PATH
```

或者列於 `$HOME/.bashrc` 中。
:::

## 利用 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 建立 Linux 核心測試環境

首先，找一個容納 6 GB 空間的目錄，你需要記住絕對路徑，之後我們還用得到。為了行文的便利，我們用 `/home/ubuntu` 來稱呼。

:::success
:warning: 以下命令都該用一般使用者權限來執行，切勿用 `root` 帳號
:::

執行以下命令來取得 Linux 核心原始程式碼:
```shell
$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux
$ cd linux
$ git checkout -b linux-6.1.y origin/linux-6.1.y
```

使用 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 選取預設核心組態並編譯:
```shell
$ virtme-configkernel --defconfig
$ make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu- -j$(nproc)
```
> 針對 Arm64 處理器架構，改為 `make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)` 命令

:::success
:warning: 這邊需要確保 `CONFIG_DEBUG_INFO` 在組態中有被開啟，以利後續實驗使用。方法是在 `$ make` 命令前，執行 `$ grep CONFIG_DEBUG_INFO .config`，預期要看到 `CONFIG_DEBUG_INFO=y`
:::

在編譯結束後，預期可見以下訊息:
```
Kernel: arch/x86/boot/bzImage is ready
```
> 針對 Arm64 處理器架構，是 `arch/arm64/boot/Image`

透過以下命令，在 QEMU 虛擬機器中啟動 Linux 核心:
```shell
$ virtme-run --kdir . --mods=auto
```

:::success
:warning: 看到 `virtme-init` 開頭的訊息時，請保持耐心等待
> 上方命令的 `--mods=auto` 指定自動掛載 Linux 核心模組，若不需要這機制，可改為 `$ virtme-run --kimg arch/x86/boot/bzImage
`
:::

預期可見以下訊息:
```
virtme-init: console is ttyS0
root@(none):/# 
```

這時你可在 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 輸入一些命令，例如:
```shell
# uname -a
```

:::success
為了區隔模擬環境和宿主 (host，即 Ubuntu Linux) 端的終端機操作，我們約定 ==`$ `== 開頭的命令是執行於宿主端環境，而 ==`# `== 開頭的命令則執行於 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git)
:::

當你見到 `Linux (none) 6.1.21` 一類的訊息，就表示成功運作 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git)。

倘若要離開測試環境，你可按下 `Ctrl-A` 放開再按下 `X` 按鍵。

### `virtme` 的選項

除了用 `-kdir` 選項指定核心映像檔，尚可指定若干選項，例如:
* `-a` : 附加 Linux 核心啟動參數
    * 例如: `-a "nokaslr"` 抑制 ASLR
* `--disk` : 指定磁碟，測試檔案系統或 I/O 操作很好用
    ```shell
    $ dd if=/dev/zero of=/tmp/disk.img bs=1M count=1024
    $ virtme-run --kimg arch/x86/boot/bzImage --disk "disk1=/tmp/disk.img"
    ```
    之後在模擬環境中可執行 `fdisk -l /dev/sda`，隨後亦可掛載檔案系統:
    ```shell
    # mkdir -p /tmp/disk
    # mkfs.ext4 /dev/sda
    # mount /dev/sda /tmp/disk
    ```
* `--kimg` : 指定核心映像檔，例如 `-kimg arch/x86/boot/bzImage`

## 載入核心模組

使用 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 的 `kimg` 參數啟動核心映像檔後，若我們想要使用 `modprobe` 載入與核心一同編譯的核心模組時，會因為與 Ubuntu Linux 共用檔案系統，而無法從預設路徑 `/lib/modules/$(uname -r)` 中讀取相關設定檔。可以透過以下的方式來進行設定：

我們需要將核心模組安裝到一個暫存的目錄中

```shell
$ make modules_install INSTALL_MOD_PATH=/home/ubuntu/test-kmod
```

:::success
:warning: 暫存目錄需避免設定於 `/tmp` 目錄之下，因為宿主的 `/tmp` 與模擬環境中的 `/tmp` 無法共通
:::

接著啟動模擬環境，並將前述放置核心模組的目錄掛載到 `/lib/modules/`

```shell
# mount --bind /home/ubuntu/test-kmod/lib/modules /lib/modules
```

設定完畢後，就能夠使用 `modprobe` 載入預先編譯好的核心模組。例如載入 TLS 模組

```shell
# modprobe tls
```

並且可以使用 `lsmod` 列出目前載入的所有核心模組及其相依性。

## 編譯和測試核心模組

[virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 建立的虛擬環境所用的檔案系統是共用 Ubuntu Linux 的檔案系統 (透過 [9P over VirtIO](https://www.linux-kvm.org/page/9p_virtio))，因此你可以在裡頭編譯核心模組！

還記得之前記住的絕對路徑吧？Linux 核心原始程式碼就在 `/home/ubuntu/linux` 中，不過在 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 創造的虛擬環境中，該路徑是唯讀。為了便利起見，我們就在 `/tmp` 目錄實驗。你可以研讀 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 文件，以得知檔案分享和權限處理的機制。

依據 UNIX 慣例，我們用 Hello World 核心模組來示範：首先在 `/tmp` 建立實驗用的目錄:
```shell
# mkdir -p /tmp/hello
```

建立檔案 `/tmp/hello/hello.c`，其內容為:
```cpp
#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);
```

還要有對應的 `/tmp/hello/Makefile`，內容如下: (記得要更換 `/home/ubuntu` 這個路徑)
```cpp
KDIR=/home/ubuntu/linux

obj-m += hello.o

PWD := $(shell pwd)
# KDIR := $(PWD)/..
# 上面這行會導致錯誤，會使 KDIR 導引到錯誤位置 by hankTaro

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

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

接著在 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 編譯核心模組:
```shell
# make
```

:::success
:warning: 在虛擬環境中，要等待一陣子，請保持耐心。之後應該在宿主端編譯核心及其模組。
:::

預期可見以下輸出:
```
make[1]: Entering directory '/home/ubuntu/linux'
  CC [M]  /tmp/hello/hello.o
  MODPOST 1 modules
  CC [M]  /tmp/hello/hello.mod.o
  LD [M]  /tmp/hello/hello.ko
make[1]: Leaving directory '/home/ubuntu/linux'
```

萬事俱備，就來測試:
```shell
# insmod hello.ko 
```

預期可見以下訊息:
```
[ 2096.143949] hello: loading out-of-tree module taints kernel.
[ 2096.160912] Hello World! - init
```

移除核心模組也行:
```shell
# rmmod hello
```

## 透過 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 來測試 Linux 核心

我們故意在 Linux 核心原始程式碼做以下更動:
```diff
--- a/init/main.c
+++ b/init/main.c
@@ -1372,6 +1372,7 @@ static int __ref kernel_init(void *unused)
 
        rcu_end_inkernel_boot();
 
+       *(char *) NULL = 0;
        if (ramdisk_execute_command) {
                ret = run_init_process(ramdisk_execute_command);
                if (!ret)
```

透過上述的 `$ make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu-` 命令來編譯核心模組，接著用 `$ virtme-run --kimg arch/x86/boot/bzImage` 啟動 Linux 核心，預期可見到以下錯誤訊息:
```
[    1.976785] Call Trace:
[    1.977534]  ret_from_fork+0x22/0x40
[    1.977835] Modules linked in:
[    1.978013] CR2: 0000000000000000
[    1.978486] ---[ end trace eeb768630587adc1 ]---
[    1.978687] RIP: 0010:kernel_init+0x3f/0x100
[    1.978838] Code: 00 0f 84 d2 00 00 00 e8 3f 7a 5d ff e8 ba 86 55 ff e8 95 2d 56 ff c7 05 bf 47 a1 00 02 00 00 00 e8 b6 0c 6d ff e8 11 2c 5d ff <c6> 04 25 00 00 00 00 00 48 8b 3d 4a 4b fd 00 48 85 ff 74 21 e8 28
[    1.979560] RSP: 0018:ffffbbf280013f50 EFLAGS: 00000246
[    1.979770] RAX: 0000000000000000 RBX: ffffffff9bb72490 RCX: 0000000000000000
[    1.980045] RDX: 0000000000000005 RSI: ffff9bb447498018 RDI: 000000000002e6b0
[    1.980333] RBP: 0000000000000000 R08: 0000000000000163 R09: 0000000000000049
[    1.980613] R10: fffff56d00147b88 R11: 4b363735203a7972 R12: 0000000000000000
[    1.980902] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
[    1.981133] FS:  0000000000000000(0000) GS:ffff9bb447a00000(0000) knlGS:0000000000000000
[    1.981433] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    1.981644] CR2: 0000000000000000 CR3: 000000000520a000 CR4: 00000000000006f0
[    1.982090] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000009
[    1.982676] Kernel Offset: 0x1a000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[    1.983192] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000009 ]---
```

我們在即將啟動 `init` 程式之前，故意做非預期的記憶體操作，這時就觸發 Linux 核心註冊的例外處理機制。

:::warning
上述實驗結束後，請還原程式碼並編譯核心:
```shell
$ git reset --hard HEAD
$ make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu- -j$(nproc)
```
:::

## 搭配 [crash](https://crash-utility.github.io) 進行核心偵錯

許多時候，除了在觸發 kernel panic 時系統會提供對應的 call trace 外，我們也需要交叉比對其他的資訊，諸如 `dmesg` 或是所有行程，方可定位出問題所在，而 crash 就是一款可用來偵錯的工具。

簡單來說，[crash](https://crash-utility.github.io) 是一種針對核心偵錯特化的一種 GDB

`crash` 的維護者，Red Hat 工程師 David Aderson 在〈[Whilte Paper: Crash Utility](https://crash-utility.github.io/crash_whitepaper.html)〉提到：
> While gdb is an incredibly powerful tool, it is designed to debug user programs, and is not at all "kernel-aware".

### 安裝 [crash](https://crash-utility.github.io)

雖然 Ubuntu Linux 提供預先編譯好的 crash 套件，但因為較新的核心 (v5.17 以上) 內部結構已有調整，因此需要 v8.0.1 以上之 crash 才能解析。

我們可以在 [crash-utility](https://crash-utility.github.io) 下載 8.0.2 版本的 crash 並進行編譯與安裝：

```shell
$ wget https://github.com/crash-utility/crash/archive/8.0.2.tar.gz -O crash.tar.gz
$ tar xf crash.tar.gz
$ cd crash-8.0.2
$ make -j$(nproc)
$ sudo make install
```

:::success
:warning: 編譯 `crash` 需要較長的時間，因為要從 GDB 原始程式碼建構，後者又有一系列相依的套件要編譯。
:::

### 產生 kernel dump

因為 [virtme](https://git.kernel.org/pub/scm/utils/kernel/virtme/virtme.git) 使用到 QEMU 來建立虛擬環境，因此我們可用 QEMU 的功能選項來產生執行時的 kernel dump。

首先我們需要在啟動虛擬環境的命令中加入 `--qemu-opts -qmp tcp:localhost:4444,server,nowait` 這樣的參數，啟動 QEMU 的 [QEMU Machine Protocol (QMP)](https://wiki.qemu.org/Documentation/QMP) 功能，命令如下：

```shell
$ virtme-run --kimg arch/x86/boot/bzImage \
             --qemu-opts -qmp tcp:localhost:4444,server,nowait
```

啟動虛擬環境後，我們使用 [Linux Magic System Request](https://www.kernel.org/doc/html/latest/admin-guide/sysrq.html) 來觸發 kernel panic

```shell
# echo c > /proc/sysrq-trigger
```

預期可以看到系統發生 kernel panic 並印出相關訊息。

:::danger
:rotating_light: 請確保該命令執行在**模擬環境**之中，若執行在宿主系統上會導致目前使用的系統崩潰
:::

維持虛擬環境繼續執行的情況下，我們回到宿主系統，使用 [QMP](https://wiki.qemu.org/Documentation/QMP) 來與現行 QEMU 環境通訊，擷取目前虛擬環境的 kernel dump。

依照以下的步驟產生 kernel dump

1. 使用 telnet 與 QEMU 連線，預期可以看到 [QMP](https://wiki.qemu.org/Documentation/QMP) 的歡迎訊息
```shell
$ telnet localhost 4444
{"QMP": {"version": {"qemu": {"micro": 0, "minor": 6, "major": 1}, "package": ""}, "capabilities": []}}
```

2. 輸入以下的命令進入 [QMP](https://wiki.qemu.org/Documentation/QMP) 的命令模式：
```shell
telnet> { "execute": "qmp_capabilities" }
```

3. 執行命令將虛擬環境的記憶體內容傾到於指定的檔案中
```shell
telnet> { "execute": "dump-guest-memory", "arguments": {"paging": false, "protocol": "file:$out_file" }}
```

### 執行 crash 並分析 kernel dump

準備好含有 debug symbol 的 vmlinux 以及 kernel dump，就可以開始使用 crash 偵錯
```shell
$ crash /path/to/your/vmlinux /path/to/your/vmcore.img

<...>

      KERNEL: vmlinux                           
    DUMPFILE: vmcore.img
        CPUS: 1
        DATE: Mon Mar 20 22:24:12 CST 2023
      UPTIME: 00:00:32
LOAD AVERAGE: 1.00, 0.27, 0.09
       TASKS: 57
    NODENAME: (none)
     RELEASE: 6.2.7
     VERSION: #2 SMP PREEMPT_DYNAMIC Sat Mar 18 20:58:35 CST 2023
     MACHINE: x86_64  (1991 Mhz)
      MEMORY: 127.5 MB
       PANIC: "Kernel panic - not syncing: sysrq triggered crash"
         PID: 137
     COMMAND: "bash"
        TASK: ffff8fd4c2520000  [THREAD_INFO: ffff8fd4c2520000]
         CPU: 0
       STATE: TASK_RUNNING (PANIC)

crash> 
```

進入 crash 後，除了會先印出當前使用核心的基本訊息以及 panic 的原因之外，我們也可以透過 shell 風格的命令來印出發生 panic 時 kernel ring buffer 的內容

下面示範印出 ring buffer 最後 30 行的內容：

```shell
crash> dmesg | tail -30
[   32.542695] sysrq: Trigger a crash
[   32.542986] Kernel panic - not syncing: sysrq triggered crash
[   32.543488] CPU: 0 PID: 137 Comm: bash Not tainted 6.2.7 #2
[   32.543848] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[   32.544343] Call Trace:
[   32.545231]  <TASK>
[   32.545606]  dump_stack_lvl+0x37/0x50
[   32.546117]  panic+0x2fd/0x320
[   32.546275]  ? _printk+0x57/0x80
[   32.546440]  sysrq_handle_crash+0x15/0x20
[   32.546622]  __handle_sysrq+0xa1/0x180
[   32.546794]  write_sysrq_trigger+0x23/0x40
[   32.546969]  proc_reg_write+0x54/0xa0
[   32.547134]  ? preempt_count_add+0x4c/0xa0
[   32.547281]  vfs_write+0xc4/0x3c0
[   32.547407]  ? __do_sys_newfstatat+0x35/0x60
[   32.547532]  ksys_write+0x5e/0xe0
[   32.547664]  do_syscall_64+0x3f/0x90
[   32.547800]  entry_SYSCALL_64_after_hwframe+0x72/0xdc
[   32.548174] RIP: 0033:0x7f4ebe5bba37
[   32.548608] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24
[   32.549317] RSP: 002b:00007ffda4922dd8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[   32.549692] RAX: ffffffffffffffda RBX: 0000000000000002 RCX: 00007f4ebe5bba37
[   32.550009] RDX: 0000000000000002 RSI: 000056527820daf0 RDI: 0000000000000001
[   32.550323] RBP: 000056527820daf0 R08: 0000000000000000 R09: 000056527820daf0
[   32.550641] R10: 0000000000000077 R11: 0000000000000246 R12: 0000000000000002
[   32.550929] R13: 00007f4ebe6c1780 R14: 00007f4ebe6bd600 R15: 00007f4ebe6bca00
[   32.551330]  </TASK>
[   32.552166] Kernel Offset: 0x17000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[   32.552936] ---[ end Kernel panic - not syncing: sysrq triggered crash ]---
```

從上述的結果，我們可以得知 panic 是被 PID 為 `137` 的 bash 所觸發

同時我們也可以利用 `ps` 查看該核心所有行程和狀態
```shell
crash> ps
      PID    PPID  CPU       TASK        ST  %MEM      VSZ      RSS  COMM
        0       0   0  ffffffff99a14a00  RU   0.0        0        0  [swapper/0]
        1       0   0  ffff8fd4c1128000  IN   1.7     4492     2228  virtme-init
        2       0   0  ffff8fd4c1128e40  IN   0.0        0        0  [kthreadd]
        3       2   0  ffff8fd4c1129c80  ID   0.0        0        0  [rcu_gp]
        4       2   0  ffff8fd4c112aac0  ID   0.0        0        0  [rcu_par_gp]
        5       2   0  ffff8fd4c112b900  ID   0.0        0        0  [slub_flushwq]
        6       2   0  ffff8fd4c112c740  ID   0.0        0        0  [netns]
        7       2   0  ffff8fd4c112d580  ID   0.0        0        0  [kworker/0:0]
        8       2   0  ffff8fd4c112e3c0  ID   0.0        0        0  [kworker/0:0H]
       <...>
       83      74   0  ffff8fd4c1b6b900  IN   2.6    22988     3464  systemd-udevd
       84      74   0  ffff8fd4c1b6c740  IN   2.6    22988     3388  systemd-udevd
       85      74   0  ffff8fd4c1b6d580  IN   2.5    22988     3308  systemd-udevd
       86      74   0  ffff8fd4c1b6e3c0  IN   2.8    22988     3612  systemd-udevd
>     137       1   0  ffff8fd4c2520000  RU   2.0     4644     2656  bash
```

於是我們可以使用 `bt` 調查編號 137 的行程對應的 call stack 及其暫存器狀態

```shell
crash> bt 137
PID: 137      TASK: ffff8fd4c2520000  CPU: 0    COMMAND: "bash"
 #0 [ffff9d09c026bd90] panic at ffffffff98084829
 #1 [ffff9d09c026be10] sysrq_handle_crash at ffffffff985efac5
 #2 [ffff9d09c026be18] __handle_sysrq at ffffffff985f00f1
 #3 [ffff9d09c026be48] write_sysrq_trigger at ffffffff985f0703
 #4 [ffff9d09c026be58] proc_reg_write at ffffffff982fd284
 #5 [ffff9d09c026be70] vfs_write at ffffffff98274cf4
 #6 [ffff9d09c026bf00] ksys_write at ffffffff982751ae
 #7 [ffff9d09c026bf38] do_syscall_64 at ffffffff98e2175f
 #8 [ffff9d09c026bf50] entry_SYSCALL_64_after_hwframe at ffffffff990000ae
    RIP: 00007f4ebe5bba37  RSP: 00007ffda4922dd8  RFLAGS: 00000246
    RAX: ffffffffffffffda  RBX: 0000000000000002  RCX: 00007f4ebe5bba37
    RDX: 0000000000000002  RSI: 000056527820daf0  RDI: 0000000000000001
    RBP: 000056527820daf0   R8: 0000000000000000   R9: 000056527820daf0
    R10: 0000000000000077  R11: 0000000000000246  R12: 0000000000000002
    R13: 00007f4ebe6c1780  R14: 00007f4ebe6bd600  R15: 00007f4ebe6bca00
    ORIG_RAX: 0000000000000001  CS: 0033  SS: 002b
```

如果我們懷疑其中的 `sysrq_handle_crash` 函式可能是觸發 panic 的元兇，則可以使用類似 GDB 的命令 `list` 列出該函式對應的原始碼

```shell
crash> gdb list sysrq_handle_crash
146     #else
147     #define sysrq_unraw_op (*(const struct sysrq_key_op *)NULL)
148     #endif /* CONFIG_VT */
149     
150     static void sysrq_handle_crash(int key)
151     {
152             /* release the RCU read lock before crashing */
153             rcu_read_unlock();
154     
155             panic("sysrq triggered crash\n");
```

從上面的結果我們可以看到在第 155 行的程式碼實際呼叫 `panic`，導致核心進入 panic 的處理機制。

## 參考資料
* [Debugging the Linux kernel with GDB](https://sergioprado.blog/debugging-the-linux-kernel-with-gdb/)