OS/2 codes: Prevent SIGFPE
OS/2 has a very famous bug. It's releated to SIGFPE. When operating floating point arithmetics, SIGFPE occurs due to an exception such as divide by zero. On normal condition, this should be processed without exceptions.
How can this SIGFPE be prevented ?
First, find the cause of SIGFPE. It is that some DLLs do not restore a FPU control word after it changes the word when loading. So what to have to do is to mask off the FPU control word, again. There are 3 ways for this.
The first way is to use _control87(). If the executable uses only imported DLLs, then all the DLLs are loaded before a main routine of a program is started. So if we mask off the FPU control word at begining, then all the problem will be solved. Usually, use the following codes.
However, this approach has a shortcoming. When some DLLs are load dynamically, they can also alter the FPU word. Then SIGFPE can occur, again.
OS/2 API to load a DLL dynamically is DosLoadModule(). So if we provide a wrapper function for it, we can prevent SIGFPE, again.
This code is just a skleton. You should include line 4 in every source file using DosLoadModule(). Or you can use compiler command line option such as -D of gcc for every sources.
However, this approach also have a problem. If you use this, you should recompile all the sources using DosLoadModule(). So if you have a library whose source is not available, this approach is useless.
This is true for other functions such as dlopen() loading DLLs dynamically.
SIGFPE is an exception. So if we register a handler for SIGFPE and modify a control word, then SIGFPE can be ignored safely. The following codes show how to do this.
However, this also has a defect. You should register this handler at every threads. Like DosLoadModule(), you can wrap thread starter functions such as DosCreateThread() or _beginthread(). But a better way is to provide this in libc. Actually, this task was requested in kLIBC project(See http://trac.netlabs.org/libc/ticket/296) . The best is, of course, to fix those buggy DLLs.
So far, we've found out the ways to prevent SIGFPE.Out of these ways, I recommend Way 1 and Way 3.(2014/09/06) And I wish they will be included in kLIBC ASAP.
// ----- 2016/01/14
Since kLIBC 0.6.6, FPU control word is masked off at startup. See http://trac.netlabs.org/libc/ticket/312. And loading modules dynamically was also enhanced to prevent SIGFPE. See http://trac.netlabs.org/libc/ticket/317.
// ----- 2014/09/10
List of APIs which change FPU CW but do not restore it.
How can this SIGFPE be prevented ?
First, find the cause of SIGFPE. It is that some DLLs do not restore a FPU control word after it changes the word when loading. So what to have to do is to mask off the FPU control word, again. There are 3 ways for this.
1. _control87()
The first way is to use _control87(). If the executable uses only imported DLLs, then all the DLLs are loaded before a main routine of a program is started. So if we mask off the FPU control word at begining, then all the problem will be solved. Usually, use the following codes.
1 2 3 4 5 6 7 8 9 10 | #include <float.h> int main( void ) { _control87( MCW_EM, MCW_EM ); .... return 0; } |
However, this approach has a shortcoming. When some DLLs are load dynamically, they can also alter the FPU word. Then SIGFPE can occur, again.
2. Wrapping functions loading DLLs dynamically
OS/2 API to load a DLL dynamically is DosLoadModule(). So if we provide a wrapper function for it, we can prevent SIGFPE, again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #define INCL_DOS #include <os2.h> #define DosLoadModule MyDosLoadModule APIRET MyDosLoadModule( PSZ pszName, ULONG cbName, PSZ pszModName, PHMODULE phmod ) { APIRET rc = DosLoadModule( pszName, cbName, pszModName, phmod ); _control87( MCW_EM, MCW_EM ); return rc; } int main( void ) { _control87( MCW_EM, MCW_EM ); // some routines to load DLLs dynamically return 0; } |
This code is just a skleton. You should include line 4 in every source file using DosLoadModule(). Or you can use compiler command line option such as -D of gcc for every sources.
However, this approach also have a problem. If you use this, you should recompile all the sources using DosLoadModule(). So if you have a library whose source is not available, this approach is useless.
This is true for other functions such as dlopen() loading DLLs dynamically.
3. Signal handler for SIGFPE
SIGFPE is an exception. So if we register a handler for SIGFPE and modify a control word, then SIGFPE can be ignored safely. The following codes show how to do this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #define INCL_DOS #include <os2.h> #include <stdio.h> #include <float.h> // this handler was excerpted from Mozilla repository static ULONG _System fpeHandler( PEXCEPTIONREPORTRECORD p1, PEXCEPTIONREGISTRATIONRECORD p2, PCONTEXTRECORD p3, PVOID p4 ) { printf("XCPT no. = %x\n", p1->ExceptionNum ); switch( p1->ExceptionNum ) { case XCPT_FLOAT_DIVIDE_BY_ZERO : { unsigned cw = p3->ctx_env[0]; if ((cw & MCW_EM) != MCW_EM) { /* Mask out all floating point exceptions */ p3->ctx_env[0] |= MCW_EM; /* Following two lines set precision to 53 bit mantissa. See jsnum.c */ p3->ctx_env[0] &= ~MCW_PC; p3->ctx_env[0] |= PC_53; printf("----- XCPT_CONTINUE_EXECUTION\n"); return XCPT_CONTINUE_EXECUTION; } break; } } printf("----- XCPT_CONTINUE_SEARCH\n"); return XCPT_CONTINUE_SEARCH; } int main(void) { float f, f2; EXCEPTIONREGISTRATIONRECORD regRec = { 0, }; // mask on all exceptions _control87( 0, MCW_EM ); regRec.ExceptionHandler = ( ERR )fpeHandler; DosSetExceptionHandler( ®Rec ); f2 = 1.5; f = .0; f = f2 / f; printf("f = %f\n", f ); DosUnsetExceptionHandler( ®Rec ); return 0; } |
However, this also has a defect. You should register this handler at every threads. Like DosLoadModule(), you can wrap thread starter functions such as DosCreateThread() or _beginthread(). But a better way is to provide this in libc. Actually, this task was requested in kLIBC project(See http://trac.netlabs.org/libc/ticket/296) . The best is, of course, to fix those buggy DLLs.
So far, we've found out the ways to prevent SIGFPE.
// ----- 2016/01/14
Since kLIBC 0.6.6, FPU control word is masked off at startup. See http://trac.netlabs.org/libc/ticket/312. And loading modules dynamically was also enhanced to prevent SIGFPE. See http://trac.netlabs.org/libc/ticket/317.
// ----- 2014/09/10
List of APIs which change FPU CW but do not restore it.
- WinCreateMsgQueue()
- DosLoadModule
- DosFreeModule
- DosRead
- DosWrite
댓글
댓글 쓰기