Files
mercury/runtime/mercury_memory_handlers.c
Julien Fischer 36ac880e97 Fix bug #243: the management of memory zones on Windows was broken, causing a
Branches: main, 11.07

Fix bug #243: the management of memory zones on Windows was broken, causing a
memory leak in trseg and (presumably) stseg grades.  In grades that use
conservative GC on most systems (e.g. Linux or Mac OS X), allocation of memory
for memory zones is handled by the Boehm collector.  On Windows, the situation
was a bit different because we were trying to emulate mprotect style memory
protection.  Instead of allocating the memory for the zone using the Boehm
collector, the memory was reserved using a call to VirtualAlloc and then all
the memory within the zone was added to the Boehm GC's root set.  We could
then use VirtualAlloc and friends to emulate mprotect.  In principle, this
is fine, in practice there are number of problems with it.

When a memory zone was deallocated the call to VirtualFree was silently failing
(the return value of this call wasn't checked) -- this meant that the memory in
the zone was not being freed, hence the leak.   The immediate cause of this is
that we are calling VirtualFree with the wrong argument values for those used
in the corresponding call to VirtulAlloc.  Unfortunately, putting the "correct"
value in, just results in a segmentation fault.  (I suspect we haven't actually
been allocating the memory correctly either.)

Aside from this a second problem is that while we register the words in the
memory zone as GC roots when we allocate it, we do not remove them from the
GC root set when we deallocate the zone.  One rather obvious reason for
this is that the Boehm GC doesn't support doing this on Windows!

Yet another issue with all this is that while MR_protect_pages calls
VirtualAlloc, the documentation in the code suggests it should be calling
VirtualProtect.

The fix here is to replace the VirtualAlloc way of managing memory zones on
Windows with the Boehm GC approach used on all the other systems.  The former
is pretty much completely broken and only appeared to work because most grades
only allocate a few memory zones at program startup and never deallocate them.
This does mean the mprotect emulation we were doing on Windows will no longer
work, but that's the lesser of two evils.  (It may be possible to reinstate
something like it using VirtualProtect, but that will require looking carefully
at exactly how the Boehm collector allocates memory on Windows.)

runtime/mercury_memory_zones.c:
	Use the Boehm GC for allocating memory zones on Windows when
	in grade that uses conservative GC.

	Delete the flawed approach that uses VirtualAlloc.

runtime/mercury_conf_param.h:
	Do not define the macro MR_WIN32_VIRTUAL_ALLOC on Windows: we no
	longer use VirtualAlloc anywhere.

	Do not define MR_PROTECTPAGE and MR_CHECK_OVERFLOW_VIA_MPROTECT
	on Windows, since we no longer do any mprotect emulation on
	Windows.

runtime/mercury_memory_handlers.c:
	Properly protect some code in the handler for Windows memory
	access errors that assumes that memory zones have a redzone.
	(After this change they won't have a redzone.)
2011-12-07 11:29:16 +00:00

1049 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);
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);
}