Ticket UUID: | 1061224 | |||
Title: | "expr sqrt(-1)" crash with EXCEPTION_FLT_INVALID_OPERATION | |||
Type: | Bug | Version: | obsolete: 8.5a2 | |
Submitter: | davygrvy | Created on: | 2004-11-05 22:32:45 | |
Subsystem: | 50. Embedding Support | Assigned To: | kennykb | |
Priority: | 5 Medium | Severity: | ||
Status: | Closed | Last Modified: | 2004-11-12 00:04:24 | |
Resolution: | Invalid | Closed By: | davygrvy | |
Closed on: | 2004-11-11 17:04:24 | |||
Description: |
Stack trace: KERNEL32! RaiseException MSVCRTD! _raise_exc MSVCRTD! _87except MSVCRTD! _startOneArgErrorHandling MSVCRTD! sqrt TclExecuteByteCode TclCompEvalObj Tcl_EvalObjEx NamespaceEvalCmd Tcl_NamespaceObjCmd TclEvalObjvInternal TclExecuteByteCode TclCompEvalObj Tcl_EvalObjEx Tcl_CatchObjCmd TclEvalObjvInterna TclExecuteByteCode Tcl_ExprObj Tcl_ExprBooleanObj Tcl_IfObjCmd TclEvalObjvInternal Tcl_EvalEx Tcl_Eva fake_Tcl_Eval The script I asked it to eval was "expr sqrt(-1)". When my fake_Tcl_Eval() modifies the registers as you suggested, the app killing exception 0xC0000090 is avoided: int fake_Tcl_Eval (Tcl_Interp *interp, char *string) { Tcl_DString dst; int result; Tcl_DStringInit(&dst); // ensure no upper ascii character combos look like // UTF-8 start codes. // Tcl_ExternalToUtfDString(XiRCEncoding, string, -1, &dst); int mask = _controlfp( 0, 0 ); // Some math functions need to be protected from raising // exceptions: /tcl expr sqrt(-1) _controlfp( _CW_DEFAULT, _MCW_EM ); result = Tcl_Eval(interp, Tcl_DStringValue(&dst)); _controlfp( mask, _MCW_EM ); Tcl_DStringFree(&dst); return result; } Can Tcl assume the responsibility of protecting it's own math calls by this method rather than the client of the API? | |||
User Comments: |
davygrvy added on 2004-11-12 00:04:24:
Logged In: YES user_id=7549 Ok, I give in. This shouldn't be Tcl's responsibility. I research more on this, and it's just a nasty behavior in a mixed Borland (exe) and MS compiled (dll) application just like you jenglish had explained it. davygrvy added on 2004-11-09 10:20:14: Logged In: YES user_id=7549 Initial call in my bridge: struct Tcl_NotifierProcs us = { Tcl_SetTimer, XiRCwaitForEventProc, 0L, 0L, Tcl_InitNotifier, Tcl_FinalizeNotifier, Tcl_AlertNotifier, Tcl_ServiceModeHook }; originalW4EP = (Tcl_WaitForEventProc *) GetProcAddress(hTclMod, "Tcl_WaitForEvent"); Tcl_SetNotifier(&us); // Need to specify our own! Thunk stuff ended up being: struct ThunkData { int cwords; }; void EnterToMsvcrt (ThunkData *data) { // See https://sourceforge.net/support/tracker.php?aid=1061224 data->cwords = _controlfp(0, 0); _controlfp(CW_DEFAULT, 0xFFFF); } void LeaveFromMsvcrt (ThunkData *data) { _clearfp(); _controlfp(data->cwords, 0xFFFF); } #define ENTER \ ThunkData data; EnterToMsvcrt(&data) #define LEAVE \ LeaveFromMsvcrt(&data) All the exports to the app are protected by ENTER and LEAVE: int fake_Tcl_EvalFile (Tcl_Interp *interp, char *fileName) { ENTER; int result = Tcl_EvalFile(interp, fileName); LEAVE; return result; } My Tcl_WaitForEvent got a bit tricky, though, to prevent an infinite loop: int XiRCwaitForEventProc (Tcl_Time *timePtr) { ENTER; int result; struct Tcl_NotifierProcs stuff = { Tcl_SetTimer, originalW4EP, 0L, 0L, Tcl_InitNotifier, Tcl_FinalizeNotifier, Tcl_AlertNotifier, Tcl_ServiceModeHook }; Tcl_SetNotifier(&stuff); result = originalW4EP(timePtr); stuff.waitForEventProc = XiRCwaitForEventProc; Tcl_SetNotifier(&stuff); LEAVE; return result; } I'll make a note here if this passes the acid test I'll start working on now. I think I've got XiRCwaitForEventProc upside down, but not sure yet. davygrvy added on 2004-11-09 08:15:15: Logged In: YES user_id=7549 Having to create my own notifier to manage the non-parented calls is not "less difficult" as jenglish would have thought. This will take me a couple days or so to get working. davygrvy added on 2004-11-07 02:36:21: Logged In: YES user_id=7549 The problem here is that normally, hardware exceptions are caught by the C runtime (msvcrt) unless changed. As Tcl is not specifically resetting the runtime's exception mask for each time a math function is called, the control mask follows the global settings. As part of my application is not accible to me in source, I can not change the rest of the DLLs usage of the global control mask. But if Tcl dis-associated itself to a global exception setting and was responsible just for its own calls, all is fixable. davygrvy added on 2004-11-07 01:29:56: Logged In: YES user_id=7549 no nobody added on 2004-11-07 00:55:20: Logged In: NO On Linux/Alpha, similar difficulties were long ago solved with a -mieee switch to gcc. Isn't there something similar on your platform? davygrvy added on 2004-11-06 07:30:49: File Added - 107772: patch.txt davygrvy added on 2004-11-06 07:30:48: Logged In: YES user_id=7549 Here's another, slightly cleaner patch. davygrvy added on 2004-11-06 06:55:49: File Added - 107767: patch.txt Logged In: YES user_id=7549 modified ExprUnaryFunc() in tclExecute.c to support saving/restoring and clearing math errors. Is this bad? ExprBinaryFunc and the others would be next I guess. What if they all called a common Save() and Restore/Clear() so the #ifdefs just sit one function? davygrvy added on 2004-11-06 06:24:54: Logged In: YES user_id=7549 Personally, I don't think it should be my responsibility to handle the math errors that the core throws to me as a user of the API. davygrvy added on 2004-11-06 06:08:12: Logged In: YES user_id=7549 Adding _clear87(); before resetting the mask solves this. Good, but should this be the responsibility of Tcl itself, not for me as a client of the API? davygrvy added on 2004-11-06 05:47:28: Logged In: YES user_id=7549 Wait! That trick works with a msvcrtd.dll loaded, but not the release build. I can't seem to trace it. The exception appears to be raised _after_ the Tcl_Eval that invoked it. |
Attachments:
- patch.txt [download] added by davygrvy on 2004-11-06 07:30:49. [details]