/* ** vim: ts=4 sw=4 expandtab */ /* ** Copyright (C) 1998, 2000, 2002, 2005-2007, 2010-2011 The University of Melbourne. ** This file may only be copied under the terms of the GNU Library General ** Public License - see the file COPYING.LIB in the Mercury distribution. */ /* ** This module defines the signal handlers for memory zones. ** These handlers are invoked when memory is accessed outside of ** the memory zones, or at the protected region at the end of a ** memory zone (if available). */ /*---------------------------------------------------------------------------*/ #include "mercury_imp.h" #ifdef MR_HAVE_UNISTD_H #include #endif #include #include /* ** This include must come before anything else that might include . ** See the commments in mercury_signal.h. */ #include "mercury_signal.h" #ifdef MR_HAVE_SYS_SIGINFO_H #include #endif #ifdef MR_HAVE_SYS_SIGNAL_H /* on FREEBSD we need to include before */ #include #endif #ifdef MR_HAVE_MPROTECT #include #endif #ifdef MR_HAVE_UCONTEXT_H #include #endif #ifdef MR_HAVE_SYS_UCONTEXT_H #include #endif #include "mercury_trace_base.h" #include "mercury_memory_zones.h" #include "mercury_memory_handlers.h" #include "mercury_faultaddr.h" #include "mercury_threadscope.h" /*---------------------------------------------------------------------------*/ #ifdef MR_HAVE_SIGINFO #if defined(MR_HAVE_SIGCONTEXT_STRUCT) #if defined(MR_HAVE_SIGCONTEXT_STRUCT_3ARG) static void complex_sighandler_3arg(int, int, struct sigcontext_struct); #else static void complex_sighandler(int, struct sigcontext_struct); #endif #elif defined(MR_HAVE_SIGINFO_T) static void complex_bushandler(int, siginfo_t *, void *); static void complex_segvhandler(int, siginfo_t *, void *); #else #error "MR_HAVE_SIGINFO defined but don't know how to get it" #endif #else static void simple_sighandler(int); #endif #ifdef MR_HAVE_SIGINFO #if defined(MR_HAVE_SIGCONTEXT_STRUCT) #if defined(MR_HAVE_SIGCONTEXT_STRUCT_3ARG) #define bus_handler complex_sighandler_3arg #define segv_handler complex_sighandler_3arg #else #define bus_handler complex_sighandler #define segv_handler complex_sighandler #endif #elif defined(MR_HAVE_SIGINFO_T) #define bus_handler complex_bushandler #define segv_handler complex_segvhandler #else #error "MR_HAVE_SIGINFO defined but don't know how to get it" #endif #else #define bus_handler simple_sighandler #define segv_handler simple_sighandler #endif /* ** round_up(amount, align) returns `amount' rounded up to the nearest ** alignment boundary. `align' must be a power of 2. */ static void MR_print_dump_stack(void); static MR_bool MR_try_munprotect(void *address, void *context); static char *MR_explain_context(void *context); static MR_Code *get_pc_from_context(void *the_context); static MR_Word *get_sp_from_context(void *the_context); static MR_Word *get_curfr_from_context(void *the_context); static void leave_signal_handler(int sig); #define STDERR 2 /* ** Note that we cannot assume that the memory zones ** have been initialized here, since MR_setup_signals() ** gets called before MR_init_memory_zones(). ** However, the code here will work fine if ** used_memory_zones is null. */ static MR_bool MR_try_munprotect(void *addr, void *context) { #if !defined(MR_HAVE_SIGINFO) return MR_FALSE; #else MR_Word *fault_addr; MR_MemoryZone *zone; fault_addr = (MR_Word *) addr; zone = MR_get_used_memory_zones_readonly(); if (MR_memdebug) { fprintf(stderr, "caught fault at %p\n", (void *)addr); } while(zone != NULL) { #ifdef MR_CHECK_OVERFLOW_VIA_MPROTECT if (MR_memdebug) { fprintf(stderr, "checking %s#%" MR_INTEGER_LENGTH_MODIFIER "d: %p - %p\n", zone->MR_zone_name, zone->MR_zone_id, (void *) zone->MR_zone_redzone, (void *) zone->MR_zone_top); } if (zone->MR_zone_redzone <= fault_addr && fault_addr <= zone->MR_zone_top) { if (MR_memdebug) { fprintf(stderr, "address is in %s#% " MR_INTEGER_LENGTH_MODIFIER "d redzone\n", zone->MR_zone_name, zone->MR_zone_id); } return zone->MR_zone_handler(fault_addr, zone, context); } #endif zone = zone->MR_zone_next; } if (MR_memdebug) { fprintf(stderr, "address not in any redzone.\n"); } return MR_FALSE; #endif /* MR_HAVE_SIGINFO */ } MR_bool MR_null_handler(MR_Word *fault_addr, MR_MemoryZone *zone, void *context) { return MR_FALSE; } /* ** MR_fatal_abort() prints an error message, possibly a stack dump, ** and then exits. It is like MR_fatal_error(), except that it is safe to call ** from a signal handler. */ static void MR_fatal_abort(void *context, const char *main_msg, int dump) { char *context_msg; int ret; context_msg = MR_explain_context(context); do { ret = write(STDERR, main_msg, strlen(main_msg)); } while (ret == -1 && MR_is_eintr(errno)); do { ret = write(STDERR, context_msg, strlen(context_msg)); } while (ret == -1 && MR_is_eintr(errno)); MR_trace_report_raw(STDERR); if (dump) { MR_print_dump_stack(); } _exit(1); } MR_bool MR_default_handler(MR_Word *fault_addr, MR_MemoryZone *zone, void *context) { #ifndef MR_CHECK_OVERFLOW_VIA_MPROTECT return MR_FALSE; #else MR_Word *new_zone; size_t zone_size; new_zone = (MR_Word *) MR_round_up((MR_Unsigned) fault_addr + sizeof(MR_Word), MR_unit); if (new_zone <= zone->MR_zone_hardmax) { zone_size = (char *) new_zone - (char *) zone->MR_zone_redzone; if (MR_memdebug) { fprintf(stderr, "trying to unprotect %s#%" MR_INTEGER_LENGTH_MODIFIER "d from %p to %p (%x)\n", zone->MR_zone_name, zone->MR_zone_id, (void *) zone->MR_zone_redzone, (void *) new_zone, (int) zone_size); } if (MR_protect_pages((char *) zone->MR_zone_redzone, zone_size, PROT_READ|PROT_WRITE) < 0) { char buf[2560]; sprintf(buf, "Mercury runtime: cannot unprotect %s#%" MR_INTEGER_LENGTH_MODIFIER "d zone", zone->MR_zone_name, zone->MR_zone_id); perror(buf); exit(1); } zone->MR_zone_redzone = new_zone; if (MR_memdebug) { fprintf(stderr, "successful: %s#%" MR_INTEGER_LENGTH_MODIFIER "d redzone now %p to %p\n", zone->MR_zone_name, zone->MR_zone_id, (void *) zone->MR_zone_redzone, (void *) zone->MR_zone_top); } #if defined(MR_NATIVE_GC) && !defined(MR_HIGHLEVEL_CODE) MR_schedule_agc(get_pc_from_context(context), get_sp_from_context(context), get_curfr_from_context(context)); #endif return MR_TRUE; } else { char buf[2560]; if (MR_memdebug) { fprintf(stderr, "can't unprotect last page of %s#%" MR_INTEGER_LENGTH_MODIFIER "d\n", zone->MR_zone_name, zone->MR_zone_id); fflush(stdout); } #ifdef MR_STACK_EXTEND_DEBUG MR_restore_transient_registers(); fprintf(stderr, "sp = %p, maxfr = %p\n", MR_sp, MR_maxfr); MR_debug_memory_zone(stderr, zone); #endif sprintf(buf, "\nMercury runtime: memory zone %s#%" MR_INTEGER_LENGTH_MODIFIER "d overflowed\n", zone->MR_zone_name, zone->MR_zone_id); MR_fatal_abort(context, buf, MR_TRUE); } return MR_FALSE; #endif } void MR_setup_signals(void) { /* ** When using Microsoft Visual C structured exceptions don't set any ** signal handlers. ** See mercury_wrapper.c for the reason why. */ #ifndef MR_MSVC_STRUCTURED_EXCEPTIONS #ifdef SIGBUS MR_setup_signal(SIGBUS, (MR_Code *) bus_handler, MR_TRUE, "cannot set SIGBUS handler"); #endif MR_setup_signal(SIGSEGV, (MR_Code *) segv_handler, MR_TRUE, "cannot set SIGSEGV handler"); #endif } static char * MR_explain_context(void *the_context) { static char buf[100]; #if defined(MR_HAVE_SIGCONTEXT_STRUCT) #ifdef MR_PC_ACCESS struct sigcontext_struct *context = the_context; void *pc_at_signal = (void *) context->MR_PC_ACCESS; sprintf(buf, "PC at signal: %ld (%lx)\n", (long)pc_at_signal, (long)pc_at_signal); #else buf[0] = '\0'; #endif #elif defined(MR_HAVE_SIGINFO_T) #ifdef MR_PC_ACCESS ucontext_t *context = the_context; #ifdef MR_PC_ACCESS_GREG sprintf(buf, "PC at signal: %ld (%lx)\n", (long) context->uc_mcontext.gregs[MR_PC_ACCESS], (long) context->uc_mcontext.gregs[MR_PC_ACCESS]); #else sprintf(buf, "PC at signal: %ld (%lx)\n", (long) context->uc_mcontext.MR_PC_ACCESS, (long) context->uc_mcontext.MR_PC_ACCESS); #endif #else /* not MR_PC_ACCESS */ /* if MR_PC_ACCESS is not set, we don't know the context */ /* therefore we return an empty string to be printed */ buf[0] = '\0'; #endif /* not MR_PC_ACCESS */ #else /* not MR_HAVE_SIGINFO_T && not MR_HAVE_SIGCONTEXT_STRUCT */ buf[0] = '\0'; #endif return buf; } #if defined(MR_HAVE_SIGCONTEXT_STRUCT) #if defined(MR_HAVE_SIGCONTEXT_STRUCT_3ARG) static void complex_sighandler_3arg(int sig, int code, struct sigcontext_struct sigcontext) #else static void complex_sighandler(int sig, struct sigcontext_struct sigcontext) #endif { void *address = (void *) MR_GET_FAULT_ADDR(sigcontext); #ifdef MR_PC_ACCESS void *pc_at_signal = (void *) sigcontext.MR_PC_ACCESS; #endif switch(sig) { case SIGSEGV: /* ** If we're debugging, print the segv explanation ** messages before we call MR_try_munprotect. But if ** we're not debugging, only print them if ** MR_try_munprotect fails. */ if (MR_memdebug) { fflush(stdout); fprintf(stderr, "\n*** Mercury runtime: " "caught segmentation violation ***\n"); } if (MR_try_munprotect(address, &sigcontext)) { if (MR_memdebug) { fprintf(stderr, "returning from " "signal handler\n\n"); } return; } if (!MR_memdebug) { fflush(stdout); fprintf(stderr, "\n*** Mercury runtime: " "caught segmentation violation ***\n"); } break; #ifdef SIGBUS case SIGBUS: fflush(stdout); fprintf(stderr, "\n*** Mercury runtime: " "caught bus error ***\n"); break; #endif default: fflush(stdout); fprintf(stderr, "\n*** Mercury runtime: " "caught unknown signal %d ***\n", sig); break; } #ifdef MR_PC_ACCESS fprintf(stderr, "PC at signal: %ld (%lx)\n", (long) pc_at_signal, (long) pc_at_signal); #endif fprintf(stderr, "address involved: %p\n", address); MR_trace_report(stderr); MR_print_dump_stack(); MR_dump_prev_locations(); leave_signal_handler(sig); } /* end complex_sighandler() */ #elif defined(MR_HAVE_SIGINFO_T) static void complex_bushandler(int sig, siginfo_t *info, void *context) { fflush(stdout); if (sig != SIGBUS || !info || info->si_signo != SIGBUS) { MR_fatal_abort(context, "\n*** Mercury runtime: " "caught strange bus error ***\n", 1); } fprintf(stderr, "\n*** Mercury runtime: "); fprintf(stderr, "caught bus error ***\n"); if (info->si_code > 0) { fprintf(stderr, "cause: "); switch (info->si_code) { #ifdef BUS_ADRALN case BUS_ADRALN: fprintf(stderr, "invalid address alignment\n"); break; #endif #ifdef BUS_ADRERR case BUS_ADRERR: fprintf(stderr, "non-existent physical address\n"); break; #endif #ifdef BUS_OBJERR case BUS_OBJERR: fprintf(stderr, "object specific hardware error\n"); break; #endif #ifdef BUS_PAGE_FAULT case BUS_PAGE_FAULT: fprintf(stderr, "page fault protection base\n"); break; #endif #ifdef BUS_SEGNP_FAULT case BUS_SEGNP_FAULT: fprintf(stderr, "segment not present\n"); break; #endif #ifdef BUS_STK_FAULT case BUS_STK_FAULT: fprintf(stderr, "stack segment\n"); break; #endif #ifdef BUS_SEGM_FAULT case BUS_SEGM_FAULT: fprintf(stderr, "segment protection base\n"); break; #endif default: fprintf(stderr, "unknown\n"); break; } /* end switch */ fprintf(stderr, "%s", MR_explain_context(context)); fprintf(stderr, "address involved: %p\n", (void *) info->si_addr); } /* end if */ MR_trace_report(stderr); MR_print_dump_stack(); MR_dump_prev_locations(); leave_signal_handler(sig); } /* end complex_bushandler() */ static void MR_explain_segv(siginfo_t *info, void *context) { fflush(stdout); fprintf(stderr, "\n*** Mercury runtime: "); fprintf(stderr, "caught segmentation violation ***\n"); if (!info) { return; } if (info->si_code > 0) { fprintf(stderr, "cause: "); switch (info->si_code) { #ifdef SEGV_MAPERR case SEGV_MAPERR: fprintf(stderr, "address not mapped to object\n"); break; #endif #ifdef SEGV_ACCERR case SEGV_ACCERR: fprintf(stderr, "bad permissions for mapped object\n"); break; #endif default: fprintf(stderr, "unknown\n"); break; } fprintf(stderr, "%s", MR_explain_context(context)); fprintf(stderr, "address involved: %p\n", (void *) info->si_addr); } /* end if */ } static void complex_segvhandler(int sig, siginfo_t *info, void *context) { if (sig != SIGSEGV || !info || info->si_signo != SIGSEGV) { MR_fatal_abort(context, "\n*** Mercury runtime: " "caught strange segmentation violation ***\n", 1); } /* ** If we're debugging, print the segv explanation messages ** before we call MR_try_munprotect. But if we're not debugging, ** only print them if MR_try_munprotect fails. */ if (MR_memdebug) { MR_explain_segv(info, context); } if (MR_try_munprotect(info->si_addr, context)) { if (MR_memdebug) { fprintf(stderr, "returning from signal handler\n\n"); } return; } if (!MR_memdebug) { MR_explain_segv(info, context); } MR_trace_report(stderr); MR_print_dump_stack(); MR_dump_prev_locations(); leave_signal_handler(sig); } /* end complex_segvhandler */ #else /* not MR_HAVE_SIGINFO_T && not MR_HAVE_SIGCONTEXT_STRUCT */ static void simple_sighandler(int sig) { fflush(stdout); fprintf(stderr, "*** Mercury runtime: "); switch (sig) { #ifdef SIGBUS case SIGBUS: fprintf(stderr, "caught bus error ***\n"); break; #endif case SIGSEGV: fprintf(stderr, "caught segmentation violation ***\n"); break; default: fprintf(stderr, "caught unknown signal %d ***\n", sig); break; } MR_print_dump_stack(); MR_dump_prev_locations(); leave_signal_handler(sig); } #endif /* not MR_HAVE_SIGINFO_T && not MR_HAVE_SIGCONTEXT_STRUCT */ #ifdef MR_MSVC_STRUCTURED_EXCEPTIONS static const char *MR_find_exception_name(DWORD exception_code); static void MR_explain_exception_record(EXCEPTION_RECORD *rec); static void MR_dump_exception_record(EXCEPTION_RECORD *rec); static MR_bool MR_exception_record_is_access_violation(EXCEPTION_RECORD *rec, void **address_ptr, int *access_mode_ptr); /* ** Exception code and their string representation */ #define DEFINE_EXCEPTION_NAME(a) {a,#a} typedef struct { DWORD exception_code; const char *exception_name; } MR_ExceptionName; static const MR_ExceptionName MR_exception_names[] = { DEFINE_EXCEPTION_NAME(EXCEPTION_ACCESS_VIOLATION), DEFINE_EXCEPTION_NAME(EXCEPTION_DATATYPE_MISALIGNMENT), DEFINE_EXCEPTION_NAME(EXCEPTION_BREAKPOINT), DEFINE_EXCEPTION_NAME(EXCEPTION_SINGLE_STEP), DEFINE_EXCEPTION_NAME(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_DENORMAL_OPERAND), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_DIVIDE_BY_ZERO), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_INEXACT_RESULT), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_INVALID_OPERATION), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_OVERFLOW), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_STACK_CHECK), DEFINE_EXCEPTION_NAME(EXCEPTION_FLT_UNDERFLOW), DEFINE_EXCEPTION_NAME(EXCEPTION_INT_DIVIDE_BY_ZERO), DEFINE_EXCEPTION_NAME(EXCEPTION_INT_OVERFLOW), DEFINE_EXCEPTION_NAME(EXCEPTION_PRIV_INSTRUCTION), DEFINE_EXCEPTION_NAME(EXCEPTION_IN_PAGE_ERROR), DEFINE_EXCEPTION_NAME(EXCEPTION_ILLEGAL_INSTRUCTION), DEFINE_EXCEPTION_NAME(EXCEPTION_NONCONTINUABLE_EXCEPTION), DEFINE_EXCEPTION_NAME(EXCEPTION_STACK_OVERFLOW), DEFINE_EXCEPTION_NAME(EXCEPTION_INVALID_DISPOSITION), DEFINE_EXCEPTION_NAME(EXCEPTION_GUARD_PAGE), DEFINE_EXCEPTION_NAME(EXCEPTION_INVALID_HANDLE) }; /* ** Retrieve the name of a Win32 exception code as a string */ static const char * MR_find_exception_name(DWORD exception_code) { int i; for (i = 0; i < sizeof(MR_exception_names) / sizeof(MR_ExceptionName); i++) { if (MR_exception_names[i].exception_code == exception_code) { return MR_exception_names[i].exception_name; } } return "Unknown exception code"; } /* ** Was a page accessed read/write? The MSDN documentation doens't define ** symbolic constants for these alternatives. */ #define READ 0 #define WRITE 1 /* ** Explain an EXCEPTION_RECORD content into stderr. */ static void MR_explain_exception_record(EXCEPTION_RECORD *rec) { fprintf(stderr, "\n"); fprintf(stderr, "\n*** Explanation of the exception record"); if (rec == NULL) { fprintf(stderr, "\n*** Cannot explain because it is NULL"); return; } else { void *address; int access_mode; /* If the exception is an access violation */ if (MR_exception_record_is_access_violation(rec, &address, &access_mode)) { MR_MemoryZone *zone; /* Display AV address and access mode */ fprintf(stderr, "\n*** An access violation occured" " at address 0x%08lx, while attempting" " to ", (unsigned long) address); if (access_mode == READ) { fprintf(stderr, "\n*** read " "inaccessible data"); } else if (access_mode == WRITE) { fprintf(stderr, "\n*** write to an " "inaccessible (or protected)" " address"); } else { fprintf(stderr, "\n*** ? [unknown access " "mode %d (strange...)]", access_mode); } #if defined(MR_CHECK_OVERFLOW_VIA_MPROTECT) fprintf(stderr, "\n*** Trying to see if this " "stands within a mercury zone..."); /* ** Browse the mercury memory zones to see if the ** AV address references one of them. */ zone = MR_get_used_memory_zones_readonly(); while(zone != NULL) { fprintf(stderr, "\n*** Checking zone %s#%" MR_INTEGER_LENGTH_MODIFIER "d: " "0x%08lx - 0x%08lx - 0x%08lx", zone->MR_zone_name, zone->MR_zone_id, (unsigned long) zone->MR_zone_bottom, (unsigned long) zone->MR_zone_redzone, (unsigned long) zone->MR_zone_top); if ((zone->MR_zone_redzone <= address) && (address <= zone->MR_zone_top)) { fprintf(stderr, "\n*** Address is within" " redzone of " "%s#%" MR_INTEGER_LENGTH_MODIFIER "d (!!zone overflowed!!)\n", zone->MR_zone_name, zone->MR_zone_id); } else if ((zone->MR_zone_bottom <= address) && (address <= zone->MR_zone_top)) { fprintf(stderr, "\n*** Address is" " within zone %s#%" MR_INTEGER_LENGTH_MODIFIER "d\n", zone->MR_zone_name, zone->MR_zone_id); } /* ** Don't need to call handler, because it ** has much less information than we do. */ /* return zone->MR_zone_handler(fault_addr, zone, rec); */ zone = zone->MR_zone_next; } #endif /* MR_CHECK_OVERFLOW_VIA_MPROTECT */ } return; } } /* ** Dump an EXCEPTION_RECORD content into stderr. */ static void MR_dump_exception_record(EXCEPTION_RECORD *rec) { int i; if (rec == NULL) { return; } fprintf(stderr, "\n*** Exception record at 0x%08lx:", (unsigned long) rec); fprintf(stderr, "\n*** MR_Code : 0x%08lx (%s)", (unsigned long) rec->ExceptionCode, MR_find_exception_name(rec->ExceptionCode)); fprintf(stderr, "\n*** Flags : 0x%08lx", (unsigned long) rec->ExceptionFlags); fprintf(stderr, "\n*** Address : 0x%08lx", (unsigned long) rec->ExceptionAddress); for (i = 0; i < rec->NumberParameters; i++) { fprintf(stderr, "\n*** Parameter %d : 0x%08lx", i, (unsigned long) rec->ExceptionInformation[i]); } fprintf(stderr, "\n*** Next record : 0x%08lx", (unsigned long) rec->ExceptionRecord); /* Try to explain the exception more "gracefully" */ MR_explain_exception_record(rec); MR_dump_exception_record(rec->ExceptionRecord); } /* ** Return MR_TRUE iff exception_ptrs indicates an access violation. ** If MR_TRUE, the dereferenced address_ptr is set to the accessed address and ** the dereferenced access_mode_ptr is set to the desired access ** (0 = read, 1 = write) */ static MR_bool MR_exception_record_is_access_violation(EXCEPTION_RECORD *rec, void **address_ptr, int *access_mode_ptr) { if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { if (rec->NumberParameters >= 2) { (*access_mode_ptr) = (int) rec->ExceptionInformation[0]; (*address_ptr) = (void *) rec->ExceptionInformation[1]; return MR_TRUE; } } return MR_FALSE; } /* ** Filter a Win32 exception (to be called in the __except filter part). ** Possible return values are: ** ** EXCEPTION_CONTINUE_EXECUTION (-1) ** Exception is dismissed. Continue execution at the point where ** the exception occurred. ** ** EXCEPTION_CONTINUE_SEARCH (0) ** Exception is not recognized. Continue to search up the stack for ** a handler, first for containing try-except statements, then for ** handlers with the next highest precedence. ** ** EXCEPTION_EXECUTE_HANDLER (1) ** Exception is recognized. Transfer control to the exception handler ** by executing the __except compound statement, then continue ** execution at the assembly instruction that was executing ** when the exception was raised. */ int MR_filter_win32_exception(LPEXCEPTION_POINTERS exception_ptrs) { void *address; int access_mode; /* If the exception is an access violation */ if (MR_exception_record_is_access_violation( exception_ptrs->ExceptionRecord, &address, &access_mode)) { /* If we can unprotect the memory zone */ if (MR_try_munprotect(address, exception_ptrs)) { if (MR_memdebug) { fprintf(stderr, "returning from " "signal handler\n\n"); } /* Continue execution where it stopped */ return EXCEPTION_CONTINUE_EXECUTION; } } /* ** We can't handle the exception. Just dump all the information we got */ fflush(stdout); fprintf(stderr, "\n*** Mercury runtime: Unhandled exception "); MR_dump_exception_record(exception_ptrs->ExceptionRecord); printf("\n"); MR_print_dump_stack(); MR_dump_prev_locations(); fprintf(stderr, "\n\n*** Now passing exception to default handler\n\n"); fflush(stderr); /* ** Pass exception back to upper handler. In most cases, this ** means activating UnhandledExceptionFilter, which will display ** a dialog box asking to user ro activate the Debugger or simply ** to kill the application */ return EXCEPTION_CONTINUE_SEARCH; } #endif /* MR_MSVC_STRUCTURED_EXCEPTIONS */ /* ** get_pc_from_context: ** Given the signal context, return the program counter at the time ** of the signal, if available. If it is unavailable, return NULL. */ static MR_Code * get_pc_from_context(void *the_context) { MR_Code *pc_at_signal = NULL; #if defined(MR_HAVE_SIGCONTEXT_STRUCT) #ifdef MR_PC_ACCESS struct sigcontext_struct *context = the_context; pc_at_signal = (MR_Code *) context->MR_PC_ACCESS; #else pc_at_signal = (MR_Code *) NULL; #endif #elif defined(MR_HAVE_SIGINFO_T) #ifdef MR_PC_ACCESS ucontext_t *context = the_context; #ifdef MR_PC_ACCESS_GREG pc_at_signal = (MR_Code *) context->uc_mcontext.gregs[MR_PC_ACCESS]; #else pc_at_signal = (MR_Code *) context->uc_mcontext.MR_PC_ACCESS; #endif #else /* not MR_PC_ACCESS */ /* if MR_PC_ACCESS is not set, we don't know the context */ pc_at_signal = (MR_Code *) NULL; #endif /* not MR_PC_ACCESS */ #else /* not MR_HAVE_SIGINFO_T && not MR_HAVE_SIGCONTEXT_STRUCT */ pc_at_signal = (MR_Code *) NULL; #endif return pc_at_signal; } /* ** get_sp_from_context: ** Given the signal context, return the Mercury register "MR_sp" at ** the time of the signal, if available. If it is unavailable, ** return NULL. ** ** XXX We only define this function in LLDS accurate gc grades for the moment, ** because it's unlikely to compile everywhere. It relies on ** MR_real_reg_number_sp being defined, which is the name/number of the ** machine register that is used for MR_sp. ** Need to fix this so it works when the register is in a fake reg too. */ static MR_Word * get_sp_from_context(void *the_context) { MR_Word *sp_at_signal = NULL; #if defined(MR_NATIVE_GC) && !defined(MR_HIGHLEVEL_CODE) #if defined(MR_HAVE_SIGCONTEXT_STRUCT) #ifdef MR_PC_ACCESS struct sigcontext_struct *context = the_context; sp_at_signal = (MR_Word *) context->MR_real_reg_number_sp; #else sp_at_signal = (MR_Word *) NULL; #endif #elif defined(MR_HAVE_SIGINFO_T) #ifdef MR_PC_ACCESS struct sigcontext *context = the_context; #ifdef MR_PC_ACCESS_GREG sp_at_signal = (MR_Word *) context->gregs[MR_real_reg_number_sp]; #else sp_at_signal = (MR_Word *) context->sc_regs[MR_real_reg_number_sp]; #endif #else /* not MR_PC_ACCESS */ /* ** if MR_PC_ACCESS is not set, we don't know how to get at the ** registers */ sp_at_signal = (MR_Word *) NULL; #endif /* not MR_PC_ACCESS */ #else /* not MR_HAVE_SIGINFO_T && not MR_HAVE_SIGCONTEXT_STRUCT */ sp_at_signal = (MR_Word *) NULL; #endif #else /* !MR_NATIVE_GC */ sp_at_signal = (MR_Word *) NULL; #endif /* !MR_NATIVE_GC */ return sp_at_signal; } /* ** get_sp_from_context: ** Given the signal context, return the Mercury register "MR_sp" at ** the time of the signal, if available. If it is unavailable, ** return NULL. ** ** XXX We only define this function in accurate gc grades for the moment, ** because it's unlikely to compile everywhere. It relies on ** MR_real_reg_number_sp being defined, which is the name/number of the ** machine register that is used for MR_sp. ** Need to fix this so it works when the register is in a fake reg too. */ static MR_Word * get_curfr_from_context(void *the_context) { MR_Word *curfr_at_signal; /* ** XXX this is implementation dependent, need a better way ** to do register accesses at signals. ** ** It's in mr8 or mr9 which is in the fake regs on some architectures, ** and is a machine register on others. ** So don't run the garbage collector on those archs. */ curfr_at_signal = MR_curfr; return curfr_at_signal; } static void MR_print_dump_stack(void) { const char *msg = "This may have been caused by a stack overflow, due to unbounded recursion.\n"; int ret; do { ret = write(STDERR, msg, strlen(msg)); } while (ret == -1 && MR_is_eintr(errno)); } static void leave_signal_handler(int sig) { fprintf(stderr, "exiting from signal handler\n"); #if defined(MR_THREAD_SAFE) && defined(MR_THREADSCOPE) if (MR_all_engine_bases) { int i; for (i = 0; i < MR_num_threads; i++) { if (MR_all_engine_bases[i] && MR_all_engine_bases[i]->MR_eng_ts_buffer) { MR_threadscope_finalize_engine(MR_all_engine_bases[i]); } } } MR_finalize_threadscope(); #endif MR_reset_signal(sig); raise(sig); }