summaryrefslogtreecommitdiff
path: root/mark.c
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2022-09-27 08:46:06 +0300
committerIvan Maidanski <ivmai@mail.ru>2022-09-27 15:47:31 +0300
commit39b8b51f1f1048507b3ad86697dd0c02e006ed68 (patch)
tree02d4bc52c31544e97078c884c47a8d73cee9c814 /mark.c
parent8c5f0fc6c63a804c00a001dd093c0fa998b01485 (diff)
downloadbdwgc-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.c141
1 files changed, 23 insertions, 118 deletions
diff --git a/mark.c b/mark.c
index 06b69461..57ed94c8 100644
--- a/mark.c
+++ b/mark.c
@@ -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 */