From: Eric Botcazou Date: Thu, 24 May 2018 13:05:59 +0000 (+0000) Subject: [Ada] Handle version 2 of Windows unwinding information structures X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=45c6d7849670dc3fd2dc4686c3c31dc6cb7bf49e;p=gcc.git [Ada] Handle version 2 of Windows unwinding information structures 2018-05-24 Eric Botcazou 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 --- diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index cf19019775d..32e4b6a6dee 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,13 @@ +2018-05-24 Eric Botcazou + + * 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 * exp_ch7.adb, sem_ch3.adb, sem_res.adb: Minor reformatting. diff --git a/gcc/ada/raise-gcc.c b/gcc/ada/raise-gcc.c index 05d1a40276a..7558414d5a3 100644 --- a/gcc/ada/raise-gcc.c +++ b/gcc/ada/raise-gcc.c @@ -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__) diff --git a/gcc/ada/seh_init.c b/gcc/ada/seh_init.c index 39418115667..98b9e4e6327 100644 --- a/gcc/ada/seh_init.c +++ b/gcc/ada/seh_init.c @@ -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