文章目录[隐藏]
可以用Debug来跟踪一个程序的运行过程,这通常是必须要做的工作。我们写的程序在逻辑上不一定总是正确,对于简单的错误,仔细检查一下源程序就可以发现;而对于
隐藏较深的错误,就必须对程序的执行过程进行跟踪分析才容易发现。
下面以在前面的内容中生成的可执行文件行过程进行跟踪。1.exe为例,讲解如何用Debug对程序的执 现在我们知道,在DOS中运行一个程序的时候,是由command将程序从可执行文件中加载入内存,并使其得以执行。但是,这样我们不能逐条指令地看到程序的执行过程,因为command的程序加载,设置CS:IP指向程序的入口的操作是连续完成的,而当CS:IP一指向程序的入口,command就放弃了CPU的控制权,CPU立即开始运行程序,直至程序结束。
为了观察程序的运行过程,可以使用Debug n Debug可以将程序加载入内存,设置CS:IP指向程序的入口,但Debug并不放弃对CPU的控制,这样,我们就可以使用Debug的相关命令来单步执行程序,查看每一条指令的执行结果。
具体方法如图4.18所示。
在提示符后输入“debug l.exe",按Enter键,Debug将程序从l .exe中加载入内存,进行相关的初始化后设置CS:IP指向程序的入口。
接下来可以用R命令看一下各个寄存器的设置情况,如图4.19所示。
可以看到,Debug将程序从可执行文件加载入内存后,cx中存放的是程序的长度。l.exe中程序的机器码共有15个字节。则l .exe加载后,cx中的内容为000FH
现在程序已从l.exe中装入内存,接下来查看一下它的内容,可是我们查看哪里的内容呢?程序被装入内存的什么地方?我们如何得知?
这里,需要讲解一下在DOS系统中.EXE文件中的程序的加载过程。图4.20针对我们的问题,简要地展示了这个过程。
注意,有一步称为重定位的工作在图4.20中没有讲解,因为这个问题和操作系统的关系较大,我们不作讨论。
那么,我们的程序被装入内存的什么地方?我们如何得知?从图4.20中我们知道以下的信息。
(l)程序加载后,ds中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0;
(2)这个内存区的前256个字节中存放的是PSP, DOS用来和程序进行通信。从256字节处向后的空间存放的是程序。
所以,从ds中可以得到PSP的段地址SA, PSP的偏移地址为0,则物理地址为SA*16+0
因为PSP占256(1 OOH)字节,所以程序的物理地址是:
SA*16+0+256=SA*16+16* 16+0=(SA+16)*16+0
可用段地址和偏移地址表示为:SA+10H:0
现在,我们看一下图4.19中DS的值,DS=129E,则PSP的地址为129E:0,程序的地址为12AE:0(即129E+10:0)0图4.19中,CS=12AE, IP=0000, CS:IP指向程序的第一条指令。注意,源程序中的
指令是mov ax,0123H,在Debug中记为mov ax,0123,这是因为Debug默认所有数据都用
可以用U命令看一下其他指令,如图4.21所示。
可以看到,从12AE:0000-12AE:000E都是程序的机器码。
现在,我们可以开始跟踪了,用T命令单步执行程序中的每一条指令,并观察每条指令的执行结果,到了int 21,我们要用P
命令执行,如图4.22所示。
图4.22中,int 21执行后,显示出“Program terminated normally",返回到Debug中。表示程序正常结束。注意,要使用P命令执行int 21。这里不必考虑是为什么,只要记住这一点就可以了。
需要注意的是,在DOS中运行程序时,是command将程序加载入内存,所以程序运行结束后返回到command中,而在这里是Debug将程序加载入内存,所以程序运行结束后要返回到Debug中。
使用Q
命令退出Debug,将返回到command,因为Debug是由command加载运行的。在DOS,用“debug l.exe”运行Debug对l .exe进行跟踪时,程序加载的顺序是:command加载Debug Debug加载1 .exe。返回的顺序是:从l.exe中的程序返回到Debug,从Debug返回到command
编程、编译、连接、跟踪
(1)将下面的程序保存为t1 .asm文件,将其生成可执行文件t1 .exe
(2)用Debug跟踪tl.exe的执行过程,写出每一步执行后,相关寄存器中的内容和栈顶的内容。
(3) PSP的头两个字节是CD 20,用Debug加载t1 .exe,查看PSP的内容。