[Ada] improve unwinding of exception through signal handles on hp-ux

Arnaud Charlet charlet@adacore.com
Tue Mar 15 15:02:00 GMT 2005


Manually tested under pa-hpux, ia64-vms.
Tested on i686-linux. Committed on mainline.

The PC values the GCC unwinder sees when propagating an exception usually
are call return addresses. At each step, we are interested in checking the
EH region of the call itself, so the PC value at hand is adjusted to search
for the right region. When unwinding through a signal handler, the PC value
we get is most often the address of the faulting instruction. This is the
the instruction of interest eh-region-wise to start with, so the unwinder's
adjustment should be avoided in this case. The unwinder is not able to do
that properly, though, so it always adjusts and we need to compensate for
this. The scheme here is to tweak the PC value before the unwinder sees it,
from the signal handler as achieved for Java. We are using
__gnat_adjust_context_for_raise for that very purpose.

In addition, provide user flexibility in the choice of VMS conditions to
resignal, which used to be hardcoded and thus impossible to control.

Test program (should display 'Got exception!' 10 times):
with Ada.Text_IO; use Ada.Text_IO;
with System; use System;

procedure Simple is
   A : Integer; for A'Address use System.Null_Address;
begin
   for K in 1 .. 10 loop
      begin
         A := 1;
      exception
         when others => Put_Line ("Got exception!");
      end;
   end loop;
end Simple;


2005-03-08  Olivier Hainque  <hainque@adacore.com>

	* s-intman-posix.adb (Notify_Exception): Adjust signature, as handler
	for sigactions with SA_SIGINFO set. Call
	__gnat_adjust_context_for_raise before raising, to perform the
	potentially required adjustments to the machine context for the GCC
	unwinder.

	* raise.h (__gnat_adjust_context_for_raise): New prototype.

	* init.c (__gnat_adjust_context_for_raise) HPUX: Initial revision.
	Adjust PC by one in the provided machine context.
	(__gnat_install_handler) HPUX: Set SA_SIGINFO in the sigaction flags,
	so that the handler is passed the context structure to adjust prior to
	the raise.
	(__gnat_error_handler) HPUX: Adjust the signature to match what an
	SA_SIGINFO sigaction should look like. Call
	__gnat_adjust_context_for_raise before actually raising.
	(__gnat_adjust_context_for_raise): Default noop to help PC
	adjustments before raise from signal handlers.
	(__gnat_error_handler): Indirectly call a predicate function to
	determine if a condition should be resignaled or not.
	(__gnat_set_resignal_predicate): User interface to modify the predicate.
	(__gnat_default_resignal_p): Default GNAT predicate.

-------------- next part --------------
Index: s-intman-posix.adb
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ada/s-intman-posix.adb,v
retrieving revision 1.1
diff -u -p -r1.1 s-intman-posix.adb
--- s-intman-posix.adb	14 May 2004 10:02:00 -0000	1.1
+++ s-intman-posix.adb	15 Mar 2005 13:54:40 -0000
@@ -6,7 +6,7 @@
 --                                                                          --
 --                                  B o d y                                 --
 --                                                                          --
---          Copyright (C) 1992-2003, Free Software Foundation, Inc.         --
+--          Copyright (C) 1992-2005, Free Software Foundation, Inc.         --
 --                                                                          --
 -- GNARL is free software; you can  redistribute it  and/or modify it under --
 -- terms of the  GNU General Public License as published  by the Free Soft- --
@@ -88,7 +88,10 @@ package body System.Interrupt_Management
    -- Local Subprograms --
    -----------------------
 
-   procedure Notify_Exception (signo : Signal);
+   procedure Notify_Exception
+     (signo    : Signal;
+      siginfo  : System.Address;
+      ucontext : System.Address);
    --  This function identifies the Ada exception to be raised using
    --  the information when the system received a synchronous signal.
    --  Since this function is machine and OS dependent, different code
@@ -101,7 +104,24 @@ package body System.Interrupt_Management
    Signal_Mask : aliased sigset_t;
    --  The set of signals handled by Notify_Exception
 
