diff mbox

Support Solaris 11/SPARC in MD_FALLBACK_FRAME_STATE_FOR (PR ada/41929)

Message ID ydd62smx13h.fsf@manam.CeBiTec.Uni-Bielefeld.DE
State New
Headers show

Commit Message

Rainer Orth Feb. 14, 2011, 12:16 p.m. UTC
gnat.dg/null_pointer_deref1.adb and several other EH-related execution
tests were failing on Solaris 11/SPARC.  Since the current way of having
hard-coded offsets to the ucontext_t on the stack was both hard to
follow and fragile, I've decided to rework sparc/sol2-unwind.h along the
lines of what I've done earlier for i386/sol2-unwind.h, i.e. walk the
stack.  This is based on the stack layout learned from the OpenSolaris
sources:

http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/sparc/v9/os/v9dep.c#880

By using a single #ifdef and mcontext_t, the 32 and 64-bit differences
can be localized to two different IS_SIGHANDLER functions.  I've also
followed the i386 version in adding the code snippets that are matched
in IS_SIGHANDLER to make the whole thing easier to follow.

The result has been bootstrapped by Eric (thanks for this and debugging
why a first version caused some regressions on Solaris 8) and myself on
Solaris 8, 9, 10 and 11, and I've performed quite a bit of software
archaeology (i.e. stared at old libc.so.1 and libthread.so.1 patches) to
make sure noting got missed in the process.

This fixes not only PR ada/41929, but also all remaining EH-related
failures in ACATS, gnat.dg and libjava on Solaris 11, bringing it en par
with Solaris 10.

While sparc/sol2-unwind.h is identical between the 4.5 branch and
mainline and this patch fixes quite a number of Solaris 11 failures,
Eric correctly pointed out that Solaris 7 is supposed to be supported on
the 4.5 branch, though deprecated.  I'll therefore won't apply the patch
there until I can verify that the IS_SIGHANDLER code sequences are ok
for S7, too.  I don't run S7 any longer, and there is no Solaris 7
branded zone, so I cannot really test it.

Installed.

	Rainer


2011-02-05  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>

	gcc:
	PR ada/41929
	* config/sparc/sol2-unwind.h: Include <sys/frame.h>, <sys/stack.h>
	(IS_SIGHANDLER): Define.
	(sparc64_is_sighandler): New function, split off from
	sparc64_fallback_frame_state.
	(sparc_is_sighandler): New function, split off from
	sparc_fallback_frame_state.
	(sparc64_fallback_frame_state): Merge with ...
	(sparc_fallback_frame_state): ... this into ...
	(MD_FALLBACK_FRAME_STATE_FOR): ... this.
	Change new_cfa to long.
	Remove regs_off, fpu_save_off, fpu_save.
	Define nframes, mctx.
	Use IS_SIGHANDLER, handler_args, mctx, walk stack instead of
	hardcoded offsets.

	gcc/testsuite:
	PR ada/41929
	* gnat.dg/null_pointer_deref1.exp: Don't skip on
	sparc*-sun-solaris2.11.

Comments

Eric Botcazou Nov. 28, 2011, 8:35 p.m. UTC | #1
> The result has been bootstrapped by Eric (thanks for this and debugging
> why a first version caused some regressions on Solaris 8) and myself on
> Solaris 8, 9, 10 and 11, and I've performed quite a bit of software
> archaeology (i.e. stared at old libc.so.1 and libthread.so.1 patches) to
> make sure noting got missed in the process.

All EH-related tests fail with a recent upgrade on Solaris 10.  Interestingly, 
the old implementation keeps working with the upgrade.  It seems that the 
__sighndlr case has been over-simplified:

  /* Look for the __sighndlr pattern.  */
  else if (    *(unsigned int *)(pc-(4*5)) == sighndlr_pattern[0]
	    && *(unsigned int *)(pc-(4*4)) == sighndlr_pattern[1]
	    && *(unsigned int *)(pc-(4*3)) == sighndlr_pattern[2]
	    && *(unsigned int *)(pc-(4*2)) == sighndlr_pattern[3]
	    && *(unsigned int *)(pc-(4*1)) == sighndlr_pattern[4]
	    && *(unsigned int *)(pc-(4*0)) == sighndlr_pattern[5]
	    && *(unsigned int *)(pc+(4*1)) == sighndlr_pattern[6] )
    {
      /* We have observed different calling frames among different
	 versions of the operating system, so that we need to
	 discriminate using the upper frame.  We look for the return
	 address of the caller frame (there is an offset of 15 words
	 between the frame address and the place where this return
	 address is stored) in order to do some more pattern matching.  */
      unsigned int cuh_pattern
	= *(unsigned int *)(*(unsigned int *)(this_cfa + 15*4) - 4);

      if (cuh_pattern == 0xd407a04c)
	{
	  /* This matches the call_user_handler pattern for Solaris 10.
	     There are 2 cases so we look for the return address of the
	     caller's caller frame in order to do more pattern matching.  */
	  unsigned int sah_pattern
	    = *(unsigned int *)(*(unsigned int *)(this_cfa + 96 + 15*4) - 4);

          if (sah_pattern == 0x92100019)
	    /* This is the same setup as for Solaris 9, see below.  */
	    regs_off = 96 + 96 + 96 + 160;
	  else
	    /* We need to move up three frames (the kernel frame, the
	       call_user_handler frame, the __sighndlr frame).  Two of them
	       have the minimum stack frame size (kernel and __sighndlr
	       frames) of 96 bytes, and there is another with a stack frame
	       of 160 bytes (the call_user_handler frame).  The ucontext_t
	       structure is after this offset.  */
	    regs_off = 96 + 96 + 160;
	}
      else if (cuh_pattern == 0x9410001a || cuh_pattern == 0x9410001b)
	/* This matches the call_user_handler pattern for Solaris 9 and
	   for Solaris 8 running inside Solaris Containers respectively.
	   We need to move up four frames (the kernel frame, the signal
	   frame, the call_user_handler frame, the __sighndlr frame).
	   Three of them have the minimum stack frame size (kernel,
	   signal, and __sighndlr frames) of 96 bytes, and there is
	   another with a stack frame of 160 bytes (the call_user_handler
	   frame).  The ucontext_t structure is after this offset.  */
	regs_off = 96 + 96 + 96 + 160;
      else
	/* We need to move up three frames (the kernel frame, the
	   sigacthandler frame, and the __sighndlr frame).  Two of them
	   have the minimum stack frame size (kernel and __sighndlr
	   frames) of 96 bytes, and there is another with a stack frame
	   of 216 bytes (the sigacthandler frame).  The ucontext_t 
	   structure is after this offset.  */
	regs_off = 96 + 96 + 216;
    }

