[Ada] Handle version 2 of Windows unwinding information structures
authorEric Botcazou <ebotcazou@adacore.com>
Thu, 24 May 2018 13:05:59 +0000 (13:05 +0000)
committerPierre-Marie de Rodat <pmderodat@gcc.gnu.org>
Thu, 24 May 2018 13:05:59 +0000 (13:05 +0000)
2018-05-24  Eric Botcazou  <ebotcazou@adacore.com>

gcc/ada/

* raise-gcc.c (__gnat_SEH_error_handler): Remove prototype.
(__gnat_personality_seh0): Adjust and beef up comments, and
fix formatting throughout.
(__gnat_adjust_context): Deal minimally with version 2.
* seh_init.c (__gnat_map_SEH): Fix formatting.
(_gnat_SEH_error_handler): Adjust comments.
(__gnat_install_SEH_handler): Fix formatting.

From-SVN: r260659

gcc/ada/ChangeLog
gcc/ada/raise-gcc.c
gcc/ada/seh_init.c

index cf19019..32e4b6a 100644 (file)
@@ -1,3 +1,13 @@
+2018-05-24  Eric Botcazou  <ebotcazou@adacore.com>
+
+       * raise-gcc.c (__gnat_SEH_error_handler): Remove prototype.
+       (__gnat_personality_seh0): Adjust and beef up comments, and
+       fix formatting throughout.
+       (__gnat_adjust_context): Deal minimally with version 2.
+       * seh_init.c (__gnat_map_SEH): Fix formatting.
+       (_gnat_SEH_error_handler): Adjust comments.
+       (__gnat_install_SEH_handler): Fix formatting.
+
 2018-05-24  Hristian Kirtchev  <kirtchev@adacore.com>
 
        * exp_ch7.adb, sem_ch3.adb, sem_res.adb: Minor reformatting.