-   procedure Notify_Exception (signo : Signal) is
+   procedure Notify_Exception
+     (signo    : Signal;
+      siginfo  : System.Address;
+      ucontext : System.Address)
+   is
+      pragma Unreferenced (siginfo);
+
+      --  The GCC unwinder requires adjustments to the signal's machine
+      --  context to be able to properly unwind through the signal handler.
+      --  This is achieved by the target specific subprogram below, provided
+      --  by init.c to be usable by the non-tasking handler also.
+
+      procedure Adjust_Context_For_Raise
+        (signo    : Signal;
+         ucontext : System.Address);
+      pragma Import
+        (C, Adjust_Context_For_Raise, "__gnat_adjust_context_for_raise");
+
       Result  : Interfaces.C.int;
 
    begin
@@ -111,6 +131,11 @@ package body System.Interrupt_Management
       Result := pthread_sigmask (SIG_UNBLOCK, Signal_Mask'Access, null);
       pragma Assert (Result = 0);
 
+      --  Perform the necessary context adjustments required by the GCC/ZCX
+      --  unwinder, harmless in the SJLJ case.
+
+      Adjust_Context_For_Raise (signo, ucontext);
+
       --  Check that treatment of exception propagation here
       --  is consistent with treatment of the abort signal in
       --  System.Task_Primitives.Operations.
@@ -179,12 +204,12 @@ begin
 
       --  Setting SA_SIGINFO asks the kernel to pass more than just the signal
       --  number argument to the handler when it is called. The set of extra
-      --  parameters typically includes a pointer to a structure describing
-      --  the interrupted context. Although the Notify_Exception handler does
-      --  not use this information, it is actually required for the GCC/ZCX
-      --  exception propagation scheme because on some targets (at least
-      --  alpha-tru64), the structure contents are not even filled when this
-      --  flag is not set.
+      --  parameters includes a pointer to the interrupted context, which the
+      --  ZCX propagation scheme needs.
+
+      --  Most man pages for sigaction mention that sa_sigaction should be set
+      --  instead of sa_handler when SA_SIGINFO is on.  In practice, the two
+      --  fields are actually union'ed and located at the same offset.
 
       --  On some targets, we set sa_flags to SA_NODEFER so that during the
       --  handler execution we do not change the Signal_Mask to be masked for
Index: raise.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ada/raise.h,v
retrieving revision 1.10
diff -u -p -r1.10 raise.h
--- raise.h	10 Feb 2005 13:54:45 -0000	1.10
+++ raise.h	15 Mar 2005 13:54:40 -0000
@@ -70,5 +70,6 @@ extern void __gnat_initialize		(void *);
 extern void __gnat_init_float		(void);
 extern void __gnat_install_handler	(void);
 extern void __gnat_install_SEH_handler  (void *);
+extern void __gnat_adjust_context_for_raise (int, void *);
 
 extern int gnat_exit_status;
Index: init.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ada/init.c,v
retrieving revision 1.44
diff -u -p -r1.44 init.c
--- init.c	10 Feb 2005 15:43:53 -0000	1.44
+++ init.c	15 Mar 2005 13:54:40 -0000
@@ -547,7 +547,7 @@ __gnat_initialize (void *eh ATTRIBUTE_UN
 {
 }
 
-/* Routines called by 5amastop.adb.  */
+/* Routines called by s-mastop-tru64.adb.  */
 
 #define SC_GP 29
 
@@ -558,7 +558,7 @@ __gnat_get_code_loc (struct sigcontext *
 }
 
 void
-__gnat_enter_handler ( struct sigcontext *context, char *pc)
+__gnat_enter_handler (struct sigcontext *context, char *pc)
 {
   context->sc_pc = (long) pc;
   context->sc_regs[SC_GP] = exc_lookup_gp (pc);
@@ -578,11 +578,29 @@ __gnat_machine_state_length (void)
 #elif defined (__hpux__)
 
 #include <signal.h>
+#include <sys/ucontext.h>
 
-static void __gnat_error_handler (int);
+static void
+__gnat_error_handler (int sig, siginfo_t *siginfo, void *ucontext);
+
+/* __gnat_adjust_context_for_raise - see comments along with the default
+   version later in this file.  */
+
+#define HAVE_GNAT_ADJUST_CONTEXT_FOR_RAISE
+
+void
+__gnat_adjust_context_for_raise (int signo ATTRIBUTE_UNUSED, void *ucontext)
+{
+  mcontext_t *mcontext = &((ucontext_t *) ucontext)->uc_mcontext;
+
+  if (UseWideRegs (mcontext))
+    mcontext->ss_wide.ss_32.ss_pcoq_head_lo ++;
+  else
+    mcontext->ss_narrow.ss_pcoq_head ++;
+}
 
 static void
-__gnat_error_handler (int sig)
+__gnat_error_handler (int sig, siginfo_t *siginfo, void *ucontext)
 {
   struct Exception_Data *exception;
   char *msg;
@@ -610,6 +628,8 @@ __gnat_error_handler (int sig)
       msg = "unhandled signal";
     }
 
+  __gnat_adjust_context_for_raise (sig, ucontext);
+
   Raise_From_Signal_Handler (exception, msg);
 }
 
@@ -637,8 +657,8 @@ __gnat_install_handler (void)
 
   sigaltstack (&stack, NULL);
 
-  act.sa_handler = __gnat_error_handler;
-  act.sa_flags = SA_NODEFER | SA_RESTART | SA_ONSTACK;
+  act.sa_sigaction = __gnat_error_handler;
+  act.sa_flags = SA_NODEFER | SA_RESTART | SA_ONSTACK | SA_SIGINFO;
   sigemptyset (&act.sa_mask);
 
   /* Do not install handlers if interrupt state is "System" */
@@ -835,9 +855,16 @@ __gnat_initialize (void *eh ATTRIBUTE_UN
       implementation of __gnat_portable_no_block_spawn, __gnat_portable_wait */
    __gnat_plist_init();
 
+   /* Note that we do not activate this for the compiler itself to avoid a
+      bootstrap path problem.  Older version of gnatbind will generate a call
+      to __gnat_initialize() without argument. Therefore we cannot use eh in
+      this case.  It will be possible to remove the following #ifdef at some
+      point.  */
+#ifdef IN_RTS
    /* Install the Structured Exception handler.  */
    if (eh)
      __gnat_install_SEH_handler (eh);
+#endif
 }
 
 /***************************************/
@@ -1285,6 +1312,70 @@ struct descriptor_s {unsigned short len,
 
 long __gnat_error_handler (int *, void *);
 
+/* To deal with VMS conditions and their mapping to Ada exceptions,
+   the __gnat_error_handler routine below is installed as an exception
+   vector having precedence over DEC frame handlers.  Some conditions
+   still need to be handled by such handlers, however, in which case
+   __gnat_error_handler needs to return SS$_RESIGNAL.  Consider for
+   instance the use of a third party library compiled with DECAda and
+   performing it's own exception handling internally.
+
+   To allow some user-level flexibility, which conditions should be
+   resignaled is controlled by a predicate function, provided with the
+   condition value and returning a boolean indication stating whether
+   this condition should be resignaled or not.
+
+   That predicate function is called indirectly, via a function pointer,
+   by __gnat_error_handler, and changing that pointer is allowed to the
+   the user code by way of the __gnat_set_resignal_predicate interface.
+
+   The user level function may then implement what it likes, including
+   for instance the maintenance of a dynamic data structure if the set
+   of to be resignalled conditions has to change over the program's
+   lifetime.
+
+   ??? This is not a perfect solution to deal with the possible
+   interactions between the GNAT and the DECAda exception handling
+   models and better (more general) schemes are studied.  This is so
+   just provided as a conveniency workaround in the meantime, and
+   should be use with caution since the implementation has been kept
+   very simple.  */
+
+typedef int
+resignal_predicate (int code);
+
+/* Default GNAT predicate for resignaling conditions.  */
+
+static int
+__gnat_default_resignal_p (int code)
+{
+  return
+    code == CMA$_EXIT_THREAD
+    || code == SS$_DEBUG /* Gdb attach, resignal to merge activate gdbstub. */
+    || code == 1409786   /* Nickerson bug #33 ??? */
+    || code == 1381050   /* Nickerson bug #33 ??? */
+    || code == 20480426  /* RDB-E-STREAM_EOF */
+    || code == 11829410  /* Resignalled as Use_Error for CE10VRC */
+  ;
+}
+
+/* Static pointer to predicate that the __gnat_error_handler exception
+   vector invokes to determine if it should resignal a condition.  */
+
+static resignal_predicate * __gnat_resignal_p = __gnat_default_resignal_p;
+
+/* User interface to change the predicate pointer to PREDICATE. Reset to
+   the default if PREDICATE is null.  */
+
+void
+__gnat_set_resignal_predicate (resignal_predicate * predicate)
+{
+  if (predicate == 0)
+    __gnat_resignal_p = __gnat_default_resignal_p;
+  else
+    __gnat_resignal_p = predicate;
+}
+
 long
 __gnat_error_handler (int *sigargs, void *mechargs)
 {
@@ -1301,30 +1392,10 @@ __gnat_error_handler (int *sigargs, void
   long curr_invo_handle;
   long *mstate;
 
-  /* Resignaled condtions aren't effected by by pragma Import_Exception */
-
-  switch (sigargs[1])
-  {
-
-    case CMA$_EXIT_THREAD:
-      return SS$_RESIGNAL;
-
-    case SS$_DEBUG: /* Gdb attach, resignal to merge activate gdbstub. */
-      return SS$_RESIGNAL;
-
-    case 1409786: /* Nickerson bug #33 ??? */
-      return SS$_RESIGNAL;
-
-    case 1381050: /* Nickerson bug #33 ??? */
-      return SS$_RESIGNAL;
-
-    case 20480426: /* RDB-E-STREAM_EOF */
-      return SS$_RESIGNAL;
-
-    case 11829410: /* Resignalled as Use_Error for CE10VRC */
-      return SS$_RESIGNAL;
-
-  }
+  /* Check for conditions to resignal which aren't effected by pragma
+     Import_Exception.  */
+  if (__gnat_resignal_p (sigargs [1]))
+    return SS$_RESIGNAL;
 
 #ifdef IN_RTS
   /* See if it's an imported exception. Beware that registered exceptions
@@ -1901,3 +1972,43 @@ __gnat_init_float (void)
 {
 }
 #endif
+
+/***********************************/
+/* __gnat_adjust_context_for_raise */
+/***********************************/
+
+#ifndef HAVE_GNAT_ADJUST_CONTEXT_FOR_RAISE
+
+/* All targets without a specific version will use an empty one */
+
+/* UCONTEXT is a pointer to a context structure received by a signal handler
+   about to propagate an exception. Adjust it to compensate the fact that the
+   generic unwinder thinks the corresponding PC is a call return address.  */
+
+void
+__gnat_adjust_context_for_raise (int signo ATTRIBUTE_UNUSED,
+				 void *ucontext ATTRIBUTE_UNUSED)
+{
+  /* The point is that the interrupted context PC typically is the address
+     that we should search an EH region for, which is different from the call
+     return address case. The target independant part of the GCC unwinder
+     don't differentiate the two situations, so we compensate here for the
+     adjustments it will blindly make.
+
+     signo is passed because on some targets for some signals the PC in
+     context points to the instruction after the faulting one, in which case
+     the unwinder adjustment is still desired.  */
+
+  /* On a number of targets, we have arranged for the adjustment to be
+     performed by the MD_FALLBACK_FRAME_STATE circuitry, so we don't provide a
+     specific instance of this routine.  The MD_FALLBACK doesn't have access
+     to the signal number, though, so the compensation is systematic there and
+     might be wrong in some cases.  */
+
+  /* Having the compensation wrong leads to potential failures.  A very
+     typical case is what happens when there is no compensation and a signal
+     triggers for the first instruction in a region : the unwinder adjustment
+     has it search in the wrong EH region.  */
+}
+
+#endif


More information about the Gcc-patches mailing list