这个 x86 代码中的零标志是如何设置的?

How is the zero flag set in this x86 code?

由于以下 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 表示 "equals" 条件为 true。 (所以小于或等于 setle 也是正确的)。所以 setle cl 应该给你 1.

R10 = 1 时,R10D = 1。 R10D & R10D != 0 所以 ZF 应该 被清除。因此 r10dNOT <= 0,所以 setle 在 CL 中放置了一个 0 并且你确实得到了一个 #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 = 0r10d = 1)。

正如预期的那样,R10D > 0(不是 <= 0)导致 ECX = 0。

尝试越过 idiv 或使用 continueEFLAGS = [ RF IF ] 后。所以你的 GDB 寄存器转储没有意义,除非不同的 CPU 或 OS 可以在错误时用 FLAGS 做一些不同的事情。