9.9 根据位移进行转移的意义
目前转移命令有一下几种:
jmp short 标号
jmp near ptr 标号
jcxz 标号
loop 标号
等几种汇编指令,它们对IP的修改是根据转移目的地之和转移起始地址之间的位移来进行的。在他们对应的机器码中不包含转移的目的地址,而包含的是到目的地之的位移。
这种设计,方便了程序段在内存中的浮动装配。
例如:
汇编指令 | 机器代码 |
---|---|
mov cx, 6 |
B9 06 00 |
mov ax, 10h |
B8 10 00 |
s:add ax, ax |
01 C0 |
loop s |
E2 FC |
无论s处的指令的实际地址是多少,loop指令的转移位移是不变的。
9.10 编译器对转移位移超界的检测
如果在源程序中出现了转移范围超界的问题,在编译的时候,编译器将报错。
比如,下面的程序将引起编译错误:
assume cs:code
code segment
start:jmp short s
db 128 dup (0)
s:mov ax, 0ffffh
code ends
end start
jmp shorts s
的转移范围是-128~127,IP最多向后移动127个字节。
注意,
jmp 2000:0100
等转移指令,是Debug中使用的汇编指令,汇编编译器并不认识。如果在源程序中使用,编译时也会报错。
实验8 分析程序
分析下面代码,思考是否可以正确返回。
assume cs:codesg
codesg segment
mov ax, 4c00h
int 21h
start: mov ax, 0
s: nop
nop
mov di, offset s
mov si, offset s2
mov ax, cs:[si]
mov cs:[di], ax
s0: jmp short s
s1: mov ax, 0
int 21h
mov ax, 0
s2: jmp short s1
nop
codesg ends
end start
首先将程序进行编译,并使用Debug的T命令对代码进行逐条跟踪。
代码进行跟踪以后,发现当运行到s0处执行第一次跳转到s,由于之前替换了s和s2的IP地址,所以这里直接跳转到s2。但是运行到s2执行jmp之前,本应该跳转到s1,却直接跳转到了IP=0000,导致程序重新回到代码段最一开始,并终止了程序。
为了分析这个问题,我们首先利用Debug的U命令查看机器码,
可以看出第一次运行到0016时即s0处执行第一次jmp跳转时,(IP)=(IP)+位移量,即0008h = 0018h + (-16),-16对应的补码为F0,所以此处机器码为EBF0。
而当第二次跳转之前,程序运行到了0020,这时的机器码为EBF6,F6为(-10)的补码。(PS:这里的计算为 20h - 10 = 32 - 10 = 22 = 18h 为s0的位置),而且值得注意的是,之前执机器码EBF0时,IP地址又增加了两位,所以这时的地址为 0008h + 0002h = 000ah,000ah + (-10) = 0000h,所以此时的IP为0000h,段地址CS:0,所以直接指向代码段最开头,执行程序终止。