作者:张华 发表于: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 "[email protected]" | 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
参数解释如下:
- 其中-s为开启GDB的调试端口1234,而-S则表示运行QEMU时冻结待GDB执行(c)ontinue操作。
- console=ttyS0" -nographic表示不开新的图形化窗口,直接使用敲命令的bash窗口
- -append "root=/dev/sda init=sbin/init应该与initrd文件里的init脚本一致。
使用gdb调试内核
qemu的-s参数会默认在1234端口开启gdbserver。
[email protected]:~$ sudo netstat -anp |grep 1234
tcp 0 0 0.0.0.0:1234 0.0.0.0:* LISTEN 24309/qemu-system-x
[email protected]:~$ /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