stack overflow checking on Linux

Fergus Henderson fjh at cs.mu.oz.au
Wed Feb 12 18:49:36 AEDT 1997


Hi Zoltan,

Please feel free to review the following change.

Cheers,
	Fergus.


Add support for detecting memory zone overflow on Linux i386.
The bad news is that in order to get the information you need
(namely the fault address) in signal handlers, you have to grovel
through undocumented kernel structures in <asm/sigcontext.h>. 
The good news is that at least you can get it.

configure.in:
runtime/conf.h.in:
	Add auto-detection for <asm/sigcontext.h>.
	Change the meaning of HAVE_SIGINFO: it is now set if there is
	any way of getting the signal information we need,
	whether it is via <asm/sigcontext.h> or via <sys/siginfo.h>.
	Rename the old HAVE_SIGINFO, which indicates the
	presence of <sys/siginfo.h>, to HAVE_SYS_SIGINFO.
	Add HAVE_SIGINFO_T and HAVE_SIGCONTEXT_STRUCT.

runtime/memory.c:
	Add support for getting signal information using sigcontext_struct
	rather than siginfo_t.

Index: conf.h.in
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/conf.h.in,v
retrieving revision 1.11
diff -u -r1.11 conf.h.in
--- conf.h.in	1997/02/12 01:06:31	1.11
+++ conf.h.in	1997/02/12 07:19:59
@@ -30,11 +30,18 @@
 **	HAVE_MPROTECT		the machine has the mprotect() syscall.
 **	HAVE_STRERROR		the machine has the strerror() function.
 **	RETSIGTYPE		the return type of signal handlers.
-**	HAVE_SIGINFO		signal handlers are given siginfo arguments,
-**				and we have <sys/siginfo.h>
-**	HAVE_UCONTEXT		signal handlers are given ucontext arguments,
-**				and we have <ucontext.h>
+**	HAVE_SIGINFO		true if we can _somehow_ figure out the
+**				fault address for SIGSEGVs.
+**	HAVE_SIGINFO_T		true if we can figure out the
+**				fault address for SIGSEGVs using sigaction
+**				and siginfo_t.
+**	HAVE_SIGCONTEXT_STRUCT	true if normal signal handlers are given
+**				sigcontext_struct arguments that we can use to
+**				figure out the fault address for SIGSEGVs.
+**	HAVE_SYS_SIGINFO	we have <sys/siginfo.h>
+**	HAVE_UCONTEXT		we have <ucontext.h>
 **	HAVE_SYS_UCONTEXT	we have <sys/ucontext.h>
+**	HAVE_ASM_SIGCONTEXT	we have <asm/sigcontext.h> (e.g. i386 Linux)
 **	HAVE_SYS_TIME		we have <sys/time.h>
 **	HAVE_SYS_PARAM		we have <sys/param.h>
 **	HAVE_SETITIMER		we have setitimer()
@@ -56,8 +63,12 @@
 #undef	HAVE_STRERROR
 #undef	RETSIGTYPE
 #undef	HAVE_SIGINFO
+#undef	HAVE_SIGINFO_T
+#undef	HAVE_SIGCONTEXT_STRUCT
+#undef	HAVE_SYS_SIGINFO
 #undef	HAVE_UCONTEXT
 #undef	HAVE_SYS_UCONTEXT
+#undef	HAVE_ASM_SIGCONTEXT
 #undef	HAVE_SYS_TIME
 #undef	HAVE_SYS_PARAM
 #undef	HAVE_SETITIMER
Index: configure.in
===================================================================
RCS file: /home/staff/zs/imp/mercury/configure.in,v
retrieving revision 1.81
diff -u -r1.81 configure.in
--- configure.in	1997/02/09 05:57:20	1.81
+++ configure.in	1997/02/12 07:20:06
@@ -212,9 +212,9 @@
 AC_RETSIGTYPE
 AC_HAVE_FUNCS(sysconf getpagesize memalign mprotect sigaction setitimer strerror)
 #-----------------------------------------------------------------------------#
-AC_CHECK_HEADER(sys/siginfo.h, HAVE_SIGINFO_H=1)
-if test "$HAVE_SIGINFO_H" = 1; then
-	AC_DEFINE(HAVE_SIGINFO)
+AC_CHECK_HEADER(sys/siginfo.h, HAVE_SYS_SIGINFO_H=1)
+if test "$HAVE_SYS_SIGINFO_H" = 1; then
+	AC_DEFINE(HAVE_SYS_SIGINFO)
 fi
 #-----------------------------------------------------------------------------#
 AC_CHECK_HEADER(ucontext.h, HAVE_UCONTEXT_H=1)
