簡單記錄下可行的kernel調試工具和步驟。
(本文是從本人的github上遷移過來的)
調試使用4.18版本的kernel, 用到的工具是qemu+gdb
1. 調試環境說明
Host主機是centos7.6的環境
2. 編譯需要調試的內核(4.18 version)
首先下載內核tar包:
wget https://mirrors.aliyun.com/linux-kernel/v4.x/linux-4.18.2.tar.gz
可能需要安裝:
yum install flex.x86_64 bison.x86_64 elfutils-libelf-devel -y
可能需要安裝的依賴:
yum groupinstall "Development Tools" -y && yum install ncurses-devel -y && yum install hmaccalc zlib-devel binutils-devel elfutils-libelf-devel -y
yum install openssl-devel -y
yum install bc -y
yum install glibc-static -y
yum install openssl -y
解壓后開始編譯:
make defconfig
make kvmconfig
make -j4
參考:https://www.cnblogs.com/powerrailgun/category/1554674.html
以上步驟結束后會產生用於調試的內核bzImage鏡像,位於arch/x86/boot/bzImage
更多可選的:
config - Update current config utilising a line-oriented program'
@echo ' nconfig - Update current config utilising a ncurses menu based program'
@echo ' menuconfig - Update current config utilising a menu based program'
@echo ' xconfig - Update current config utilising a Qt based front-end'
@echo ' gconfig - Update current config utilising a GTK+ based front-end'
@echo ' oldconfig - Update current config utilising a provided .config as base'
@echo ' localmodconfig - Update current config disabling modules not loaded'
@echo ' localyesconfig - Update current config converting local mods to core'
@echo ' defconfig - New config with default from ARCH supplied defconfig'
@echo ' savedefconfig - Save current config as ./defconfig (minimal config)'
@echo ' allnoconfig - New config where all options are answered with no'
@echo ' allyesconfig - New config where all options are accepted with yes'
@echo ' allmodconfig - New config selecting modules when possible'
@echo ' alldefconfig - New config with all symbols set to default'
@echo ' randconfig - New config with random answer to all options'
@echo ' listnewconfig - List new options'
@echo ' olddefconfig - Same as oldconfig but sets new symbols to their'
@echo ' default value without prompting'
@echo ' kvmconfig - Enable additional options for kvm guest kernel support'
@echo ' xenconfig - Enable additional options for xen dom0 and guest kernel support'
@echo ' tinyconfig - Configure the tiniest possible kernel'
@echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)'
3. 編譯較新版本的qemu
下載qemu-4.1.1.tar.gz版本
https://download.qemu.org/
解壓之。
具體需要的依賴包視實際情況而定,參考:
yum install pixman-devel -y
最終安裝后的版本:
pixman-devel-0.34.0-1.el7.x86_64
pixman-0.34.0-1.el7.x86_64
可能還會安裝諸如:bison和flex這樣的工具。
為了不和以后系統安裝的qemu發生沖突,這里將qemu安裝到/data目錄下:
./configure --enable-rbd --enable-debug --enable-trace-backends=simple --enable-debug-stack-usage --enable-kvm --enable-vnc --prefix=/data --target-list=x86_64-softmmu
注意:
如果不加上--target-list=x86_64-softmmu選項,那么會編譯所有平台的模擬器,即就是不限於x86_64
--enable-rbd可能需要安裝librdb庫。--enable-trace-backends=simple該選項可能會影響性能,故生產環境中應關閉該選項,調試環境中推薦開啟。
接着,開始編譯和安裝:
make -j4
make install
4. 編譯較新版本的gdb
download gdb source code:
wget https://mirrors.ustc.edu.cn/gnu/gdb/gdb-8.2.tar.gz
wget https://github.com/libexpat/libexpat/releases/download/R_2_2_8/expat-2.2.8.tar.bz2
4.1 先編譯expat
解壓:expat-2.2.8.tar.bz2
然后開始執行編譯:
./configure --prefix=/data/expat-2.2.8
make -j4 && make install
4.2 開始編譯安裝gdb
and edit gdb/remote.c
將remote_target::process_g_packet函數中的一部分內容修改成這個樣子,否則gdb調試的時候可能出錯:
/* 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);
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-8.2的目錄下,編譯gdb:
./configure --prefix=/opt --with-expat --includedir=/data/expat-2.2.8/include/ --libdir=/data/expat-2.2.8/lib
還需要安裝一個包,否則在make install時會出現錯誤。
yum install texinfo -y
然后執行make:
make
make install
這里假設我的expat放置在/data/路徑下。
5. 制作rootfs
在第1步,已經將內核編譯好了,還需要一個rootfs,用於啟動完整的Linux系統。
參考以下腳本制作一個rootfs:
yum install debootstrap.noarch -y
rm -rf /data/ubuntu_1604
rm -rf /data/rootfs.img
/data/bin/qemu-img create /data/rootfs.img 5G
mkfs.ext4 /data/rootfs.img
mkdir /data/ubuntu_1604
mount -o loop /data/rootfs.img /data/ubuntu_1604
debootstrap --arch=amd64 xenial /data/ubuntu_1604 https://mirrors.aliyun.com/ubuntu/
#auto login
mkdir -p /data/ubuntu_1604/etc/systemd/system/serial-getty@ttyS0.service.d
cat << EOF > /data/ubuntu_1604/etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty --noissue --autologin root %I $TERM
Type=idle
EOF
umount /data/ubuntu_1604
經過以上步驟,制作好了一個rootfs,再結合第1步生成的內核bzImage,就可以在qemu中啟動了。
以上制作的rootfs是Ubuntu的,如果要制作基於CentOS的rootfs,參考鏈接:http://linuxcoming.com/blog/2019/07/04/build_ram_os_of_centos.html
6. 調試實例
參考以下腳本,啟動一個調試實例:
/data/qemu-4.1.0/x86_64-softmmu/qemu-system-x86_64 -kernel linux-4.8/arch/x86/boot/bzImage \
-drive file=./rootfs.img,if=virtio \
-netdev tap,id=tap0,ifname=virbr0-nic,vhost=on,script=no -m 2048 \
-device virtio-net-pci,netdev=tap0 \
-append "root=/dev/vda rw console=ttyS0 nokaslr" \
-nographic \
--enable-kvm -S -gdb tcp::8889
可能會用到的qemu參數:
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk0,id=virtio-disk0 -drive file=fake.img,format=qcow2,if=none,id=drive-virtio-disk0,cache=writeback
### 或者直接使用以下命令行可以調試qemu+Linux kernel
/opt/bin/gdb -nh --args /data/bin/qemu-system-x86_64 -m 1024 -nographic \
-kernel /root/code/linux-4.18.2/arch/x86/boot/bzImage \
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk0,id=virtio-disk0 \
-drive format=raw,if=none,id=drive-virtio-disk0,cache=writeback,file=/data/rootfs.img \
-netdev tap,id=tap0,ifname=virbr0-nic,vhost=no,script=no \
-device virtio-net-pci,netdev=tap0 \
-append "root=/dev/vda rw console=ttyS0 nokaslr" -S --enable-kvm -gdb tcp::8889
注意以上的磁盤那里不要再指定if=virtio,因此我們已經通過id指定了。重復指定會報錯。
另外,添加這樣的參數可以用於測試legacy和modern參數:-device disable-modern=on,disable-legacy=off。
gdb加上-nh參數是不讓其使用~/.gdbinit中設置的命令。
參考:https://heiko-sieger.info/tuning-vm-disk-performance/
上面將qemu運行起來后,qemu會在初始階段停住等待gdb的調試命令。
下面,將使用gdb調試內核了。
# Attach gdb
gdb ./linux-4.18.2/vmlinux
(gdb) target remote :8889
或者將上述初始化命令寫入到~/.gdbinit文件中,如下:
add-auto-load-safe-path /root/code/linux-4.18.2/vmlinux-gdb.py
file /root/code/linux-4.18.2/vmlinux
directory /root/code/linux-4.18.2/
target remote:8889
注意:在設置breakpoint的時候,對於QEMU模擬的VM,可以使用break,但對於KVM模擬的VM,需要使用hbreak。
7. QEMU trace的使用
假設在第3步啟用了--enable-trace-backends=simple這個選項來編譯qemu,那么可以嘗試使用下qemu的trace功能幫助調試。
以下兩個鏈接有助於使用qemu trace:
https://blog.csdn.net/scaleqiao/article/details/50787340
https://blog.csdn.net/weixin_34144450/article/details/91744086
參考鏈接:
https://www.anmolsarma.in/post/single-step-kernel/
https://www.binss.me/blog/how-to-debug-linux-kernel/
在docker 容器中運行:
創建容器:
docker run --rm -it --name build_upstream_qemu -d --cap-add NET_ADMIN --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --device=/dev/kvm:/dev/kvm --device=/dev/net/tun:/dev/net/tun -v /root/kernel:/root/kernel docker.io/centos:centos7 bash
