调试在64位Debian上编译好的Linux 0.11(二)
来源:互联网 发布:阿里云系统升级 编辑:程序博客网 时间:2024/04/29 10:59
调试在64位Debian上编译好的Linux 0.11(二)
本机环境:
- SMP Debian 3.11.6-1 (2013-10-27) x86_64 GNU/Linux
- gcc (Debian 4.8.2-5) 4.8.2
- GNU assembler (GNU Binutils for Debian) 2.23.90.20131116
- GNU ld (GNU Binutils for Debian) 2.23.90.20131116
- Bochs x86 Emulator 2.6
编译时的一些设定:
./MakefileRAMDISK = -DRAMDISK=512 #设定虚拟盘大小为512KBROOT_DEV=FLOPPYLD =ld -m elf_i386 -Ttext 0 -e startup_32
2.问题
2.在显示出“Loading system”信息后就停止运行(续上篇文章)
类似的,main()
中drive_info=DRIVE_INFO
一行对应的汇编代码也使用了rep
,而此时DF置位。在main()
函数执行前使DF置位的命令只可能在boot/下的文件中出现。使用egrep寻找知:
~/Src/LinuxKernel/0.11/linux-0.11-deb$ egrep -nr '\<std\>' boot/
boot/head.s:209: std
在213行加个cld
就好。
3.又出现类似问题2.1的情况
使用gdb单步调试,发现在转入进程1并执行init()
->setup()
->bread()
调用时,触发page fault,且在do_no_page()
->get_free_page()
时跳转到一个全零的内存区域。检查汇编代码发现get_free_page()
的地址并没有错误,但是其代码应该在的地方被清零了。
初步猜测是
- bootsect加载system模块时出错;
- setup.s移动system模块时出错;
get_free_page()
所在的内存区域被违规擦写了。
重新调试程序,刚进入main()
函数时立即查看get_free_page()
所在内存位置,发现其代码就在该处,因此可以排除前两条猜测。设断点于bread()
函数,单步调试发现从bread()
->ll_rw_block()
->make_request()
->add_request()
->do_hd_request()
->reset_hd()
->reset_hd()
->hd_out()
的整个过程都未出现问题。这时时钟中断被触发(注意这是第一次触发时钟中断),继续step into发现在time_interrupt()
->do_timer()
的过程中,do_timer()
调用了一个next_timer
指向的函数,然而next_timer
按理来说应该被初始化为NULL。查看其值发现next_timer = 0x113
。
再次从头开始调试,发现在setup.s
中把system模块从0x10000移到0x00000之后,next_timer
就已经是非NULL值了。进一步检查发现sched.c中定义的所有全局static变量都被错误初始化了(我不知道为什么,如果有人知道的话请一定留言告诉我,谢谢!)。
修改方法:在sched_init()
函数最后将上述全局static变量手动初始化:
/* * all the static variable with global scope defined in this * file are initialized incorrectly. I don't know why :-( */memset(timer_list, 0, sizeof(timer_list));if (next_timer != NULL) next_timer = NULL;for (i=0; i<4; i++) { wait_motor[i] = NULL; mon_timer[i] = moff_timer[i] = 0;}
之后可以正确进入mount_root()
函数并在屏幕上打印
Insert root floppy and press ENTER
的提示。
4.插入系统盘并回车后,系统假死
从mount_root()
函数开始调试,仔细检查从
mount_root()
->read_super()
->check_disk_change()
->floppy_change()
->floppy_on()
->sleep_on()
->...
的整个流程,均未发现问题。然而在mount_root()
调用iget()
以获取第一个空闲inode时,发现inode_table[]
也没有被正确地初始化为全零。意识到可能还有更多的全局变量/静态全局变量的初始化存在问题,我检查了内核中定义的所有全局变量/静态全局变量,发现还有last_task_used_math
、startup_time
、jiffies
、last_pid
也未正确初始化。手动将它们重置即可,或是干脆写个函数来一次性解决这个问题。(见这里)
5.init()函数第一次调用open()时出错,无法打开“/dev/tty0”
修复后可以成功装载根文件系统,接下来执行到open处再次出错。单步调试进入open_namei()
->dir_namei()
->get_dir()
->find_entry()
后发现,传给find_entry()
的参数name在调用match()
后值发生变化,然而match()
函数不应该修改name的值:
static int match(int len,const char * name,struct dir_entry * de){ register int same; if (!de || !de->inode || len > NAME_LEN) return 0; if (len < NAME_LEN && de->name[len]) return 0; __asm__("cld\n\t" "fs ; repe ; cmpsb\n\t" "setz %%al" :"=a" (same) :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)); return same; }
为name增加一个watch point可注意到,在调用match()
前,name被从edx寄存器移到esi寄存器中,而match()
函数在未保存esi的情况下使用且改变了esi寄存器的值(通过rep指令),且在match()
返回后,find_entry()
依然通过esi中的地址来取name指向的字符串,这就造成每次调用match()
后,name的值都不一样了。
通过反汇编find_entry()
的目标代码发现,其调用match()
时没有使用call指令,而是直接把match()
的汇编代码嵌入到自己的汇编代码中去了,然后因其指定输入寄存器esi中的内容为name,所以之前有一个把name从edx移入esi的动作。gcc默认用来传递参数的寄存器是eax、edx、ecx,因此gcc会自动考虑是否需要“保存/恢复”这三个寄存器的指令。而match()
使用内联汇编时操作的esi、edi作为手动指定的寄存器,需要自己增加保存/恢复的指令。
修改方法:在match()
的内联汇编的前后使用push/pop保存并恢复esi寄存器的值。
注意到内核代码中存在大量内联汇编,它们都可能出现类似问题,因此应给所有使用esi/edi作为输入寄存器的内联汇编代码前后加上一对pushl/popl。
3.结语
做完上述修改后内核就可以在bochs中运行了。我把所有对内核代码的修改生成了一个patch文件,放在github上。由于水平所限,有一些问题的原理我也不清楚,解决的时候带有一点猜的性质,如果大家发现文中存在什么错误,还请一定指出。
- 调试在64位Debian上编译好的Linux 0.11(二)
- 调试在64位Debian上编译好的Linux 0.11(一)
- Tomcat 在linux 服务器上的部署(32位debian)
- 交叉编译在x86上调试好的qt程序
- 64位Debian Sid下编译Linux 0.11内核
- 已经编译好的64位GCC,如何在Redhat上运行
- 在64位linux上编译32位程序
- 怎么在64位linux机器上编译32位的openssl库
- 在debian上调试OCI
- 在64位linux上编译曾经在32位linux编译过,含有32位目标文件的错误的解决办法
- 关于在32位linux(debian)虚拟机上搭建iphone toolchain实战
- 汇编语言DosBox下在64位上运行实现编译(masm)与调试即debug命令。
- 在64位linux上编译32位程序 for i386 intel
- 在64位Windows上编译hsdis
- ORACLE9404 64位 在Redhat linux AS 4.6 (64位)上的安装
- 解决在WIN7(64位)上Mex无法编译的问题
- 编译好的JavaWeb工程在Linux环境下的Tomcat上进行部署
- 在64位的CentOS上编译32位的程序
- 第十四周项目四拓展2-选择排序
- 基于wax的lua IOS插件开发
- acm - cry for no one
- MacOS开启php
- 0欧电阻的作用
- 调试在64位Debian上编译好的Linux 0.11(二)
- tomcat默认访问项目
- windows消息机制
- GE纳滤膜制取中低压锅炉软化水的工艺说明
- crack the code interview 2.4
- 设计模式实战应用之二:观察者模式
- 微策略面试题:在旋转后的数组中查找元素(二分查找)
- 简单定时器Demo
- LA 3485Bridge