|
内存或者寄存器中表示带符号整数经常是难以识别的,除非知道期望什么。有时候 GNU 调试器能够有所帮助,但是有时候甚至它会混淆,如下例所示:
.section .data
data:
.int -45
.section .text
.global _start
_start:
nop
movl $-345, %ecx
movw $0xffb1, %dx
movl data, %ebx
movl $1, %eax
int $0x80 在 gdb 中走过 movl data, %ebx 后,用 info reg 命令查看寄存器得到:ecx 0xfffffea7 -345
edx 0xffb1 65457
ebx 0xffffffd3 -45 上面,EBX 和 ECX 中的值如期望一样,看到了负值,但是在 EDX 寄存器中,我们看到的是正数(原本打算填个正数进去)。这是因为,调试器假设整个 EDX 寄存器包含的是一个双字带符号整数(32位)。但我们在把 0xffb1 送进 EDX 时,0xffb1 是一个单字 (0xffb1),所以 GDB 解释出的信息“错误”了。但是,寄存器中的数据仍然是正确的(0xffb1),之所以被看成是正数,那是因为高一个字被 0 扩展了!所以,像在这样左右为难的情况下,我们需要对整数做正确的扩展。
1、扩展无符号整数
把无符号整数转换为位数更大的值时(比如把字转换为双字),必须确保所有的高位部分都被设置为 0 ,而不是简单的把一个值赋值给另外一个值,比如:这样不能确保 EBX 寄存器的高位部分都包含零。为了完成这个操作,必须使用两条指令:movl $0, %ebx
movw %ax, %ebx 上面第一条指令保证了 EBX 寄存器的每一位都是 0,然后可以安全地把 AX 寄存器中的无符号整数值传给 EBX 寄存器。
但是,Intel 还提供了 MOVZX 指令,简化了上面的操作,格式如下:movzx source, destination 其中,source 可以是 8 位或 16 位寄存器或者内存位置,destination 可以是 16 位或者 32 位寄存器。如:
.section .text
.global _start
_start:
nop
movl $279, %ecx
movzx %cl, %ebx
movl $1, %eax
int $0x80 调试时,在 movl $1, %eax 处下断,运行,然后观察相关寄存器:ecx 0x117 279
ebx 0x17 23 可以看到,MOVZX 指令只传送了 ECX 寄存器的低位字节,而用 0 填充了 EBX 中的剩余字节,这样就在 EBX 寄存器中生成了 0x17 这个值。
2、扩展带符号整数
扩展带符号整数和扩展无符号整数是不同的。使用 0 填充高位会改变负数的数据值。例如,把 -1 (11111111) 传送给双字会变成 0000000011111111,在带符号整数表示法中它是 +127 ,而不是 -1 。为了使带符号扩展能够起作用,新添加的位必须被设置为 1。因此,新的双字将生成值 1111111111111111 ,这是带符号整数表示法的值 -1,这是正确的。
Intel 提供了 MOVSX 指令可以达到扩展带符号数的功能,它允许扩展带符号整数并且保留符号。如下程序所示:
.section .text
.global _start
_start:
nop
movw $-79, %cx
movl $0, %ebx
movw %cx, %bx
movsx %cx, %eax
movl $1, %eax
movl $0, %ebx
int $0x80 使用 gdb 调试时,在 movsx 处下断,运行程序,然后查看 info reg 查看寄存器中的内容:此时,ecx 中的内容是 0x0000ffb1,低 16 位包含的值是 0xffb1,它是带符号整数格式 -79。当 CX 寄存器被传送给 EBX 寄存器时,EBX 寄存器包含的值是 0x0000ffb1,它是带符号整数格式 65457 ,这不是我们所期望的。
在使用 MOVSX 把 CX 寄存器传给 EAX 寄存器之后,EAX 寄存器包含的值是 0xFFFFFFB1,它是带符号整数格式的 -79。MOVSX 指令正确地为这个值添加了高位部分的 1 。 |
|