mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-11 20:03:28 +00:00
runtime/mercury_wrapper.c: Declare MR_usage to be a noreturn function. This avoids warnings in other parts of this module. runtime/mercury_context.c Only define MR_write_out_profiling_parallel_execution if MR_PROFILE_PARALLEL_EXECUTION_SUPPORT is defined; it is otherwise unused. Fix incorrect conversion specifiers in some calls to fprintf. Only define the function action_shutdown_engine and friends if MR_THREAD_SAFE is defined. Do not define MR_checking_pending_contexts if MR_HIGHLEVEL_CODE is defined. runtime/mercury_debug.c: Protect a group of function prototypes with MR_LOWLEVEL_DEBUG. (The corresponding definitions were already protected by this macro.) runtime/mercury_label.c: Delete the declaration of the function compare_entry_by_addr as that function is not defined anywhere. Delete an unused local variable. runtime/mercury_make_type_info_body.h: Delete an unused local variable. runtime/mercury_memory.c: Delete left over function prototypes for try_munprotect and explain_context. (The functions were moved into the module mercury_memory_handlers a long time ago.) runtime/mercury_memory_handlers.c: Protect functions that are only used in the low-level agc grades by the appropriate macros. runtime/mercury_minimal.c: runtime/mercury_mm_own_stacks.c: Don't define some global variables in high-level C grades that are not necessary in those grades. runtime/mercury_tabling.c: Only define MR_table_assert_failed if MR_TABLE_DEBUG is defined since it is otherwise unused.
1053 lines
30 KiB
C
1053 lines
30 KiB
C
/*
|
|
** 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 <unistd.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
** This include must come before anything else that might include <signal.h>.
|
|
** See the commments in mercury_signal.h.
|
|
*/
|
|
#include "mercury_signal.h"
|
|
|
|
#ifdef MR_HAVE_SYS_SIGINFO_H
|
|
#include <sys/siginfo.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_SYS_SIGNAL_H
|
|
/* on FREEBSD we need to include <sys/signal.h> before <ucontext.h> */
|
|
#include <sys/signal.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_MPROTECT
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_UCONTEXT_H
|
|
#include <ucontext.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_SYS_UCONTEXT_H
|
|
#include <sys/ucontext.h>
|
|
#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);
|
|
#if defined(MR_NATIVE_GC) && !defined(MR_HIGHLEVEL_CODE)
|
|
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);
|
|
#endif
|
|
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 */
|
|
|
|
#if defined(MR_NATIVE_GC) && !defined(MR_HIGHLEVEL_CODE)
|
|
/*
|
|
** 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;
|
|
}
|
|
#endif /* MR_NATIVE_GC && not MR_HIGHLEVEL_CODE */
|
|
|
|
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);
|
|
}
|