手把手教你利用VS Code+Qemu+GDB除錯Linux核心
背景
一直以來,都對linux系統的理解都是在應用層面,看過了《UNIX環境高階程式設計》,對於系統中的一些模組的實現方法還缺乏深刻的認識,故想研究下Linux核心機制。
單純閱讀原始碼還是不如一步一步除錯核心理解深刻,對於除錯核心的方法,網上也有不少,主要是利用Qemu+GDB對核心進行除錯,但網上的資料大多零散,步驟記錄不夠詳細,筆者在實現過程中走了很多彎路,而且直接利用GDB除錯檢視程式碼還不甚方便,所以這裡使用VS Code+Qemu+GDB來進行Linux核心除錯,記錄過程中的主要步驟。
環境
由於個人主機是MacOS 10.14.5,所以在Parrales Desktop虛擬機器使用Ubuntu 14.04 amd64搭建核心除錯環境。
若主機本身就是Linux系統,則無需安裝虛擬機器,可直接進行核心除錯環境的搭建,效能更佳。
主要步驟
概述
- 虛擬機器(Parrales Desktop,vmware等均可)中安裝Ubuntu(如果主機本身就是Linux系統,本步驟可略)
- 下載linux kernel,編譯生成bzImage
- 更新GCC,G++,GDB
- 安裝Qemu
- 安裝VS Code,並進行相關配置
虛擬機器Ubuntu安裝
虛擬機器(Parrales Desktop,vmware等均可)中安裝Ubuntu 14.04,這裡Ubuntu版本可以自由選擇,只要Parrales tools能夠正常安裝即可。
另外,最好選擇x64版本,效能會更好一些。具體安裝過程略。 由於原始apt更新速度較慢,需要更新國內源,這裡使用清華apt源
vim /etc/apt/sources.list
清華apt源
核心編譯
#安裝編譯相關依賴 apt-get install libncurses5-dev libssl-dev bison flex libelf-dev gcc make openssl libc6-dev #這裡選擇清華源,國內速度會快很多 wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v4.x/linux-4.5.tar.gz #解壓 tar -xvf linux-4.5.tar.gz cd linux-4.5 #配置編譯選項,這裡可以進行核心編譯的各種配置,由於預設已經勾選了除錯相關的配置,可直接esc退出儲存 make menuconfig #開始多執行緒編譯,首次此步會等待較長時間,後續由於已經生成了中間檔案,速度會變快 make -j8
編譯完成後,目錄下會生成以下
./vmLinux
./arch/x86/boot/bzImage
其中vmLinux為GDB所需的除錯Map檔案,bzImage為大核心檔案
如果需要安裝核心可以進行以下步驟(此步非必須)
#如果需要安裝核心,
make modules_install
make install
安裝後,重啟主機,可以在Grub中選擇新的核心。
更新GCC,G++,GDB
由於系統預設的GDB在除錯核心時會出現“Remote ‘g’ packet reply is too long”的錯誤,我們需要修改GDB的原始碼,而編譯新版的GDB原始碼需要新版的GCC和G++,故需要更新以下:
#安裝GCC-9,G++-9
sudo apt install software-properties-common
sudo apt-get update
sudo apt install gcc-9 g++-9
#安裝後執行
gcc -v
#若是新版本gcc,則完成,若非新版本,需要將gcc連結到gcc-9
編譯安裝GDB
#下載GDB,這裡使用北交的gnu源,國內速度會快很多
wget https://mirror.bjtu.edu.cn/gnu/gdb/gdb-8.3.tar.xz
tar -xvf gdb-8.3.tar.xz
#在gdb/remote.c檔案下作如下修改
/* Further sanity checks, with knowledge of the architecture. */
// if (buf_len > 2 * rsa->sizeof_g_packet)
// error (_("Remote 'g' packet reply is too long (expected %ld bytes, got %d "
// "bytes): %s"),
// rsa->sizeof_g_packet, buf_len / 2,
// rs->buf.data ());
if (buf_len > 2 * rsa->sizeof_g_packet) {
rsa->sizeof_g_packet = buf_len;
for (i = 0; i < gdbarch_num_regs (gdbarch); i++){
if (rsa->regs[i].pnum == -1)
continue;
if (rsa->regs[i].offset >= rsa->sizeof_g_packet)
rsa->regs[i].in_g_packet = 0;
else
rsa->regs[i].in_g_packet = 1;
}
}
#編譯GDB
./configure
./make -j8
sudo ./make install
#開啟GDB,看是否為新版本
gdb
Qemu配置
#安裝qemu
apt-get install qemu
製作helloworld的rootfs用於測試
touch main.c
鍵入以下程式碼
#include <stdio>
int main()
{
printf("hello world!");
printf("hello world!");
printf("hello world!");
printf("hello world!");
fflush(stdout);
while(1);
return 0;
}
編譯
gcc --static -o helloworld main.c
echo helloworld | cpio -o --format=newc > rootfs
Qemu直接執行測試(非必須)
qemu-system-x86_64 \
-kernel ./arch/x86/boot/bzImage \
-initrd ./rootfs \
-append "root=/dev/ram rdinit=/helloworld"
Qemu 開啟GDB除錯
qemu-system-x86_64 \
-kernel ./arch/x86/boot/bzImage \
-initrd ./rootfs \
-append "root=/dev/ram rdinit=/helloworld" \
-smp 2 \
-s -S
進行以上會開啟Qemu並進入等待除錯狀態,此時可以直接gdb除錯,如下(非必須)
gdb ./vmLinux
#以下進行除錯
target remote:1234
b start_kernel
c
可以發現核心被斷點在start_kernel函式上
VS code配置
官網下載安裝vscode:https://code.visualstudio.com
1. vscode開啟kernel原始碼資料夾
2. 安裝gdb debug外掛
3. Debug->Open Configurations,做以下配置
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "kernel-debug",
"type": "cppdbg",
"request": "launch",
"miDebuggerServerAddress": "127.0.0.1:1234",
"program": "${workspaceFolder}/vmlinux",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"MIMode": "gdb",
}
]
}
此時將斷點設在init/main.c中的start_kernel函式中,然後Qemu 開啟手機號碼轉讓地圖GDB除錯,vscode start debug即可開始除錯核心。