index 05d1a40..7558414 100644 (file)
@@ -1457,9 +1457,6 @@ __gnat_Unwind_ForcedUnwind (_Unwind_Exception *e ATTRIBUTE_UNUSED,
        (STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC)
 #define STATUS_GCC_THROW               GCC_EXCEPTION (0)
 
-EXCEPTION_DISPOSITION __gnat_SEH_error_handler
- (struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
-
 struct Exception_Data *
 __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg);
 
@@ -1481,22 +1478,30 @@ __gnat_create_machine_occurrence_from_signal_handler (Exception_Id,
 /* Modify the IP value saved in the machine frame.  This is really a kludge,
    that will be removed if we could propagate the Windows exception (and not
    the GCC one).
+
    What is very wrong is that the Windows unwinder will try to decode the
-   instruction at IP, which isn't valid anymore after the adjust.  */
+   instruction at IP, which isn't valid anymore after the adjustment.  */
 
 static void
 __gnat_adjust_context (unsigned char *unw, ULONG64 rsp)
 {
   unsigned int len;
 
-  /* Version = 1, no flags, no prologue.  */
-  if (unw[0] != 1 || unw[1] != 0)
+  /* Version 1 or 2.  */
+  if (unw[0] != 1 && unw[0] != 2)
+    return;
+  /* No flags, no prologue.  */
+  if (unw[1] != 0)
     return;
   len = unw[2];
-  /* No frame pointer.  */
+  /* No frame.  */
   if (unw[3] != 0)
     return;
-  unw += 4;
+  /* ??? Skip the first 2 undocumented opcodes for version 2.  */
+  if (unw[0] == 2)
+    unw += 8;
+  else
+    unw += 4;
   while (len > 0)
     {
       /* Offset in prologue = 0.  */
@@ -1541,9 +1546,7 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
                         PCONTEXT ms_orig_context,
                         PDISPATCHER_CONTEXT ms_disp)
 {
-  /* Possibly transform run-time errors into Ada exceptions.  As a small
-     optimization, we call __gnat_SEH_error_handler only on non-user
-     exceptions.  */
+  /* Possibly transform run-time errors into Ada exceptions.  */
   if (!(ms_exc->ExceptionCode & STATUS_USER_DEFINED))
     {
       struct Exception_Data *exception;
@@ -1557,13 +1560,21 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
                       + ms_disp->FunctionEntry->EndAddress))
        {
          /* This is a fault in this function.  We need to adjust the return
-            address before raising the GCC exception.  */
+            address before raising the GCC exception.  In order to do that,
+            we need to locate the machine frame that has been pushed onto
+            the stack in response to the hardware exception, so we will do
+            a private unwinding from here, i.e. the frame of the personality
+            routine, up to the frame immediately following the frame of this
+            function.  This frame corresponds to a dummy prologue which is
+            never actually executed but instead appears before the real entry
+            point of an interrupt routine and exists only to provide a place
+            to simulate the push of a machine frame.  */
          CONTEXT context;
          PRUNTIME_FUNCTION mf_func = NULL;
          ULONG64 mf_imagebase;
          ULONG64 mf_rsp = 0;
 
-         /* Get the context.  */
+         /* Get the current context.  */
          RtlCaptureContext (&context);
 
          while (1)
@@ -1574,27 +1585,31 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
              ULONG64 EstablisherFrame;
 
              /* Get function metadata.  */
-             RuntimeFunction = RtlLookupFunctionEntry
-               (context.Rip, &ImageBase, ms_disp->HistoryTable);
+             RuntimeFunction
+               = RtlLookupFunctionEntry (context.Rip, &ImageBase,
+                                         ms_disp->HistoryTable);
+
+             /* Stop once we reached the frame of this function.  */
              if (RuntimeFunction == ms_disp->FunctionEntry)
                break;
+
              mf_func = RuntimeFunction;
              mf_imagebase = ImageBase;
              mf_rsp = context.Rsp;
 
-             if (!RuntimeFunction)
-               {
-                 /* In case of failure, assume this is a leaf function.  */
-                 context.Rip = *(ULONG64 *) context.Rsp;
-                 context.Rsp += 8;
-               }
-             else
+             if (RuntimeFunction)
                {
                  /* Unwind.  */
                  RtlVirtualUnwind (0, ImageBase, context.Rip, RuntimeFunction,
                                    &context, &HandlerData, &EstablisherFrame,
                                    NULL);
                }
+             else
+               {
+                 /* In case of failure, assume this is a leaf function.  */
+                 context.Rip = *(ULONG64 *) context.Rsp;
+                 context.Rsp += 8;
+               }
 
              /* 0 means bottom of the stack.  */
              if (context.Rip == 0)
@@ -1603,6 +1618,8 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
                  break;
                }
            }
+
+         /* If we have found the machine frame, adjust the return address.  */
          if (mf_func != NULL)
            __gnat_adjust_context
              ((unsigned char *)(mf_imagebase + mf_func->UnwindData), mf_rsp);
@@ -1611,16 +1628,16 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
       exception = __gnat_map_SEH (ms_exc, &msg);
       if (exception != NULL)
        {
-         struct _Unwind_Exception *exc;
+         /* Directly convert the system exception into a GCC one.
 
-         /* Directly convert the system exception to a GCC one.
             This is really breaking the API, but is necessary for stack size
             reasons: the normal way is to call Raise_From_Signal_Handler,
-            which build the exception and calls _Unwind_RaiseException, which
-            unwinds the stack and will call this personality routine. But
-            the Windows unwinder needs about 2KB of stack.  */
-         exc = __gnat_create_machine_occurrence_from_signal_handler
-           (exception, msg);
+            which builds the exception and calls _Unwind_RaiseException,
+            which unwinds the stack and will call this personality routine.
+            But the Windows unwinder needs about 2KB of stack.  */
+         struct _Unwind_Exception *exc
+           = __gnat_create_machine_occurrence_from_signal_handler (exception,
+                                                                   msg);
          memset (exc->private_, 0, sizeof (exc->private_));
          ms_exc->ExceptionCode = STATUS_GCC_THROW;
          ms_exc->NumberParameters = 1;
@@ -1629,9 +1646,11 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
 
     }
 
-  return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context,
-                               ms_disp, __gnat_personality_imp);
+  return
+    _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, ms_disp,
+                          __gnat_personality_imp);
 }
+
 #endif /* SEH */
 
 #if !defined (__USING_SJLJ_EXCEPTIONS__)
