IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]搭建基于qemu + eclipse的kernel调试环境(by quqi99)

    quqi99发表于 2016-02-06 22:18:35
    love 0

    作者:张华  发表于:2016-02-06版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明

    ( http://blog.csdn.net/quqi99 )

    使用qemu结合eclipse或者DDD等gdb的图形前端,跟踪协议栈或者文件系统内存管理等都会非常方便。就是与硬件驱动相关的跟踪可能差点。

    编译内核

    下载Linux Kernel源码,并编译生成压缩的kernel镜像(/bak/linux/linux-2.6/arch/x86_64/boot/bzImage)与用于gdb的非压缩的kernel ELF文件(/bak/linux/linux-2.6/vmlinux)。  
     cd /bak/linux/ && git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git   
    如何编译内核参考:编译linux kernel及制作initrd ( by quqi99 )   
    sudo apt-get install libncurses5-dev  
    make menuconfig   make -j 8 bzImage

    制作initrd

    制作initrd, 使用initrd时的kernel要使用CONFIG_BLK_DEV_INITRD=y编译。参见我的博客:http://blog.csdn.net/quqi99/article/details/11860241   
    sudo apt-get install build-essential initramfs-tools   
    sudo make modules_install            #将生成/lib/modules/4.5.0-rc2+   
    mkinitramfs -o initrd.img -v 4.5.0-rc2+

    使用busybox制作initrd

    mkdir -p /bak/linux/initramfs/{bin,sbin,etc,proc,sys,newroot}cd /bak/linuxtouch initramfs/etc/mdev.confwget http://jootamam.net/initramfs-files/busybox-1.10.1-static.bz2 -O - | bunzip2 > initramfs/bin/busyboxchmod +x initramfs/bin/busyboxtouch initramfs/init

    chmod +x initramfs/init

    initramfs/init文件如下:

    #!/bin/sh
    #Mount things needed by this script
    mount -t proc proc /proc
    mount -t sysfs sysfs /sys
    #Disable kernel messages from popping onto the screen
    echo 0 > /proc/sys/kernel/printk
    #Clear the screen
    clear
    #Create all the symlinks to /bin/busybox
    busybox --install -s
    #Create device nodes
    mknod /dev/null c 1 3
    mknod /dev/tty c 5 0
    mdev -s
    #Function for parsing command line options with "=" in them
    # get_opt("init=/sbin/init") will return "/sbin/init"
    get_opt() {
    	echo "$@" | cut -d "=" -f 2
    }
    #Defaults
    init="/sbin/init"
    root="/dev/hda1"
    #Process command line options
    for i in $(cat /proc/cmdline); do
    	case $i in
    		root\=*)
    			root=$(get_opt $i)
    			;;
    		init\=*)
    			init=$(get_opt $i)
    			;;
    	esac
    done
    #Mount the root device
    mount "${root}" /newroot
    #Check if $init exists and is executable
    if [[ -x "/newroot/${init}" ]] ; then
    	#Unmount all other mounts so that the ram used by
    	#the initramfs can be cleared after switch_root
    	umount /sys /proc
    	
    	#Switch to the new root and execute init
    	exec switch_root /newroot "${init}"
    fi
    #This will only be run if the exec above failed
    echo "Failed to switch_root, dropping to a shell"
    exec sh

    cd initramfs
    find . | cpio -H newc -o > ../initramfs.cpio
    cd ..
    cat initramfs.cpio | gzip > initramfs.igz

    但上述busybox-1.10.1-static.bz2似乎没有ext2模块不能识别qemu的-hda参数传进去ext2格式的硬盘,所以最后改成从busybox-1.24.0的源码编译。
    wget https://busybox.net/downloads/busybox-1.24.0.tar.bz2
    make menuconfig 
       CONFIG_MKFS_EXT2=y
              Busybox Settings  --->  
               Build Options  ---> 
                   [*] Build BusyBox as a static binary (no shared libs) //静态方式编译
       make & make install
       cp -avR /bak/linux/busybox-1.24.0/_install/* /bak/linux/initramfs/


    qemu加载内核

    wget http://www.nongnu.org/qemu/linux-0.2.img.bz2
    sudo qemu-system-x86_64 -hda /bak/images/linux-0.2.img -hdb /bak/linux/disk.img -kernel /bak/linux/linux-2.6/arch/x86_64/boot/bzImage -initrd /bak/linux/initramfs.igz -append "root=/dev/sda init=sbin/init console=ttyS0" -nographic -smp 1,cores=1 -S -s 
    参数解释如下:
    1.    其中-s为开启GDB的调试端口1234,而-S则表示运行QEMU时冻结待GDB执行(c)ontinue操作。
    2.    console=ttyS0" -nographic表示不开新的图形化窗口,直接使用敲命令的bash窗口
    3.    -append "root=/dev/sda init=sbin/init应该与initrd文件里的init脚本一致。


    使用gdb调试内核

       qemu的-s参数会默认在1234端口开启gdbserver。
       hua@node1:~$ sudo netstat -anp |grep 1234
       tcp    0     0 0.0.0.0:1234        0.0.0.0:*          LISTEN      24309/qemu-system-x
       hua@node1:~$ /bak/java/gdb/bin/gdb /bak/linux/linux-2.6/vmlinux 
        ...
       (gdb) target remote localhost:1234
       Remote debugging using localhost:1234
       0x0000000000000000 in irq_stack_union ()
       (gdb) b start_kernel
       Breakpoint 1 at 0xffffffff81d66b09: file init/main.c, line 498.
       (gdb) info registers
       (gdb) bt
       (gdb) c
       (gdb) list
       (gdb) set architecture
       Requires an argument. Valid arguments are i386, i386:x86-64, i386:x64-32, i8086, i386:intel,i386:x86-64:intel, i386:x64-32:intel, auto.

    使用eclipse调试内核

    1, Linux源码size太大,设置workspace全局禁止使用eclipse去给代码做自动build。索引可以仍然交由eclipse来做,这样方便在eclipse中进行搜索及代码导航。
       - Preferences -> Generl -> Workspace -> Build automatically (Disable)
    2, 将Kernel源码导入为eclipse工程, toolChain选为Linux GCC.
          Import -> C/C++ -> Existing Code as Makefile Project
    3, 创建一个debug启动器(Debug configurations -> C/C++ Remote Application)
       选择GDB(DSF) Manual Remote Debugging Launcher
       Main TAB -> -C/C++ Application指向实际uncompress kernel: /bak/linux/linux-2.6/vmlinux
       Main TAB -> -Disable auto build
       Debugger TAB -> Stop on startup at 'start_kernel'
       Debugger TAB -> connection -> Host Name or IP Address -> = localhost
       Debugger TAB -> connection -> Port number = 1234


    编译gdb解决错误“Remote 'g' packet reply is too long”

       cd /bak/java && wget http://ftp.gnu.org/gnu/gdb/gdb-7.7.tar.gz
       修改gdb/remote.c文件,在process_g_packet函数里,将如下代码:
       if (buf_len > 2 * rsa->sizeof_g_packet)
         error (_("Remote 'g' packet reply is too long: %s"), 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;
      }
    }

      ./configure --prefix=/bak/java/gdb && make && make install
       接下来重新配置下Eclipse,点击菜单“Run”->“Debug Configurations…”,在弹出的对话框中,切换到“Debugger”下的“Main”页,修改“GDB debugger:”为刚编译出来的GDB(/bak/java/gdb/bin/gdb),而不是默认的gdb
     

    参考

    [1] http://blog.chinaunix.net/uid-26009923-id-3825761.html
    [2] http://mgalgs.github.io/2012/03/23/how-to-build-a-custom-linux-kernel-for-qemu.html
    [3] http://www.kgdb.info/kgdb/use_kgdb/using_kgdb_base_qemu/


    附录1, 使用cscope创建索引

    1, 创建cscope.files
    LNX=/bak/linux/linux-2.6
    cd /
    find  $LNX                                                                \
    -path "$LNX/arch/*" ! -path "$LNX/arch/i386*" -prune -o               \
    -path "$LNX/include/asm-*" ! -path "$LNX/include/asm-i386*" -prune -o \
    -path "$LNX/tmp*" -prune -o                                           \
    -path "$LNX/Documentation*" -prune -o                                 \
    -path "$LNX/scripts*" -prune -o                                       \
    -path "$LNX/drivers*" -prune -o                                       \
    -name "*.[chxsS]" -print >/bak/linux/linux-2.6/cscope/cscope.files
    2, 创建索引数据库
    cd /bak/linux/linux-2.6/cscope
    3, 使用索引数据库
    cscope -d






沪ICP备19023445号-2号
友情链接