crt: crt_handler: Propagate SEH EXCEPTION_INT_OVERFLOW exception also in 64-bit binaries

Division INT_MIN/-1 is undefined behavior in C and on x86 idivl instruction
generates Divide Exception. Windows in this case raises SEH exception
EXCEPTION_INT_OVERFLOW.

For 32-bit binaries this EXCEPTION_INT_OVERFLOW is not handled by
_gnu_exception_handler() and __mingw_SEH_error_handler() at all.
Case for this exception is hidden inside #ifdef _WIN64. So exception
propagates back to Windows which shows Dr. Watson dialog (if is not
disabled) and kill the process.

For 64-bit binaries this exception in those two functions is handled,
silently ignored and then instructed Windows to restart that fault
instruction. So this logic cause an infinite loop in application without
showing any error and without propagating error back to Windows.

On Linux, this x86 Divide Exception for both 32-bit and 64-bit is
translated to SIGFPE signal for which C application can register handler.

Fix the problem of x86-64 infinite loop by handling the
EXCEPTION_INT_OVERFLOW via SIGFPE signal if is registered by application.
If is not registered then propagate exception back to Windows which could
launch Dr. Watson or kill the process.

This changes also behavior for 32-bit builds as it allows to use SIGFPE too.

Bug: https://stackoverflow.com/questions/25363379/different-results-for-idiv-instruction
Co-authored-by: LIU Hao <lh_mouse@126.com>
Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/crt/crt_handler.c b/mingw-w64-crt/crt/crt_handler.c
index e0e7764..4c344d3 100644
--- a/mingw-w64-crt/crt/crt_handler.c
+++ b/mingw-w64-crt/crt/crt_handler.c
@@ -147,6 +147,7 @@
       /* fall through. */
 
     case EXCEPTION_INT_DIVIDE_BY_ZERO:
+    case EXCEPTION_INT_OVERFLOW:
       /* test if the user has set SIGFPE */
       old_handler = signal (SIGFPE, SIG_DFL);
       if (old_handler == SIG_IGN)
@@ -167,7 +168,6 @@
     case EXCEPTION_DATATYPE_MISALIGNMENT:
     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
     case EXCEPTION_FLT_STACK_CHECK:
-    case EXCEPTION_INT_OVERFLOW:
     case EXCEPTION_INVALID_HANDLE:
     /*case EXCEPTION_POSSIBLE_DEADLOCK: */
       action = ExceptionContinueExecution;
@@ -248,6 +248,7 @@
       /* fall through. */
 
     case EXCEPTION_INT_DIVIDE_BY_ZERO:
+    case EXCEPTION_INT_OVERFLOW:
       /* test if the user has set SIGFPE */
       old_handler = signal (SIGFPE, SIG_DFL);
       if (old_handler == SIG_IGN)
@@ -268,7 +269,6 @@
     case EXCEPTION_DATATYPE_MISALIGNMENT:
     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
     case EXCEPTION_FLT_STACK_CHECK:
-    case EXCEPTION_INT_OVERFLOW:
     case EXCEPTION_INVALID_HANDLE:
     /*case EXCEPTION_POSSIBLE_DEADLOCK: */
       action = EXCEPTION_CONTINUE_EXECUTION;
diff --git a/mingw-w64-crt/testcases/Makefile.am b/mingw-w64-crt/testcases/Makefile.am
index 45a61d6..5fceb0e 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -57,6 +57,7 @@
   t_stprintf1_a \
   t_stprintf1_u \
   t_setjmp \
+  t_sigfpe \
   t_sigv \
   t_speed_powl \
   t_stat \
diff --git a/mingw-w64-crt/testcases/t_sigfpe.c b/mingw-w64-crt/testcases/t_sigfpe.c
new file mode 100644
index 0000000..61e0e22
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_sigfpe.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <assert.h>
+#include <limits.h>
+
+static jmp_buf buf;
+
+static void __attribute__((noreturn)) catch_sigfpe(int signum)
+{
+  printf("SIGFPE exception caught\n");
+  assert(signum == SIGFPE);
+  longjmp(buf, 1);
+}
+
+int main(void)
+{
+#if defined(__arm__) || defined(__aarch64__) || defined(__arm64ec__)
+  puts("Division overflow is ignored on ARM");
+  return 77;
+#else
+  signal(SIGFPE, catch_sigfpe);
+  if (!setjmp(buf))
+  {
+    puts("execute: INT_MIN/-1");
+    volatile int a = INT_MIN;
+    volatile int b = -1;
+    volatile int c = a / b;
+    (void)c;
+    puts("FAILED, program continued");
+    return 1;
+  }
+  puts("PASSED, program recovered");
+  return 0;
+#endif
+}