diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2022-09-27 08:46:06 +0300 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2022-09-27 15:47:31 +0300 |
commit | 39b8b51f1f1048507b3ad86697dd0c02e006ed68 (patch) | |
tree | 02d4bc52c31544e97078c884c47a8d73cee9c814 /mark.c | |
parent | 8c5f0fc6c63a804c00a001dd093c0fa998b01485 (diff) | |
download | bdwgc-39b8b51f1f1048507b3ad86697dd0c02e006ed68.tar.gz |
Fix missing recovery from faults in GC_mark_some on Win64 if MinGW
Issue #454 (bdwgc).
This fixes rare SIGSEGV events in GC_mark_some on Windows (and Wine)
when Windows asynchronously removes some temporary data roots if the
collector is built e.g. with a MinGW-w64 compiler.
Now, on Windows, the faults are caught by setjmp/longjmp if SEH is not
supported by the compiler.
Also, the asm-based implementation of SEH faults handling for x86 is
deleted (thus, no specific handling of x86 any more) because it relied
on a certain compilation mode (that the standard function prologue is
generated and frame pointer is not omitted).
* include/private/gc_priv.h [WRAP_MARK_SOME && __GNUC__] (GC_jmp_buf,
GC_setup_temporary_fault_handler, GC_reset_fault_handler): Declare.
* include/private/gcconfig.h [(MSWIN32 || MSWINCE) && !NO_CRT
&& !NO_WRAP_MARK_SOME] (WRAP_MARK_SOME): Define regardless of __GNUC__
and I386; remove FIXME item; update TODO item.
* mark.c [__MINGW32__ && !__MINGW_EXCPT_DEFINE_PSDK && __i386__]
(__MINGW_EXCPT_DEFINE_PSDK): Do not define; remove comment.
* mark.c [MSWIN32 && __GNUC__]: Do not include excpt.h.
* mark.c [WRAP_MARK_SOME] (GC_mark_some_inner): Update comment.
* mark.c [WRAP_MARK_SOME && (MSWIN32 || MSWINCE) && __GNUC__]
(ext_ex_regn): Remove type declaration.
* mark.c [WRAP_MARK_SOME && (MSWIN32 || MSWINCE) && __GNUC__]
(mark_ex_handler): Remove static function.
* mark.c [WRAP_MARK_SOME] (GC_mark_some): Update comment (remove exact
versions of Windows).
* mark.c [WRAP_MARK_SOME && (MSWIN32 || MSWINCE) && __GNUC__]
(GC_mark_some): Use GC_setup_temporary_fault_handler, SETJMP() and
GC_reset_fault_handler instead of asm-based emulation of __try/except.
* mark.c [WRAP_MARK_SOME && (!MSWIN32 && !MSWINCE || __GNUC__)
&& !DEFAULT_VDB] (GC_mark_some): WARN() only if USE_PROC_FOR_LIBRARIES.
* mark.c [WRAP_MARK_SOME && !MSWIN32 && !MSWINCE] (GC_mark_some):
Remove rm_handler label (call GC_reset_fault_handler on handle_ex
instead).
* mark.c [WRAP_MARK_SOME && !MSWIN32 && !MSWINCE && __GNUC__
&& GC_WIN32_THREADS && !GC_PTHREADS] (GC_mark_some): Call
GC_started_thread_while_stopped() (after GC_reset_fault_handler).
* mark.c [WRAP_MARK_SOME && GC_WIN32_THREADS && !GC_PTHREADS]
(GC_mark_some): Define handle_thr_start label regardless of MSWIN32
and MSWINCE.
* mark.c [WRAP_MARK_SOME] (GC_mark_some): Return FALSE instead of
setting ret_val to FALSE and going to rm_handler (because
GC_reset_fault_handler is already called).
* os_dep.c [WRAP_MARK_SOME && __GNUC__] (GC_fault_handler_t): Define
type.
* os_dep.c [WRAP_MARK_SOME && __GNUC__] (GC_set_and_save_fault_handler,
GC_jmp_buf, GC_fault_handler, GC_setup_temporary_fault_handler,
GC_reset_fault_handler): Define.
Diffstat (limited to 'mark.c')
-rw-r--r-- | mark.c | 141 |
1 files changed, 23 insertions, 118 deletions
@@ -15,22 +15,8 @@ * */ -#if defined(__MINGW32__) && !defined(__MINGW_EXCPT_DEFINE_PSDK) \ - && defined(__i386__) /* cannot use macros from gcconfig.h */ - /* Otherwise EXCEPTION_REGISTRATION type declaration from winnt.h */ - /* might be used. That declaration has "handler" callback with NTAPI */ - /* attribute. The proper type (with "handler" field compatible with */ - /* GC mark_ex_handler) is declared in excpt.h. The given macro is */ - /* defined before any system header include. */ -# define __MINGW_EXCPT_DEFINE_PSDK 1 -#endif - #include "private/gc_pmark.h" -#if defined(MSWIN32) && defined(__GNUC__) -# include <excpt.h> -#endif - /* Make arguments appear live to compiler. Put here to minimize the */ /* risk of inlining. Used to minimize junk left in registers. */ GC_ATTR_NOINLINE @@ -302,8 +288,9 @@ static void push_roots_and_advance(GC_bool push_all, ptr_t cold_gc_frame) /* collection, the world may be running. */ #ifdef WRAP_MARK_SOME /* For Win32, this is called after we establish a structured */ - /* exception handler, in case Windows unmaps one of our root */ - /* segments. */ + /* exception (or signal) handler, in case Windows unmaps one */ + /* of our root segments. Note that this code should never */ + /* generate an incremental GC write fault. */ STATIC GC_bool GC_mark_some_inner(ptr_t cold_gc_frame) #else GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) @@ -426,128 +413,36 @@ static void push_roots_and_advance(GC_bool push_all, ptr_t cold_gc_frame) } #ifdef WRAP_MARK_SOME - -# if (defined(MSWIN32) || defined(MSWINCE)) && defined(__GNUC__) - - typedef struct { - EXCEPTION_REGISTRATION ex_reg; - void *alt_path; - } ext_ex_regn; - - static EXCEPTION_DISPOSITION mark_ex_handler( - struct _EXCEPTION_RECORD *ex_rec, void *est_frame, - struct _CONTEXT *context, void *disp_ctxt) - { - UNUSED_ARG(disp_ctxt); - if (ex_rec->ExceptionCode == STATUS_ACCESS_VIOLATION) { - ext_ex_regn *xer = (ext_ex_regn *)est_frame; - - /* Unwind from the inner function assuming the standard */ - /* function prologue. */ - /* Assumes code has not been compiled with */ - /* -fomit-frame-pointer. */ - context->Esp = context->Ebp; - context->Ebp = *((DWORD *)context->Esp); - context->Esp = context->Esp - 8; - - /* Resume execution at the "real" handler within the */ - /* wrapper function. */ - context->Eip = (DWORD )(xer->alt_path); - - return ExceptionContinueExecution; - - } else { - return ExceptionContinueSearch; - } - } -# endif /* __GNUC__ && MSWIN32 */ - GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) { GC_bool ret_val; -# if defined(MSWIN32) || defined(MSWINCE) -# ifndef __GNUC__ - /* Windows 98 appears to asynchronously create and remove */ + /* Windows appears to asynchronously create and remove */ /* writable memory mappings, for reasons we haven't yet */ /* understood. Since we look for writable regions to */ /* determine the root set, we may try to mark from an */ /* address range that disappeared since we started the */ /* collection. Thus we have to recover from faults here. */ - /* This code does not appear to be necessary for Windows */ - /* 95/NT/2000+. Note that this code should never generate */ - /* an incremental GC write fault. */ /* This code seems to be necessary for WinCE (at least in */ /* the case we'd decide to add MEM_PRIVATE sections to */ /* data roots in GC_register_dynamic_libraries()). */ /* It's conceivable that this is the same issue with */ /* terminating threads that we see with Linux and */ /* USE_PROC_FOR_LIBRARIES. */ - +# if (defined(MSWIN32) || defined(MSWINCE)) && !defined(__GNUC__) __try { ret_val = GC_mark_some_inner(cold_gc_frame); } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { goto handle_ex; } -# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) - /* With DllMain-based thread tracking, a thread may have */ - /* started while we were marking. This is logically */ - /* equivalent to the exception case; our results are */ - /* invalid and we have to start over. This cannot be */ - /* prevented since we can't block in DllMain. */ - if (GC_started_thread_while_stopped()) - goto handle_thr_start; -# endif - rm_handler: - return ret_val; - -# else /* __GNUC__ */ - - /* Manually install an exception handler since GCC does */ - /* not yet support Structured Exception Handling (SEH) on */ - /* Win32. */ - ext_ex_regn er; - -# if GC_GNUC_PREREQ(4, 7) || GC_CLANG_PREREQ(3, 3) -# pragma GCC diagnostic push - /* Suppress "taking the address of label is non-standard" warning. */ -# if defined(__clang__) || GC_GNUC_PREREQ(6, 0) -# pragma GCC diagnostic ignored "-Wpedantic" -# else - /* GCC before ~4.8 does not accept "-Wpedantic" quietly. */ -# pragma GCC diagnostic ignored "-pedantic" -# endif - er.alt_path = &&handle_ex; -# pragma GCC diagnostic pop -# elif !defined(CPPCHECK) /* pragma diagnostic is not supported */ - er.alt_path = &&handle_ex; -# endif - er.ex_reg.handler = mark_ex_handler; - __asm__ __volatile__ ("movl %%fs:0, %0" : "=r" (er.ex_reg.prev)); - __asm__ __volatile__ ("movl %0, %%fs:0" : : "r" (&er)); - ret_val = GC_mark_some_inner(cold_gc_frame); - /* Prevent GCC from considering the following code unreachable */ - /* and thus eliminating it. */ - if (er.alt_path == 0) - goto handle_ex; -# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) - if (GC_started_thread_while_stopped()) - goto handle_thr_start; -# endif - rm_handler: - /* Uninstall the exception handler. */ - __asm__ __volatile__ ("mov %0, %%fs:0" : : "r" (er.ex_reg.prev)); - return ret_val; -# endif /* __GNUC__ */ - -# else /* !MSWIN32 */ +# else /* Here we are handling the case in which /proc is used for root */ /* finding, and we have threads. We may find a stack for a */ /* thread that is in the process of exiting, and disappears */ /* while we are marking it. This seems extremely difficult to */ /* avoid otherwise. */ -# ifndef DEFAULT_VDB +# if defined(USE_PROC_FOR_LIBRARIES) && !defined(DEFAULT_VDB) if (GC_auto_incremental) { static GC_bool is_warned = FALSE; @@ -561,13 +456,25 @@ static void push_roots_and_advance(GC_bool push_all, ptr_t cold_gc_frame) GC_setup_temporary_fault_handler(); if(SETJMP(GC_jmp_buf) != 0) goto handle_ex; ret_val = GC_mark_some_inner(cold_gc_frame); - rm_handler: GC_reset_fault_handler(); +# endif + +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + /* With DllMain-based thread tracking, a thread may have */ + /* started while we were marking. This is logically equivalent */ + /* to the exception case; our results are invalid and we have */ + /* to start over. This cannot be prevented since we can't */ + /* block in DllMain. */ + if (GC_started_thread_while_stopped()) + goto handle_thr_start; +# endif return ret_val; -# endif /* !MSWIN32 */ handle_ex: /* Exception handler starts here for all cases. */ +# if !defined(MSWIN32) && !defined(MSWINCE) || defined(__GNUC__) + GC_reset_fault_handler(); +# endif { static word warned_gc_no; @@ -578,8 +485,7 @@ handle_ex: warned_gc_no = GC_gc_no; } } -# if (defined(MSWIN32) || defined(MSWINCE)) && defined(GC_WIN32_THREADS) \ - && !defined(GC_PTHREADS) +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) handle_thr_start: # endif /* We have bad roots on the stack. Discard mark stack. */ @@ -591,8 +497,7 @@ handle_ex: # endif GC_invalidate_mark_state(); GC_scan_ptr = NULL; - ret_val = FALSE; - goto rm_handler; /* Back to platform-specific code. */ + return FALSE; } #endif /* WRAP_MARK_SOME */ |