to

  if(/* Solaris 8+ - multi-threaded
       ----------------------------
       <__sighndlr>:	save  %sp, -96, %sp
       <__sighndlr+4>:	mov  %i0, %o0
       <__sighndlr+8>:	mov  %i1, %o1
       <__sighndlr+12>:	call  %i3
       <__sighndlr+16>:	mov  %i2, %o2
       <__sighndlr+20>:	ret 		<--- PC
       <__sighndlr+24>:	restore  */
        pc[-5] == 0x9de3bfa0
     && pc[-4] == 0x90100018
     && pc[-3] == 0x92100019
     && pc[-2] == 0x9fc6c000
     && pc[-1] == 0x9410001a
     && pc[ 0] == 0x81c7e008
     && pc[ 1] == 0x81e80000)
    {
      if (/* Solaris 8 /usr/lib/libthread.so.1
	    ----------------------------------
	    <sigacthandler+1796>:     mov  %i0, %o0  */
	  savpc[-1] == 0x90100018)
	{
	  /* We need to move up two frames:

		<signal handler>	<-- context->cfa
		__sighndlr
		sigacthandler
		<kernel>
	  */
	  *nframes = 2;
	}
      else /* Solaris 8 /usr/lib/lwp/libthread.so.1, Solaris 9+
	     --------------------------------------------------  */
	{
	  /* We need to move up three frames:

		<signal handler>	<-- context->cfa
		__sighndlr
		call_user_handler
		sigacthandler
		<kernel>
	  */
	  *nframes = 3;
	}
      return 1;
    }

In particular, the old fallback case (2 frames) has been changed (to 3 frames), 
The upgrade seems to have reverted to 2 frames because of a sibling call.
diff mbox

Patch

Index: gcc/config/sparc/sol2-unwind.h
===================================================================
--- gcc/config/sparc/sol2-unwind.h	(revision 170124)
+++ gcc/config/sparc/sol2-unwind.h	(working copy)
@@ -1,5 +1,5 @@ 
 /* DWARF2 EH unwinding support for SPARC Solaris.
-   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -26,224 +26,130 @@ 
    state data appropriately.  See unwind-dw2.c for the structs.  */
 
 #include <ucontext.h>
+#include <sys/frame.h>
+#include <sys/stack.h>
 
 #if defined(__arch64__)
 
-#define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
+#define IS_SIGHANDLER sparc64_is_sighandler
 