@@ -228,6 +228,11 @@
 	fi
 fi
 #-----------------------------------------------------------------------------#
+AC_CHECK_HEADER(asm/sigcontext.h, HAVE_ASM_SIGCONTEXT_H=1)
+if test "$HAVE_ASM_SIGCONTEXT_H" = 1; then
+	AC_DEFINE(HAVE_ASM_SIGCONTEXT)
+fi
+#-----------------------------------------------------------------------------#
 AC_CHECK_HEADER(sys/param.h, HAVE_SYS_PARAM_H=1)
 if test "$HAVE_SYS_PARAM_H" = 1; then
 	AC_DEFINE(HAVE_SYS_PARAM)
@@ -238,14 +243,22 @@
 	AC_DEFINE(HAVE_SYS_TIME)
 fi
 #-----------------------------------------------------------------------------#
+#
+# check the basics of sigaction
+#
 if test "$ac_cv_func_sigaction" = yes; then
 	AC_MSG_CHECKING(for sigaction field name)
 	AC_CACHE_VAL(mercury_cv_sigaction_field,
 	AC_TRY_RUN([
+
 	#include <signal.h>
 	#include <stdlib.h>
+
+	#define FAULT_ADDRESS ((int *)64)
+
 	extern void handler(int signum, siginfo_t *info, void *context);
-	main() {
+
+	int main() {
 		struct sigaction act;
 		act.sa_flags = SA_SIGINFO | SA_RESTART;
 		act.sa_sigaction = handler;
@@ -253,21 +266,167 @@
 			exit(1);
 		if (sigaction(SIGSEGV, &act, NULL) != 0)
 			exit(1);
-		exit(0);
+		/* provoke a SIGSEGV */
+		(*FAULT_ADDRESS)++;
+		exit(1);
 	}
+
 	void handler(int signum, siginfo_t *info, void *context) {
-		return;
+		if (signum == SIGSEGV &&
+		    info->si_signo == SIGSEGV &&
+		    info->si_code > 0 &&
+		    (int *)info->si_addr == FAULT_ADDRESS)
+		{
+			exit(0);
+		}
 	}],
 	[mercury_cv_sigaction_field=sa_sigaction],
 	[mercury_cv_sigaction_field=sa_handler],
 	[mercury_cv_sigaction_field=sa_handler]))
 	AC_MSG_RESULT($mercury_cv_sigaction_field)
 	AC_DEFINE_UNQUOTED(SIGACTION_FIELD,$mercury_cv_sigaction_field)
-#-----------------------------------------------------------------------------#
-	if test "$HAVE_SIGINFO_H" = 1 &&
-	   (test "$HAVE_UCONTEXT_H" = 1 || test "$HAVE_SYS_UCONTEXT_H" = 1)
-	then
-		AC_MSG_CHECKING(for pc access at signals)
+	if test $mercury_cv_sigaction_field = sa_handler; then
+		AC_DEFINE(HAVE_SIGINFO)
+	fi
+fi
+#
+# check the basics of sigcontext_struct
+#
+AC_MSG_CHECKING(for working sigcontext_struct)
+AC_CACHE_VAL(mercury_cv_sigcontext_struct,
+mercury_cv_sigcontext_struct=no
+AC_TRY_RUN([
+
+#define __KERNEL__
+#include <signal.h>
+#undef __KERNEL
+
+#ifdef HAVE_ASM_SIGCONTEXT
+#include <asm/sigcontext.h>
+#endif
+
+#include <stdio.h>
+
+extern void handler(int signum, struct sigcontext_struct info);
+
+#define FAULT_ADDRESS ((int *)64)
+
+int main() {
+	signal(SIGSEGV, (void (*)(int))handler);
+
+	/* provoke a SIGSEGV */
+	(*FAULT_ADDRESS)++;
+
+	exit(1);
+}
+
+void handler(int signum, struct sigcontext_struct context) {
+	if (signum == SIGSEGV && (int *) context.cr2 == FAULT_ADDRESS) {
+		exit(0);
+	}
+}], [mercury_cv_sigcontext_struct=yes], [true], [true]))
+AC_MSG_RESULT($mercury_cv_sigcontext_struct)
+if test $mercury_cv_sigcontext_struct = yes; then
+	AC_DEFINE(HAVE_SIGCONTEXT_STRUCT)
+	AC_DEFINE(HAVE_SIGINFO)
+
+	#
+	# check for sigcontext_struct.eip
+	#
+	AC_MSG_CHECKING(for sigcontext_struct pc access at signals)
+	AC_CACHE_VAL(mercury_cv_pc_access,
+	mercury_cv_pc_access=no
+	AC_TRY_RUN([
+	#define __KERNEL__
+	#include <signal.h>
+	#undef __KERNEL
+
+	#ifdef HAVE_ASM_SIGCONTEXT
+	#include <asm/sigcontext.h>
+	#endif
+
+	#include <stdio.h>
+
+	extern void handler(int signum, struct sigcontext_struct info);
+
+	#define FAULT_ADDRESS ((int *)64)
+
+	int main() {
+		signal(SIGSEGV, (void (*)(int))handler);
+
+		/* provoke a SIGSEGV */
+		(*FAULT_ADDRESS)++;
+
+		exit(1);
+	}
+
+	void handler(int signum, struct sigcontext_struct info) {
+		if (signum == SIGSEGV &&
+			(int *) info.cr2 == FAULT_ADDRESS &&
+			(long) info.eip != 0)
+		{
+			exit(0);
+		}
+	}], [mercury_cv_pc_access=yes], [true], [true]))
+	AC_MSG_RESULT($mercury_cv_pc_access)
+else
+	AC_MSG_CHECKING(for siginfo_t)
+	AC_CACHE_VAL(mercury_cv_siginfo_t,
+	mercury_cv_siginfo_t=no
+	AC_TRY_RUN([
+	#include <stdio.h>
+	#include <signal.h>
+	#ifdef HAVE_SYS_SIGINFO
+	#include <sys/siginfo.h>
+	#endif
+	#ifdef HAVE_UCONTEXT
+	#include <ucontext.h>
+	#endif
+	#ifdef HAVE_SYS_UCONTEXT
+	#include <sys/ucontext.h>
+	#endif
+	int save_signum = 0;
+	int save_cause;
+	int save_pc;
+	extern void handler(int signum, siginfo_t *info, void *context);
+	int main() {
+		struct sigaction act;
+		act.sa_flags = SA_SIGINFO | SA_RESTART;
+		act.$mercury_cv_sigaction_field = handler;
+		if (sigemptyset(&act.sa_mask) != 0)
+			exit(1);
+		if (sigaction(SIGSEGV, &act, NULL) != 0)
+			exit(1);
+		if (kill(getpid(), SIGSEGV) != 0)
+			exit(1);
+		if (save_signum == 0)
+			exit(1);
+		exit(0);
+	}
+	void handler(int signum, siginfo_t *info, void *context) {
+		save_signum = signum;
+		switch (info->si_code) {
+		case SEGV_MAPERR:	save_cause = info->si_code;
+					break;
+		case SEGV_ACCERR:	save_cause = info->si_code;
+					break;
+		}
+		switch (info->si_code) {
+		case BUS_ADRALN:	save_cause = info->si_code;
+					break;
+		case BUS_ADRERR:	save_cause = info->si_code;
+					break;
+		case BUS_OBJERR:	save_cause = info->si_code;
+					break;
+		}
+		save_cause = info->si_code;
+	}], [mercury_cv_siginfo_t=yes], [true], [true]))
+	AC_MSG_RESULT($mercury_cv_siginfo_t)
+	if test $mercury_cv_siginfo_t = yes; then
+		AC_DEFINE(HAVE_SIGINFO_T)
+		AC_DEFINE(HAVE_SIGINFO)
+
+		AC_MSG_CHECKING(for siginfo pc access at signals)
 		AC_CACHE_VAL(mercury_cv_pc_access,
 		mercury_cv_pc_access=no
 		AC_TRY_RUN([
@@ -283,7 +442,7 @@
 		int save_cause;
 		int save_pc;
 		extern void handler(int signum, siginfo_t *info, void *context);
-		main() {
+		int main() {
 			struct sigaction act;
 			act.sa_flags = SA_SIGINFO | SA_RESTART;
 			act.$mercury_cv_sigaction_field = handler;
@@ -299,20 +458,6 @@
 		}
 		void handler(int signum, siginfo_t *info, void *context) {
 			save_signum = signum;
-			switch (info->si_code) {
-			case SEGV_MAPERR:	save_cause = info->si_code;
-						break;
-			case SEGV_ACCERR:	save_cause = info->si_code;
-						break;
-			}
-			switch (info->si_code) {
-			case BUS_ADRALN:	save_cause = info->si_code;
-						break;
-			case BUS_ADRERR:	save_cause = info->si_code;
-						break;
-			case BUS_OBJERR:	save_cause = info->si_code;
-						break;
-			}
 			save_cause = info->si_code;
 			/* Don't use array indexing - the square brackets
 			   are autoconf quote characters */
@@ -333,7 +478,7 @@
 		int save_cause;
 		int save_pc;
 		extern void handler(int signum, siginfo_t *info, void *context);
-		main() {
+		int main() {
 			struct sigaction act;
 			act.sa_flags = SA_SIGINFO | SA_RESTART;
 			act.$mercury_cv_sigaction_field = handler;
@@ -349,20 +494,6 @@
 		}
 		void handler(int signum, siginfo_t *info, void *context) {
 			save_signum = signum;
-			switch (info->si_code) {
-			case SEGV_MAPERR:	save_cause = info->si_code;
-						break;
-			case SEGV_ACCERR:	save_cause = info->si_code;
-						break;
-			}
-			switch (info->si_code) {
-			case BUS_ADRALN:	save_cause = info->si_code;
-						break;
-			case BUS_ADRERR:	save_cause = info->si_code;
-						break;
-			case BUS_OBJERR:	save_cause = info->si_code;
-						break;
-			}
 			save_cause = info->si_code;
 			save_pc = *(((ucontext_t *) context)->uc_mcontext.gregs
 					+ CTX_EPC);
@@ -381,7 +512,7 @@
 		int save_cause;
 		int save_pc;
 		extern void handler(int signum, siginfo_t *info, void *context);
-		main() {
+		int main() {
 			struct sigaction act;
 			act.sa_flags = SA_SIGINFO | SA_RESTART;
 			act.$mercury_cv_sigaction_field = handler;
@@ -397,20 +528,6 @@
 		}
 		void handler(int signum, siginfo_t *info, void *context) {
 			save_signum = signum;
-			switch (info->si_code) {
-			case SEGV_MAPERR:	save_cause = info->si_code;
-						break;
-			case SEGV_ACCERR:	save_cause = info->si_code;
-						break;
-			}
-			switch (info->si_code) {
-			case BUS_ADRALN:	save_cause = info->si_code;
-						break;
-			case BUS_ADRERR:	save_cause = info->si_code;
-						break;
-			case BUS_OBJERR:	save_cause = info->si_code;
-						break;
-			}
 			save_cause = info->si_code;
 			save_pc = ((ucontext_t *) context)->uc_mcontext.sc_pc;
 		}], [mercury_cv_pc_access=sc_pc;mercury_cv_pc_access_greg=no],
@@ -430,7 +547,7 @@
 AC_CACHE_VAL(mercury_cv_word_type,
 	AC_TRY_RUN([
 	#include <stdio.h>
-	main() {
+	int main() {
 		FILE *fp;
 
 		fp = fopen("conftest.tags", "w");
@@ -459,7 +576,7 @@
 if test "$mercury_cv_word_type" = unknown; then
 	AC_TRY_RUN([
 	#include <stdio.h>
-	main() {
+	int main() {
 		FILE *fp;
 
 		fp = fopen("conftest.tags", "w");

Index: memory.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/memory.c,v
retrieving revision 1.65
diff -u -r1.65 memory.c
--- memory.c	1997/02/12 02:15:45	1.65
+++ memory.c	1997/02/12 07:37:20
@@ -29,47 +29,63 @@
 
 /*---------------------------------------------------------------------------*/
 
-#include "imp.h"
-#include "conf.h"
-
-#include <unistd.h>
+#include "regs.h"	/* must come first, due to gloabl register vars */
+#include "conf.h"	/* must come second */
 
-#ifdef	HAVE_SIGINFO
-#include	<sys/siginfo.h>
-#endif 
+#ifdef HAVE_SIGCONTEXT_STRUCT
+  /*
+  ** On some systems (e.g. most versions of Linux) we need to #define
+  ** __KERNEL__ to get sigcontext_struct from <signal.h>.
+  ** This stuff must come before anything else that might include <signal.h>,
+  ** otherwise the #define __KERNEL__ may not work.
+  */
+  #define __KERNEL__
+  #include <signal.h>	/* must come third */
+  #undef __KERNEL__
+
+  #ifdef HAVE_ASM_SIGCONTEXT
+    #include <asm/sigcontext.h>
+  #endif 
+#else
+  #include <signal.h>
+#endif
 
+#include <unistd.h>
 #include <stdio.h>
 #include <string.h>
 
+#ifdef HAVE_SYS_SIGINFO
+  #include <sys/siginfo.h>
+#endif 
+
 #ifdef	HAVE_MPROTECT
-#include <sys/mman.h>
+  #include <sys/mman.h>
 #endif
 
-#include <signal.h>
-
 #ifdef	HAVE_UCONTEXT
-#include <ucontext.h>
+  #include <ucontext.h>
 #endif
+
 #ifdef	HAVE_SYS_UCONTEXT
-#include <sys/ucontext.h>
+  #include <sys/ucontext.h>
 #endif
 
+#include "imp.h"
+
+/*---------------------------------------------------------------------------*/
+
 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
-#define	getpagesize()	sysconf(_SC_PAGESIZE)
-#else
-#ifndef	HAVE_GETPAGESIZE
-#define	getpagesize()	8192
-#endif
+  #define	getpagesize()	sysconf(_SC_PAGESIZE)
+#elif !defined(HAVE_GETPAGESIZE)
+  #define	getpagesize()	8192
 #endif
 
 #ifdef	CONSERVATIVE_GC
-#define memalign(a,s)   GC_MALLOC_UNCOLLECTABLE(s)
+  #define	memalign(a,s)   GC_MALLOC_UNCOLLECTABLE(s)
+#elif defined(HAVE_MEMALIGN)
+  extern void	*memalign(size_t, size_t);
 #else
-#ifdef	HAVE_MEMALIGN
-extern	void	*memalign(size_t, size_t);
-#else
-#define	memalign(a,s)	malloc(s)
-#endif
+  #define	memalign(a,s)	malloc(s)
 #endif
 
 /*
@@ -87,33 +103,39 @@
 */
 #ifdef  HAVE_MPROTECT
 
-#ifdef CONSERVATIVE_GC
-	/*
-	** The conservative garbage collectors scans through
-	** all these areas, so we need to allow reads.
-	** XXX This probably causes efficiency problems:
-	** too much memory for the GC to scan, and it probably
-	** all gets paged in.
-	*/
-#define MY_PROT PROT_READ
-#else
-#define MY_PROT PROT_NONE
-#endif
+  #ifdef CONSERVATIVE_GC
+    /*
+    ** The conservative garbage collectors scans through
+    ** all these areas, so we need to allow reads.
+    ** XXX This probably causes efficiency problems:
+    ** too much memory for the GC to scan, and it probably
+    ** all gets paged in.
+    */
+    #define MY_PROT PROT_READ
+  #else
+    #define MY_PROT PROT_NONE
+  #endif
 
-/* The BSDI BSD/386 1.1 headers don't define PROT_NONE */
-#ifndef PROT_NONE
-#define PROT_NONE 0
-#endif
+  /* The BSDI BSD/386 1.1 headers don't define PROT_NONE */
+  #ifndef PROT_NONE
+    #define PROT_NONE 0
+  #endif
 
-#endif
+#endif /* HAVE_MPROTECT */
 
 /*---------------------------------------------------------------------------*/
 
-#ifdef	HAVE_SIGINFO
-static	void	complex_bushandler(int, siginfo_t *, void *);
-static	void	complex_segvhandler(int, siginfo_t *, void *);
+#ifdef HAVE_SIGINFO
+  #if defined(HAVE_SIGCONTEXT_STRUCT)
+    static	void	complex_sighandler(int, struct sigcontext_struct);
+  #elif defined(HAVE_SIGINFO_T)
+    static	void	complex_bushandler(int, siginfo_t *, void *);
+    static	void	complex_segvhandler(int, siginfo_t *, void *);
+  #else
+    #error "HAVE_SIGINFO defined but don't know how to get it"
+  #endif
 #else
-static	void	simple_sighandler(int);
+  static	void	simple_sighandler(int);
 #endif
 
 /*
@@ -124,10 +146,11 @@
 #define round_up(amount, align) ((((amount) - 1) | ((align) - 1)) + 1)
 
 static	void	setup_mprotect(void);
+
 #ifdef	HAVE_SIGINFO
-static	bool	try_munprotect(void *, void *);
-static	char	*explain_context(ucontext_t *);
-#endif
+  static	bool	try_munprotect(void *address, void *context);
+  static	char	*explain_context(void *context);
+#endif /* HAVE_SIGINFO */
 
 static	void	setup_signal(void);
 
@@ -143,15 +166,15 @@
 MemoryZone *free_memory_zones;
 
 MemoryZone *detstack_zone;
-#ifndef	SPEED
-MemoryZone *dumpstack_zone;
-int	dumpindex;
-#endif
 MemoryZone *nondetstack_zone;
 #ifndef CONSERVATIVE_GC
-MemoryZone *heap_zone;
-MemoryZone *solutions_heap_zone;
-Word       *solutions_heap_pointer;
+  MemoryZone *heap_zone;
+  MemoryZone *solutions_heap_zone;
+  Word       *solutions_heap_pointer;
+#endif
+#ifndef	SPEED
+  MemoryZone *dumpstack_zone;
+  int	dumpindex;
 #endif
 
 static	size_t		unit;
@@ -247,7 +270,7 @@
 init_memory_arena()
 {
 #ifdef	PARALLEL
-  #ifndef	CONSERVATIVE_GC
+  #ifndef CONSERVATIVE_GC
 	if (numprocs > 1) {
 		fatal_error("shared memory not implemented");
 	}
@@ -284,10 +307,10 @@
 #ifndef SPEED
 		zone_table[i].max = NULL;
 #endif
-#ifdef	HAVE_MPROTECT
-#ifdef	HAVE_SIGINFO
+#ifdef HAVE_MPROTECT
+  #ifdef HAVE_SIGINFO
 		zone_table[i].redzone = NULL;
-#endif
+  #endif
 		zone_table[i].hardmax = NULL;
 #endif
 		if (i+1 < MAX_ZONES) {
@@ -329,7 +352,7 @@
 	solutions_heap_pointer = solutions_heap_zone->min;
 
 #endif
-}
+} /* end init_heap() */
 
 MemoryZone *
 get_zone(void)
@@ -426,11 +449,11 @@
 	total_size = size + unit;
 #endif
 
-  #ifdef	PARALLEL
+#ifdef	PARALLEL
 	if (numprocs > 1) {
 		fatal_error("shared memory not supported yet");
 	}
-  #endif
+#endif
 	base = memalign(unit, total_size);
 	if (base == NULL) {
 		char buf[2560];
@@ -439,12 +462,12 @@
 	}
 
 	return construct_zone(name, id, base, size, offset, redsize, handler);
-}
+} /* end create_zone() */
 
 MemoryZone *
 construct_zone(const char *name, int id, Word *base,
 		size_t size, size_t offset, size_t redsize,
-		bool ((*handler)(Word *addr, MemoryZone *zone, void *context)))
+		ZoneHandler handler)
 {
 	MemoryZone	*zone;
 	size_t		total_size;
@@ -480,7 +503,7 @@
 	** setup the redzone+hardzone
 	*/
 #ifdef	HAVE_MPROTECT
-#ifdef	HAVE_SIGINFO
+  #ifdef HAVE_SIGINFO
 	zone->redzone_base = zone->redzone = (Word *)
 			round_up((Unsigned)base + size - redsize, unit);
 	if (mprotect((char *)zone->redzone, redsize + unit, MY_PROT) < 0) {
@@ -490,7 +513,7 @@
 			zone->name, zone->id, zone->bottom, zone->redzone);
 		fatal_error(buf);
 	}
-#else	/* !HAVE_SIGINFO */
+  #else	/* not HAVE_SIGINFO */
 	zone->hardmax = (Word *) ((char *)zone->top-unit);
 	if (mprotect((char *)zone->hardmax, unit, MY_PROT) < 0) {
 		char buf[2560];
@@ -499,8 +522,8 @@
 			zone->name, zone->id, zone->bottom, zone->hardmax);
 		fatal_error(buf);
 	}
-#endif	/* HAVE_SIGINFO */
-#endif	/* HAVE_MPROTECT */
+  #endif /* not HAVE_SIGINFO */
+#endif	/* not HAVE_MPROTECT */
 
 	return zone;
 } /* end construct_zone() */
@@ -523,9 +546,6 @@
 #endif
 }
 
-#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
-	/* try_munprotect is only useful if we have SIGINFO */
-
 #define STDERR 2
 
 #ifdef	SPEED
@@ -537,7 +557,7 @@
 	write(STDERR, msg, strlen(msg));
 }
 
-#else /* !SPEED */
+#else /* not SPEED */
 
 static void 
 print_dump_stack(void)
@@ -573,14 +593,17 @@
 		}
 
 		write(STDERR, buf, strlen(buf));
-	}
+	} /* end while */
 
 	strcpy(buf, "\nend of stack dump\n");
 	write(STDERR, buf, strlen(buf));
 
 } /* end print_dump_stack() */
 
-#endif /* SPEED */
+#endif /* not SPEED */
+
+#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
+	/* try_munprotect is only useful if we have SIGINFO */
 
 /*
 ** fatal_abort() prints an error message, possibly a stack dump, and then exits.
@@ -593,7 +616,7 @@
 {
 	char	*context_msg;
 
-	context_msg = explain_context((ucontext_t *) context);
+	context_msg = explain_context(context);
 	write(STDERR, main_msg, strlen(main_msg));
 	write(STDERR, context_msg, strlen(context_msg));
 
@@ -679,22 +702,16 @@
 		(void *) zone->top);
 	}
 	return TRUE;
-    }
-    else
-    {
+    } else {
+	char buf[2560];
 	if (memdebug) {
 	    fprintf(stderr, "can't unprotect last page of %s#%d\n",
 		zone->name, zone->id);
 	    fflush(stdout);
 	}
-
-	{
-	    char buf[2560];
-	    sprintf(buf,
-		    "\nMercury runtime: memory zone %s#%d overflowed\n",
-		    zone->name, zone->id);
-	    fatal_abort(context, buf, FALSE);
-	}
+	sprintf(buf, "\nMercury runtime: memory zone %s#%d overflowed\n",
+		zone->name, zone->id);
+	fatal_abort(context, buf, FALSE);
     }
 
     return FALSE;
@@ -729,7 +746,102 @@
 
 #endif /* not HAVE_MPROTECT || not HAVE_SIGINFO */
 
-#ifdef	HAVE_SIGINFO
+#if defined(HAVE_SIGCONTEXT_STRUCT)
+
+static void
+setup_signal(void)
+{
+	if (signal(SIGBUS, (void(*)(int))complex_sighandler) == SIG_ERR)
+	{
+		perror("cannot set SIGBUS handler");
+		exit(1);
+	}
+
+	if (signal(SIGSEGV, (void(*)(int))complex_sighandler) == SIG_ERR)
+	{
+		perror("cannot set SIGSEGV handler");
+		exit(1);
+	}
+}
+
+static void
+complex_sighandler(int sig, struct sigcontext_struct sigcontext)
+{
+	void *address = (void *) sigcontext.cr2;
+  #ifdef PC_ACCESS
+	void *pc_at_signal = (void *) sigcontext.PC_ACCESS;
+  #endif
+
+	switch(sig) {
+		case SIGSEGV:
+			/*
+			** If we're debugging, print the segv explanation
+			** messages before we call try_munprotect.  But if
+			** we're not debugging, only print them if
+			** try_munprotect fails.
+			*/
+			if (memdebug) {
+				fflush(stdout);
+				fprintf(stderr, "\n*** Mercury runtime: "
+					"caught segmentation violation ***\n");
+			}
+			if (try_munprotect(address, &sigcontext)) {
+				if (memdebug) {
+					fprintf(stderr, "returning from "
+						"signal handler\n\n");
+				}
+				return;
+			}
+			if (!memdebug) {
+				fflush(stdout);
+				fprintf(stderr, "\n*** Mercury runtime: "
+					"caught segmentation violation ***\n");
+			}
+			break;
+
+		case SIGBUS:
+			fflush(stdout);
+			fprintf(stderr, "\n*** Mercury runtime: "
+					"caught bus error ***\n");
+			break;
+
+		default:
+			fflush(stdout);
+			fprintf(stderr, "\n*** Mercury runtime: "
+					"caught unknown signal %d ***\n", sig);
+			break;
+	}
+
+  #ifdef 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);
+
+	print_dump_stack();
+	dump_prev_locations();
+	fprintf(stderr, "exiting from signal handler\n");
+	exit(1);
+} /* end complex_sighandler() */
+
+static char *
+explain_context(void *the_context)
+{
+	static	char	buf[100];
+  #ifdef PC_ACCESS
+	struct sigcontext_struct *context = the_context;
+	void *pc_at_signal = (void *) context->PC_ACCESS;
+
+	sprintf(buf, "PC at signal: %ld (%lx)\n",
+		(long)pc_at_signal, (long)pc_at_signal);
+  #else
+	buf[0] = '\0';
+  #endif
+
+	return buf;
+}
+
+#elif defined(HAVE_SIGINFO_T)
 
 static void 
 setup_signal(void)
@@ -788,13 +900,15 @@
 		default:
 			fprintf(stderr, "unknown\n");
 			break;
-		}
 
-		fprintf(stderr, "%s", explain_context((ucontext_t *) context));
+		} /* end switch */
+
+		fprintf(stderr, "%s", explain_context(context));
 		fprintf(stderr, "address involved: %p\n",
 			(void *) info->si_addr);
-	}
+	} /* end if */
 
+	print_dump_stack();
 	dump_prev_locations();
 	fprintf(stderr, "exiting from signal handler\n");
 	exit(1);
@@ -829,12 +943,12 @@
 			break;
 		}
 
-		fprintf(stderr, "%s", explain_context((ucontext_t *) context));
+		fprintf(stderr, "%s", explain_context(context));
 		fprintf(stderr, "address involved: %p\n",
 			(void *) info->si_addr);
 
-	}
-}
+	} /* end if */
+} /* end explain_segv() */
 
 static void 
 complex_segvhandler(int sig, siginfo_t *info, void *context)
