zoukankan      html  css  js  c++  java
  • 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
    
  • 相关阅读:
    安装elasticsearch 和 kibana
    docker 安装 nignx 并将对应配置文件映射
    linux 操作笔记
    docker 一些常用的命令手记
    c# 异步 多线程
    从零开始在.net中使用Nhibernate对数据库进行操作详细步骤(20121128)
    NHibernate框架的HQL增删改查
    2012年11月19日 利用wifiap简单实现Wifi无线Web认证
    逻辑结构和物理结构
    日志
  • 原文地址:https://www.cnblogs.com/powerrailgun/p/12102447.html
Copyright © 2011-2022 走看看