我们今天由于以下x86程序集中的idiv指令中的算术异常而导致崩溃。
mov r10d,DWORD PTR [rbp+0x70]
xor ecx,ecx
mov eax,r15d
test r10d,r10d
setle cl
cdq
idiv ecx
这里是gdb中所有寄存器的值:
rax 0x64 100
rbx 0xaebc30 11451440
rcx 0x0 0
rdx 0x0 0
rsi 0x1 1
rdi 0xaebc88 11451528
rbp 0x7fa56809b840 0x7fa56809b840
rsp 0x7fa56effc080 0x7fa56effc080
r8 0x12 18
r9 0x100016a0000a72e 72059148816131886
r10 0x1 1
r11 0x0 0
r12 0x9e2bb8 10365880
r13 0x0 0
r14 0xaebc80 11451520
r15 0x64 100
rip 0x495ebb 0x495ebb
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
由于设置了零标志,setle cl
将0写入ecx,这将导致被零除。我不明白的是,首先如何设置零标志。 r10的值为1,因此据我所知test r10d,r10d
应该将其取消设置。 cdq
似乎没有对其进行修改,所以我不确定这里发生了什么。
具有更多x86经验的人可以理解问题所在吗?是否需要更多信息?
并且此寄存器转储是从idiv
之前(或GDB捕获SIGFPE之后应该是同一件事)?
您向后看:ZF = 1表示“等于”条件为true。 (因此,小于或等于setle
也是如此)。因此,setle cl
应该给您一个1
。
[R10 = 1时,R10D = 1。 R10D & R10D != 0
,因此清除ZF 应该。因此,r10d
为NOT <= 0,因此setle
将0
放在CL中,并且您确实获得了#DE
除法异常。
使用此NASM源代码:
mov r10d, 1
xor ecx,ecx
mov eax, 0x64
test r10d,r10d
setle cl
cdq
idiv ecx
[使用nasm -felf64 foo.asm
&& ld foo.o -o foo
内置于Linux静态可执行文件中(入口点默认位于.text部分的顶部),我将其运行在GDB下(因为这比仔细考虑所有细节要快,我想查看EFLAGS是否被故障的idiv更改了。]
使用GDB的starti
命令从顶部开始,并且一步一步。 (使用layout reg
)。
就在idiv
之前,我得到EFLAGS = [ IF ]
,ecx = 0
。(使用r10d = 1
)。
按预期,R10D> 0(不是<= 0)导致ECX = 0。
[试图越过idiv
,或使用continue
后,EFLAGS = [ RF IF ]
。因此,除非故障发生时不同的CPU或OS可以对FLAGS做一些不同的事情,否则您的GDB寄存器转储是没有意义的。