@@ -867,36 +981,43 @@
 		explain_segv(info, context);
 	}
 
+	print_dump_stack();
 	dump_prev_locations();
 	fprintf(stderr, "exiting from signal handler\n");
 	exit(1);
-}
+} /* end complex_segvhandler */
 
 static char *
-explain_context(ucontext_t *context)
+explain_context(void *the_context)
 {
 	static	char	buf[100];
 
-#ifdef PC_ACCESS
-#ifdef PC_ACCESS_GREG
+  #ifdef PC_ACCESS
+
+	ucontext_t *context = the_context;
+
+    #ifdef PC_ACCESS_GREG
 	sprintf(buf, "PC at signal: %ld (%lx)\n",
 		(long) context->uc_mcontext.gregs[PC_ACCESS],
 		(long) context->uc_mcontext.gregs[PC_ACCESS]);
-#else
+    #else
 	sprintf(buf, "PC at signal: %ld (%lx)\n",
 		(long) context->uc_mcontext.PC_ACCESS,
 		(long) context->uc_mcontext.PC_ACCESS);
-#endif
-#else /* ! PC_ACCESS */
+    #endif
+
+  #else /* not PC_ACCESS */
+
 	/* if PC_ACCESS is not set, we don't know the context */
 	/* therefore we return an empty string to be printed  */
 	buf[0] = '\0';
-#endif /* ! PC_ACCESS */
+
+  #endif /* not PC_ACCESS */
 
 	return buf;
 }
 
-#else /* ! HAVE_SIGINFO */
+#else /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
 
 static void 
 setup_signal(void)
@@ -933,12 +1054,13 @@
 		break;
 	}
 
+	print_dump_stack();
 	dump_prev_locations();
 	fprintf(stderr, "exiting from signal handler\n");
 	exit(1);
 }
 
-#endif /* ! HAVE_SIGINFO */
+#endif /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
 
 #ifdef	CONSERVATIVE_GC
 
@@ -962,7 +1084,11 @@
 	return tmp;
 }
 
-#else
+#elif defined(PARALLEL)
+
+  #error "shared memory not implemented"
+
+#else /* not CONSERVATIVE_GC && not PARALLEL */
 
 void *
 allocate_bytes(size_t numbytes)

-- 
Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3         |     -- the last words of T. S. Garp.



More information about the developers mailing list