(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);
/* 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. */
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;
+ 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)
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)
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);
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;
}
- 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__)
****************************************************************************/
/* 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. */
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;
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)
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;
}
#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,
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"
"\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];
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