Date: Sat, 3 Mar 2012 12:02:23 +1100 (EST) From: Bruce Evans <brde@optusnet.com.au> To: Tijl Coosemans <tijl@FreeBSD.org> Cc: svn-src-head@FreeBSD.org, svn-src-all@FreeBSD.org, src-committers@FreeBSD.org, Bruce Evans <brde@optusnet.com.au> Subject: Re: svn commit: r232275 - in head/sys: amd64/include i386/include pc98/include x86/include Message-ID: <20120303110551.Q1494@besplex.bde.org> In-Reply-To: <201203022231.43186.tijl@freebsd.org> References: <201202282217.q1SMHrIk094780@svn.freebsd.org> <201203012347.32984.tijl@freebsd.org> <20120302132403.P929@besplex.bde.org> <201203022231.43186.tijl@freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, 2 Mar 2012, Tijl Coosemans wrote: > On Friday 02 March 2012 05:11:21 Bruce Evans wrote: [... Lots about complications for longjmp() from a signal handler] > Thanks, that was quite informative. C11 does say something about the > FP env and signals now though: > > ``When the processing of the abstract machine is interrupted by receipt > of a signal, the values of objects that are neither lock-free atomic > objects nor of type volatile sig_atomic_t are unspecified, as is the > state of the floating-point environment. The value of any object > modified by the handler that is neither a lock-free atomic object nor > of type volatile sig_atomic_t becomes indeterminate when the handler > exits, as does the state of the floating-point environment if it is > modified by the handler and not restored to its original state.'' Interesting. This is almost exactly the same as my justification for not caring about old signal handlers getting a dirty FP state. Since signal handlers can't do anything with static objects except write to volatile sig_atomic_t ones, they can't do anything useful with FP. But old standards seem to allow them to do silly things like calculate pi to a billion decimal places and store the result encoded in an array of about 1 billion sig_atomic_t's, or better yet, calculate 2.0 + 2.0 and store the result in a smaller array of sig_atomic_t's. Oops, the above doesn't clearly say of the unspecified state is for the signal handler, or after the signal handler returns, or both. The second sentence it only clearly says that certain objects and state become indeterminate after bad behaviour by signal handlers. But we want longjmp()s from signal handlers to be usually not bad behaviour. The above at least allows them to work right. Maybe longjmp() can determine if it is invoked in a signal handler and do the right thing then. longjmp()s for machines that are too complicated for me have to do sophisticated stack unwinding. This might require even more sophisticated unwinding for signal handlers. This reminds me that POSIX (?) weasels out of some of the complications by only requiring a single level of nested signal handlers to work. > This means a signal handler must not rely on the state of the FP env. > It may install its own FP env if needed (e.g. FE_DFL_ENV), but then it > must restore the original before returning. This allows for the > rounding mode to be silently modified for integer conversions for > instance. Well, I think the kernel can't trust the signal handler to do that. It also can't trust the compiler. The compiler can reasonably use FP for anything it wants if this is (the compiler thinks) transparent to the abstract machine, but the compiler can't know when the code is for a signal handler without complicated directives. Then on x86, integer operations may use the same SSE registers (although perhaps not mxcsr) as FP uses, and again the compiler can't reasonably know when not to do this. So the kernel must restore at least the shared registers, and for SSE this means restoring using f*rstor which handles the whole state at no extra cost. So the interesting points for signal handlers move to: - should signal handlers have to initialize their own state if they want to use FP explicitly? I think they should. - should signal handlers have to initialize their own state if they want to use FP or shared registers implicitly (because the compiler wants to)? No. The kernel must handle this transparently, much like it does now, and I think this makes the previous case work transparently too. The kernel tries to do this lazily, but it doesn't do this very well (it copies the state several times in sendsig() and sigreturn()). - when the signal handler wants to modify the interrupted state, how does it do this? There is minimal support for this. The easiest way to modify it is to modify the current state and then longjmp() instead of returning. - how can signal handlers and debuggers even see the interrupted state? gdb has less clue about this than it did 20 years ago. Users can probably use debuggers to follow various pointers to the saved state if they know more about this than signal handlers and debuggers. > If longjmp is not supposed to change the FP env then, when called from > a signal handler, either the signal handler must install a proper FP > env before calling longjmp or a proper FP env must be installed after > the target setjmp call. Otherwise the FP env is unspecified. Better handle the usual case right like it used to be, without the signal handler having to do anything, by always saving a minimal environment in setjmp(), but now only restoring it for longjmp() in signal handlers. The minimal environment doesn't include any normal register on at least amd64 and i386 (except for i387 it includes the stack and the tags -- these must be empty on return from any function call). Again there is a problem with transparent use of FP or SSE by the compiler. An average SIGINT handler that doesn't want to do any explicit FP and just wants to longjmp() back to the main loop can't be expected to understand this stuff better than standards, kernels and compilers and have the complications neccessary to fix up the FP state after the compiler has transparently (it thinks) used FP or SSE. Bruce
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120303110551.Q1494>