index 3941811..98b9e4e 100644 (file)
@@ -30,7 +30,7 @@
  ****************************************************************************/
 
 /*  This unit contains support for SEH (Structured Exception Handling).
-    Right now the only implementation is for Win32.  */
+    Right now the only implementation is for Win32 and Cygwin.  */
 
 #if defined (_WIN32) || (defined (__CYGWIN__) && defined (__SEH__))
 /* Include system headers, before system.h poisons malloc.  */
@@ -64,8 +64,7 @@ extern struct Exception_Data storage_error;
 extern struct Exception_Data tasking_error;
 extern struct Exception_Data _abort_signal;
 
-#define Raise_From_Signal_Handler \
-                      ada__exceptions__raise_from_signal_handler
+#define Raise_From_Signal_Handler ada__exceptions__raise_from_signal_handler
 extern void Raise_From_Signal_Handler (struct Exception_Data *, const char *)
   ATTRIBUTE_NORETURN;
 
@@ -81,8 +80,8 @@ EXCEPTION_DISPOSITION __gnat_SEH_error_handler
 struct Exception_Data *
 __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg);
 
-/* Convert an SEH exception to an Ada one.  Return the exception ID
-   and set MSG with the corresponding message.  */
+/* Convert an SEH exception to an Ada one.  Return the exception ID and set
+   MSG to the corresponding message.  */
 
 struct Exception_Data *
 __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg)
@@ -90,19 +89,18 @@ __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg)
   switch (ExceptionRecord->ExceptionCode)
     {
     case EXCEPTION_ACCESS_VIOLATION:
-      /* If the failing address isn't maximally-aligned or if the page
-        before the faulting page is not accessible, this is a program error.
-      */
+      /* If the failing address isn't maximally aligned or if the page before
+        the faulting page is not accessible, this is a program error.  */
       if ((ExceptionRecord->ExceptionInformation[1] & 3) != 0
          || IsBadCodePtr
-         ((FARPROC)(ExceptionRecord->ExceptionInformation[1] + 4096)))
+            ((FARPROC)(ExceptionRecord->ExceptionInformation[1] + 4096)))
        {
          *msg = "EXCEPTION_ACCESS_VIOLATION";
          return &program_error;
        }
       else
        {
-         /* otherwise it is a stack overflow  */
+         /* Otherwise this is a stack overflow.  */
          *msg = "stack overflow or erroneous memory access";
          return &storage_error;
        }
@@ -175,6 +173,8 @@ __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg)
 
 #if !(defined (_WIN64) && defined (__SEH__))
 
+/* The "fake" exception handler to be associated with the .text section.  */
+
 EXCEPTION_DISPOSITION
 __gnat_SEH_error_handler (struct _EXCEPTION_RECORD* ExceptionRecord,
                          void *EstablisherFrame ATTRIBUTE_UNUSED,
@@ -192,45 +192,47 @@ __gnat_SEH_error_handler (struct _EXCEPTION_RECORD* ExceptionRecord,
       msg = "unhandled signal";
     }
 
-#if ! defined (_WIN64)
+#if !defined (_WIN64)
   /* This call is important as it avoids locking the second time we catch a
-     signal. Note that this routine is documented as internal to Windows and
-     should not be used.  */
-
+     signal; it's equivalent to RtlUnwind (EstablisherFrame, NULL, NULL, 0);
+     Note that this routine is documented as internal to Windows and should
+     not be used.  */
   _global_unwind2 (EstablisherFrame);
-  /* Call equivalent to RtlUnwind (EstablisherFrame, NULL, NULL, 0); */
 #endif
 
   Raise_From_Signal_Handler (exception, msg);
 }
+
 #endif /* !(defined (_WIN64) && defined (__SEH__)) */
 
 #if defined (_WIN64)
