Commit 45c6d784 by Eric Botcazou Committed by Pierre-Marie de Rodat

[Ada] Handle version 2 of Windows unwinding information structures

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
parent b6784d90
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> 2018-05-24 Hristian Kirtchev <kirtchev@adacore.com>
* exp_ch7.adb, sem_ch3.adb, sem_res.adb: Minor reformatting. * exp_ch7.adb, sem_ch3.adb, sem_res.adb: Minor reformatting.
......
...@@ -1457,9 +1457,6 @@ __gnat_Unwind_ForcedUnwind (_Unwind_Exception *e ATTRIBUTE_UNUSED, ...@@ -1457,9 +1457,6 @@ __gnat_Unwind_ForcedUnwind (_Unwind_Exception *e ATTRIBUTE_UNUSED,
(STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC) (STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC)
#define STATUS_GCC_THROW GCC_EXCEPTION (0) #define STATUS_GCC_THROW GCC_EXCEPTION (0)
EXCEPTION_DISPOSITION __gnat_SEH_error_handler
(struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
struct Exception_Data * struct Exception_Data *
__gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg); __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg);
...@@ -1481,22 +1478,30 @@ __gnat_create_machine_occurrence_from_signal_handler (Exception_Id, ...@@ -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, /* 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 that will be removed if we could propagate the Windows exception (and not
the GCC one). the GCC one).
What is very wrong is that the Windows unwinder will try to decode the 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 static void
__gnat_adjust_context (unsigned char *unw, ULONG64 rsp) __gnat_adjust_context (unsigned char *unw, ULONG64 rsp)
{ {
unsigned int len; unsigned int len;
/* Version = 1, no flags, no prologue. */ /* Version 1 or 2. */
if (unw[0] != 1 || unw[1] != 0) if (unw[0] != 1 && unw[0] != 2)
return;
/* No flags, no prologue. */
if (unw[1] != 0)
return; return;
len = unw[2]; len = unw[2];
/* No frame pointer. */ /* No frame. */
if (unw[3] != 0) if (unw[3] != 0)
return; return;
unw += 4; /* ??? Skip the first 2 undocumented opcodes for version 2. */
if (unw[0] == 2)
unw += 8;
else
unw += 4;
while (len > 0) while (len > 0)
{ {
/* Offset in prologue = 0. */ /* Offset in prologue = 0. */
...@@ -1541,9 +1546,7 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, ...@@ -1541,9 +1546,7 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
PCONTEXT ms_orig_context, PCONTEXT ms_orig_context,
PDISPATCHER_CONTEXT ms_disp) PDISPATCHER_CONTEXT ms_disp)
{ {
/* Possibly transform run-time errors into Ada exceptions. As a small /* Possibly transform run-time errors into Ada exceptions. */
optimization, we call __gnat_SEH_error_handler only on non-user
exceptions. */
if (!(ms_exc->ExceptionCode & STATUS_USER_DEFINED)) if (!(ms_exc->ExceptionCode & STATUS_USER_DEFINED))
{ {
struct Exception_Data *exception; struct Exception_Data *exception;
...@@ -1557,13 +1560,21 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, ...@@ -1557,13 +1560,21 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
+ ms_disp->FunctionEntry->EndAddress)) + ms_disp->FunctionEntry->EndAddress))
{ {
/* This is a fault in this function. We need to adjust the return /* 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; CONTEXT context;
PRUNTIME_FUNCTION mf_func = NULL; PRUNTIME_FUNCTION mf_func = NULL;
ULONG64 mf_imagebase; ULONG64 mf_imagebase;
ULONG64 mf_rsp = 0; ULONG64 mf_rsp = 0;
/* Get the context. */ /* Get the current context. */
RtlCaptureContext (&context); RtlCaptureContext (&context);
while (1) while (1)
...@@ -1574,27 +1585,31 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, ...@@ -1574,27 +1585,31 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
ULONG64 EstablisherFrame; ULONG64 EstablisherFrame;
/* Get function metadata. */ /* Get function metadata. */
RuntimeFunction = RtlLookupFunctionEntry RuntimeFunction
(context.Rip, &ImageBase, ms_disp->HistoryTable); = RtlLookupFunctionEntry (context.Rip, &ImageBase,
ms_disp->HistoryTable);
/* Stop once we reached the frame of this function. */
if (RuntimeFunction == ms_disp->FunctionEntry) if (RuntimeFunction == ms_disp->FunctionEntry)
break; break;
mf_func = RuntimeFunction; mf_func = RuntimeFunction;
mf_imagebase = ImageBase; mf_imagebase = ImageBase;
mf_rsp = context.Rsp; mf_rsp = context.Rsp;
if (!RuntimeFunction) if (RuntimeFunction)
{
/* In case of failure, assume this is a leaf function. */
context.Rip = *(ULONG64 *) context.Rsp;
context.Rsp += 8;
}
else
{ {
/* Unwind. */ /* Unwind. */
RtlVirtualUnwind (0, ImageBase, context.Rip, RuntimeFunction, RtlVirtualUnwind (0, ImageBase, context.Rip, RuntimeFunction,
&context, &HandlerData, &EstablisherFrame, &context, &HandlerData, &EstablisherFrame,
NULL); 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. */ /* 0 means bottom of the stack. */
if (context.Rip == 0) if (context.Rip == 0)
...@@ -1603,6 +1618,8 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, ...@@ -1603,6 +1618,8 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
break; break;
} }
} }
/* If we have found the machine frame, adjust the return address. */
if (mf_func != NULL) if (mf_func != NULL)
__gnat_adjust_context __gnat_adjust_context
((unsigned char *)(mf_imagebase + mf_func->UnwindData), mf_rsp); ((unsigned char *)(mf_imagebase + mf_func->UnwindData), mf_rsp);
...@@ -1611,16 +1628,16 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, ...@@ -1611,16 +1628,16 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
exception = __gnat_map_SEH (ms_exc, &msg); exception = __gnat_map_SEH (ms_exc, &msg);
if (exception != NULL) 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 This is really breaking the API, but is necessary for stack size
reasons: the normal way is to call Raise_From_Signal_Handler, reasons: the normal way is to call Raise_From_Signal_Handler,
which build the exception and calls _Unwind_RaiseException, which which builds the exception and calls _Unwind_RaiseException,
unwinds the stack and will call this personality routine. But which unwinds the stack and will call this personality routine.
the Windows unwinder needs about 2KB of stack. */ But the Windows unwinder needs about 2KB of stack. */
exc = __gnat_create_machine_occurrence_from_signal_handler struct _Unwind_Exception *exc
(exception, msg); = __gnat_create_machine_occurrence_from_signal_handler (exception,
msg);
memset (exc->private_, 0, sizeof (exc->private_)); memset (exc->private_, 0, sizeof (exc->private_));
ms_exc->ExceptionCode = STATUS_GCC_THROW; ms_exc->ExceptionCode = STATUS_GCC_THROW;
ms_exc->NumberParameters = 1; ms_exc->NumberParameters = 1;
...@@ -1629,9 +1646,11 @@ __gnat_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame, ...@@ -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, return
ms_disp, __gnat_personality_imp); _GCC_specific_handler (ms_exc, this_frame, ms_orig_context, ms_disp,
__gnat_personality_imp);
} }
#endif /* SEH */ #endif /* SEH */
#if !defined (__USING_SJLJ_EXCEPTIONS__) #if !defined (__USING_SJLJ_EXCEPTIONS__)
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
****************************************************************************/ ****************************************************************************/
/* This unit contains support for SEH (Structured Exception Handling). /* 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__)) #if defined (_WIN32) || (defined (__CYGWIN__) && defined (__SEH__))
/* Include system headers, before system.h poisons malloc. */ /* Include system headers, before system.h poisons malloc. */
...@@ -64,8 +64,7 @@ extern struct Exception_Data storage_error; ...@@ -64,8 +64,7 @@ extern struct Exception_Data storage_error;
extern struct Exception_Data tasking_error; extern struct Exception_Data tasking_error;
extern struct Exception_Data _abort_signal; extern struct Exception_Data _abort_signal;
#define Raise_From_Signal_Handler \ #define Raise_From_Signal_Handler ada__exceptions__raise_from_signal_handler
ada__exceptions__raise_from_signal_handler
extern void Raise_From_Signal_Handler (struct Exception_Data *, const char *) extern void Raise_From_Signal_Handler (struct Exception_Data *, const char *)
ATTRIBUTE_NORETURN; ATTRIBUTE_NORETURN;
...@@ -81,8 +80,8 @@ EXCEPTION_DISPOSITION __gnat_SEH_error_handler ...@@ -81,8 +80,8 @@ EXCEPTION_DISPOSITION __gnat_SEH_error_handler
struct Exception_Data * struct Exception_Data *
__gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg); __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg);
/* Convert an SEH exception to an Ada one. Return the exception ID /* Convert an SEH exception to an Ada one. Return the exception ID and set
and set MSG with the corresponding message. */ MSG to the corresponding message. */
struct Exception_Data * struct Exception_Data *
__gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg) __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg)
...@@ -90,19 +89,18 @@ __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) switch (ExceptionRecord->ExceptionCode)
{ {
case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_ACCESS_VIOLATION:
/* If the failing address isn't maximally-aligned or if the page /* If the failing address isn't maximally aligned or if the page before
before the faulting page is not accessible, this is a program error. the faulting page is not accessible, this is a program error. */
*/
if ((ExceptionRecord->ExceptionInformation[1] & 3) != 0 if ((ExceptionRecord->ExceptionInformation[1] & 3) != 0
|| IsBadCodePtr || IsBadCodePtr
((FARPROC)(ExceptionRecord->ExceptionInformation[1] + 4096))) ((FARPROC)(ExceptionRecord->ExceptionInformation[1] + 4096)))
{ {
*msg = "EXCEPTION_ACCESS_VIOLATION"; *msg = "EXCEPTION_ACCESS_VIOLATION";
return &program_error; return &program_error;
} }
else else
{ {
/* otherwise it is a stack overflow */ /* Otherwise this is a stack overflow. */
*msg = "stack overflow or erroneous memory access"; *msg = "stack overflow or erroneous memory access";
return &storage_error; return &storage_error;
} }
...@@ -175,6 +173,8 @@ __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg) ...@@ -175,6 +173,8 @@ __gnat_map_SEH (EXCEPTION_RECORD* ExceptionRecord, const char **msg)
#if !(defined (_WIN64) && defined (__SEH__)) #if !(defined (_WIN64) && defined (__SEH__))
/* The "fake" exception handler to be associated with the .text section. */
EXCEPTION_DISPOSITION EXCEPTION_DISPOSITION
__gnat_SEH_error_handler (struct _EXCEPTION_RECORD* ExceptionRecord, __gnat_SEH_error_handler (struct _EXCEPTION_RECORD* ExceptionRecord,
void *EstablisherFrame ATTRIBUTE_UNUSED, void *EstablisherFrame ATTRIBUTE_UNUSED,
...@@ -192,45 +192,47 @@ __gnat_SEH_error_handler (struct _EXCEPTION_RECORD* ExceptionRecord, ...@@ -192,45 +192,47 @@ __gnat_SEH_error_handler (struct _EXCEPTION_RECORD* ExceptionRecord,
msg = "unhandled signal"; msg = "unhandled signal";
} }
#if ! defined (_WIN64) #if !defined (_WIN64)
/* This call is important as it avoids locking the second time we catch a /* 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 signal; it's equivalent to RtlUnwind (EstablisherFrame, NULL, NULL, 0);
should not be used. */ Note that this routine is documented as internal to Windows and should
not be used. */
_global_unwind2 (EstablisherFrame); _global_unwind2 (EstablisherFrame);
/* Call equivalent to RtlUnwind (EstablisherFrame, NULL, NULL, 0); */
#endif #endif
Raise_From_Signal_Handler (exception, msg); Raise_From_Signal_Handler (exception, msg);
} }
#endif /* !(defined (_WIN64) && defined (__SEH__)) */ #endif /* !(defined (_WIN64) && defined (__SEH__)) */
#if defined (_WIN64) #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 /* On x86-64/Windows the EH mechanism is no more based on a chained list of
to retrieve the exception handler (similar to ZCX GCC mechanism). So in handlers addresses on the stack. Instead unwinding information is used
order to register an exception handler we need to put in the final to retrieve the exception handler (similar to DWARF2 unwinding). So in
executable some unwinding information. This information might be present order to register an exception handler, we need to put in the binary
statically in the image file inside the .pdata section or registered some unwinding information. This information can be present statically
through RtlAddFunctionTable API. Currently the GCC toolchain does not in the image file inside the .pdata section or registered through the
generate the .pdata information for each function. As we don't need to RtlAddFunctionTable API. In the case where the GCC toolchain does not
handle SEH exceptions except for signal handling we are registering a generate the .pdata information for each function, we don't really need
"fake" unwinding data that associate a SEH exception handler to the to handle SEH exceptions except for signal handling, so we register a
complete .text section. As we never return from the handler, the system "fake" unwinding data that associates a SEH exception handler with the
does not try to do the final unwinding using the pdata information. The complete .text section. As we never return from the handler, the system
unwinding is handled by the runtime using either the GNAT SJLJ mechanism does not try to do the final unwinding using the .pdata information and
or the ZCX GCC mechanism. the unwinding is handled by the runtime using the GNAT or GCC mechanism.
Solutions based on SetUnhandledExceptionFilter have been discarded as this 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 Using AddVectoredExceptionHandler should also be discarded as it overrides
all SEH exception handlers that might be present in the program itself and 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 the loaded DLL; for example it results in unexpected behavior in the Win32
Win32 subsystem. */ subsystem. */
#ifndef __SEH__ #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 asm
( (
" .section .rdata, \"dr\"\n" " .section .rdata, \"dr\"\n"
...@@ -250,19 +252,21 @@ asm ...@@ -250,19 +252,21 @@ asm
"\n" "\n"
" .text\n" " .text\n"
); );
#endif /* __SEH__ */ #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) 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) */ #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 /* Install the Win32 SEH exception handler. Note that the caller must have
space. This is needed as the SEH exception handler must be on the stack of allocated 8 bytes on the stack and pass the pointer to this stack space.
the thread. This is needed as the SEH exception handler must be on the stack of the
thread.
int buf[2]; int buf[2];
...@@ -271,31 +275,32 @@ void __gnat_install_SEH_handler (void *eh ATTRIBUTE_UNUSED) ...@@ -271,31 +275,32 @@ void __gnat_install_SEH_handler (void *eh ATTRIBUTE_UNUSED)
main(); main();
This call must be done before calling the main procedure or the thread 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 void
__gnat_install_SEH_handler (void *ER) __gnat_install_SEH_handler (void *ER)
{ {
int *ptr; int *ptr;
/* put current handler in ptr */ /* Put current handler in PTR. */
asm ("mov %%fs:(0),%0" : "=r" (ptr)); asm ("mov %%fs:(0),%0" : "=r" (ptr));
((int *)ER)[0] = (int)ptr; /* previous handler */ ((int *)ER)[0] = (int)ptr; /* previous handler */
((int *)ER)[1] = (int)__gnat_SEH_error_handler; /* new 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)); asm volatile ("mov %0,%%fs:(0)": : "r" (ER));
} }
#endif #endif
#else /* defined (_WIN32) */ #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) void __gnat_install_SEH_handler (void *eh ATTRIBUTE_UNUSED)
{ {
} }
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment