2-3 课堂练习2.2:中断/异常的处理过程
第一关第二关忘了保存具体的过程只提供了txt中的答案,第三关实验过程基本相同,我会在第三关补上
注意:第一关要解压缩exp1里的内核文件,第二、三关要解压缩exp2中的,只需操作一次即可,我第三关解压缩只为示范
第1关除零异常分析
任务描述
分析版本 1.1 内核,回答下列问题: 1.在函数 main 的语句jiffies = jiffies/0;
所对应的汇编指令片段中,有一个 idiv 指令,此指令的地址是多少? 2.在该 idiv 指令执行之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 3.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少?
相关知识
为了完成本关任务,你需要掌握: 1.如何设置某版本的内核为分析对象; 2.如何开始用 gdb 调试内核; 3.查看 C 语句编译之后对应的汇编指令片段; 4.分析响应中断/异常时,CPU 做了哪些工作; 5.查看当前寄存器的状态; 6.查看当前栈顶的状态。
实验准备
本关卡使用版本 1.1 内核作为分析对象,内核文件存放在/data/workspace/myshixun/exp1
文件夹中,可以将其解压到linux-0.11-lab
下使用。
如何设置某版本的内核为分析对象
下面以版本1内核为例进行讲解。
首先解压版本1内核源码。使用cp
命令将/data/workspace/myshixun/exp1
中的1.1.tgz
复制到~/os/
目录下;
切换到~/os/linux-0.11-lab
目录下,将1.1.tgz
解压到当前目录下;
然后调整cur的指向。先使用rm -rf cur
将cur
删除,再使用ln
命令创建符号链接。
如何开始用 gdb 调试内核
先关闭bochs虚拟机,然后打开两个终端,其中一个终端在linux-0.11-lab
目录下运行rungdb
脚本,以启动 bochs 虚拟机并等待 gdb 连接;
在另一个终端里切换到目录~/os/linux-0.11-lab/
,然后启动脚本./mygdb
,这个命令会启动 gdb 并读入内核符号信息,同时会通过执行0.gdb
中的调试命令来连接到 bochs 虚拟机,并进而跟踪到 main 函数入口。
查看 C 语句编译之后对应的汇编指令片段
如果要查看某条 C 语句编译之后对应的汇编指令片段,可以在该 C 语句处设置断点,并跟踪到该断点,然后反汇编,所看到的当前指令之后的一段汇编指令就对应于该 C 语句。
例如,jiffies = jiffies/0;
是文件 main.c 的第 147 行,可以如下方式查看:
上面显示的汇编指令中,有一行前面有箭头标识,此即为当前指令,即马上将要执行的指令。
分析响应中断/异常时,CPU 做了哪些工作
-
切换到核心栈,并在其中保存中断现场。
-
转到中断处理程序去运行,并切换到核心态。
如下图所示:
上图显示了栈中中断现场的结构,(OLD SS:OLD ESP) 描述了用户栈顶的位置,(OLD CS:OLD EIP) 描述了恢复点的位置。
如何查看当前寄存器的状态
使用 gdb 调试命令 info registers 即可,如下所示:
也可以单独查看某一个寄存器:
如何查看当前栈顶的状态
可以使用命令 x 来查看:
上面显示了栈顶的 5 个长字,是某异常发生时的中断现场,其中存储的用户栈顶的位置是 0x17:0x2573c ,存储的恢复点的位置是 0xf:0x7967 。需要注意的是,x86 中栈是从高地址向低地址方向增长的,这里的栈顶位置是 0x1fa0c 。
编程要求
根据相关知识,回答问题:(将答案填写在/data/workspace/myshixun/第一关.txt
中) 1.在函数 main 的语句jiffies = jiffies/0;
所对应的汇编指令片段中,有一个 idiv 指令,此指令的地址是多少? 2.在该 idiv 指令执行之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 3.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少?
答案
第2关int指令分析
任务描述
分析版本1内核,回答下列问题: 1.函数 task1 的第一个int 0x81
指令执行之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 2.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少?
相关知识
为了完成本关任务,你需要掌握: 1.开始使用 gdb 调试内核; 2.跟踪到函数 task1 的第一个int 0x81
指令; 3.响应中断/异常时,CPU 做的工作; 4.查看当前寄存器的状态; 5.查看当前栈顶的状态。
环境准备
本关卡使用版本 1 内核,文件存放在/data/workspace/myshixun/exp2/
中,将版本 1 内核设置为分析对象。
开始使用 gdb 调试内核
先关闭bochs虚拟机,然后打开两个终端,其中一个终端在linux-0.11-lab
目录下运行rungdb
脚本,以启动 bochs 虚拟机并等待 gdb 连接;
在另一个终端里切换到目录~/os/linux-0.11-lab/
,然后启动脚本./mygdb
,这个命令会启动 gdb 并读入内核符号信息,同时会通过执行0.gdb
中的调试命令来连接到 bochs 虚拟机,并进而跟踪到 main 函数入口。
跟踪到函数 task1 的第一个int 0x81
指令
可以在函数task1入口处设置断点,跟踪到该处后再单步执行到第一个int 0x81
指令,如下所示:
上面显示的汇编指令中,有一行前面有箭头标识,此即为当前指令,即马上将要执行的指令。
响应中断/异常时,CPU 做的工作
切换到核心栈,并在其中保存中断现场。
转到中断处理程序去运行,并切换到核心态。 如下图所示:
上图显示了栈中中断现场的结构,(OLD SS:OLD ESP) 描述了用户栈顶的位置,(OLD CS:OLD EIP) 描述了恢复点的位置。
查看当前寄存器的状态
使用 gdb 调试命令 info registers 即可,如下所示:
也可以单独查看某一个寄存器:
查看当前栈顶的状态
可以使用命令 x 来查看:
上面显示了栈顶的 5 个长字,是某异常发生时的中断现场,其中存储的用户栈顶的位置是 0x17:0x2573c ,存储的恢复点的位置是 0xf:0x7967 。需要注意的是,x86 中栈是从高地址向低地址方向增长的,这里的栈顶位置是 0x1fa0c 。
答案
第3关iret指令分析
任务描述
本关任务回答问题: 1.函数 task1 的第一个int 0x81
指令执行时,会转到对应的中断/异常处理程序去运行,请问在该处理程序的 iret 指令执行之前,指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少? 2.使用 si 命令执行了该 iret 指令后,新指令位置和栈位置分别是多少?
相关知识
为了完成本关任务,你需要掌握: 1.跟踪到函数 task1 的第一个int 0x81
指令; 2.跟踪到一个中断/异常处理程序对应的 iret 指令; 3.iret 指令执行时,CPU 做的工作; 4.查看当前寄存器的状态; 5.查看当前栈顶的状态。
环境准备
本关卡使用版本 1 内核作为分析对象,请确认当前分析对象是否正确。
开启 gdb 调试,跟踪到 main 函数入口,方法与之前关卡一样。
跟踪到函数 task1 的第一个int 0x81
指令
可以在函数task1入口处设置断点,跟踪到该处后再单步执行到第一个int 0x81
指令,如下所示:
上面显示的汇编指令中,有一行前面有箭头标识,此即为当前指令,即马上将要执行的指令。
跟踪到一个中断/异常处理程序对应的 iret 指令
方法类似于中断/异常的恢复点分析。通过反汇编找到 iret 指令,然后跟踪过去。
中断/异常的恢复点分析
当一个中断/异常被 gdb 捕获时,通常正在运行中断处理程序,这时可以继续跟踪,直至回到恢复点指令。以时钟中断为例,为了从函数 do_timer 跟踪到恢复点,可以如下操作:
由上图可见时钟中断处理程序的入口是 timer_interrupt 函数。 跟踪到当前函数(do_timer)执行完毕返回到 timer_interrupt 函数;
跟踪到 timer_interrupt 函数(用汇编语言写的)末尾的 iret 指令;
执行该 iret 指令,返回到恢复点;
通过反汇编命令:disas ,分析恢复点指令的地址。
iret 指令执行时,CPU 做的工作
从中断现场恢复使用用户栈,回到以前的 CPU 状态(一般是用户态),回到恢复点继续运行。是中断发生时 CPU 所做动作的逆过程。此时,核心栈里存放着中断现场,如下图所示:
上图显示了栈中中断现场的结构,(OLD SS:OLD ESP) 描述了用户栈顶的位置,(OLD CS:OLD EIP) 描述了恢复点的位置。
如何查看当前寄存器的状态
使用 gdb 调试命令 info registers 即可,如下所示:
也可以单独查看某一个寄存器:
如何查看当前栈顶的状态
可以使用命令 x 来查看:
上面显示了栈顶的 5 个长字,是某异常发生时的中断现场,其中存储的用户栈顶的位置是 0x17:0x2573c ,存储的恢复点的位置是 0xf:0x7967 。需要注意的是,x86 中栈是从高地址向低地址方向增长的,这里的栈顶位置是 0x1fa0c 。
编程要求
根据相关知识,回答问题:(将答案填写在/data/workspace/myshixun/第三关.txt
中) 1.函数 task1 的第一个int 0x81
指令执行时,会转到对应的中断/异常处理程序去运行,请问在该处理程序的 iret 指令执行之前,指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 2.此时栈中保存的恢复点位置和用户栈位置分别是多少? 3.使用 si 命令执行了该 iret 指令后,新指令位置和栈位置分别是多少?
实验过程及答案
打开一个新的终端
2-4 课后作业2.2:中断/异常的处理过程
第1关陷入指令分析
任务描述
分析版本1内核,回答下列问题: 1.在 0 号进程执行 fork 系统调用中的陷入指令(int 0x80)之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 2.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少? 3.此时栈中保存的恢复点位置和用户栈位置分别是多少?
相关知识
为了完成本关任务,你需要掌握: 1.跟踪到系统调用的陷入指令(int 0x80)执行之前; 2.响应中断/异常时,CPU 做了哪些工作; 3.查看当前寄存器的状态; 4.查看当前栈顶的状态
环境准备
本关卡使用版本 1 内核进行分析,设置方式与以前相同。
启用 gdb 进行调试,跟踪到 main 函数入口,方法与前面的实训相同。
跟踪到系统调用的陷入指令(int 0x80)执行之前
一般而言,可以在该系统调用处设置断点,跟踪到该断点,然后使用 si 命令单步执行到陷入指令。但是,对于 main 函数里的 fork 系统调用,不能这样操作(因为 gdb 有时断点设置不够准),可以先跟踪到前一行的 move_to_user_mode 语句,然后使用 n 单步执行,即可到达 fork 系统调用的开始,此时再反汇编,找到陷入指令:
然后再通过 si 单步执行,跟踪到陷入指令 (int 0x80) 。
响应中断/异常时,CPU 做了哪些工作
从中断现场恢复使用用户栈,回到以前的 CPU 状态(一般是用户态),回到恢复点继续运行。是中断发生时 CPU 所做动作的逆过程。此时,核心栈里存放着中断现场,如下图所示:
上图显示了栈中中断现场的结构,(OLD SS:OLD ESP) 描述了用户栈顶的位置,(OLD CS:OLD EIP) 描述了恢复点的位置。
如何查看当前寄存器的状态
使用 gdb 调试命令 info registers 即可,如下所示:
也可以单独查看某一个寄存器:
如何查看当前栈顶的状态
可以使用命令 x 来查看:
上面显示了栈顶的 5 个长字,是某异常发生时的中断现场,其中存储的用户栈顶的位置是 0x17:0x2573c ,存储的恢复点的位置是 0xf:0x7967 。需要注意的是,x86 中栈是从高地址向低地址方向增长的,这里的栈顶位置是 0x1fa0c 。
编程要求
根据相关知识,回答问题:(将答案填写在/data/workspace/myshixun/第四关.txt
中) 1.在 0 号进程执行 fork 系统调用中的陷入指令(int 0x80)之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 2.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少?
实验过程及答案
本关卡使用版本 1 内核进行分析,设置方式与以前相同。
启用 gdb 进行调试,跟踪到 main 函数入口,方法与前面的实训相同。
ls /data/workspace/myshixun/exp2 cp /data/workspace/myshixun/exp2/1.tgz ~/os
cd os/linux-0.11-lab tar -zxvf ../1.tgz 1
rm -rf cur ln -s 1.1 cur ls
cd 1/linux make
cd ../.. ./rungdb
另开一个终端
cd os/linux-0.11-lab ./mygdb
b 145 c n
x/5i $eip si x/5i $eip
第一题
info reg
第二题
si info reg
第三题
x/5wx $esp