-static _Unwind_Reason_Code
-sparc64_fallback_frame_state (struct _Unwind_Context *context,
-			      _Unwind_FrameState *fs)
+static int
+sparc64_is_sighandler (unsigned int *pc, unsigned int *savpc, int *nframes)
 {
-  void *pc = context->ra;
-  void *this_cfa = context->cfa;
-  void *new_cfa, *ra_location, *shifted_ra_location;
-  int regs_off;
-  int fpu_save_off;
-  unsigned char fpu_save;
-  int i;
+  if (/* Solaris 8 - single-threaded
+	----------------------------
+	<sigacthandler+24>:  add  %g5, %o7, %o2
+	<sigacthandler+28>:  ldx  [ %o2 + 0xfa0 ], %g5
+	<sigacthandler+32>:  sra  %i0, 0, %o0
+	<sigacthandler+36>:  sllx  %o0, 3, %g4
+	<sigacthandler+40>:  ldx  [ %g4 + %g5 ], %l0
+	<sigacthandler+44>:  call  %l0
+	<sigacthandler+48>:  mov  %i2, %o2
+	<sigacthandler+52>:  cmp  %i3, 8	<--- PC  */
+      (   pc[-7] == 0x9401400f
+       && pc[-6] == 0xca5aafa0
+       && pc[-5] == 0x913e2000
+       && pc[-4] == 0x892a3003
+       && pc[-3] == 0xe0590005
+       && pc[-2] == 0x9fc40000
+       && pc[-1] == 0x9410001a
+       && pc[ 0] == 0x80a6e008)
 
-  /* This is the observed pattern for the sigacthandler in Solaris 8.  */
-  unsigned int sigacthandler_sol8_pattern []
-    = {0x9401400f, 0xca5aafa0, 0x913e2000, 0x892a3003,
-       0xe0590005, 0x9fc40000, 0x9410001a, 0x80a6e008};
+      || /* Solaris 9 - single-threaded
+	   ----------------------------
+	   The pattern changes slightly in different versions of the
+	   operating system, so we skip the comparison against pc[-6] for
+	   Solaris 9.
 
-  /* This is the observed pattern for the sigacthandler in Solaris 9.  */ 
-  unsigned int sigacthandler_sol9_pattern []
-    = {0xa33e2000, 0x00000000, 0x892c7003, 0x90100011,
-       0xe0590005, 0x9fc40000, 0x9410001a, 0x80a46008};
+	   <sigacthandler+24>:  sra  %i0, 0, %l1
 
-  /* This is the observed pattern for the __sighndlr.  */
-  unsigned int sighndlr_pattern []
-    = {0x9de3bf50, 0x90100018, 0x92100019, 0x9fc6c000,
-       0x9410001a, 0x81c7e008, 0x81e80000};
+	   Solaris 9 5/02:
+	   <sigacthandler+28>:  ldx  [ %o2 + 0xf68 ], %g5
+	   Solaris 9 9/05:
+	   <sigacthandler+28>:  ldx  [ %o2 + 0xe50 ], %g5
 
-  /* Deal with frame-less function from which a signal was raised.  */
-  if (_Unwind_IsSignalFrame (context))
+	   <sigacthandler+32>:  sllx  %l1, 3, %g4
+	   <sigacthandler+36>:  mov  %l1, %o0
+	   <sigacthandler+40>:  ldx  [ %g4 + %g5 ], %l0
+	   <sigacthandler+44>:  call  %l0
+	   <sigacthandler+48>:  mov  %i2, %o2
+	   <sigacthandler+52>:  cmp  %l1, 8	<--- PC  */
+      (   pc[-7] == 0xa33e2000
+       /* skip pc[-6] */
+       && pc[-5] == 0x892c7003
+       && pc[-4] == 0x90100011
+       && pc[-3] == 0xe0590005
+       && pc[-2] == 0x9fc40000
+       && pc[-1] == 0x9410001a
+       && pc[ 0] == 0x80a46008))
     {
-      /* The CFA is by definition unmodified in this case.  */
-      fs->regs.cfa_how = CFA_REG_OFFSET;
-      fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
-      fs->regs.cfa_offset = 0;
+      /* We need to move up one frame:
 
-      /* This is the canonical RA column.  */
-      fs->retaddr_column = 15;
-
-      return _URC_NO_REASON;
+		<signal handler>	<-- context->cfa
+		sigacthandler
+		<kernel>
+      */
+      *nframes = 1;
+      return 1;
     }
 
-  /* Look for the sigacthandler pattern.  The pattern changes slightly
-     in different versions of the operating system, so we skip the
-     comparison against pc-(4*6) for Solaris 9.  */
-  if ((    *(unsigned int *)(pc-(4*7)) == sigacthandler_sol8_pattern[0]
-	&& *(unsigned int *)(pc-(4*6)) == sigacthandler_sol8_pattern[1]
-	&& *(unsigned int *)(pc-(4*5)) == sigacthandler_sol8_pattern[2]
-	&& *(unsigned int *)(pc-(4*4)) == sigacthandler_sol8_pattern[3]
-	&& *(unsigned int *)(pc-(4*3)) == sigacthandler_sol8_pattern[4]
-	&& *(unsigned int *)(pc-(4*2)) == sigacthandler_sol8_pattern[5]
-	&& *(unsigned int *)(pc-(4*1)) == sigacthandler_sol8_pattern[6]
-	&& *(unsigned int *)(pc-(4*0)) == sigacthandler_sol8_pattern[7] ) ||
-      (    *(unsigned int *)(pc-(4*7)) == sigacthandler_sol9_pattern[0]
-	/* skip pc-(4*6) */
-	&& *(unsigned int *)(pc-(4*5)) == sigacthandler_sol9_pattern[2]
-	&& *(unsigned int *)(pc-(4*4)) == sigacthandler_sol9_pattern[3]
-	&& *(unsigned int *)(pc-(4*3)) == sigacthandler_sol9_pattern[4]
-	&& *(unsigned int *)(pc-(4*2)) == sigacthandler_sol9_pattern[5]
-	&& *(unsigned int *)(pc-(4*1)) == sigacthandler_sol9_pattern[6]
-	&& *(unsigned int *)(pc-(4*0)) == sigacthandler_sol9_pattern[7] ) )
-    /* We need to move up two frames (the kernel frame and the handler
-       frame).  Minimum stack frame size is 176 bytes (128 + 48): 128
-       bytes for spilling register window (16 extended words for in
-       and local registers), and 6 extended words to store at least
-       6 arguments to callees, The kernel frame and the sigacthandler
-       both have this minimal stack.  The ucontext_t structure is after
-       this offset.  */
-    regs_off = 176 + 176;
-
-  /* Look for the __sighndlr pattern.  */
-  else if (    *(unsigned int *)(pc-(4*5)) == sighndlr_pattern[0]
-	    && *(unsigned int *)(pc-(4*4)) == sighndlr_pattern[1]
-	    && *(unsigned int *)(pc-(4*3)) == sighndlr_pattern[2]
-	    && *(unsigned int *)(pc-(4*2)) == sighndlr_pattern[3]
-	    && *(unsigned int *)(pc-(4*1)) == sighndlr_pattern[4]
-	    && *(unsigned int *)(pc-(4*0)) == sighndlr_pattern[5]
-	    && *(unsigned int *)(pc+(4*1)) == sighndlr_pattern[6] )
+  if (/* Solaris 8+ - multi-threaded
+	----------------------------
+	<__sighndlr>:        save  %sp, -176, %sp
+	<__sighndlr+4>:      mov  %i0, %o0
+	<__sighndlr+8>:      mov  %i1, %o1
+	<__sighndlr+12>:     call  %i3
+	<__sighndlr+16>:     mov  %i2, %o2
+	<__sighndlr+20>:     ret 		<--- PC
+	<__sighndlr+24>:     restore  */
+         pc[-5] == 0x9de3bf50
+      && pc[-4] == 0x90100018
+      && pc[-3] == 0x92100019
+      && pc[-2] == 0x9fc6c000
+      && pc[-1] == 0x9410001a
+      && pc[ 0] == 0x81c7e008
+      && pc[ 1] == 0x81e80000)
     {
-      /* We have observed different calling frames among different
-	 versions of the operating system, so that we need to
-	 discriminate using the upper frame.  We look for the return
-	 address of the caller frame (there is an offset of 15 double
-	 words between the frame address and the place where this return
-	 address is stored) in order to do some more pattern matching.  */
-      unsigned int cuh_pattern
-	= *(unsigned int *)(*(unsigned long *)(this_cfa + 15*8) - 4);
+      if (/* Solaris 8 /usr/lib/sparcv9/libthread.so.1
+	    ------------------------------------------
+	    Before patch 108827-08:
+	    <sigacthandler+1760>:     st  %g4, [ %i1 + 0x1c ]
 
-      if (cuh_pattern == 0xd25fa7ef)
+	    Since patch 108827-08:
+	    <sigacthandler+1816>:     st  %l0, [ %i4 + 0x10 ]  */
+	        savpc[-1] == 0xc826601c
+	     || savpc[-1] == 0xe0272010)
 	{
-	  /* This matches the call_user_handler pattern for Solaris 10.
-	     There are 2 cases so we look for the return address of the
-	     caller's caller frame in order to do more pattern matching.  */
-	  unsigned int sah_pattern
-	    = *(unsigned int *)(*(unsigned long *)(this_cfa + 176 + 15*8) - 4);
+	  /* We need to move up three frames:
 
-          if (sah_pattern == 0x92100019)
-	    /* This is the same setup as for Solaris 9, see below.  */
-	    regs_off = 176 + 176 + 176 + 304;
-	  else
-	    /* We need to move up three frames (the kernel frame, the
-	       call_user_handler frame, the __sighndlr frame).  Two of them
-	       have the minimum stack frame size (kernel and __sighndlr
-	       frames) of 176 bytes, and there is another with a stack frame
-	       of 304 bytes (the call_user_handler frame).  The ucontext_t
-	       structure is after this offset.  */
-	    regs_off = 176 + 176 + 304;
+		<signal handler>	<-- context->cfa
+		__sighndlr
+		sigacthandler
+		<kernel>
+	  */
+	  *nframes = 2;
 	}
-      else if (cuh_pattern == 0x9410001a || cuh_pattern == 0x94100013)
-	/* This matches the call_user_handler pattern for Solaris 9 and
-	   for Solaris 8 running inside Solaris Containers respectively.
-	   We need to move up four frames (the kernel frame, the signal
-	   frame, the call_user_handler frame, the __sighndlr frame).
-	   Three of them have the minimum stack frame size (kernel,
-	   signal, and __sighndlr frames) of 176 bytes, and there is
-	   another with a stack frame of 304 bytes (the call_user_handler
-	   frame).  The ucontext_t structure is after this offset.  */
-	regs_off = 176 + 176 + 176 + 304;
-      else
-	/* We need to move up three frames (the kernel frame, the
-	   sigacthandler frame, and the __sighndlr frame).  The kernel
-	   frame has a stack frame size of 176, the __sighndlr frames of
-	   304 bytes, and there is a stack frame of 176 bytes for the
-	   sigacthandler frame.  The ucontext_t structure is after this
-	   offset.  */
-	regs_off = 176 + 304 + 176;
-    }
-
-  /* Exit if the pattern at the return address does not match the
-     previous three patterns.  */
-  else
-    return _URC_END_OF_STACK;
-
-  /* FPU information can be extracted from the ucontext_t structure 
-     that is the third argument for the signal handler, that is saved
-     in the stack.  There are 64 bytes between the beginning of the
-     ucontext_t argument of the signal handler and the uc_mcontext
-     field.  There are 176 bytes between the beginning of uc_mcontext
-     and the beginning of the fpregs field.  */
-  fpu_save_off = regs_off + (8*10) + 176;
-
-  /* The fpregs field contains 32 extended words at the beginning that
-     contain the FPU state.  Then there are 2 extended words and two
-     bytes.  */
-  fpu_save = *(unsigned char *)(this_cfa + fpu_save_off + (8*32) + (2*8) + 2);
-
-  /* We need to get the frame pointer for the kernel frame that
-     executes when the signal is raised.  This frame is just the
-     following to the application code that generated the signal, so
-     that the later's stack pointer is the former's frame pointer.
-     The stack pointer for the interrupted application code can be
-     calculated from the ucontext_t structure (third argument for the
-     signal handler) that is saved in the stack.  There are 10 words
-     between the beginning of the  ucontext_t argument  of the signal
-     handler and the uc_mcontext.gregs field that contains the
-     registers saved by the signal handler.  */
-  new_cfa = *(void **)(this_cfa + regs_off + (8*10) + (REG_SP*8));
-  /* The frame address is %sp + STACK_BIAS in 64-bit mode. */
-  new_cfa += 2047;
-  fs->regs.cfa_how = CFA_REG_OFFSET;
-  fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
-  fs->regs.cfa_offset = new_cfa - this_cfa;
-
-  /* Restore global and out registers (in this order) from the
-     ucontext_t structure, uc_mcontext.gregs field.  */
-  for (i = 1; i < 16; i++)
-    {
-      /* We never restore %sp as everything is purely CFA-based.  */
-      if ((unsigned int) i == __builtin_dwarf_sp_column ())
-	continue;
-
-      /* First the global registers and then the out registers.  */
-      fs->regs.reg[i].how = REG_SAVED_OFFSET;
-      fs->regs.reg[i].loc.offset
-	= this_cfa + regs_off + (8*10) + ((REG_Y+i)*8) - new_cfa;
-    }
-
-  /* Just above the stack pointer there are 16 extended words in which
-     the register window (in and local registers) was saved.  */
-  for (i = 0; i < 16; i++)
-    {
-      fs->regs.reg[i + 16].how = REG_SAVED_OFFSET;
-      fs->regs.reg[i + 16].loc.offset = i*8;
-    }
-
-  /* Check whether we need to restore FPU registers.  */
-  if (fpu_save)
-    {
-      for (i = 0; i < 64; i++)
+      else /* Solaris 8 /usr/lib/lwp/sparcv9/libthread.so.1, Solaris 9+
+	     ----------------------------------------------------------  */
 	{
-	  if (i > 32 && (i & 1))
-	    continue;
+	  /* We need to move up three frames:
 
-	  fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
-	  fs->regs.reg[i + 32].loc.offset
-	    = this_cfa + fpu_save_off + (i*4) - new_cfa;
+		<signal handler>	<-- context->cfa
+		__sighndlr
+		call_user_handler
+		sigacthandler
+		<kernel>
+	  */
+	  *nframes = 3;
 	}
+      return 1;
     }
 
-  /* State the rules to find the kernel's code "return address", which is
-     the address of the active instruction when the signal was caught.
-     On the SPARC, since RETURN_ADDR_OFFSET (essentially 8) is defined, we
-     need to preventively subtract it from the purported return address.  */
-  ra_location = this_cfa + regs_off + (8*10) + (REG_PC*8);
-  shifted_ra_location = this_cfa + regs_off + (8*10) + (REG_Y*8);
-  *(void **)shifted_ra_location = *(void **)ra_location - 8;
-  fs->retaddr_column = 0;
-  fs->regs.reg[0].how = REG_SAVED_OFFSET;
-  fs->regs.reg[0].loc.offset = shifted_ra_location - new_cfa;
-  fs->signal_frame = 1;
-
-  return _URC_NO_REASON;
+  return 0;
 }
 
