=================================
本文系本站原创,欢迎转载!
转载请注明出处:http://blog.csdn.net/gdt_a20
=================================
概要:看kernel代码的时候,变量多了,就记不清楚了,尤其是读内存部分代码的时候,传来传去,常常不知道
改成什么样子了,内存布局到底变成什么了,也许是我记忆力太不行了,利用qemu可以在线调试内核,
如同你用gdb调试app一样,断点,打印变量值等等,qemu算是个模拟器吧,说虚拟机也可以,虽然bug很多,但是还不失为一个好工具,如果对qemu陌生,那android的模拟器相信大家都很熟悉了吧,那个也是基于开源的qemu修改的,好了让我们赶快开始调试的旅程吧.
一、环境
#$cat /etc/issue
Ubuntu 11.10 \n \l
二、gdb准备
#$$ gdb --version
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>.
我的gdb默认会有问题,当调试开启后gdb vmlinux会出现:
Remote 'g' packet reply is too long: 00000000000000000020e30100000000000000000000
000060eecf81ffffffff000000010000000048eecf81fffff
...
为了解决这个问题,我们需要重新编译一个gdb,
#$wget ftp://sourceware.org/pub/gdb/releases/gdb-7.4.tar.gz
#$tar xzvf gdb-7.4.tar.gz
#$cd gdb-7.4
我们需要针对上面的问题做如下修改:
在gdb/remote.c中
注释掉:
[cpp] view plain copy print ?
- 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)
error (_(“Remote ‘g’ packet reply is too long: %s”), rs->buf);
并在后面添加:
[cpp] view plain copy print ?
- 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;
- }
- }
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前我们还要一个库,texinfo,否则gdb编译过程中会出现
makeinfo错误:missing makeinfo --split-size=5000000 ...
#$sudo apt-get install texinfo
好了现在可以configure了,
#$configure --prefix=/opt/gdb_7_4 //临时装在这里吧
#$mkdir /opt/gdb_7_4
#$make && make install
三、kernel准备
我们还需要一个bzImage,也就是个胖内核,qemu只钟爱肥的,由于我们目的只在调试内核,所以别的
可以不准备,有兴趣的可以装个整套的,做个虚拟机看看,
#$wget ftp://ftp.kernel.org/pub/linux/kernel/v3.x/linux-3.2.tar.gz
#$tar xzvf linux-3.2.tar.gz
#$cd linux-3.2/
#$make defconfig
暂且用个x86做试验,我们要用gdb连接,需要把调试信息加入内核,
否则gdb时候会出现:
no debugging symbols found
这里就别去修改Makefile添加-g了,直接在内核配置选项里打开就可以.
#$make menuconfig
在kernel hacking中选中compile the kernel with debug info,
这样编译出来的kernel才是真正的肥
#$make bzImage
四、准备qemu.
好了万事具备可以开始qemu了,
我们首先添加一个库,不然qemu运行会卡在:
VNC server running on `127.0.0.1:5900'
#$sudo apt-get install libsdl1.2-dev
#$wget http://wiki.qemu.org/download/qemu-1.0.tar.gz
#$tar xzvf qemu-1.0.tar.gz
#$sudo mkdir /opt/qemu_bin
#$cd qemu-1.0
#$./configure --prefix=/opt/qemu_bin --target-list=x86_64-softmmu
我的系统是64的,这里有i386-softmmu,arm平台等等,可以有选择
#$make && make install
这样就可以了,1.0的版本我这样安装使用后没有遇到单步无法运行的问题,
但这个问题在以前版本确实存在,当初单步会直接跳到中断里面,
如果有遇到这个问题的版本可以参考下面的修改( http://blog.chinaunix.net/space.php?uid=10285909&do=blog&id=2964933):
[cpp] view plain copy print ?
- 以qemu-0.9.0为例:
- 1 target-i386\cpu.h CPUX86State 结构加入:
- int intr_cnt;
- int intr_scnt;
- 2 vl.c main函数中加入初始化:
- first_cpu->intr_cnt = 0;
- 3 target-i386\Helper.c 加入中断深度的计算
- do_interrupt_protected 函数中加入
- env->intr_cnt++;
- helper_iret_protected 函数中加入
- env->intr_cnt--;
- 4 Exec.c cpu_single_step执行单步时保存当前的中断深度
- env->intr_scnt = env->intr_cnt;
- 5 target-i386\Translate.c DisasContext结构加入:
- CPUState *env;
- 6 target-i386\Translate.c gen_eob函数加入插入debug时的判断
- if (s->singlestep_enabled) {
- if (s->env->intr_scnt==s->env->intr_cnt){
- gen_op_debug();
- s->is_jmp = 3;
- return;
- }
- }
- 在函数gen_intermediate_code_internal中加入:
- dc->env = env;
以qemu-0.9.0为例:
1 target-i386\cpu.h CPUX86State 结构加入:
int intr_cnt; //当前的中断深度
int intr_scnt; //保存的中断深度
2 vl.c main函数中加入初始化:
first_cpu->intr_cnt = 0;
3 target-i386\Helper.c 加入中断深度的计算
do_interrupt_protected 函数中加入
env->intr_cnt++;
helper_iret_protected 函数中加入
env->intr_cnt--;
4 Exec.c cpu_single_step执行单步时保存当前的中断深度
env->intr_scnt = env->intr_cnt;
5 target-i386\Translate.c DisasContext结构加入:
CPUState *env;
6 target-i386\Translate.c gen_eob函数加入插入debug时的判断
if (s->singlestep_enabled) {
if (s->env->intr_scnt==s->env->intr_cnt){//加了这个if判断
gen_op_debug();
s->is_jmp = 3;
return;
}
}
在函数gen_intermediate_code_internal中加入:
dc->env = env;
五、运行调试
#$/opt/qemu_bin/bin/qemu-system-i386 -S -kernel arch/x86/boot/bzImage -m 1024
ctrl+alt+1 与
ctrl+alt+2可以切换,前者是屏幕输出,后者是qemu控制台
运行起来后是黑屏,我们要切换到控制台,用鼠标点击窗口,然后ctrl+alt+2,
如下图所示输入,然后回车,ctrl+alt+1切换回来,ctrl+alt切出鼠标.
在另一个终端中,
#$cd linux-3.2/
#$/opt/gdb_7.4/bin/gdb vmlinux
(gdb) target remote localhost:1234
好了可以正式开始你的调试之旅了,可以先
#b start_kernel
#continue
#list
#n
等等.
注意:
不要把vmlinux从源码目录考出来运行,因为gdb list是需要当前上下文列出代码执行到哪里的.
另外-m 参数用于设置内存大小,这个在调试内存时候尤其有用,你可以清晰的打印除normal内存
高端内存布局等等^.^!
好了差不多就这么多吧,最后附上几幅截图:
===================================
================================
===================================
=================================
=================================
Thanks