Linux Kernel调试环境搭建(正式版)

简单记录下可行的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
posted @ 2019-12-26 15:46  Linux-inside  阅读(1498)  评论(0编辑  收藏  举报