+#define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
+
 #define MD_FROB_UPDATE_CONTEXT sparc64_frob_update_context
 
 static void
@@ -258,42 +164,150 @@ 
       && fs->regs.cfa_how == CFA_REG_OFFSET
       && fs->regs.cfa_offset != 0
       && !fs->signal_frame)
-    context->cfa -= 2047;
+    context->cfa -= STACK_BIAS;
 }
 
 #else
 
+#define IS_SIGHANDLER sparc_is_sighandler
+
+static int
+sparc_is_sighandler (unsigned int *pc, unsigned int * savpc, int *nframes)
+{
+  if (/* Solaris 8, 9 - single-threaded
+        -------------------------------
+	The pattern changes slightly in different versions of the operating
+	system, so we skip the comparison against pc[-6].
+
+	<sigacthandler+16>:  add  %o1, %o7, %o3
+	<sigacthandler+20>:  mov  %i1, %o1
+
+	<sigacthandler+24>:  ld  [ %o3 + <offset> ], %o2
+
+	<sigacthandler+28>:  sll  %i0, 2, %o0
+	<sigacthandler+32>:  ld  [ %o0 + %o2 ], %l0
+	<sigacthandler+36>:  mov  %i0, %o0
+	<sigacthandler+40>:  call  %l0
+	<sigacthandler+44>:  mov  %i2, %o2
+	<sigacthandler+48>:  cmp  %i0, 8	<--- PC  */
+         pc[-8] == 0x9602400f
+      && pc[-7] == 0x92100019
+      /* skip pc[-6] */
+      && pc[-5] == 0x912e2002
+      && pc[-4] == 0xe002000a
+      && pc[-3] == 0x90100018
+      && pc[-2] == 0x9fc40000
+      && pc[-1] == 0x9410001a
+      && pc[ 0] == 0x80a62008)
+    {
+      /* Need to move up one frame:
+
+		<signal handler>	<-- context->cfa
+		sigacthandler
+		<kernel>
+      */
+      *nframes = 1;
+      return 1;
+    }
+
+  if (/* Solaris 8 - multi-threaded
+	---------------------------
+	<__libthread_segvhdlr+212>:  clr  %o2
+	<__libthread_segvhdlr+216>:  ld  [ %fp + -28 ], %l0
+	<__libthread_segvhdlr+220>:  mov  %i4, %o0
+	<__libthread_segvhdlr+224>:  mov  %i1, %o1
+	<__libthread_segvhdlr+228>:  call  %l0
+	<__libthread_segvhdlr+232>:  mov  %i2, %o2
+	<__libthread_segvhdlr+236>:  ret		<--- PC
+	<__libthread_segvhdlr+240>:  restore
+	<__libthread_segvhdlr+244>:  cmp  %o1, 0  */
+         pc[-6] == 0x94102000
+      && pc[-5] == 0xe007bfe4
+      && pc[-4] == 0x9010001c
+      && pc[-3] == 0x92100019
+      && pc[-2] == 0x9fc40000
+      && pc[-1] == 0x9410001a
+      && pc[ 0] == 0x81c7e008
+      && pc[ 1] == 0x81e80000
+      && pc[ 2] == 0x80a26000)
+    {
+      /* Need to move up one frame:
+
+		<signal handler>	<-- context->cfa
+		__libthread_segvhdlr
+		<kernel>
+      */
+      *nframes = 1;
+      return 1;
+    }
+
+  if(/* Solaris 8+ - multi-threaded
+       ----------------------------
+       <__sighndlr>:	save  %sp, -96, %sp
+       <__sighndlr+4>:	mov  %i0, %o0
+       <__sighndlr+8>:	mov  %i1, %o1
+       <__sighndlr+12>:	call  %i3
+       <__sighndlr+16>:	mov  %i2, %o2
+       <__sighndlr+20>:	ret 		<--- PC
+       <__sighndlr+24>:	restore  */
+        pc[-5] == 0x9de3bfa0
+     && pc[-4] == 0x90100018
+     && pc[-3] == 0x92100019
+     && pc[-2] == 0x9fc6c000
+     && pc[-1] == 0x9410001a
+     && pc[ 0] == 0x81c7e008
+     && pc[ 1] == 0x81e80000)
+    {
+      if (/* Solaris 8 /usr/lib/libthread.so.1
+	    ----------------------------------
+	    <sigacthandler+1796>:     mov  %i0, %o0  */
+	  savpc[-1] == 0x90100018)
+	{
+	  /* We need to move up two frames:
+
+		<signal handler>	<-- context->cfa
+		__sighndlr
+		sigacthandler
+		<kernel>
+	  */
+	  *nframes = 2;
+	}
+      else /* Solaris 8 /usr/lib/lwp/libthread.so.1, Solaris 9+
+	     --------------------------------------------------  */
+	{
+	  /* We need to move up three frames:
+
+		<signal handler>	<-- context->cfa
+		__sighndlr
+		call_user_handler
+		sigacthandler
+		<kernel>
+	  */
+	  *nframes = 3;
+	}
+      return 1;
+    }
+
+  return 0;
+}
+
 #define MD_FALLBACK_FRAME_STATE_FOR sparc_fallback_frame_state
 
