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>
* exp_ch7.adb, sem_ch3.adb, sem_res.adb: Minor reformatting.
......
......@@ -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__)
......
......@@ -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
......
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