4

I develop an app in Xcode 4.5 (llvm 4.1 compiler) for ios 5/6 and use signal and exception handlers to log errors. However I've found that division by zero never raises SIGFPE signal. On linux systems I can use feenableexcept to set traps. But this is not defined in ios.

Setting appropriate bits to fenv_t.__fpscr doesn't work, at least for iphone 4 and 3gs.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Tertium
  • 6,049
  • 3
  • 30
  • 51

2 Answers2

5

The feenableexcept function is a linux function that is not part of standard C or POSIX. There is no portable way to enable SIGFPE.

In fact, you need different code to enable SIGFPE on the iOS simulator and on iOS devices, because the simulator runs x86 and the device runs ARM.

I think (but have not tested) that you can enable SIGFPE by getting an fenv_t using the fegetenv function, turning some bits on or off in the fenv_t, and then passing it to the fesetenv function. The definition of fenv_t is processor-specific. Take a look at fenv.h.

For ARM, fenv_t contains a field named __fpscr. This is the floating point status and control register. The bits you are allowed to toggle are enumerated in fenv.h as __fpscr_trap_invalid, __fpscr_trap_divbyzero, etc. Presumably you want to turn on the __fpscr_trap_divbyzero bit.

For x86, fenv_t contains two fields of interest: __control (the x87 control word) and __mxcsr (the SSE control/status register).

The bits you can toggle in __control are defined by the FE_INEXACT, FE_UNDERFLOW, etc. constants defined in fenv.h. I think you have to turn the bits off to enable SIGFPE for those exceptions. Check the processor manual, §8.1.5.

The bits you can toggle in __mxcsr are defined by the _MM_MASK_INVALID, __MM_MASK_DENORM, etc. constants in xmmintrin.h. I think that you need to turn the bits off to enable SIGFPE. Check the processor manual, §10.2.3.

    fenv_t fe;
    if (fegetenv(&fe) != 0) {
        // error
    }

#if defined __arm__
    fe.__fpscr |= __fpscr_trap_divbyzero;
#elif defined __i386__
    fe.__control &= ~FE_DIVBYZERO;
    fe.__mxcsr &= ~_MM_MASK_DIV_ZERO;
#else
#error unknown processor architecture
#endif

    if (fesetenv(&fe) != 0) {
        // error
    }

You may also need to do #pragma STDC FENV_ACCESS ON for all processors.

Again, I haven't tested any of this. Good luck.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • This doesn't work, even though fesetenv sets the values ok. By the way this i386 magic doesn't work either. – Tertium Oct 07 '12 at 16:55
  • 1
    This will only enable the cpu to set the flag, it will not raise an exception. If you enable the flag as described by Rob, you will need to check for the flag either each run loop or after you perform the division. Like so: int set_excepts = fetestexcept(FE_DIVBYZERO); if (set_excepts & FE_DIVBYZERO) { cerr<<"Divide by zero!!!"< – Snickers Mar 08 '13 at 20:14
  • This sounds like a right approach, and worked for desktop and simulator, but I wasn't able to make it work for the device - neither for `__arm__`, nor for `__arm64__`. I've debugged it instruction by instruction and reached the point where fesetenv () executes FMXR instruction to write updated value into FPSCR, but value of the register does not change. – kjam May 26 '15 at 13:59
  • Apple's implementations of the ARM architecture don't appear to support floating-point trapping. This is a permitted option in implementing the architecture, according to ARM Ltd's reference manuals. – John Dallman Feb 01 '17 at 16:59
0

Your options to solve this problem are:

Do what Rob said by using one function to enable the floating point divide by zero trapping, and then checking the flag periodically:

#include "fenv.h"
#include <iostream>
#include <csignal>
void CheckForDivideByZeroException()
{
    int set_excepts;
    set_excepts = fetestexcept(FE_DIVBYZERO);

    if (set_excepts & FE_DIVBYZERO) {
        cerr<<"Divide by zero!!!"<<endl;
        feclearexcept(FE_DIVBYZERO);
    }
}
void EnableDivideByZeroExceptions()
{
    fenv_t fe;
    if (fegetenv(&fe) != 0) {
        cerr<<"ERROR - Unable to get floating point status and control register"<<endl;
        raise(SIGINT);
    }

#if defined __arm__
    fe.__fpscr |= __fpscr_trap_divbyzero;
#elif defined __i386__
    fe.__control &= ~FE_DIVBYZERO;
    fe.__mxcsr &= ~_MM_MASK_DIV_ZERO;
#else
#error unknown processor architecture
#endif

    if (fesetenv(&fe) != 0) {
        cout<<"ERROR - Unable to trap div by zero errors"<<endl;
        raise(SIGINT);
    }
}
#endif

OR your other option is to enable the flag in xcode that will raise an exception with all undefined behavior by adding the following to your OTHER_CFLAGS build setting

-fcatch-undefined-behavior
Snickers
  • 1,603
  • 11
  • 6