一直有个小教程没有写给大家,那就是使用GDB调试和-monitor调试,借这次代码整理,跟大家说一下怎么用。这里我使用到了objdump工具,gdb调试工具,这些工具都可以直接apt-get获得。当然我还用到了qemu的自带调试功能-monitor
这一节就是把system文件夹下lds文件的. = 0x8200;删掉,这是个历史遗留问题哈哈,刚开始写的时候加上了这行代码,导致了后来写程序众多不便,于是决定删掉他,这一节不感兴趣的可以直接跳过了。
一、修改system文件夹
1、打开lds文件,删掉 . = 0x8200; 这个历史遗留问题
2、打开system.s 进行如下修改,我们看看程序还能不能正常运行。
########################################start 32
############set GDT
movl gdt_base+0x8200, %eax################修改
############0# empty GDT
movl $0x00000000, 0(%eax)
movl $0x00000000, 4(%eax)
############1# code GDT
movl $0x8200ffff, 8(%eax)
movl $0x00409a00, 12(%eax)
############2# data GDT
movl $0x0000ffff, 16(%eax)
movl $0x00cf9200, 20(%eax)
############3# stack GDT
movl $0x00007a00, 24(%eax)
movl $0x00409600, 28(%eax)
#close interrupt
cli
lgdt gdt_size+0x8200 #configuration ################修改
保存,运行,哎呦?还不错,至少没有大错误,鼠标和我们的文字没有了,怎么回事?我也不知道,debug!
二、debug过程记录
其实我是不太想写这个的,因为想要把完整的debug过程记录下来还是需要费点时间,但是为了体现一下怎么用qemu和dbg调试系统内核,而且这部分的教程真是少之又少,所以还是狠狠心写下来吧。当然,具体DBG的所有命令解释,-monitor的所有命令解释,objdump的所有命令解释我就不赘述了,这些资料网上还是有很多的,我只是记录我的debug过程,给大家一个大体的概念。
1、首先我注意到了鼠标和字符同时没有了,显示这两个的函数是我在另外的.c文件中定义的,所以问题应该是出在寻址上。
2、进入系统代码文件夹,执行命令:objdump -D bin/system.elf ,反汇编.elf文件。找到SysMain函数,阅读汇编源码,看看问题是不是出在这里。似乎问题不大。没有找到问题。
151: e8 bb 03 00 00 call 511
156: 83 c4 10 add $0x10,%esp
159: 83 ec 04 sub $0x4,%esp
15c: 6a 00 push $0x0
15e: 6a 32 push $0x32
160: 6a 32 push $0x32
3、执行objdump -D -b binary -m i386 bin/system.bin 命令反汇编我们的raw binary文件,额,好长,而且没有条理,疯了,那也得看。在2、中我们看到SysMain在0xf6处,所以直奔0xf6,找到push 0xfffffff传参就找到我们函数调用位置了。传完参数,最后call了0x511,似乎也没有问题。完了,咋回事儿?
4、不能就这样放弃啊,该放大招了,打开loader文件夹Makefile,修改如下,意思是启动虚拟机的时候stop
qemu-system-i386 -fda $(BIN_DIR)/loader.img -boot a -gdb tcp::1234 -S -monitor stdio
# qemu-system-i386 -fda $(BIN_DIR)/loader.img -boot a -gdb tcp::1234 -monitor stdio
5、make,啊哈,虚拟机停住了,再打开一个控制台,启动gdb,执行target remote localhost:1234 连接虚拟机,成功
6、设置断点 break *0x8200 ,因为我们把system加载到了这个位置运行的嘛,所以启动system的一瞬间,我们停住它,然后在dbg中输入命令c,continue的简写,让它执行起来,然后中断在0x8200处,完成
7、停住了,这个时候我们启动反汇编模式,layout asm,ip上下的代码就清晰可见了,注意layout asm在启动保护模式分段之后就不能用了,因为它无视CS寄存器……
8、用鼠标往下翻汇编源码,找到ljmp,我们要在这里再停下来,break *0x8274 再插入一个断点,continue
9、又停住了,下边我们就不能再用layout asm了,所以我们先关掉它,ctrl+x 然后按a,关掉了。执行命令si,这个是执行一步汇编的意思,我们看它跳到哪里了。
10、我的跳到了0x79处,但是我们有段选择子,偏移是0x8200,所以现在执行的指令应该位于0x8279处,我们再打开一个发控制台,objdump -D -b binary -m i386 bin/system.bin,看着我们的反汇编代码。这个时候0x79就应该与我们的代码一一对应了,看看0x79处是什么,可能是混乱的,没关系
11、再在gdb中回车一下,看看执行到哪里了,我的是0x7d
7d: 8e d8 mov %eax,%ds
12、最上面的指令因为对齐的原因,翻译的不准确,就不看了,0x7d这才是我们的汇编代码
13、第一个call 到了0xf6,再打开一个控制台,objdump -D bin/system.elf,看看0xf6是什么,main函数,没问题
14、在gdb不停的回车执行si,直接回车表示执行上一条命令,现在进入了main函数依然没有发现问题
15、找到call 0x511,这条指令在0x151位置,因为我们知道0x511是输出字符的函数,我们这设置一个断点break *0x8351
16、continue,停了下来,输入si单步调试,看看到哪里去了,停住了!问题来了,为啥停住了??
17、再检查寄存器,擦,IDT为0x1140,又犯傻了,我们把偏移0x8200去掉后,数据段也不正确了
18、把system.s进行修改,改成如下,暂时可以用了。
############2# data GDT
movl $0x8200ffff, 16(%eax)
movl $0x00cf9200, 20(%eax)
19、背景变成这样子了,那是因为数据段修改后显存存放位置不正确,把所有显存写入的地址都减去0x8200,当然这也只是权宜之计,我们先这样做
20、好了,显存恢复了正常,中断还是不能使用,去InitIDT把 DefaultIntCallBack的-0x8200去掉,还是不能用,频繁重启,去查看一下IDT寄存器的基址,居然还是0x1140,写程序就是在慢慢犯傻的过程中改进,因为LIDT需要的是绝对地址嘛,也是佩服自己的智商,直接去void InitIDT()中,给idt+0x8200/8就好了嘛!注意是0x8200/8,因为指针每加1,是以自身大小为单位+1,我们要地址加0x8200,所以要除以自身单位。修改后的InitIDT()
for (i=1;i<0x30;i++)
{
idt[i].offset1 = (short)((int)(void*)(DefaultIntCallBack));
idt[i].selector = 0x0008;
idt[i].no_use = 0x8e00;
idt[i].offset2 = (short)(((int)(void*)(DefaultIntCallBack))>>16);
}
FunctionLidt(0x30*8-1,idt+0x8200/8);
这次make一下,成功!我们看到中断函数被调用了,虽然不断重启,原因是中断结束后需要iret,我们没有做这个工作(偷懒的方法是在中断最后加一个while(1)),所以我们是成功的!
21、这次调试就记录到这里了,当然如果自己会调试内核最好了,有问题尽管问!