tags:

views:

73

answers:

1

I've tried to learn the signal handling in C, when found strange behaviour.
When x /= y; executed in the context of the main function the signal handler works. But when the same executed in some function (bad_func) handler is ignored however signal handler for SIGFPE is already set.

Q: Why SIGFPE wasn't caught in a function by my global signal handler even _control87 was called?

(MS VC 2010):

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <float.h>

jmp_buf g_jb_MainFunc;

void hook_zd (int i)
{
    printf("Result :%i\n",i);
    longjmp(g_jb_MainFunc, 5);
}
void bad_func(void)
{
    double x = 0., y = 0.;
    puts("hello1");
    //abort();
    x /= y;
    puts("bye1");
}
int main(int argc, char* argv[])
{
    double x = 0., y = 0.;
    signal(SIGFPE, hook_zd);
    signal(SIGABRT, hook_zd);
    puts("hello");
    _control87(0, _MCW_EM );
    int res;
    if (! (res = setjmp(g_jb_MainFunc)))
    {
        //abort();
        //x /= y;
        bad_func();
    } else
    {
        printf("Jumped here from: %i\n",res);
    }
    puts("bye");

    return 0;
}
+1  A: 

It works for me unless I compile with optimizations turned on. For example, if I compile it from the command line as cl mysigtest.cpp, the exceptions work as expected. But if I compile it cl /O1 mysigtest.cpp, then it does not show the exception.

The disassembled code shows the problem:

?bad_func@@YAXXZ (void __cdecl bad_func(void)):
  00000000: 68 00 00 00 00     push        offset ??_C@_06CKBHOFLC@hello1?$AA@
  00000005: E8 00 00 00 00     call        _puts
  0000000A: 68 00 00 00 00     push        offset ??_C@_04EEFJMNKA@bye1?$AA@
  0000000F: E8 00 00 00 00     call        _puts
  00000014: 83 C4 08           add         esp,8
  00000017: C3                 ret

The division has been optimized out. Try it with optimizations turned off, or change bad_func to the following. For me, it "defeated" the optimizer:

double bad_func(double y)
{
    double x = 0.; 
    puts("hello1");
    //abort();
    x /= y;
    puts("bye1");
    return x;
}

And change the call to it:

bad_func( 0.0 );
Mark Wilkins
Thanks! I'll check it on asm level, if this is due to optimization!
outmind
Nice! It's also good to add _fpreset(); before longjmp in hook_zd, if we want to continue execution after SIGFPE. Otherway the program crashes in my environment next time FPU is used.
outmind