ucontext in signal handler

Warlich, Christof christof.warlich at siemens.com
Fri Apr 26 07:46:50 EDT 2013


Hi,

I'm porting a MIPS-based realtime control system that runs directly on top
of the MIPS hardware (i.e. no dedicated operating systen inbetween) to x86
Linux. The systen is open to execute user code, and it catches floating
point exceptions (e.g. underflow and overflow) by replacing out of range
numbers with "sensible" values. To remain compatible whith the old system,
I somehow need to do the same under Linux.

I started out quite cheerfully whith some initial test code by registering
a signal handler for floation point exceptions that just skips across any 
floating point instruction that causes an exception. (Note that the function
instructionSize() calculates the length of the assembler instruction pointed
to by ist argument):

void fpeHandler(int, siginfo_t *info, void *context) {
    int size = instructionSize((unsigned char *) info->si_addr);
    ((ucontext_t *) context)->uc_mcontext.gregs[REG_EIP] += size;
}

This works great so far: When the signal handler returns, my program
resumes right _after_ the instruction that caused the exception.

But there is (much) more to do: To start with, the exception flags need to
be cleared, otherwise any following floation point instructions would throw
the exception again, even if they would not normally cause one.

Initially, this seemed to be an easy task again, as the floating point
registers are as well available in ucontext_t. So I looked at the source
code of glibc to see the implementation of feclearexcept() to learn how to
clear exception flags:

Int feclearexcept (int excepts) {
  fenv_t temp;
  unsigned int mxcsr;
  /* Mask out unsupported bits/exceptions.  */
  excepts &= FE_ALL_EXCEPT;
  /* Bah, we have to clear selected exceptions.  Since there is no
     `fldsw' instruction we have to do it the hard way.  */
  __asm__ ("fnstenv %0" : "=m" (*&temp));
  /* Clear the relevant bits.  */
  temp.__status_word &= excepts ^ FE_ALL_EXCEPT;
  /* Put the new data in effect.  */
  __asm__ ("fldenv %0" : : "m" (*&temp));
  /* And the same procedure for SSE.  */
  __asm__ ("stmxcsr %0" : "=m" (*&mxcsr));
  /* Clear the relevant bits.  */
  mxcsr &= ~excepts;
  /* And put them into effect.  */
  __asm__ ("ldmxcsr %0" : : "m" (*&mxcsr));
  /* Success.  */
  return 0;
}

So I need to clear the relevant bits in the processor's floating point status
word and in the mxcsr register. Looks easy. But ...

1) While ucontext_t _does_ have the status register, e.g.:
   ((ucontext_t *) context)->uc_mcontext.fpregs->status &= FE_ALL_EXCEPT ^ FE_ALL_EXCEPT;
   , the register mxcsr is _not_ part of ucontext_t.
2) Even worse, the following link suggests that only the general purpose
   registers are restored from ucontext_t, while the floating point registers
   in ucontext_t are ignored:
   http://valgrind.10908.n7.nabble.com/need-FPU-and-SSE-state-in-sigcontext-ucontext-td19844.html

As everything is so nicely available for general purpose registers, why are
things not done in the same way for floating point registers?

Is there any other way to achive my goal?

If not, are there any realistic chances (for a kernel newbie) to fix this
in the kernel? I already looked at arch/x86/kernel/signal.c, but it looks
difficult to get on track.

Thanks for any help,

Chris


More information about the Kernelnewbies mailing list