+#endif
+
 static _Unwind_Reason_Code
-sparc_fallback_frame_state (struct _Unwind_Context *context,
-			    _Unwind_FrameState *fs)
+MD_FALLBACK_FRAME_STATE_FOR (struct _Unwind_Context *context,
+			     _Unwind_FrameState *fs)
 {
   void *pc = context->ra;
+  struct frame *fp = (struct frame *) context->cfa;
+  int nframes;
   void *this_cfa = context->cfa;
-  void *new_cfa, *ra_location, *shifted_ra_location;
-  int regs_off;
-  int fpu_save_off;
-  unsigned char fpu_save;
+  long new_cfa;
+  void *ra_location, *shifted_ra_location;
+  mcontext_t *mctx;
   int i;
 
-  /* This is the observed pattern for the sigacthandler.  */
-  unsigned int sigacthandler_pattern []
-    = {0x9602400f, 0x92100019, 0x00000000, 0x912e2002,
-       0xe002000a, 0x90100018, 0x9fc40000, 0x9410001a,
-       0x80a62008};
-
-  /* This is the observed pattern for the __libthread_segvhdlr.  */
-  unsigned int segvhdlr_pattern []
-    = {0x94102000, 0xe007bfe4, 0x9010001c, 0x92100019,
-       0x9fc40000, 0x9410001a, 0x81c7e008, 0x81e80000,
-       0x80a26000};
-
-  /* This is the observed pattern for the __sighndlr.  */
-  unsigned int sighndlr_pattern []
-    = {0x9de3bfa0, 0x90100018, 0x92100019, 0x9fc6c000,
-       0x9410001a, 0x81c7e008, 0x81e80000};
-
   /* Deal with frame-less function from which a signal was raised.  */
   if (_Unwind_IsSignalFrame (context))
     {
@@ -308,102 +322,27 @@ 
       return _URC_NO_REASON;
     }
 
-  /* Look for the sigacthandler pattern.  The pattern changes slightly
-     in different versions of the operating system, so we skip the
-     comparison against pc-(4*6).  */
-  if (    *(unsigned int *)(pc-(4*8)) == sigacthandler_pattern[0]
-       && *(unsigned int *)(pc-(4*7)) == sigacthandler_pattern[1]
-       /* skip pc-(4*6) */
-       && *(unsigned int *)(pc-(4*5)) == sigacthandler_pattern[3]
-       && *(unsigned int *)(pc-(4*4)) == sigacthandler_pattern[4]
-       && *(unsigned int *)(pc-(4*3)) == sigacthandler_pattern[5]
-       && *(unsigned int *)(pc-(4*2)) == sigacthandler_pattern[6]
-       && *(unsigned int *)(pc-(4*1)) == sigacthandler_pattern[7]
-       && *(unsigned int *)(pc-(4*0)) == sigacthandler_pattern[8] )
-    /* We need to move up two frames (the kernel frame and the handler
-       frame).  Minimum stack frame size is 96 bytes (64 + 4 + 24): 64
-       bytes for spilling register window (16 words for in and local
-       registers), 4 bytes for a pointer to space for callees
-       returning structs, and 24 bytes to store at least six argument
-       to callees.  The ucontext_t structure is after this offset.  */
-    regs_off = 96 + 96;
+  if (IS_SIGHANDLER (pc, (unsigned int *)fp->fr_savpc, &nframes))
+    {
+      struct handler_args {
+	struct frame frwin;
+	ucontext_t ucontext;
+      } *handler_args;
+      ucontext_t *ucp;
 
-  /* Look for the __libthread_segvhdlr pattern.  */
-  else if (    *(unsigned int *)(pc-(4*6)) == segvhdlr_pattern[0]
-	    && *(unsigned int *)(pc-(4*5)) == segvhdlr_pattern[1]
-	    && *(unsigned int *)(pc-(4*4)) == segvhdlr_pattern[2]
-	    && *(unsigned int *)(pc-(4*3)) == segvhdlr_pattern[3]
-	    && *(unsigned int *)(pc-(4*2)) == segvhdlr_pattern[4]
-	    && *(unsigned int *)(pc-(4*1)) == segvhdlr_pattern[5]
-	    && *(unsigned int *)(pc-(4*0)) == segvhdlr_pattern[6]
-	    && *(unsigned int *)(pc+(4*1)) == segvhdlr_pattern[7]
-	    && *(unsigned int *)(pc+(4*2)) == segvhdlr_pattern[8] )
-    /* We need to move up four frames (the kernel frame, the
-       sigacthandler frame, the __sighndlr frame, and the
-       __libthread_segvhdlr).  Two of them have the minimum
-       stack frame size (kernel and __sighndlr frames) of 96 bytes,
-       other has a stack frame of 216 bytes (the sigacthandler frame),
-       and there is another with a stack frame of 128 bytes (the
-       __libthread_segvhdlr).  The ucontext_t structure is after this
-       offset.  */
-    regs_off = 96 + 96 + 128 + 216;
+      /* context->cfa points into the frame after the saved frame pointer and
+         saved pc (struct frame).
 
-  /* Look for the __sighndlr pattern.  */
-  else if (    *(unsigned int *)(pc-(4*5)) == sighndlr_pattern[0]
-	    && *(unsigned int *)(pc-(4*4)) == sighndlr_pattern[1]
-	    && *(unsigned int *)(pc-(4*3)) == sighndlr_pattern[2]
-	    && *(unsigned int *)(pc-(4*2)) == sighndlr_pattern[3]
-	    && *(unsigned int *)(pc-(4*1)) == sighndlr_pattern[4]
-	    && *(unsigned int *)(pc-(4*0)) == sighndlr_pattern[5]
-	    && *(unsigned int *)(pc+(4*1)) == sighndlr_pattern[6] )
-    {
-      /* We have observed different calling frames among different
-	 versions of the operating system, so that we need to
-	 discriminate using the upper frame.  We look for the return
-	 address of the caller frame (there is an offset of 15 words
-	 between the frame address and the place where this return
-	 address is stored) in order to do some more pattern matching.  */
-      unsigned int cuh_pattern
-	= *(unsigned int *)(*(unsigned int *)(this_cfa + 15*4) - 4);
+         The ucontext_t structure is in the kernel frame after a struct
+         frame.  Since the frame sizes vary even within OS releases, we
+         need to walk the stack to get there.  */
 
-      if (cuh_pattern == 0xd407a04c)
-	{
-	  /* This matches the call_user_handler pattern for Solaris 10.
-	     There are 2 cases so we look for the return address of the
-	     caller's caller frame in order to do more pattern matching.  */
-	  unsigned int sah_pattern
-	    = *(unsigned int *)(*(unsigned int *)(this_cfa + 96 + 15*4) - 4);
+      for (i = 0; i < nframes; i++)
+	fp = (struct frame *) ((char *)fp->fr_savfp + STACK_BIAS);
 
-          if (sah_pattern == 0x92100019)
-	    /* This is the same setup as for Solaris 9, see below.  */
-	    regs_off = 96 + 96 + 96 + 160;
-	  else
-	    /* We need to move up three frames (the kernel frame, the
-	       call_user_handler frame, the __sighndlr frame).  Two of them
-	       have the minimum stack frame size (kernel and __sighndlr
-	       frames) of 96 bytes, and there is another with a stack frame
-	       of 160 bytes (the call_user_handler frame).  The ucontext_t
-	       structure is after this offset.  */
-	    regs_off = 96 + 96 + 160;
-	}
-      else if (cuh_pattern == 0x9410001a || cuh_pattern == 0x9410001b)
-	/* This matches the call_user_handler pattern for Solaris 9 and
-	   for Solaris 8 running inside Solaris Containers respectively.
-	   We need to move up four frames (the kernel frame, the signal
-	   frame, the call_user_handler frame, the __sighndlr frame).
-	   Three of them have the minimum stack frame size (kernel,
-	   signal, and __sighndlr frames) of 96 bytes, and there is
-	   another with a stack frame of 160 bytes (the call_user_handler
-	   frame).  The ucontext_t structure is after this offset.  */
-	regs_off = 96 + 96 + 96 + 160;
-      else
-	/* We need to move up three frames (the kernel frame, the
-	   sigacthandler frame, and the __sighndlr frame).  Two of them
-	   have the minimum stack frame size (kernel and __sighndlr
-	   frames) of 96 bytes, and there is another with a stack frame
-	   of 216 bytes (the sigacthandler frame).  The ucontext_t 
-	   structure is after this offset.  */
-	regs_off = 96 + 96 + 216;
+      handler_args = (struct handler_args *) fp;
+      ucp = &handler_args->ucontext;
+      mctx = &ucp->uc_mcontext;
     }
 
   /* Exit if the pattern at the return address does not match the
@@ -411,32 +350,13 @@ 
   else
     return _URC_END_OF_STACK;
 
-  /* FPU information can be extracted from the ucontext_t structure
-     that is the third argument for the signal handler, that is saved
-     in the stack.  There are 10 words between the beginning of the
-     ucontext_t argument of the signal handler and the uc_mcontext
-     field.  There are 80 bytes between the beginning of uc_mcontext
-     and the beginning of the fpregs field.  */
-  fpu_save_off = regs_off + (4*10) + (4*20);
+  new_cfa = mctx->gregs[REG_SP];
+  /* The frame address is %sp + STACK_BIAS in 64-bit mode.  */
+  new_cfa += STACK_BIAS;
 
-  /* The fpregs field contains 32 words at the beginning that contain
-     the FPU state.  Then there are 2 words and two bytes.  */
-  fpu_save = *(unsigned char *)(this_cfa + fpu_save_off + (4*32) + (2*4) + 2);
-
-  /* We need to get the frame pointer for the kernel frame that
-     executes when the signal is raised.  This frame is just the
-     following to the application code that generated the signal, so
-     that the later's stack pointer is the former's frame pointer.
-     The stack pointer for the interrupted application code can be
-     calculated from the ucontext_t structure (third argument for the
-     signal handler) that is saved in the stack.  There are 10 words
-     between the beginning of the  ucontext_t argument  of the signal
-     handler and the uc_mcontext.gregs field that contains the
-     registers saved by the signal handler.  */
-  new_cfa = *(void **)(this_cfa + regs_off + (4*10) + (REG_SP*4));
   fs->regs.cfa_how = CFA_REG_OFFSET;
   fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
-  fs->regs.cfa_offset = new_cfa - this_cfa;
+  fs->regs.cfa_offset = new_cfa - (long) this_cfa;
 
   /* Restore global and out registers (in this order) from the
      ucontext_t structure, uc_mcontext.gregs field.  */
@@ -446,44 +366,54 @@ 
       if ((unsigned int) i == __builtin_dwarf_sp_column ())
 	continue;
 
-      /* First the global registers and then the out registers */
+      /* First the global registers and then the out registers.  */
       fs->regs.reg[i].how = REG_SAVED_OFFSET;
-      fs->regs.reg[i].loc.offset
-	= this_cfa + regs_off + (4*10) + ((REG_Y+i)*4) - new_cfa;
+      fs->regs.reg[i].loc.offset = (long)&mctx->gregs[REG_Y + i] - new_cfa;
     }
 
-  /* Just above the stack pointer there are 16 words in which the
-     register window (in and local registers) was saved.  */
+  /* Just above the stack pointer there are 16 extended words in which
+     the register window (in and local registers) was saved.  */
   for (i = 0; i < 16; i++)
     {
       fs->regs.reg[i + 16].how = REG_SAVED_OFFSET;
-      fs->regs.reg[i + 16].loc.offset = i*4;
+      fs->regs.reg[i + 16].loc.offset = i*sizeof(long);
     }
 
   /* Check whether we need to restore FPU registers.  */
-  if (fpu_save)
+  if (mctx->fpregs.fpu_qcnt)
     {
       for (i = 0; i < 32; i++)
 	{
 	  fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
 	  fs->regs.reg[i + 32].loc.offset
-	    = this_cfa + fpu_save_off + (i*4) - new_cfa;
+	    = (long)&mctx->fpregs.fpu_fr.fpu_regs[i] - new_cfa;
 	}
+
+#ifdef __arch64__
+      /* For 64-bit, fpu_fr.fpu_dregs contains 32 instead of 16 doubles.  */
+      for (i = 32; i < 64; i++)
+	{
+	  if (i > 32 && (i & 1))
+	    continue;
+
+	  fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
+	  fs->regs.reg[i + 32].loc.offset
+	    = (long)&mctx->fpregs.fpu_fr.fpu_dregs[i/2] - new_cfa;
+	}
+#endif
     }
 
   /* State the rules to find the kernel's code "return address", which is
      the address of the active instruction when the signal was caught.
      On the SPARC, since RETURN_ADDR_OFFSET (essentially 8) is defined, we
      need to preventively subtract it from the purported return address.  */
-  ra_location = this_cfa + regs_off + (4*10) + (REG_PC*4);
-  shifted_ra_location = this_cfa + regs_off + (4*10) + (REG_Y*4);
+  ra_location = &mctx->gregs[REG_PC];
+  shifted_ra_location = &mctx->gregs[REG_Y];
   *(void **)shifted_ra_location = *(void **)ra_location - 8;
   fs->retaddr_column = 0;
   fs->regs.reg[0].how = REG_SAVED_OFFSET;
-  fs->regs.reg[0].loc.offset = shifted_ra_location - new_cfa;
+  fs->regs.reg[0].loc.offset = (long)shifted_ra_location - new_cfa;
   fs->signal_frame = 1;
 
   return _URC_NO_REASON;
-};
-
-#endif
+}
Index: gcc/testsuite/gnat.dg/null_pointer_deref1.adb
===================================================================
--- gcc/testsuite/gnat.dg/null_pointer_deref1.adb	(revision 170124)
+++ gcc/testsuite/gnat.dg/null_pointer_deref1.adb	(working copy)
@@ -1,4 +1,4 @@ 
--- { dg-do run { target { ! "sparc*-sun-solaris2.11" } } }
+-- { dg-do run }
 -- { dg-options "-gnatp" }
 
 -- This test requires architecture- and OS-specific support code for unwinding