site_archiver@lists.apple.com Delivered-To: darwin-dev@lists.apple.com -- Terry I am part of an ad hoc group working on a Forth language standard for IEEE 754 (2008) floating point, and I'm trying to do an example implementation of alternate handling for floating- point exceptions with gcc/Darwin, for one of the C-based Forths. It's my first attempt at writing a signal handler, but I was able to get it working pretty well on my PowerPC G4, with the sample code quoted below as a guide. The sample code is apparently an update of Listing 7-1 in the 2004 version of "PowerPC Numerics". My question is, what's the analogous magic for OS X intel? AFAIU, there are two issues, enabling nondefault exception handling, and dealing with state in the handler. The sample code deals with state only by restoring it when the handler returns. I might be able to figure out the intel analog for the ppc assembly language functions used for enabling, fegetenvd() and fesetenvd() quoted below; but I wouldn't mind a little hand holding on that. I didn't find any documentation on what's going on with state restoration in myHandler(), and so don't have a clue how to change that for intel. Thanks for any suggestions. -- David ----------- /* The following sample code is slightly modified to get it to compile with gcc version 4.0.1 (Apple Inc. build 5493) under OS X 10.5.8 and ppc. The original is here: http://lists.apple.com/archives/unix-porting/2003/May/msg00026.html See also Listing 7-1 in "PowerPC Numerics", 2004: http://developer.apple.com/documentation/Performance/Conceptual/Mac_OSX_Nume... -- David N. Williams, 15-Sep-09 */ #include <stdio.h> #include <math.h> #include <fenv.h> #include <signal.h> #include <ucontext.h> #include <mach/thread_status.h> #include <stdlib.h> // for exit() #define fegetenvd(x) asm volatile("mffs %0" : "=f" (x)); #define fesetenvd(x) asm volatile("mtfsf 255,%0" : : "f" (x)); enum { FE_ENABLE_INEXACT = 0x00000008, FE_ENABLE_DIVBYZERO = 0x00000010, FE_ENABLE_UNDERFLOW = 0x00000020, FE_ENABLE_OVERFLOW = 0x00000040, FE_ENABLE_INVALID = 0x00000080, FE_ENABLE_ALL_EXCEPT = 0x000000F8 }; typedef union { struct { unsigned long hi; unsigned long lo; } i; double d; } hexdouble; // void myHandler(int sig, siginfo_t *sip, struct ucontext *scp) void myHandler(int sig, siginfo_t *sip, ucontext_t *scp) { hexdouble t; // superfluous in this example ppc_float_state_t *fs; ppc_thread_state_t *ss; // fs = &scp->uc_mcontext->fs; fs = &scp->uc_mcontext->__fs; // ss = &scp->uc_mcontext->ss; ss = &scp->uc_mcontext->__ss; fprintf(stdout, "SIGFPE at 0x%x invokes myHandler, fpscr = %08X\n", // sip->si_addr, fs->fpscr); sip->si_addr, fs->__fpscr); // fs->fpscr &= FE_ENABLE_ALL_EXCEPT; fs->__fpscr &= FE_ENABLE_ALL_EXCEPT; // Re-arms interrupts when this state is restored // ss->srr0 += 4; // Advances the PC when this state is restored ss->__srr0 += 4; // Advances the PC when this state is restored // fprintf(stdout, "fpscr = %08X\n", fs->fpscr); fprintf(stdout, "fpscr = %08X\n", fs->__fpscr); fflush(stdout); } int main( int argc, char **argv) { float s; hexdouble t; fegetenvd(t.d); t.i.lo |= FE_ENABLE_ALL_EXCEPT; // Enable hardware trapping for all exceptions fesetenvd(t.d); if (sigaction(SIGFPE, &act, (struct sigaction *)0) != 0) { // Set handler perror("Yikes"); exit(-1); } fegetenvd(t.d); s = HUGE_VALF * HUGE_VALF; // Overflow folded out by compiler s = s / (1.0 + s); // Inf/Inf raises invalid operation fegetenvd(t.d); fprintf(stdout, "In main(1), s computed as: %e , fpscr = %08X\n", s, t.i.lo); // keep the compiler honest ... s = HUGE_VALF * HUGE_VALF; // Overflow folded out by compiler s = s / (2.0 + s); // Inf/Inf raises invalid operation fegetenvd(t.d); fprintf(stdout, "In main(2), s computed as: %e , fpscr = %08X\n", s, t.i.lo); // keep the compiler honest ... return 0; } _______________________________________________ Do not post admin requests to the list. They will be ignored. Darwin-dev mailing list (Darwin-dev@lists.apple.com) Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/darwin-dev/site_archiver%40lists.appl... Your handler there functions by casting the void * argument to a ucontext_t pointer and then operating on the floating point state and the saved state register contents in the context structure which blows those values in the saved state on the stack. Then when the signal trampoline returns to kernel space via __sigreturn at the end of the signal function, it blows this back into the active thread state structure for the pre-trampoline thread state for the active thread from which the exception was raised, hopefully having fixed up the exception. It is an icredibly naieve implementation, using unsupported data structures (meaning we are not going to promise to not change them on you from software update to software update), since we can safely update our exception management code in lockstep between our runtime and our kernels, and you can't, because you are unable to make simultaneous releases with no lag. We don't make those changes capraciously, but we do ocassionally make them. The implementation is naieve because there are actually 5 possible ucontext_t layouts on the PPC ISA, and you happened to pick the G4, meaning 32 bit PPC, which is relatively the most stable possible one, and one for which there was only a single layout. For Intel ISA, there are 5 possible layouts, and that's not including all the ones that were shipped as part of technology previews in WWDC builds (I will say that there have been at least 7). For you to build an exception handler in a signal handler that did exactly the same thing on Intel, you would need to distinguish at runtime which layout was being used, and then patch up the right locations. For the structures to have the right offsets and members, you would likewise have to use a particular compilation environment (which I will not note here, since it could be misinterpreted as documenting it). Finally, since some of the registers spilled could be used to thwart system security if their contents are modified, a shadow is kept in the kernel, and they are reset back to their previous values, ignoring whatever you do to them with signal stack hacking in user space. Finally, if you are using x87, Intel floating point exceptions are inexact (if you are on a 754 working group, you will already be painfully aware of this fact). Because of this, delayed delivery of exceptions via signal handlers (the signal trampoline generally fires as a result of an AST set on a Mach kernel thread state structure as a result of a unix system call return after running up to the user space/ kernel boundary, so it could be delayed considerably, just on Tru64 UNIX) can decouple the exception state at the time of the exception from the exception state you are trying to hack saved registers on the signal stack in order to fix up. So I will tell you what I have told others who are trying to fix up exceptions in their own runtimes: handle them as Mach exceptions, rather than using signal handlers. Obviously, I can't keep you from puzzling out the bits you would need to hack to deal with runtime recognition of which of the several contexts we use in the signal stack are used in yours, since we publish both our xnu sources and our gdb changes, but if you do, you're basically on your own, and shouldn't be surprised if at some software update in the future your code stops working, for the reasons listed above. On Sep 16, 2009, at 5:17 AM, "David N. Williams" <David.N.Williams@umich.edu
wrote: // static struct sigaction act = { myHandler, (sigset_t)0, SA_SIGINFO };
static struct sigaction act = { (void (*))myHandler, (sigset_t)0, SA_SIGINFO }; This email sent to site_archiver@lists.apple.com