QEMU を使った Linux のカーネルデバッグ
QEMU を使った Linux のカーネルデバッグ方法を以下の流れで紹介します。Ubuntu 20.04 で試しています。
1. カーネルのビルド
2. 初期 RAM ディスクの作成
3. QEMU を用いてカーネルの起動
4. GDB を用いてカーネルデバッグ
1. カーネルのビルド
1.1. 必要なモジュールをインストールします。

1.2. カーネルのソースコードをダウンロードします。

1.3. 利用できるカーネルのバージョンを確認します。

1.4. 利用できるカーネルのバージョンでチェックアウトします。

1.5. デフォルトのカーネルの設定ファイルを作成します。

1.6. カーネルの設定ファイルを編集します。

「Kernel hacking」に移動します。

「Compile-time checks and compiler options」に移動します。

以下にチェックを入れます。
「Compile the kernel with debug info」
「Provide GDB scripts for kernel debugging」

「save」を実行し .config ファイルを保存します。


1.7. カーネルをビルドします。
-j$(nproc) を指定することで、稼働中のマシンの CPU コア使ってビルドします。

1.8. arch/x86_64/boot に bzImage があることを確認します。

2. 初期 RAM ディスクの作成
カーネルを起動させる際に必要になる RAM ディスクを作成します。

3. QEMU を用いてカーネルの起動
3.1. QEMU をインストールします。

3.2. カーネルを起動します。
引数の意味は以下です。
GDB からの接続待ちの状態になります。

4. GDB を用いてカーネルデバッグ
4.1. カーネルビルド時に、カーネルデバッグに便利なスクリプトが作成されています。そのスクリプトを読み込めるように設定します。

4.2. GDB を開始します。

4.3. デバッグ対象に接続します。

4.4. start_kernel にブレークポイントを張ってみます。

4.5. カーネルの実行を開始します。
start_kernel でブレークします。

4.7.「Ctrl + x, a」を押すと、ソースコードも表示されます。

4.8.「lx-」と入力しタブを 2 回押すと、スクリプトを確認できます。

4.9.「lx-version」を実行してみると、カーネルバージョンが表示されます。

4.10.「c」を入力すると、カーネルが実行されます。

情報元
Debugging the Linux Kernel with Qemu and GDB
1. カーネルのビルド
2. 初期 RAM ディスクの作成
3. QEMU を用いてカーネルの起動
4. GDB を用いてカーネルデバッグ
1. カーネルのビルド
1.1. 必要なモジュールをインストールします。
sudo apt-get install libncurses5-dev gcc make git exuberant-ctags bc libssl-dev flex bison libelf-dev |

1.2. カーネルのソースコードをダウンロードします。
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git |

1.3. 利用できるカーネルのバージョンを確認します。
cd linux-stable git tag –l |

1.4. 利用できるカーネルのバージョンでチェックアウトします。
git checkout –b <ブランチ名> <上記 1.3. で確認したカーネルのバージョン> |

1.5. デフォルトのカーネルの設定ファイルを作成します。
make x86_64_defconfig |

1.6. カーネルの設定ファイルを編集します。
make menuconfig |

「Kernel hacking」に移動します。

「Compile-time checks and compiler options」に移動します。

以下にチェックを入れます。
「Compile the kernel with debug info」
「Provide GDB scripts for kernel debugging」

「save」を実行し .config ファイルを保存します。


1.7. カーネルをビルドします。
sudo make –j$(nproc) |

1.8. arch/x86_64/boot に bzImage があることを確認します。
ls –l arch/x86_64/boot |

2. 初期 RAM ディスクの作成
カーネルを起動させる際に必要になる RAM ディスクを作成します。
mkinitramfs -o ramdisk.img |

3. QEMU を用いてカーネルの起動
3.1. QEMU をインストールします。
sudo apt install qemu-system-x86 |

3.2. カーネルを起動します。
qemu-system-x86_64 -kernel <linux-stable>/arch/x86_64/boot/bzImage -initrd <ramdisk.img のパス> -m 1024M -nographic -append "console=ttyS0 nokaslr" -s -S |
引数の意味は以下です。
引数 | 意味 |
-kernel <linux-stable>/arch/x86_64/boot/bzImage | カーネルのパスを指定 |
-initrd <ramdisk.img のパス> | 初期 RAM ディスクの指定 |
-m 1024M | 1024 Mbyte のメモリを割り当て |
-nographic | グラフィックな出力を省略 |
-append “console=ttyS0 nokaslr” | 出力を ttyS0 に設定し、kernel randomization を使用しない |
-s | GDB の接続を許可 |
-S | GDB が接続するまで、ブートさせない |
GDB からの接続待ちの状態になります。

4. GDB を用いてカーネルデバッグ
4.1. カーネルビルド時に、カーネルデバッグに便利なスクリプトが作成されています。そのスクリプトを読み込めるように設定します。
echo "add-auto-load-safe-path <linux-stable>/vmlinux-gdb.py" >> ~/.gdbinit |

4.2. GDB を開始します。
gdb <linux-stable>/vmlinux |

4.3. デバッグ対象に接続します。
target remote :1234 |

4.4. start_kernel にブレークポイントを張ってみます。
break start_kernel |

4.5. カーネルの実行を開始します。
c |

4.7.「Ctrl + x, a」を押すと、ソースコードも表示されます。

4.8.「lx-」と入力しタブを 2 回押すと、スクリプトを確認できます。

4.9.「lx-version」を実行してみると、カーネルバージョンが表示されます。

4.10.「c」を入力すると、カーネルが実行されます。

情報元
Debugging the Linux Kernel with Qemu and GDB