执行命令~/Applications/bochs-2.6.8/bin/bochs -f linux.bxrc,通过bochs启动linux。
内核未能正常启动,且由于编译完成前从未调试过,所以选择从头开始调试。
首先查看bootsect.bin运行情况。
执行命令~/Applications/bochs-2.6.8/bin/bochs -f linux.bxrc启动bochs,然后按下一次回车,出现提示“<bochs:1>”,输入b 0x7c00(bootsect.bin程序入口地址,在此打上断点)回车,然后再输入c回车,程序在断点处停住,如下所示:
(0) Breakpoint 1, 0x00007c00 in ?? ()
Next at t=14040247
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): mov ax, 0x07c0 ; b8c007
<bochs:4>
再输入n回车,一直回车直到程序不再返回。
程序停在read_track中死循环了,看了下代码,是以nasm语法修改bootsect.s时候忘记对track,sread,head三个变量的引用加上[ ]的原因,全部加上。
单步执行太慢,这里直接找到bootsect.s最后一条指令,jmp SETUPSEG:0,及jmp 0x9020:0,需要知道这条指令的地址,所以执行命令:objdump -D -b binary -m i8086 build/bootsect.bin > build/bootsect.disasm,再bootsect.disasm文件中搜索9020,找到该指令 93: ea 00 00 20 90 ljmp $0x9020,$0x0。
重新执行bochs,通过命令:b 0x90093,为该跳转指令打上断点,继续执行bochs,在断点处停住,到此为止bootsect.s程序结束。继续执行一条指令(bochs指令n),bochs显示:
(0) [0x000000090200] 9020:0000 (unk. ctxt): mov ax, 0x9000 ; b80090
正好对应setup.s第一条指令,由此程序在setup.s中运行。
同上方法,找到setup.s最后一条指令: jmp 8:0 ; jmp offset 0 of segment 8 (cs),打上断点,尝试运行到此断点,尝试成功,继续执行一条指令,输入bochs命令disasm/10,反汇编10条指令,回显如下:
<bochs:99> disasm/10
00000000: ( ): jnle .+69 ; 7f45
00000002: ( ): dec esp ; 4c
00000003: ( ): inc esi ; 46
00000004: ( ): add dword ptr ds:[ecx], eax ; 0101
00000006: ( ): add dword ptr ds:[eax], eax ; 0100
00000008: ( ): add byte ptr ds:[eax], al ; 0000
0000000a: ( ): add byte ptr ds:[eax], al ; 0000
0000000c: ( ): add byte ptr ds:[eax], al ; 0000
0000000e: ( ): add byte ptr ds:[eax], al ; 0000
00000010: ( ): add al, byte ptr ds:[eax] ; 0200
与head.s文件开始命令不符合,调查发现原因是system.bin文件不是纯二进制文件,而是elf格式,解决方案如下:
修改为
重新编译运行,程序成功进入到head.s中。
自此可以结合gdb进行内核调试,步骤如下:
1、拷贝一份linux.bxrc:cp linux.bxrc linux_gdb.bxrc
2、在linux_gdb.bxrc开头添加:gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
3、修改所有Makefile,为gcc和as编译标志加上-g选项,删除所有LDFLAGS的-s -x标志,重新编译
4、在终端下执行命令:/home/reborn/Applications/bochs-gdb/bin/bochs -f linux_gdb.bxrc,bohcs下直接回车,显示Waiting for gdb connection on port 1234
5、再开一个终端执行命令:gdb build/system.elf,进入gdb,输入命令:target remote localhost:1234,之后就可以正常的gdb调试了
接上述步骤,在gdb下为main打断点:b main,执行c命令,调试程序终止,内核程序有问题,打开system.disasm查看,发现符号链接的地址不是从0开始,找到原因,修改如下:
head.s中为startup_32加上global属性:.globl startup_32,idt,gdt,pg_dir,tmp_floppy_area
根目录Makefile的LDFLAGS修改为:LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32 -M
重新编译调试,程序成功停止在main处。
接上输入c继续执行程序,程序进入死循环,bochs窗口如下图:
在gdb终端下按下ctrl+c组合键,回到gdb命令行,输入bt指令,打印调用栈,如下所示:
(gdb) bt
#0 panic (s=0x1a2b8 "copy_page_tables called with wrong alignment") at panic.c:23
#1 0x0000af71 in copy_page_tables (from=4135, to=67108864, size=655360) at memory.c:159
#2 0x00008749 in copy_mem (nr=1, p=0xfff000) at fork.c:56
#3 0x00008a27 in copy_process (nr=1, ebp=167656, edi=4092, esi=917504, gs=23, none=31003, ebx=139264, ecx=21992, edx=139264, fs=23, es=23, ds=23, eip=25805, cs=15, eflags=1542, esp=167628, ss=23) at fork.c:115
#4 0x00007a30 in sys_fork () at system_call.s:217
#5 0x00000001 in startup_32 () at boot/head.s:18
按调用栈提示从上向下扫描代码,copy_mem中调用的get_base问题较大,看过get_base的代码后,打开system.disasm文件,搜索copy_mem,找到get_base宏代码的展开处,代码如下:
8613: 8a 30 mov (%eax),%dh
8615: 8a 11 mov (%ecx),%dl
8617: c1 e2 10 shl $0x10,%edx
861a: 66 8b 12 mov (%edx),%dx
发现edx寄存器又做输出又作输入,这里需要它只做输出,股修改如下:
sched.h中的_get_base宏
:"=d" (__base) => :"=&d" (__base)
重新编译调试运行,在schedule()中的while(1)处死循环,调试发现,当前共有两个进程,进程0(idle进程)和进程1(init进程),通过gdb查看进程1的任务数据(task_struct)不正确,最后定位到copy_process函数中的*p = *current一行,调试执行到下一行代码,对比结构体数据,发现数据并没有正确拷贝过来,打开system.disasm文件,搜索current全局变量的地址为0x23220,再搜索到copy_process的函数实现处,最终定位到汇编代码:
87dd: f3 a5 rep movsl %ds:(%esi),%es:(%edi)
结构体复制最终是通过该行进行,该条指令的复制和eflags标志寄存器的df标志位有关,查看该标志位,gdb指令i r eflags,发现该标志位被置位,而这里需要的是该标志位复位,所以做出如下修改:
在<*p = *current;>一行前加上<__asm__ __volatile__ ("cld\n\t");>代码,这么加可能会导致其他问题,暂时不理。
重新编译调试,本次从main入口单步执行到fork时发现fork是函数调用,并没有按老版编译器进行内联处理,而这里的fork必须内联,因为要保证复制进程时用户栈不被使用,所以不能有函数调用,这里进行如下修改:
main.c中其他fork,pause,sync,setup处做类似处理。
重新编译调试,如下所示:
执行到挂载文件系统了,现在需要一个文件系统(下载地址),下载linux-0.11-devel-060625.zip后解压,hdc-0.11-new.img是这里要用到的文件系统,把该文件拷贝到程序根目录,修改linux.bxrc,在最后加入:
ata0: enabled=1,ioaddr1=0x1f0,ioaddr2=0x3f0,irq=14
ata0-master: type=disk, path="hdc-0.11-new.img", mode=flat, cylinders=410, heads=16, spt=38
修改bootsect.s,把ROOT_DEV equ 0x306修改位ROOT_DEV equ 0x301。
重新编译运行,linux0.11至此成功启动,效果如下图: