assembly 从程序集中捕获/禁用SIGFPE异常

m528fe3b  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(96)

我在x86汇编中使用idiv除以0时得到一个SIGFPE异常。我如何从汇编中禁用它?我需要一个系统调用还是可以直接在x86中完成?
复制:
test.asm

default rel
global WinMain

section .data 

section .text
WinMain:
    mov rcx, 0
    mov rdx, 0
    idiv rcx

字符串
命令:
nasm -f win64 test.asm
gcc test.obj
广德银行
运行

gpnt7bae

gpnt7bae1#

TL:DR:你不能用#DE异常使硬件不出错,而且在跳过信号处理程序中的出错指令以“修复”这种情况后恢复执行也是很重要的。(您还需要将RDX和RAX设置为一些特殊值,如-10,或者一个值得注意的值,比如0xcccccccccccccccc。或者取决于什么可以让你的程序的其余部分安静地做一些有用的事情(?)而不是以不同的方式再次崩溃,也许是+1。)
对于实际的浮点异常,x87 FPU和SSE(通过MXCSR)默认具有异常掩码,因此在目标中无效操作(如被零除)时会得到NaN,没有硬件异常。POSIX要求,如果任何算术运算出现错误,并且操作系统传递了一个关于它的信号,该信号必须是SIGFPE。
你能做的最好的事情就是用信号处理程序来捕获SIGFPE(在像Linux这样的POSIX操作系统上是sigaction(2))。奇怪的是,你在谈论一个带有WinMain入口点的程序的SIGFPE,除非Windows SEH(结构化异常处理)也使用相同的SIGFPE名称?
根据POSIX,使用SIG_IGN忽略它是未定义的,并且在实践中,如果忽略信号或使信号处理程序返回而不做任何事情,则会导致无限循环。(与页面错误相同),两者都可以让处理程序看到它是哪条指令,因此,以某种方式修复这种情况(就像#PF处理程序通常所做的那样)将重新运行指令,希望下次尝试时不会出错。
所以你的处理程序应该检查是否si_code == FPE_INTDIV。如果是这样,它可以尝试将程序计数器提前到错误指令的末尾,就像它根本没有执行一样运行,而不修改FLAGS和RDX:RAX。(或者你的信号处理程序可以将它们设置为一些一致的值。)
一个线程的寄存器可以被运行在其中的Linux信号处理程序访问和修改。* Getting fault address that generated a UNIX signal * 也涵盖了ucontext_t的内容,包括GPR,包括RAX和RDX,还有RIP,程序计数器。
x86-64指令是可变长度的。您必须从RIP中的地址解码机器码,跳过前缀,查找idivdiv的操作码字节。如果是这样,请将RIP提前到该指令之后以跳过它。指令长度解码是不平凡的,因为具有可变长度寻址模式的内存源操作数是可能的:你必须对ModRM和可选的SIB进行足够的解码,以计算出总的指令长度。(x86-64的设计使这一点不会太痛苦,所以硬件可以有效地完成它:你不需要记住来自雷克斯前缀的位,如果有的话,同时对前缀后面的部分进行长度解码:* rbp not allowed as SIB base?
没有FLAGS /控制寄存器/其他硬件设置会使dividiv在除数= 0或商不适合操作数大小时不会引发#DE除法异常。例如,对于64位idiv rcx,如INT64_MIN / -1,它也会在x86-64上出错。
或者对于有符号除法的(unsigned long)-1 / 1,就像你在有符号idiv之前零扩展到RDX:RAX一样。通常使用cqo/idiv在有符号除法之前将RAX符号扩展到RDX:RAX,或者使用xor edx,edx/div在无符号除法之前零扩展到RDX。
When and why do we sign extend and use cdq with mul/div?
您可以屏蔽FP异常,默认的x87和SSE(MXCSR)状态是屏蔽FP异常,因此通常在x86-64系统上唯一可以引发SIGFPE的是整数除法。(或者可能是into指令,如果设置了OF标志,则为trap。)
相关:

相关问题