-/*  On x86_64 windows exception mechanism is no more based on a chained list
-    of handlers addresses on the stack. Instead unwinding information is used
-    to retrieve the exception handler (similar to ZCX GCC mechanism). So in
-    order to register an exception handler we need to put in the final
-    executable some unwinding information. This information might be present
-    statically in the image file inside the .pdata section or registered
-    through RtlAddFunctionTable API. Currently the GCC toolchain does not
-    generate the .pdata information for each function. As we don't need to
-    handle SEH exceptions except for signal handling we are registering a
-    "fake" unwinding data that associate a SEH exception handler to the
-    complete .text section. As we never return from the handler, the system
-    does not try to do the final unwinding using the pdata information. The
-    unwinding is handled by the runtime using either the GNAT SJLJ mechanism
-    or the ZCX GCC mechanism.
+
+/*  On x86-64/Windows the EH mechanism is no more based on a chained list of
+    handlers addresses on the stack.  Instead unwinding information is used
+    to retrieve the exception handler (similar to DWARF2 unwinding).  So in
+    order to register an exception handler, we need to put in the binary
+    some unwinding information.  This information can be present statically
+    in the image file inside the .pdata section or registered through the
+    RtlAddFunctionTable API.  In the case where the GCC toolchain does not
+    generate the .pdata information for each function, we don't really need
+    to handle SEH exceptions except for signal handling, so we register a
+    "fake" unwinding data that associates a SEH exception handler with the
+    complete .text section.  As we never return from the handler, the system
+    does not try to do the final unwinding using the .pdata information and
+    the unwinding is handled by the runtime using the GNAT or GCC mechanism.
 
     Solutions based on SetUnhandledExceptionFilter have been discarded as this
-    function is mostly disabled on last Windows versions.
+    function is mostly disabled on latest Windows versions.
+
     Using AddVectoredExceptionHandler should also be discarded as it overrides
     all SEH exception handlers that might be present in the program itself and
-    the loaded DLL (for example it results in unexpected behaviors in the
-    Win32 subsystem.  */
+    the loaded DLL; for example it results in unexpected behavior in the Win32
+    subsystem.  */
 
 #ifndef __SEH__
-  /* Don't use this trick when SEH are emitted by gcc, as it will conflict with
-     them.  */
+
+  /* Do not use this trick when GCC generates the .pdata information, since it
+     is not necessary and will conflict with the per-function data.  */
 asm
 (
  " .section .rdata, \"dr\"\n"
@@ -250,19 +252,21 @@ asm
  "\n"
  " .text\n"
 );
+
 #endif /* __SEH__ */
 
+/* Nothing to do, the handler is either not used or statically installed by
+   the asm statement just above.  */
 void __gnat_install_SEH_handler (void *eh ATTRIBUTE_UNUSED)
 {
-  /* Nothing to do, the handler is statically installed by the asm statement
-     just above.  */
 }
 
 #else /* defined (_WIN64) */
-/*  Install the Win32 SEH exception handler. Note that the caller must have
-    allocated 8 bytes on the stack and pass the pointer to this stack
-    space. This is needed as the SEH exception handler must be on the stack of
-    the thread.
+
+/*  Install the Win32 SEH exception handler.  Note that the caller must have
+    allocated 8 bytes on the stack and pass the pointer to this stack space.
+    This is needed as the SEH exception handler must be on the stack of the
+    thread.
 
        int buf[2];
 
@@ -271,31 +275,32 @@ void __gnat_install_SEH_handler (void *eh ATTRIBUTE_UNUSED)
        main();
 
    This call must be done before calling the main procedure or the thread
-   entry. The stack space must exists during all the main run.  */
+   entry.  The stack space must exist during the entire main run.  */
 
 void
 __gnat_install_SEH_handler (void *ER)
 {
   int *ptr;
 
-  /* put current handler in ptr */
-
+  /* Put current handler in PTR.  */
   asm ("mov %%fs:(0),%0" : "=r" (ptr));
 
   ((int *)ER)[0] = (int)ptr;                       /* previous handler */
   ((int *)ER)[1] = (int)__gnat_SEH_error_handler;  /* new handler */
 
-  /* ER is the new handler, set fs:(0) with this value */
-
+  /* ER is the new handler, set fs:(0) to this value.  */
   asm volatile ("mov %0,%%fs:(0)": : "r" (ER));
 }
+
 #endif
 
 #else /* defined (_WIN32) */
-/* For all non Windows targets we provide a dummy SEH install handler.  */
+
+/* For all non-Windows targets we provide a dummy SEH install handler.  */
 void __gnat_install_SEH_handler (void *eh ATTRIBUTE_UNUSED)
 {
 }
+
 #endif
 
 #ifdef __cplusplus