https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114582

--- Comment #1 from Ian Lance Taylor <ian at airs dot com> ---
The bug here, and also with PR 114581, is in unwinding from a signal call.  A
simplified version of the code for this issue is:

func main() {
    defer func() { recover() }()
    f()
}

func f() { *p = 0 }

The *p = 0 in f will raise a SIGSEGV.  The Go runtime will turn that into a
panic.  The recover in main will prevent that panic from killing the program.

The unwind code works by walking up the stack until it finds a recover.  When
it finds a recover, it records that stack frame as the end of the unwind.  It
runs the recover, then walks up the stack again to unwind it.  In the second
walk, it checks that it does not walk past the stack frame that calls recover.

Recording the stack frame is done based on what the signal handler calls the
CFA, which is, basically, the frame pointer.  It is also based on whether a
signal occurred in that frame.

In this test case, the function f is so simple that it has no frame.  That
turns out to mean that f's CFA is the same as main's CFA.  In the first stack
walk, we find main, and record main's CFA as the point where the second unwind
should stop.  In the second stack walk, when we get to f, we think we have
reached the end.  But we haven't, because we haven't reached main yet.  This
causes an assert, which shows up as a SIGABRT.

That is what happens today.  But it would work fine if we knew that a signal
occurred in f, as in fact it did.  However, we don't, because of this code in
libgcc/config/sol2-unwind.h:

  /* SIGFPE for IEEE-754 exceptions is delivered after the faulting insn
     rather than before it, so don't set fs->signal_frame in that case.
     We test whether the cexc field of the FSR is zero.  */
  if ((mctx->fpregs.fpu_fsr & 0x1f) == 0)
    fs->signal_frame = 1;

If we unconditionally set fs->signal_frame, as is done on Linux, this would
work.  And it works on 64-bit SPARC, because those flags turn out to be zero. 
But on 32-bit SPARC, they are not zero.  Specifically, bit 0 is set, indicating
that an inexact operation occurred.  I don't know why.

That code was added by
https://gcc.gnu.org/legacy-ml/gcc-patches/2013-05/msg01390.html.  If I undo
that change, so that fs->signal_frame is set unconditionally as it was before,
then the Go test passes.

I don't know why it is reasonable to assume that if the floating-point inexact
flag is set then the signal is a floating-point signal.  In this case that is
clearly incorrect.

Because the problem here appears to be a Solaris-specific problem with
Solaris-specific code, I don't plan to work any further on it.

Reply via email to