boehm-gc: revert all CACAO-specific modifications; this is now an exact copy of the...
[cacao.git] / src / mm / boehm-gc / os_dep.c
index f18253b7d64c32ba71f208848d6a733603f96bc6..9b80603a9aac3dac01cfa0b607df8b3f5225ca74 100644 (file)
  * modified is included with the above copyright notice.
  */
 
-#include "config.h"
-
 # include "private/gc_priv.h"
+# ifdef THREADS
+#   include "atomic_ops.h"
+# endif
 
 # if defined(LINUX) && !defined(POWERPC)
 #   include <linux/version.h>
@@ -50,7 +51,7 @@
 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
     && !defined(MSWINCE)
 #   include <sys/types.h>
-#   if !defined(MSWIN32) && !defined(SUNOS4)
+#   if !defined(MSWIN32)
 #      include <unistd.h>
 #   endif
 # endif
 #   include <signal.h>
 # endif
 
+#if defined(UNIX_LIKE) || defined(CYGWIN32)
+# include <fcntl.h>
+#endif
+
 #if defined(LINUX) || defined(LINUX_STACKBOTTOM)
 # include <ctype.h>
 #endif
 /* Blatantly OS dependent routines, except for those that are related  */
 /* to dynamic loading.                                                 */
 
-# if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START)
-#   define NEED_FIND_LIMIT
-# endif
-
-# if !defined(STACKBOTTOM) && defined(HEURISTIC2)
-#   define NEED_FIND_LIMIT
-# endif
-
-# if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
-#   define NEED_FIND_LIMIT
-# endif
-
-# if (defined(SVR4) || defined(AUX) || defined(DGUX) \
-      || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
-#   define NEED_FIND_LIMIT
-# endif
-
-#if defined(FREEBSD) && (defined(I386) || defined(powerpc) || defined(__powerpc__))
-#  include <machine/trap.h>
-#  if !defined(PCR)
-#    define NEED_FIND_LIMIT
-#  endif
-#endif
-
-#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) \
-    && !defined(NEED_FIND_LIMIT)
-   /* Used by GC_init_netbsd_elf() below.      */
-#  define NEED_FIND_LIMIT
-#endif
-
-#ifdef NEED_FIND_LIMIT
-#   include <setjmp.h>
-#endif
-
 #ifdef AMIGA
 # define GC_AMIGA_DEF
 # include "AmigaOS.c"
 # undef GC_AMIGA_DEF
 #endif
 
-#if defined(MSWIN32) || defined(MSWINCE)
+#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
 # define WIN32_LEAN_AND_MEAN
 # define NOSERVICE
 # include <windows.h>
+  /* It's not clear this is completely kosher under Cygwin.  But it    */
+  /* allows us to get a working GC_get_stack_base.                     */
 #endif
 
 #ifdef MACOS
 # include <sys/uio.h>
 # include <malloc.h>   /* for locking */
 #endif
-#if defined(USE_MMAP) || defined(USE_MUNMAP)
-# ifndef USE_MMAP
+
+#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX5) \
+       || ((defined(USE_MMAP) || defined(USE_MUNMAP)) \
+       && !defined(MSWIN32) && !defined(MSWINCE))
+# define MMAP_SUPPORTED
+#endif
+
+#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES)
+# if defined(USE_MUNMAP) && !defined(USE_MMAP)
     --> USE_MUNMAP requires USE_MMAP
 # endif
 # include <sys/types.h>
 # include <errno.h>
 #endif
 
-#ifdef UNIX_LIKE
-# include <fcntl.h>
-# if defined(SUNOS5SIGS) && !defined(FREEBSD)
-#  include <sys/siginfo.h>
-# endif
-  /* Define SETJMP and friends to be the version that restores */
-  /* the signal mask.                                          */
-# define SETJMP(env) sigsetjmp(env, 1)
-# define LONGJMP(env, val) siglongjmp(env, val)
-# define JMP_BUF sigjmp_buf
-#else
-# define SETJMP(env) setjmp(env)
-# define LONGJMP(env, val) longjmp(env, val)
-# define JMP_BUF jmp_buf
-#endif
-
 #ifdef DARWIN
 /* for get_etext and friends */
 #include <mach-o/getsect.h>
 
 #if defined(LINUX) && \
     (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))
+# define NEED_PROC_MAPS
+#endif
 
+#ifdef NEED_PROC_MAPS
 /* We need to parse /proc/self/maps, either to find dynamic libraries, */
 /* and/or to find the register backing store base (IA64).  Do it once  */
 /* here.                                                               */
@@ -197,35 +164,98 @@ ssize_t GC_repeat_read(int fd, char *buf, size_t count)
     return num_read;
 }
 
+#ifdef THREADS
+/* Determine the length of a file by incrementally reading it into a   */
+/* This would be silly to use on a file supporting lseek, but Linux    */
+/* /proc files usually do not.                                         */
+STATIC size_t GC_get_file_len(int f)
+{
+    size_t total = 0;
+    ssize_t result;
+#   define GET_FILE_LEN_BUF_SZ 500
+    char buf[GET_FILE_LEN_BUF_SZ];
+
+    do {
+       result = read(f, buf, GET_FILE_LEN_BUF_SZ);
+       if (result == -1) return 0;
+       total += result;
+    } while (result > 0);
+    return total;
+}
+
+STATIC size_t GC_get_maps_len(void)
+{
+    int f = open("/proc/self/maps", O_RDONLY);
+    size_t result = GC_get_file_len(f);
+    close(f);
+    return result;
+}
+#endif
+
 /*
- * Apply fn to a buffer containing the contents of /proc/self/maps.
- * Return the result of fn or, if we failed, 0.
- * We currently do nothing to /proc/self/maps other than simply read
- * it.  This code could be simplified if we could determine its size
+ * Copy the contents of /proc/self/maps to a buffer in our address space.
+ * Return the address of the buffer, or zero on failure.
+ * This code could be simplified if we could determine its size
  * ahead of time.
  */
-
-word GC_apply_to_maps(word (*fn)(char *))
+char * GC_get_maps(void)
 {
     int f;
     int result;
-    size_t maps_size = 4000;  /* Initial guess.        */
     static char init_buf[1];
     static char *maps_buf = init_buf;
     static size_t maps_buf_sz = 1;
+    size_t maps_size, old_maps_size = 0;
+
+    /* The buffer is essentially static, so there must be a single client. */
+    GC_ASSERT(I_HOLD_LOCK());
+
+    /* Note that in the presence of threads, the maps file can */
+    /* essentially shrink asynchronously and unexpectedly as   */
+    /* threads that we already think of as dead release their  */
+    /* stacks.  And there is no easy way to read the entire    */
+    /* file atomically.  This is arguably a misfeature of the  */
+    /* /proc/.../maps interface.                               */
+
+    /* Since we dont believe the file can grow                 */
+    /* asynchronously, it should suffice to first determine    */
+    /* the size (using lseek or read), and then to reread the  */
+    /* file.  If the size is inconsistent we have to retry.    */
+    /* This only matters with threads enabled, and if we use   */
+    /* this to locate roots (not the default).                 */
+
+    /* Determine the initial size of /proc/self/maps.          */
+    /* Note that lseek doesn't work, at least as of 2.6.15.    */
+#   ifdef THREADS
+       maps_size = GC_get_maps_len();
+       if (0 == maps_size) return 0;
+#   else
+       maps_size = 4000;       /* Guess */
+#   endif
 
     /* Read /proc/self/maps, growing maps_buf as necessary.    */
-        /* Note that we may not allocate conventionally, and   */
-        /* thus can't use stdio.                               */
+    /* Note that we may not allocate conventionally, and       */
+    /* thus can't use stdio.                                   */
        do {
-           if (maps_size >= maps_buf_sz) {
+           while (maps_size >= maps_buf_sz) {
              /* Grow only by powers of 2, since we leak "too small" buffers. */
              while (maps_size >= maps_buf_sz) maps_buf_sz *= 2;
              maps_buf = GC_scratch_alloc(maps_buf_sz);
+#            ifdef THREADS
+               /* Recompute initial length, since we allocated.        */
+               /* This can only happen a few times per program         */
+               /* execution.                                           */
+               maps_size = GC_get_maps_len();
+               if (0 == maps_size) return 0;
+#            endif
              if (maps_buf == 0) return 0;
            }
+           GC_ASSERT(maps_buf_sz >= maps_size + 1);
            f = open("/proc/self/maps", O_RDONLY);
            if (-1 == f) return 0;
+#          ifdef THREADS
+             old_maps_size = maps_size;
+#          endif
            maps_size = 0;
            do {
                result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
@@ -233,38 +263,48 @@ word GC_apply_to_maps(word (*fn)(char *))
                maps_size += result;
            } while (result == maps_buf_sz-1);
            close(f);
-       } while (maps_size >= maps_buf_sz);
+#          ifdef THREADS
+             if (maps_size > old_maps_size) {
+               GC_err_printf("Old maps size = %lu, new maps size = %lu\n",
+                             (unsigned long)old_maps_size,
+                             (unsigned long)maps_size);
+               ABORT("Unexpected asynchronous /proc/self/maps growth: "
+                     "Unregistered thread?");
+             }
+#          endif
+       } while (maps_size >= maps_buf_sz || maps_size < old_maps_size);
+               /* In the single-threaded case, the second clause is false.     */
         maps_buf[maps_size] = '\0';
        
     /* Apply fn to result. */
-       return fn(maps_buf);
+       return maps_buf;
 }
 
-#endif /* Need GC_apply_to_maps */
-
-#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64))
-//
-//  GC_parse_map_entry parses an entry from /proc/self/maps so we can
-//  locate all writable data segments that belong to shared libraries.
-//  The format of one of these entries and the fields we care about
-//  is as follows:
-//  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
-//  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
-//  start    end      prot          maj_dev
-//
-//  Note that since about auguat 2003 kernels, the columns no longer have
-//  fixed offsets on 64-bit kernels.  Hence we no longer rely on fixed offsets
-//  anywhere, which is safer anyway.
-//
+/*
+ *  GC_parse_map_entry parses an entry from /proc/self/maps so we can
+ *  locate all writable data segments that belong to shared libraries.
+ *  The format of one of these entries and the fields we care about
+ *  is as follows:
+ *  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
+ *  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
+ *  start    end      prot          maj_dev
+ *
+ *  Note that since about august 2003 kernels, the columns no longer have
+ *  fixed offsets on 64-bit kernels.  Hence we no longer rely on fixed offsets
+ *  anywhere, which is safer anyway.
+ */
 
 /*
  * Assign various fields of the first line in buf_ptr to *start, *end,
- * *prot_buf and *maj_dev.  Only *prot_buf may be set for unwritable maps.
+ * *prot, *maj_dev and *mapping_name.  Mapping_name may be NULL.
+ * *prot and *mapping_name are assigned pointers into the original
+ * buffer.
  */
-char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
-                                char *prot_buf, unsigned int *maj_dev)
+char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end,
+                                char **prot, unsigned int *maj_dev,
+                               char **mapping_name)
 {
-    char *start_start, *end_start, *prot_start, *maj_dev_start;
+    char *start_start, *end_start, *maj_dev_start;
     char *p;
     char *endp;
 
@@ -276,37 +316,118 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
     while (isspace(*p)) ++p;
     start_start = p;
     GC_ASSERT(isxdigit(*start_start));
-    *start = strtoul(start_start, &endp, 16); p = endp;
+    *start = (ptr_t)strtoul(start_start, &endp, 16); p = endp;
     GC_ASSERT(*p=='-');
 
     ++p;
     end_start = p;
     GC_ASSERT(isxdigit(*end_start));
-    *end = strtoul(end_start, &endp, 16); p = endp;
+    *end = (ptr_t)strtoul(end_start, &endp, 16); p = endp;
     GC_ASSERT(isspace(*p));
 
     while (isspace(*p)) ++p;
-    prot_start = p;
-    GC_ASSERT(*prot_start == 'r' || *prot_start == '-');
-    memcpy(prot_buf, prot_start, 4);
-    prot_buf[4] = '\0';
-    if (prot_buf[1] == 'w') {/* we can skip the rest if it's not writable. */
-       /* Skip past protection field to offset field */
-          while (!isspace(*p)) ++p; while (isspace(*p)) ++p;
-          GC_ASSERT(isxdigit(*p));
-       /* Skip past offset field, which we ignore */
+    GC_ASSERT(*p == 'r' || *p == '-');
+    *prot = p;
+    /* Skip past protection field to offset field */
+       while (!isspace(*p)) ++p; while (isspace(*p)) ++p;
+    GC_ASSERT(isxdigit(*p));
+    /* Skip past offset field, which we ignore */
           while (!isspace(*p)) ++p; while (isspace(*p)) ++p;
-       maj_dev_start = p;
-        GC_ASSERT(isxdigit(*maj_dev_start));
-        *maj_dev = strtoul(maj_dev_start, NULL, 16);
-    }
+    maj_dev_start = p;
+    GC_ASSERT(isxdigit(*maj_dev_start));
+    *maj_dev = strtoul(maj_dev_start, NULL, 16);
 
-    while (*p && *p++ != '\n');
+    if (mapping_name == 0) {
+      while (*p && *p++ != '\n');
+    } else {
+      while (*p && *p != '\n' && *p != '/' && *p != '[') p++;
+      *mapping_name = p;
+      while (*p && *p++ != '\n');
+    }
 
     return p;
 }
 
-#endif /* Need to parse /proc/self/maps. */    
+/* Try to read the backing store base from /proc/self/maps.            */
+/* Return the bounds of the writable mapping with a 0 major device,    */
+/* which includes the address passed as data.                          */
+/* Return FALSE if there is no such mapping.                           */
+GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp)
+{
+  char *prot;
+  ptr_t my_start, my_end;
+  unsigned int maj_dev;
+  char *maps = GC_get_maps();
+  char *buf_ptr = maps;
+  
+  if (0 == maps) return(FALSE);
+  for (;;) {
+    buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end,
+                                &prot, &maj_dev, 0);
+
+    if (buf_ptr == NULL) return FALSE;
+    if (prot[1] == 'w' && maj_dev == 0) {
+        if (my_end > addr && my_start <= addr) {
+         *startp = my_start;
+         *endp = my_end;
+         return TRUE;
+       }
+    }
+  }
+  return FALSE;
+}
+
+#if defined(REDIRECT_MALLOC)
+/* Find the text(code) mapping for the library whose name, after       */
+/* stripping the directory part, starts with nm.                       */
+GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp)
+{
+  size_t nm_len = strlen(nm);
+  char *prot;
+  char *map_path;
+  ptr_t my_start, my_end;
+  unsigned int maj_dev;
+  char *maps = GC_get_maps();
+  char *buf_ptr = maps;
+  
+  if (0 == maps) return(FALSE);
+  for (;;) {
+    buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end,
+                                &prot, &maj_dev, &map_path);
+
+    if (buf_ptr == NULL) return FALSE;
+    if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x') {
+       char *p = map_path;
+       /* Set p to point just past last slash, if any. */
+         while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') ++p;
+         while (*p != '/' && p >= map_path) --p;
+         ++p;
+       if (strncmp(nm, p, nm_len) == 0) {
+         *startp = my_start;
+         *endp = my_end;
+         return TRUE;
+       }
+    }
+  }
+  return FALSE;
+}
+#endif /* REDIRECT_MALLOC */
+
+#ifdef IA64
+static ptr_t backing_store_base_from_proc(void)
+{
+    ptr_t my_start, my_end;
+    if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) {
+       if (GC_print_stats) {
+           GC_log_printf("Failed to find backing store base from /proc\n");
+       }
+       return 0;
+    }
+    return my_start;
+}
+#endif
+
+#endif /* NEED_PROC_MAPS */    
 
 #if defined(SEARCH_FOR_DATA_START)
   /* The I386 case can be handled without a search.  The Alpha case    */
@@ -314,11 +435,11 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
   /* for recent Linux versions.  This seems to be the easiest way to   */
   /* cover all versions.                                               */
 
-# ifdef LINUX
+# if defined(LINUX) || defined(HURD)
     /* Some Linux distributions arrange to define __data_start.  Some  */
     /* define data_start as a weak symbol.  The latter is technically  */
     /* broken, since the user program may define data_start, in which  */
-    /* case we lose.  Nonetheless, we try both, prefering __data_start.        */
+    /* case we lose.  Nonetheless, we try both, preferring __data_start.*/
     /* We assume gcc-compatible pragmas.       */
 #   pragma weak __data_start
     extern int __data_start[];
@@ -329,11 +450,12 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
 
   ptr_t GC_data_start;
 
-  void GC_init_linux_data_start()
+  ptr_t GC_find_limit(ptr_t, GC_bool);
+
+  void GC_init_linux_data_start(void)
   {
-    extern ptr_t GC_find_limit();
 
-#   ifdef LINUX
+#   if defined(LINUX) || defined(HURD)
       /* Try the easy approaches first:        */
       if ((ptr_t)__data_start != 0) {
          GC_data_start = (ptr_t)(__data_start);
@@ -354,14 +476,10 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
 # define ECOS_GC_MEMORY_SIZE (448 * 1024)
 # endif /* ECOS_GC_MEMORY_SIZE */
 
-// setjmp() function, as described in ANSI para 7.6.1.1
-#undef SETJMP
-#define SETJMP( __env__ )  hal_setjmp( __env__ )
-
-// FIXME: This is a simple way of allocating memory which is
-// compatible with ECOS early releases.  Later releases use a more
-// sophisticated means of allocating memory than this simple static
-// allocator, but this method is at least bound to work.
+/* FIXME: This is a simple way of allocating memory which is           */
+/* compatible with ECOS early releases.  Later releases use a more     */
+/* sophisticated means of allocating memory than this simple static    */
+/* allocator, but this method is at least bound to work.               */
 static char memory[ECOS_GC_MEMORY_SIZE];
 static char *brk = memory;
 
@@ -384,11 +502,11 @@ static void *tiny_sbrk(ptrdiff_t increment)
 
 #if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
   ptr_t GC_data_start;
+  ptr_t GC_find_limit(ptr_t, GC_bool);
+  extern char **environ;
 
-  void GC_init_netbsd_elf()
+  void GC_init_netbsd_elf(void)
   {
-    extern ptr_t GC_find_limit();
-    extern char **environ;
        /* This may need to be environ, without the underscore, for     */
        /* some versions.                                               */
     GC_data_start = GC_find_limit((ptr_t)&environ, FALSE);
@@ -476,144 +594,37 @@ struct o32_obj {
 # define INCL_DOSMEMMGR
 # include <os2.h>
 
-
-/* Disable and enable signals during nontrivial allocations    */
-
-void GC_disable_signals(void)
-{
-    ULONG nest;
-    
-    DosEnterMustComplete(&nest);
-    if (nest != 1) ABORT("nested GC_disable_signals");
-}
-
-void GC_enable_signals(void)
-{
-    ULONG nest;
-    
-    DosExitMustComplete(&nest);
-    if (nest != 0) ABORT("GC_enable_signals");
-}
-
-
-# else
-
-#  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
-      && !defined(MSWINCE) \
-      && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
-      && !defined(NOSYS) && !defined(ECOS)
-
-#   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
-       /* Use the traditional BSD interface */
-#      define SIGSET_T int
-#      define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
-#      define SIG_FILL(set)  (set) = 0x7fffffff
-         /* Setting the leading bit appears to provoke a bug in some   */
-         /* longjmp implementations.  Most systems appear not to have  */
-         /* a signal 32.                                               */
-#      define SIGSETMASK(old, new) (old) = sigsetmask(new)
-#   else
-       /* Use POSIX/SYSV interface     */
-#      define SIGSET_T sigset_t
-#      define SIG_DEL(set, signal) sigdelset(&(set), (signal))
-#      define SIG_FILL(set) sigfillset(&set)
-#      define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old))
-#   endif
-
-static GC_bool mask_initialized = FALSE;
-
-static SIGSET_T new_mask;
-
-static SIGSET_T old_mask;
-
-static SIGSET_T dummy;
-
-#if defined(PRINTSTATS) && !defined(THREADS)
-# define CHECK_SIGNALS
-  int GC_sig_disabled = 0;
-#endif
-
-void GC_disable_signals()
-{
-    if (!mask_initialized) {
-       SIG_FILL(new_mask);
-
-       SIG_DEL(new_mask, SIGSEGV);
-       SIG_DEL(new_mask, SIGILL);
-       SIG_DEL(new_mask, SIGQUIT);
-#      ifdef SIGBUS
-           SIG_DEL(new_mask, SIGBUS);
-#      endif
-#      ifdef SIGIOT
-           SIG_DEL(new_mask, SIGIOT);
-#      endif
-#      ifdef SIGEMT
-           SIG_DEL(new_mask, SIGEMT);
-#      endif
-#      ifdef SIGTRAP
-           SIG_DEL(new_mask, SIGTRAP);
-#      endif 
-       mask_initialized = TRUE;
-    }
-#   ifdef CHECK_SIGNALS
-       if (GC_sig_disabled != 0) ABORT("Nested disables");
-       GC_sig_disabled++;
-#   endif
-    SIGSETMASK(old_mask,new_mask);
-}
-
-void GC_enable_signals()
-{
-#   ifdef CHECK_SIGNALS
-       if (GC_sig_disabled != 1) ABORT("Unmatched enable");
-       GC_sig_disabled--;
-#   endif
-    SIGSETMASK(dummy,old_mask);
-}
-
-#  endif  /* !PCR */
-
-# endif /*!OS/2 */
-
-/* Ivan Demakov: simplest way (to me) */
-#if defined (DOS4GW)
-  void GC_disable_signals() { }
-  void GC_enable_signals() { }
-#endif
+# endif /* OS/2 */
 
 /* Find the page size */
 word GC_page_size;
 
 # if defined(MSWIN32) || defined(MSWINCE)
-  void GC_setpagesize()
+  void GC_setpagesize(void)
   {
     GetSystemInfo(&GC_sysinfo);
     GC_page_size = GC_sysinfo.dwPageSize;
   }
 
 # else
-#   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) \
-       || defined(USE_MUNMAP)
-       void GC_setpagesize()
+#   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP)
+       void GC_setpagesize(void)
        {
            GC_page_size = GETPAGESIZE();
        }
 #   else
        /* It's acceptable to fake it. */
-       void GC_setpagesize()
+       void GC_setpagesize(void)
        {
            GC_page_size = HBLKSIZE;
        }
 #   endif
 # endif
 
-/* 
- * Find the base of the stack. 
- * Used only in single-threaded environment.
- * With threads, GC_mark_roots needs to know how to do this.
- * Called with allocator lock held.
- */
-# if defined(MSWIN32) || defined(MSWINCE)
+# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
+
+#ifndef CYGWIN32
+
 # define is_writable(prot) ((prot) == PAGE_READWRITE \
                            || (prot) == PAGE_WRITECOPY \
                            || (prot) == PAGE_EXECUTE_READWRITE \
@@ -622,7 +633,7 @@ word GC_page_size;
 /* The pointer p is assumed to be page aligned.                        */
 /* If base is not 0, *base becomes the beginning of the        */
 /* allocation region containing p.                             */
-word GC_get_writable_length(ptr_t p, ptr_t *base)
+STATIC word GC_get_writable_length(ptr_t p, ptr_t *base)
 {
     MEMORY_BASIC_INFORMATION buf;
     word result;
@@ -639,22 +650,47 @@ word GC_get_writable_length(ptr_t p, ptr_t *base)
     return(buf.RegionSize);
 }
 
-ptr_t GC_get_stack_base()
+GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
 {
     int dummy;
     ptr_t sp = (ptr_t)(&dummy);
     ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
     word size = GC_get_writable_length(trunc_sp, 0);
    
-    return(trunc_sp + size);
+    sb -> mem_base = trunc_sp + size;
+    return GC_SUCCESS;
 }
 
+#else /* CYGWIN32 */
+  
+/* An alternate version for Cygwin (adapted from Dave Korn's   */
+/* gcc version of boehm-gc).                                   */
+  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
+  {
+    extern void * _tlsbase __asm__ ("%fs:4");
+    sb -> mem_base = _tlsbase;
+    return GC_SUCCESS;
+  }
+  
+#endif /* CYGWIN32 */
+
+
+#define HAVE_GET_STACK_BASE
+
+/* This is always called from the main thread. */
+ptr_t GC_get_main_stack_base(void)
+{
+    struct GC_stack_base sb;
+
+    GC_get_stack_base(&sb);
+    return (ptr_t)sb.mem_base;
+}
 
 # endif /* MS Windows */
 
 # ifdef BEOS
 # include <kernel/OS.h>
-ptr_t GC_get_stack_base(){
+ptr_t GC_get_main_stack_base(void){
        thread_info th;
        get_thread_info(find_thread(NULL),&th);
        return th.stack_end;
@@ -664,13 +700,13 @@ ptr_t GC_get_stack_base(){
 
 # ifdef OS2
 
-ptr_t GC_get_stack_base()
+ptr_t GC_get_main_stack_base(void)
 {
     PTIB ptib;
     PPIB ppib;
     
     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
-       GC_err_printf0("DosGetInfoBlocks failed\n");
+       GC_err_printf("DosGetInfoBlocks failed\n");
        ABORT("DosGetInfoBlocks failed\n");
     }
     return((ptr_t)(ptib -> tib_pstacklimit));
@@ -686,29 +722,20 @@ ptr_t GC_get_stack_base()
 
 # if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
 
-#   ifdef __STDC__
-       typedef void (*handler)(int);
-#   else
-       typedef void (*handler)();
-#   endif
+    typedef void (*handler)(int);
 
 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
     || defined(HURD) || defined(NETBSD)
        static struct sigaction old_segv_act;
-#      if defined(IRIX5) || defined(HPUX) \
-       || defined(HURD) || defined(NETBSD)
+#      if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \
+       || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
            static struct sigaction old_bus_act;
 #      endif
 #   else
         static handler old_segv_handler, old_bus_handler;
 #   endif
     
-#   ifdef __STDC__
-      void GC_set_and_save_fault_handler(handler h)
-#   else
-      void GC_set_and_save_fault_handler(h)
-      handler h;
-#   endif
+    void GC_set_and_save_fault_handler(handler h)
     {
 #      if defined(SUNOS5SIGS) || defined(IRIX5)  \
         || defined(OSF1) || defined(HURD) || defined(NETBSD)
@@ -728,12 +755,11 @@ ptr_t GC_get_stack_base()
                /* and setting a handler at the same time.              */
                (void) sigaction(SIGSEGV, 0, &old_segv_act);
                (void) sigaction(SIGSEGV, &act, 0);
-               (void) sigaction(SIGBUS, 0, &old_bus_act);
-               (void) sigaction(SIGBUS, &act, 0);
 #        else
                (void) sigaction(SIGSEGV, &act, &old_segv_act);
-#              if defined(IRIX5) \
-                  || defined(HPUX) || defined(HURD) || defined(NETBSD)
+#              if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
+                  || defined(HPUX) || defined(HURD) || defined(NETBSD) \
+                  || defined(FREEBSD)
                    /* Under Irix 5.x or HP/UX, we may get SIGBUS.      */
                    /* Pthreads doesn't exist under Irix 5.x, so we     */
                    /* don't have to worry in the threads case.         */
@@ -749,30 +775,33 @@ ptr_t GC_get_stack_base()
     }
 # endif /* NEED_FIND_LIMIT || UNIX_LIKE */
 
-# ifdef NEED_FIND_LIMIT
+# if defined(NEED_FIND_LIMIT) || \
+     defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)
   /* Some tools to implement HEURISTIC2        */
 #   define MIN_PAGE_SIZE 256   /* Smallest conceivable page size, bytes */
-    /* static */ JMP_BUF GC_jmp_buf;
     
     /*ARGSUSED*/
-    void GC_fault_handler(sig)
-    int sig;
+    STATIC void GC_fault_handler(int sig)
     {
         LONGJMP(GC_jmp_buf, 1);
     }
 
-    void GC_setup_temporary_fault_handler()
+    void GC_setup_temporary_fault_handler(void)
     {
+       /* Handler is process-wide, so this should only happen in */
+       /* one thread at a time.                                  */
+       GC_ASSERT(I_HOLD_LOCK());
        GC_set_and_save_fault_handler(GC_fault_handler);
     }
     
-    void GC_reset_fault_handler()
+    void GC_reset_fault_handler(void)
     {
 #       if defined(SUNOS5SIGS) || defined(IRIX5) \
           || defined(OSF1) || defined(HURD) || defined(NETBSD)
          (void) sigaction(SIGSEGV, &old_segv_act, 0);
-#        if defined(IRIX5) \
-            || defined(HPUX) || defined(HURD) || defined(NETBSD)
+#        if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
+            || defined(HPUX) || defined(HURD) || defined(NETBSD) \
+            || defined(FREEBSD)
              (void) sigaction(SIGBUS, &old_bus_act, 0);
 #        endif
 #       else
@@ -783,20 +812,19 @@ ptr_t GC_get_stack_base()
 #       endif
     }
 
-    /* Return the first nonaddressible location > p (up) or    */
+    /* Return the first non-addressable location > p (up) or   */
     /* the smallest location q s.t. [q,p) is addressable (!up).        */
     /* We assume that p (up) or p-1 (!up) is addressable.      */
-    ptr_t GC_find_limit(p, up)
-    ptr_t p;
-    GC_bool up;
+    /* Requires allocation lock.                               */
+    STATIC ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound)
     {
-        static VOLATILE ptr_t result;
-               /* Needs to be static, since otherwise it may not be    */
+        static volatile ptr_t result;
+               /* Safer if static, since otherwise it may not be       */
                /* preserved across the longjmp.  Can safely be         */
-               /* static since it's only called once, with the         */
+               /* static since it's only called with the               */
                /* allocation lock held.                                */
 
-
+       GC_ASSERT(I_HOLD_LOCK());
        GC_setup_temporary_fault_handler();
        if (SETJMP(GC_jmp_buf) == 0) {
            result = (ptr_t)(((word)(p))
@@ -804,8 +832,10 @@ ptr_t GC_get_stack_base()
            for (;;) {
                if (up) {
                    result += MIN_PAGE_SIZE;
+                   if (result >= bound) return bound;
                } else {
                    result -= MIN_PAGE_SIZE;
+                   if (result <= bound) return bound;
                }
                GC_noop1((word)(*result));
            }
@@ -816,10 +846,15 @@ ptr_t GC_get_stack_base()
        }
        return(result);
     }
+
+    ptr_t GC_find_limit(ptr_t p, GC_bool up)
+    {
+       return GC_find_limit_with_bound(p, up, up ? (ptr_t)(word)(-1) : 0);
+    }
 # endif
 
 #if defined(ECOS) || defined(NOSYS)
-  ptr_t GC_get_stack_base()
+  ptr_t GC_get_main_stack_base(void)
   {
     return STACKBOTTOM;
   }
@@ -862,33 +897,6 @@ ptr_t GC_get_stack_base()
 #endif
 
 # ifdef IA64
-    /* Try to read the backing store base from /proc/self/maps.        */
-    /* We look for the writable mapping with a 0 major device,  */
-    /* which is        as close to our frame as possible, but below it.*/
-    static word backing_store_base_from_maps(char *maps)
-    {
-      char prot_buf[5];
-      char *buf_ptr = maps;
-      word start, end;
-      unsigned int maj_dev;
-      word current_best = 0;
-      word dummy;
-  
-      for (;;) {
-        buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
-       if (buf_ptr == NULL) return current_best;
-       if (prot_buf[1] == 'w' && maj_dev == 0) {
-           if (end < (word)(&dummy) && start > current_best) current_best = start;
-       }
-      }
-      return current_best;
-    }
-
-    static word backing_store_base_from_proc(void)
-    {
-        return GC_apply_to_maps(backing_store_base_from_maps);
-    }
-
 #   ifdef USE_LIBC_PRIVATES
 #     pragma weak __libc_ia64_register_backing_store_base
       extern ptr_t __libc_ia64_register_backing_store_base;
@@ -896,6 +904,8 @@ ptr_t GC_get_stack_base()
 
     ptr_t GC_get_register_stack_base(void)
     {
+      ptr_t result;
+
 #     ifdef USE_LIBC_PRIVATES
         if (0 != &__libc_ia64_register_backing_store_base
            && 0 != __libc_ia64_register_backing_store_base) {
@@ -906,20 +916,18 @@ ptr_t GC_get_stack_base()
          return __libc_ia64_register_backing_store_base;
         }
 #     endif
-      word result = backing_store_base_from_proc();
+      result = backing_store_base_from_proc();
       if (0 == result) {
-         /* Use dumb heuristics.  Works only for default configuration. */
-         result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
-         result += BACKING_STORE_ALIGNMENT - 1;
-         result &= ~(BACKING_STORE_ALIGNMENT - 1);
-         /* Verify that it's at least readable.  If not, we goofed. */
-         GC_noop1(*(word *)result); 
+         result = GC_find_limit(GC_save_regs_in_stack(), FALSE);
+         /* Now seems to work better than constant displacement        */
+         /* heuristic used in 6.X versions.  The latter seems to       */
+         /* fail for 2.6 kernels.                                      */
       }
-      return (ptr_t)result;
+      return result;
     }
 # endif
 
-  ptr_t GC_linux_stack_base(void)
+  STATIC ptr_t GC_linux_stack_base(void)
   {
     /* We read the stack base value from /proc/self/stat.  We do this  */
     /* using direct I/O system calls in order to avoid calling malloc   */
@@ -940,15 +948,14 @@ ptr_t GC_get_stack_base()
     /* this.                                                   */  
 #   ifdef USE_LIBC_PRIVATES
       if (0 != &__libc_stack_end && 0 != __libc_stack_end ) {
-#       ifdef IA64
+#       if defined(IA64)
          /* Some versions of glibc set the address 16 bytes too        */
          /* low while the initialization code is running.              */
          if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) {
            return __libc_stack_end + 0x10;
          } /* Otherwise it's not safe to add 16 bytes and we fall      */
            /* back to using /proc.                                     */
-#      else 
-#      ifdef SPARC
+#      elif defined(SPARC)
          /* Older versions of glibc for 64-bit Sparc do not set
           * this variable correctly, it gets set to either zero
           * or one.
@@ -957,7 +964,6 @@ ptr_t GC_get_stack_base()
            return __libc_stack_end;
 #      else
          return __libc_stack_end;
-#      endif
 #      endif
       }
 #   endif
@@ -979,7 +985,7 @@ ptr_t GC_get_stack_base()
       c = stat_buf[buf_offset++];
     }
     close(f);
-    if (result < 0x10000000) ABORT("Absurd stack bottom value");
+    if (result < 0x100000) ABORT("Absurd stack bottom value");
     return (ptr_t)result;
   }
 
@@ -994,7 +1000,7 @@ ptr_t GC_get_stack_base()
 #include <sys/types.h>
 #include <sys/sysctl.h>
 
-  ptr_t GC_freebsd_stack_base(void)
+  STATIC ptr_t GC_freebsd_stack_base(void)
   {
     int nm[2] = {CTL_KERN, KERN_USRSTACK};
     ptr_t base;
@@ -1009,21 +1015,19 @@ ptr_t GC_get_stack_base()
 #endif /* FREEBSD_STACKBOTTOM */
 
 #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
-    && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS)
+    && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS) \
+    && !defined(CYGWIN32)
 
-ptr_t GC_get_stack_base()
+ptr_t GC_get_main_stack_base(void)
 {
-#   if defined(HEURISTIC1) || defined(HEURISTIC2) || \
-       defined(LINUX_STACKBOTTOM) || defined(FREEBSD_STACKBOTTOM)
-    word dummy;
-    ptr_t result;
-#   endif
-
-#   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
-
 #   ifdef STACKBOTTOM
        return(STACKBOTTOM);
 #   else
+#      if defined(HEURISTIC1) || defined(HEURISTIC2)
+         word dummy;
+#      endif
+       ptr_t result;
+#      define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
 #      ifdef HEURISTIC1
 #         ifdef STACK_GROWS_DOWN
             result = (ptr_t)((((word)(&dummy))
@@ -1069,6 +1073,85 @@ ptr_t GC_get_stack_base()
 
 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS */
 
+#if defined(GC_LINUX_THREADS) && !defined(HAVE_GET_STACK_BASE)
+
+#include <pthread.h>
+/* extern int pthread_getattr_np(pthread_t, pthread_attr_t *); */
+
+#ifdef IA64
+  ptr_t GC_greatest_stack_base_below(ptr_t bound);
+       /* From pthread_support.c */
+#endif
+
+GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
+{
+    pthread_attr_t attr;
+    size_t size;
+
+    if (pthread_getattr_np(pthread_self(), &attr) != 0) {
+       WARN("pthread_getattr_np failed\n", 0);
+       return GC_UNIMPLEMENTED;
+    }
+    if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) {
+       ABORT("pthread_attr_getstack failed");
+    }
+    pthread_attr_destroy(&attr);
+#   ifdef STACK_GROWS_DOWN
+        b -> mem_base = (char *)(b -> mem_base) + size;
+#   endif
+#   ifdef IA64
+      /* We could try backing_store_base_from_proc, but that's safe    */
+      /* only if no mappings are being asynchronously created.         */
+      /* Subtracting the size from the stack base doesn't work for at  */
+      /* least the main thread.                                                */
+      LOCK();
+      {
+       ptr_t bsp = GC_save_regs_in_stack();
+       ptr_t next_stack = GC_greatest_stack_base_below(bsp);
+       if (0 == next_stack) {
+          b -> reg_base = GC_find_limit(bsp, FALSE);
+       } else {
+         /* Avoid walking backwards into preceding memory stack and    */
+         /* growing it.                                                */
+          b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack);
+       }
+      }
+      UNLOCK();
+#   endif
+    return GC_SUCCESS;
+}
+
+#define HAVE_GET_STACK_BASE
+
+#endif /* GC_LINUX_THREADS */
+
+#ifndef HAVE_GET_STACK_BASE
+/* Retrieve stack base.                                                */
+/* Using the GC_find_limit version is risky.                   */
+/* On IA64, for example, there is no guard page between the    */
+/* stack of one thread and the register backing store of the   */
+/* next.  Thus this is likely to identify way too large a      */
+/* "stack" and thus at least result in disastrous performance. */
+/* FIXME - Implement better strategies here.                   */
+GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
+{
+#   ifdef NEED_FIND_LIMIT
+      int dummy;
+#     ifdef STACK_GROWS_DOWN
+       b -> mem_base = GC_find_limit((ptr_t)(&dummy), TRUE);
+#       ifdef IA64
+         b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE);
+#       endif
+#     else
+       b -> mem_base = GC_find_limit(&dummy, FALSE);
+#     endif
+      return GC_SUCCESS;
+#   else
+      return GC_UNIMPLEMENTED;
+#   endif
+}
+#endif
+
 /*
  * Register static data segment(s) as roots.
  * If more data segments are added later then they need to be registered
@@ -1079,7 +1162,7 @@ ptr_t GC_get_stack_base()
 
 # ifdef OS2
 
-void GC_register_data_segments()
+void GC_register_data_segments(void)
 {
     PTIB ptib;
     PPIB ppib;
@@ -1094,12 +1177,12 @@ void GC_register_data_segments()
     
     
     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
-       GC_err_printf0("DosGetInfoBlocks failed\n");
+       GC_err_printf("DosGetInfoBlocks failed\n");
        ABORT("DosGetInfoBlocks failed\n");
     }
     module_handle = ppib -> pib_hmte;
     if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
-       GC_err_printf0("DosQueryModuleName failed\n");
+       GC_err_printf("DosQueryModuleName failed\n");
        ABORT("DosGetInfoBlocks failed\n");
     }
     myexefile = fopen(path, "rb");
@@ -1160,35 +1243,136 @@ void GC_register_data_segments()
       if (!(flags & OBJWRITE)) continue;
       if (!(flags & OBJREAD)) continue;
       if (flags & OBJINVALID) {
-          GC_err_printf0("Object with invalid pages?\n");
+          GC_err_printf("Object with invalid pages?\n");
           continue;
       } 
-      GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE);
+      GC_add_roots_inner((ptr_t)O32_BASE(seg),
+                        (ptr_t)(O32_BASE(seg)+O32_SIZE(seg)), FALSE);
     }
 }
 
 # else /* !OS2 */
 
+# if defined(GWW_VDB)
+
+#   ifndef MEM_WRITE_WATCH
+#     define MEM_WRITE_WATCH 0x200000
+#   endif
+
+#   ifndef WRITE_WATCH_FLAG_RESET
+#     define WRITE_WATCH_FLAG_RESET 1
+#   endif
+
+#   if !defined(_BASETSD_H_) && !defined(_BASETSD_H)
+#     ifdef _WIN64
+        typedef unsigned __int64 ULONG_PTR;
+#     else
+        typedef unsigned long ULONG_PTR;
+#     endif
+      typedef ULONG_PTR SIZE_T;
+      typedef ULONG_PTR * PULONG_PTR;
+#   endif
+
+    typedef UINT (WINAPI * GetWriteWatch_type)(
+      DWORD, PVOID, SIZE_T, PVOID*, PULONG_PTR, PULONG);
+    static GetWriteWatch_type GetWriteWatch_func;
+    static DWORD GetWriteWatch_alloc_flag;
+
+#   define GC_GWW_AVAILABLE() (GetWriteWatch_func != NULL)
+
+    static void detect_GetWriteWatch(void)
+    {
+      static GC_bool done;
+      if (done)
+        return;
+
+#     if defined(MPROTECT_VDB)
+       {
+         char * str = GETENV("GC_USE_GETWRITEWATCH");
+#        if defined(GC_PREFER_MPROTECT_VDB)
+           if (str == NULL || (*str == '0' && *(str + 1) == '\0')) {
+             /* GC_USE_GETWRITEWATCH is unset or set to "0".           */
+             done = TRUE; /* falling back to MPROTECT_VDB strategy.    */
+             /* This should work as if GWW_VDB is undefined. */
+             return;
+           }
+#        else
+           if (str != NULL && *str == '0' && *(str + 1) == '\0') {
+             /* GC_USE_GETWRITEWATCH is set "0".                       */
+             done = TRUE; /* falling back to MPROTECT_VDB strategy.    */
+             return;
+           }
+#        endif
+       }
+#     endif
+
+      GetWriteWatch_func = (GetWriteWatch_type)
+        GetProcAddress(GetModuleHandle("kernel32.dll"), "GetWriteWatch");
+      if (GetWriteWatch_func != NULL) {
+        /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH,   */
+        /* as some versions of kernel32.dll have one but not the      */
+        /* other, making the feature completely broken.               */
+        void * page = VirtualAlloc(NULL, GC_page_size,
+                                    MEM_WRITE_WATCH | MEM_RESERVE,
+                                    PAGE_READWRITE);
+        if (page != NULL) {
+          PVOID pages[16];
+          ULONG_PTR count = 16;
+          DWORD page_size;
+          /* Check that it actually works.  In spite of some           */
+         /* documentation it actually seems to exist on W2K.           */
+         /* This test may be unnecessary, but ...                      */
+          if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET,
+                                 page, GC_page_size,
+                                 pages,
+                                 &count,
+                                 &page_size) != 0) {
+            /* GetWriteWatch always fails. */
+            GetWriteWatch_func = NULL;
+          } else {
+            GetWriteWatch_alloc_flag = MEM_WRITE_WATCH;
+          }
+          VirtualFree(page, GC_page_size, MEM_RELEASE);
+        } else {
+          /* GetWriteWatch will be useless. */
+          GetWriteWatch_func = NULL;
+        }
+      }
+      if (GC_print_stats) {
+        if (GetWriteWatch_func == NULL) {
+          GC_log_printf("Did not find a usable GetWriteWatch()\n");
+        } else {
+          GC_log_printf("Using GetWriteWatch()\n");
+        }
+      }
+      done = TRUE;
+    }
+
+# endif /* GWW_VDB */
+
 # if defined(MSWIN32) || defined(MSWINCE)
 
 # ifdef MSWIN32
   /* Unfortunately, we have to handle win32s very differently from NT,         */
   /* Since VirtualQuery has very different semantics.  In particular,  */
   /* under win32s a VirtualQuery call on an unmapped page returns an   */
-  /* invalid result.  Under NT, GC_register_data_segments is a noop and        */
-  /* all real work is done by GC_register_dynamic_libraries.  Under    */
+  /* invalid result.  Under NT, GC_register_data_segments is a no-op   */
+  /* and all real work is done by GC_register_dynamic_libraries.  Under        */
   /* win32s, we cannot find the data segments associated with dll's.   */
   /* We register the main data segment here.                           */
-  GC_bool GC_no_win32_dlls = FALSE;     
+  GC_bool GC_no_win32_dlls = FALSE;
        /* This used to be set for gcc, to avoid dealing with           */
        /* the structured exception handling issues.  But we now have   */
        /* assembly code to do that right.                              */
+
   GC_bool GC_wnt = FALSE;
-        /* This is a Windows NT derivative, i.e. NT, W2K, XP or later.  */
+         /* This is a Windows NT derivative, i.e. NT, W2K, XP or later.  */
   
-  void GC_init_win32()
+  void GC_init_win32(void)
   {
-    /* if we're running under win32s, assume that no DLLs will be loaded */
+    /* Set GC_wnt.                                                      */
+    /* If we're running under win32s, assume that no DLLs will be loaded */
+    /* I doubt anyone still runs win32s, but ...                        */
     DWORD v = GetVersion();
     GC_wnt = !(v & 0x80000000);
     GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3);
@@ -1200,7 +1384,7 @@ void GC_register_data_segments()
   ptr_t GC_least_described_address(ptr_t start)
   {  
     MEMORY_BASIC_INFORMATION buf;
-    DWORD result;
+    size_t result;
     LPVOID limit;
     ptr_t p;
     LPVOID q;
@@ -1214,7 +1398,7 @@ void GC_register_data_segments()
        if (result != sizeof(buf) || buf.AllocationBase == 0) break;
        p = (ptr_t)(buf.AllocationBase);
     }
-    return(p);
+    return p;
   }
 # endif
 
@@ -1229,14 +1413,14 @@ void GC_register_data_segments()
   /* apparently works only for NT-based Windows.                       */ 
 
   /* In the long run, a better data structure would also be nice ...   */
-  struct GC_malloc_heap_list {
+  STATIC struct GC_malloc_heap_list {
     void * allocation_base;
     struct GC_malloc_heap_list *next;
   } *GC_malloc_heap_l = 0;
 
   /* Is p the base of one of the malloc heap sections we already know  */
   /* about?                                                            */
-  GC_bool GC_is_malloc_heap_base(ptr_t p)
+  STATIC GC_bool GC_is_malloc_heap_base(ptr_t p)
   {
     struct GC_malloc_heap_list *q = GC_malloc_heap_l;
 
@@ -1247,19 +1431,19 @@ void GC_register_data_segments()
     return FALSE;
   }
 
-  void *GC_get_allocation_base(void *p)
+  STATIC void *GC_get_allocation_base(void *p)
   {
     MEMORY_BASIC_INFORMATION buf;
-    DWORD result = VirtualQuery(p, &buf, sizeof(buf));
+    size_t result = VirtualQuery(p, &buf, sizeof(buf));
     if (result != sizeof(buf)) {
       ABORT("Weird VirtualQuery result");
     }
     return buf.AllocationBase;
   }
 
-  size_t GC_max_root_size = 100000;    /* Appr. largest root size.     */
+  STATIC size_t GC_max_root_size = 100000;     /* Appr. largest root size.     */
 
-  void GC_add_current_malloc_heap()
+  void GC_add_current_malloc_heap(void)
   {
     struct GC_malloc_heap_list *new_l =
                  malloc(sizeof(struct GC_malloc_heap_list));
@@ -1281,11 +1465,9 @@ void GC_register_data_segments()
          free(new_l); return;
        }
     }
-#   ifdef CONDPRINT
-      if (GC_print_stats)
-         GC_printf1("Found new system malloc AllocationBase at 0x%lx\n",
-                     candidate);
-#   endif
+    if (GC_print_stats)
+         GC_log_printf("Found new system malloc AllocationBase at %p\n",
+                        candidate);
     new_l -> allocation_base = candidate;
     new_l -> next = GC_malloc_heap_l;
     GC_malloc_heap_l = new_l;
@@ -1300,12 +1482,6 @@ void GC_register_data_segments()
      unsigned i;
      
 #    ifndef REDIRECT_MALLOC
-       static word last_gc_no = -1;
-     
-       if (last_gc_no != GC_gc_no) {
-        GC_add_current_malloc_heap();
-        last_gc_no = GC_gc_no;
-       }
        if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size;
        if (GC_is_malloc_heap_base(p)) return TRUE;
 #    endif
@@ -1316,10 +1492,10 @@ void GC_register_data_segments()
   }
 
 # ifdef MSWIN32
-  void GC_register_root_section(ptr_t static_root)
+  STATIC void GC_register_root_section(ptr_t static_root)
   {
       MEMORY_BASIC_INFORMATION buf;
-      DWORD result;
+      size_t result;
       DWORD protect;
       LPVOID p;
       char * base;
@@ -1350,7 +1526,7 @@ void GC_register_data_segments()
   }
 #endif
   
-  void GC_register_data_segments()
+  void GC_register_data_segments(void)
   {
 #     ifdef MSWIN32
       static char dummy;
@@ -1362,9 +1538,7 @@ void GC_register_data_segments()
 
 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
-ptr_t GC_SysVGetDataStart(max_page_size, etext_addr)
-int max_page_size;
-int * etext_addr;
+ptr_t GC_SysVGetDataStart(size_t max_page_size, ptr_t etext_addr)
 {
     word text_end = ((word)(etext_addr) + sizeof(word) - 1)
                    & ~(sizeof(word) - 1);
@@ -1372,7 +1546,7 @@ int * etext_addr;
     word next_page = ((text_end + (word)max_page_size - 1)
                      & ~((word)max_page_size - 1));
     word page_offset = (text_end & ((word)max_page_size - 1));
-    VOLATILE char * result = (char *)(next_page + page_offset);
+    volatile char * result = (char *)(next_page + page_offset);
     /* Note that this isnt equivalent to just adding           */
     /* max_page_size to &etext if &etext is at a page boundary */
     
@@ -1394,28 +1568,26 @@ int * etext_addr;
 }
 # endif
 
-# if defined(FREEBSD) && (defined(I386) || defined(powerpc) || defined(__powerpc__)) && !defined(PCR)
+# if defined(FREEBSD) && (defined(I386) || defined(X86_64) || defined(powerpc) || defined(__powerpc__)) && !defined(PCR)
 /* Its unclear whether this should be identical to the above, or       */
 /* whether it should apply to non-X86 architectures.                   */
 /* For now we don't assume that there is always an empty page after    */
 /* etext.  But in some cases there actually seems to be slightly more.  */
 /* This also deals with holes between read-only data and writable data.        */
-ptr_t GC_FreeBSDGetDataStart(max_page_size, etext_addr)
-int max_page_size;
-int * etext_addr;
+ptr_t GC_FreeBSDGetDataStart(size_t max_page_size, ptr_t etext_addr)
 {
     word text_end = ((word)(etext_addr) + sizeof(word) - 1)
                     & ~(sizeof(word) - 1);
        /* etext rounded to word boundary       */
-    VOLATILE word next_page = (text_end + (word)max_page_size - 1)
+    volatile word next_page = (text_end + (word)max_page_size - 1)
                              & ~((word)max_page_size - 1);
-    VOLATILE ptr_t result = (ptr_t)text_end;
+    volatile ptr_t result = (ptr_t)text_end;
     GC_setup_temporary_fault_handler();
     if (SETJMP(GC_jmp_buf) == 0) {
        /* Try reading at the address.                          */
        /* This should happen before there is another thread.   */
        for (; next_page < (word)(DATAEND); next_page += (word)max_page_size)
-           *(VOLATILE char *)next_page;
+           *(volatile char *)next_page;
        GC_reset_fault_handler();
     } else {
        GC_reset_fault_handler();
@@ -1436,22 +1608,22 @@ int * etext_addr;
 
 #else /* !OS2 && !Windows && !AMIGA */
 
-void GC_register_data_segments()
+void GC_register_data_segments(void)
 {
-#   if !defined(PCR) && !defined(SRC_M3) && !defined(MACOS)
+#   if !defined(PCR) && !defined(MACOS)
 #     if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS)
        /* As of Solaris 2.3, the Solaris threads implementation        */
        /* allocates the data structure for the initial thread with     */
        /* sbrk at process startup.  It needs to be scanned, so that    */
        /* we don't lose some malloc allocated data structures          */
        /* hanging from it.  We're on thin ice here ...                 */
-        extern caddr_t sbrk();
+        extern caddr_t sbrk(int);
 
-       GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
+       GC_add_roots_inner(DATASTART, (ptr_t)sbrk(0), FALSE);
 #     else
-       GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
+       GC_add_roots_inner(DATASTART, (ptr_t)(DATAEND), FALSE);
 #       if defined(DATASTART2)
-         GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), FALSE);
+          GC_add_roots_inner(DATASTART2, (ptr_t)(DATAEND2), FALSE);
 #       endif
 #     endif
 #   endif
@@ -1505,48 +1677,9 @@ void GC_register_data_segments()
        && !defined(MSWIN32) && !defined(MSWINCE) \
        && !defined(MACOS) && !defined(DOS4GW) && !defined(NONSTOP)
 
-# ifdef SUNOS4
-    extern caddr_t sbrk();
-# endif
-# ifdef __STDC__
-#   define SBRK_ARG_T ptrdiff_t
-# else
-#   define SBRK_ARG_T int
-# endif
-
-
-# if 0 && defined(RS6000)  /* We now use mmap */
-/* The compiler seems to generate speculative reads one past the end of        */
-/* an allocated object.  Hence we need to make sure that the page      */
-/* following the last heap page is also mapped.                                */
-ptr_t GC_unix_get_mem(bytes)
-word bytes;
-{
-    caddr_t cur_brk = (caddr_t)sbrk(0);
-    caddr_t result;
-    SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
-    static caddr_t my_brk_val = 0;
-    
-    if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
-    if (lsbs != 0) {
-        if((caddr_t)(sbrk(GC_page_size - lsbs)) == (caddr_t)(-1)) return(0);
-    }
-    if (cur_brk == my_brk_val) {
-       /* Use the extra block we allocated last time. */
-        result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
-        if (result == (caddr_t)(-1)) return(0);
-        result -= GC_page_size;
-    } else {
-        result = (ptr_t)sbrk(GC_page_size + (SBRK_ARG_T)bytes);
-        if (result == (caddr_t)(-1)) return(0);
-    }
-    my_brk_val = result + bytes + GC_page_size;        /* Always page aligned */
-    return((ptr_t)result);
-}
-
-#else  /* Not RS6000 */
+# define SBRK_ARG_T ptrdiff_t
 
-#if defined(USE_MMAP) || defined(USE_MUNMAP)
+#if defined(MMAP_SUPPORTED)
 
 #ifdef USE_MMAP_FIXED
 #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE
@@ -1568,17 +1701,11 @@ word bytes;
 # define OPT_MAP_ANON 0
 #endif 
 
-#endif /* defined(USE_MMAP) || defined(USE_MUNMAP) */
-
-#if defined(USE_MMAP)
-/* Tested only under Linux, IRIX5 and Solaris 2 */
-
 #ifndef HEAP_START
-#   define HEAP_START 0
+#   define HEAP_START ((ptr_t)0)
 #endif
 
-ptr_t GC_unix_get_mem(bytes)
-word bytes;
+STATIC ptr_t GC_unix_mmap_get_mem(word bytes)
 {
     void *result;
     static ptr_t last_addr = HEAP_START;
@@ -1606,7 +1733,7 @@ word bytes;
        /* don't work, so we discard it and try again.                  */
        munmap(result, (size_t)(-GC_page_size) - (size_t)result);
                        /* Leave last page mapped, so we can't repeat. */
-       return GC_unix_get_mem(bytes);
+       return GC_unix_mmap_get_mem(bytes);
       }
 #   else
       GC_ASSERT(last_addr != 0);
@@ -1614,9 +1741,18 @@ word bytes;
     return((ptr_t)result);
 }
 
-#else /* Not RS6000, not USE_MMAP */
-ptr_t GC_unix_get_mem(bytes)
-word bytes;
+# endif  /* MMAP_SUPPORTED */
+
+#if defined(USE_MMAP)
+
+ptr_t GC_unix_get_mem(word bytes)
+{
+    return GC_unix_mmap_get_mem(bytes);
+}
+
+#else /* Not USE_MMAP */
+
+STATIC ptr_t GC_unix_sbrk_get_mem(word bytes)
 {
   ptr_t result;
 # ifdef IRIX5
@@ -1638,6 +1774,15 @@ word bytes;
            goto out;
        }
     }
+#   ifdef ADD_HEAP_GUARD_PAGES
+      /* This is useful for catching severe memory overwrite problems that */
+      /* span heap sections.  It shouldn't otherwise be turned on.        */
+      {
+       ptr_t guard = (ptr_t)sbrk((SBRK_ARG_T)GC_page_size);
+       if (mprotect(guard, GC_page_size, PROT_NONE) != 0)
+           ABORT("ADD_HEAP_GUARD_PAGES: mprotect failed");
+      }
+#   endif /* ADD_HEAP_GUARD_PAGES */
     result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
     if (result == (ptr_t)(-1)) result = 0;
   }
@@ -1648,14 +1793,42 @@ word bytes;
   return(result);
 }
 
-#endif /* Not USE_MMAP */
-#endif /* Not RS6000 */
-
-# endif /* UN*X */
-
-# ifdef OS2
+#if defined(MMAP_SUPPORTED)
 
-void * os2_alloc(size_t bytes)
+/* By default, we try both sbrk and mmap, in that order. */
+ptr_t GC_unix_get_mem(word bytes)
+{
+    static GC_bool sbrk_failed = FALSE;
+    ptr_t result = 0;
+
+    if (!sbrk_failed) result = GC_unix_sbrk_get_mem(bytes);
+    if (0 == result) {
+       sbrk_failed = TRUE;
+       result = GC_unix_mmap_get_mem(bytes);
+    }
+    if (0 == result) {
+       /* Try sbrk again, in case sbrk memory became available. */
+       result = GC_unix_sbrk_get_mem(bytes);
+    }
+    return result;
+}
+
+#else /* !MMAP_SUPPORTED */
+
+ptr_t GC_unix_get_mem(word bytes)
+{
+    return GC_unix_sbrk_get_mem(bytes);
+}
+
+#endif
+
+#endif /* Not USE_MMAP */
+
+# endif /* UN*X */
+
+# ifdef OS2
+
+void * os2_alloc(size_t bytes)
 {
     void * result;
 
@@ -1685,8 +1858,16 @@ SYSTEM_INFO GC_sysinfo;
 
 word GC_n_heap_bases = 0;
 
-ptr_t GC_win32_get_mem(bytes)
-word bytes;
+#ifdef GC_USE_MEM_TOP_DOWN
+  STATIC DWORD GC_mem_top_down = MEM_TOP_DOWN;
+                          /* Use GC_USE_MEM_TOP_DOWN for better 64-bit */
+                          /* testing.  Otherwise all addresses tend to */
+                          /* end up in first 4GB, hiding bugs.         */
+#else
+  STATIC DWORD GC_mem_top_down = 0;
+#endif
+
+ptr_t GC_win32_get_mem(word bytes)
 {
     ptr_t result;
 
@@ -1695,7 +1876,7 @@ word bytes;
        /* There are also unconfirmed rumors of other           */
        /* problems, so we dodge the issue.                     */
         result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
-        result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));
+        result = (ptr_t)(((word)result + HBLKSIZE - 1) & ~(HBLKSIZE-1));
     } else {
        /* VirtualProtect only works on regions returned by a   */
        /* single VirtualAlloc call.  Thus we allocate one      */
@@ -1705,19 +1886,28 @@ word bytes;
        /* This wastes a small amount of memory, and risks      */
        /* increased fragmentation.  But better alternatives    */
        /* would require effort.                                */
+        /* Pass the MEM_WRITE_WATCH only if GetWriteWatch-based */
+        /* VDBs are enabled and the GetWriteWatch function is   */
+        /* available.  Otherwise we waste resources or possibly */
+        /* cause VirtualAlloc to fail (observed in Windows 2000 */
+        /* SP2).                                                */
         result = (ptr_t) VirtualAlloc(NULL, bytes + 1,
-                                     MEM_COMMIT | MEM_RESERVE,
+#                                     ifdef GWW_VDB
+                                        GetWriteWatch_alloc_flag |
+#                                     endif
+                                     MEM_COMMIT | MEM_RESERVE
+                                     | GC_mem_top_down,
                                      PAGE_EXECUTE_READWRITE);
     }
     if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
        /* If I read the documentation correctly, this can      */
        /* only happen if HBLKSIZE > 64k or not a power of 2.   */
     if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
-    GC_heap_bases[GC_n_heap_bases++] = result;
+    if (0 != result) GC_heap_bases[GC_n_heap_bases++] = result;
     return(result);                      
 }
 
-void GC_win32_free_heap ()
+GC_API void GC_CALL GC_win32_free_heap(void)
 {
     if (GC_no_win32_dlls) {
        while (GC_n_heap_bases > 0) {
@@ -1738,8 +1928,7 @@ void GC_win32_free_heap ()
 # ifdef MSWINCE
 word GC_n_heap_bases = 0;
 
-ptr_t GC_wince_get_mem(bytes)
-word bytes;
+ptr_t GC_wince_get_mem(word bytes)
 {
     ptr_t result;
     word i;
@@ -1761,10 +1950,10 @@ word bytes;
        /* Reserve more pages */
        word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
                         & ~(GC_sysinfo.dwAllocationGranularity-1);
-       /* If we ever support MPROTECT_VDB here, we will probably need to       */
-       /* ensure that res_bytes is strictly > bytes, so that VirtualProtect    */
-       /* never spans regions.  It seems to be OK for a VirtualFree argument   */
-       /* to span regions, so we should be OK for now.                         */
+       /* If we ever support MPROTECT_VDB here, we will probably need to    */
+       /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */
+       /* never spans regions.  It seems to be OK for a VirtualFree         */
+       /* argument to span regions, so we should be OK for now.             */
        result = (ptr_t) VirtualAlloc(NULL, res_bytes,
                                      MEM_RESERVE | MEM_TOP_DOWN,
                                      PAGE_EXECUTE_READWRITE);
@@ -1795,7 +1984,6 @@ word bytes;
 /* For now, this only works on Win32/WinCE and some Unix-like  */
 /* systems.  If you have something else, don't define          */
 /* USE_MUNMAP.                                                 */
-/* We assume ANSI C to support this feature.                   */
 
 #if !defined(MSWIN32) && !defined(MSWINCE)
 
@@ -1809,23 +1997,20 @@ word bytes;
 /* Compute a page aligned starting address for the unmap       */
 /* operation on a block of size bytes starting at start.       */
 /* Return 0 if the block is too small to make this feasible.   */
-ptr_t GC_unmap_start(ptr_t start, word bytes)
+STATIC ptr_t GC_unmap_start(ptr_t start, size_t bytes)
 {
-    ptr_t result = start;
+    ptr_t result;
     /* Round start to next page boundary.       */
-        result += GC_page_size - 1;
-        result = (ptr_t)((word)result & ~(GC_page_size - 1));
+    result = (ptr_t)((word)(start + GC_page_size - 1) & ~(GC_page_size - 1));
     if (result + GC_page_size > start + bytes) return 0;
     return result;
 }
 
 /* Compute end address for an unmap operation on the indicated */
 /* block.                                                      */
-ptr_t GC_unmap_end(ptr_t start, word bytes)
+STATIC ptr_t GC_unmap_end(ptr_t start, size_t bytes)
 {
-    ptr_t end_addr = start + bytes;
-    end_addr = (ptr_t)((word)end_addr & ~(GC_page_size - 1));
-    return end_addr;
+    return (ptr_t)((word)(start + bytes) & ~(GC_page_size - 1));
 }
 
 /* Under Win32/WinCE we commit (map) and decommit (unmap)      */
@@ -1839,7 +2024,7 @@ ptr_t GC_unmap_end(ptr_t start, word bytes)
 /* We assume that GC_remap is called on exactly the same range */
 /* as a previous call to GC_unmap.  It is safe to consistently */
 /* round the endpoints in both places.                         */
-void GC_unmap(ptr_t start, word bytes)
+void GC_unmap(ptr_t start, size_t bytes)
 {
     ptr_t start_addr = GC_unmap_start(start, bytes);
     ptr_t end_addr = GC_unmap_end(start, bytes);
@@ -1874,12 +2059,13 @@ void GC_unmap(ptr_t start, word bytes)
 }
 
 
-void GC_remap(ptr_t start, word bytes)
+void GC_remap(ptr_t start, size_t bytes)
 {
     ptr_t start_addr = GC_unmap_start(start, bytes);
     ptr_t end_addr = GC_unmap_end(start, bytes);
     word len = end_addr - start_addr;
 
+    /* FIXME: Should we handle out-of-memory here? */
 #   if defined(MSWIN32) || defined(MSWINCE)
       ptr_t result;
 
@@ -1909,9 +2095,9 @@ void GC_remap(ptr_t start, word bytes)
       result = mprotect(start_addr, len,
                        PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
       if (result != 0) {
-         GC_err_printf3(
-               "Mprotect failed at 0x%lx (length %ld) with errno %ld\n",
-               start_addr, len, errno);
+         GC_err_printf(
+               "Mprotect failed at %p (length %ld) with errno %d\n",
+               start_addr, (unsigned long)len, errno);
          ABORT("Mprotect remapping failed");
       }
       GC_unmapped_bytes -= len;
@@ -1922,15 +2108,14 @@ void GC_remap(ptr_t start, word bytes)
 /* be merged.  Unmap the whole block.  This typically requires         */
 /* that we unmap a small section in the middle that was not previously */
 /* unmapped due to alignment constraints.                              */
-void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
+void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, size_t bytes2)
 {
     ptr_t start1_addr = GC_unmap_start(start1, bytes1);
     ptr_t end1_addr = GC_unmap_end(start1, bytes1);
     ptr_t start2_addr = GC_unmap_start(start2, bytes2);
-    ptr_t end2_addr = GC_unmap_end(start2, bytes2);
     ptr_t start_addr = end1_addr;
     ptr_t end_addr = start2_addr;
-    word len;
+    size_t len;
     GC_ASSERT(start1 + bytes1 == start2);
     if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2);
     if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
@@ -1951,7 +2136,14 @@ void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
          len -= free_len;
       }
 #   else
-      if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
+      if (len != 0) {
+        /* Immediately remap as above. */
+       void * result;
+        result = mmap(start_addr, len, PROT_NONE,
+                     MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
+                     zero_fd, 0/* offset */);
+        if (result != (void *)start_addr) ABORT("mmap(...PROT_NONE...) failed");
+      }
       GC_unmapped_bytes += len;
 #   endif
 }
@@ -1962,7 +2154,7 @@ void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
 /* environment, this is also responsible for marking from      */
 /* thread stacks.                                              */
 #ifndef THREADS
-void (*GC_push_other_roots)() = 0;
+void (*GC_push_other_roots)(void) = 0;
 #else /* THREADS */
 
 # ifdef PCR
@@ -1978,7 +2170,7 @@ PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy)
 }
 
 /* Push the contents of an old object. We treat this as stack  */
-/* data only becasue that makes it robust against mark stack   */
+/* data only because that makes it robust against mark stack   */
 /* overflow.                                                   */
 PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data)
 {
@@ -1987,7 +2179,7 @@ PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data)
 }
 
 
-void GC_default_push_other_roots GC_PROTO((void))
+void GC_default_push_other_roots(void)
 {
     /* Traverse data allocated by previous memory managers.            */
        {
@@ -2009,77 +2201,43 @@ void GC_default_push_other_roots GC_PROTO((void))
 
 # endif /* PCR */
 
-# ifdef SRC_M3
-
-# ifdef ALL_INTERIOR_POINTERS
-    --> misconfigured
-# endif
-
-void GC_push_thread_structures GC_PROTO((void))
-{
-    /* Not our responsibibility. */
-}
-
-extern void ThreadF__ProcessStacks();
-
-void GC_push_thread_stack(start, stop)
-word start, stop;
-{
-   GC_push_all_stack((ptr_t)start, (ptr_t)stop + sizeof(word));
-}
-
-/* Push routine with M3 specific calling convention. */
-GC_m3_push_root(dummy1, p, dummy2, dummy3)
-word *p;
-ptr_t dummy1, dummy2;
-int dummy3;
-{
-    word q = *p;
-    
-    GC_PUSH_ONE_STACK(q, p);
-}
-
-/* M3 set equivalent to RTHeap.TracedRefTypes */
-typedef struct { int elts[1]; }  RefTypeSet;
-RefTypeSet GC_TracedRefTypes = {{0x1}};
-
-void GC_default_push_other_roots GC_PROTO((void))
-{
-    /* Use the M3 provided routine for finding static roots.    */
-    /* This is a bit dubious, since it presumes no C roots.     */
-    /* We handle the collector roots explicitly in GC_push_roots */
-       RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes);
-       if (GC_words_allocd > 0) {
-           ThreadF__ProcessStacks(GC_push_thread_stack);
-       }
-       /* Otherwise this isn't absolutely necessary, and we have       */
-       /* startup ordering problems.                                   */
-}
-
-# endif /* SRC_M3 */
 
-# if defined(GC_SOLARIS_THREADS) || defined(GC_PTHREADS) || \
-     defined(GC_WIN32_THREADS)
+# if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS)
 
-extern void GC_push_all_stacks();
+extern void GC_push_all_stacks(void);
 
-void GC_default_push_other_roots GC_PROTO((void))
+STATIC void GC_default_push_other_roots(void)
 {
     GC_push_all_stacks();
 }
 
-# endif /* GC_SOLARIS_THREADS || GC_PTHREADS */
+# endif /* GC_WIN32_THREADS || GC_PTHREADS */
 
-void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
+void (*GC_push_other_roots)(void) = GC_default_push_other_roots;
 
 #endif /* THREADS */
 
 /*
  * Routines for accessing dirty  bits on virtual pages.
- * We plan to eventually implement four strategies for doing so:
+ * There are six ways to maintain this information:
  * DEFAULT_VDB:        A simple dummy implementation that treats every page
  *             as possibly dirty.  This makes incremental collection
  *             useless, but the implementation is still correct.
+ * MANUAL_VDB:  Stacks and static data are always considered dirty.
+ *             Heap pages are considered dirty if GC_dirty(p) has been
+ *             called on some pointer p pointing to somewhere inside
+ *             an object on that page.  A GC_dirty() call on a large
+ *             object directly dirties only a single page, but for
+ *             MANUAL_VDB we are careful to treat an object with a dirty
+ *             page as completely dirty.
+ *             In order to avoid races, an object must be marked dirty
+ *             after it is written, and a reference to the object
+ *             must be kept on a stack or in a register in the interim.
+ *             With threads enabled, an object directly reachable from the
+ *             stack at the time of a collection is treated as dirty.
+ *             In single-threaded mode, it suffices to ensure that no
+ *             collection can take place between the pointer assignment
+ *             and the GC_dirty() call.
  * PCR_VDB:    Use PPCRs virtual dirty bit facility.
  * PROC_VDB:   Use the /proc facility for reading dirty bits.  Only
  *             works under some SVR4 variants.  Even then, it may be
@@ -2093,38 +2251,176 @@ void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
  *             call from doing so.  It is the clients responsibility to
  *             make sure that other system calls are similarly protected
  *             or write only to the stack.
+ * GWW_VDB:     Use the Win32 GetWriteWatch functions, if available, to
+ *              read dirty bits.  In case it is not available (because we
+ *              are running on Windows 95, Windows 2000 or earlier),
+ *              MPROTECT_VDB may be defined as a fallback strategy.
  */
 GC_bool GC_dirty_maintained = FALSE;
 
+#if defined(PROC_VDB) || defined(GWW_VDB)
+
+/* Add all pages in pht2 to pht1 */
+STATIC void GC_or_pages(page_hash_table pht1, page_hash_table pht2)
+{
+    register int i;
+    
+    for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i];
+}
+
+#endif
+
+#ifdef GWW_VDB
+
+# define GC_GWW_BUF_LEN (MAXHINCR * HBLKSIZE / 4096 /* X86 page size */)
+  /* Still susceptible to overflow, if there are very large allocations, */
+  /* and everything is dirty.                                           */
+  static PVOID gww_buf[GC_GWW_BUF_LEN];
+
+# ifdef MPROTECT_VDB
+    GC_bool GC_gww_dirty_init(void)
+    {
+      detect_GetWriteWatch();
+      return GC_GWW_AVAILABLE();
+    }
+# else
+    void GC_dirty_init(void)
+    {
+      detect_GetWriteWatch();
+      GC_dirty_maintained = GC_GWW_AVAILABLE();
+    }
+# endif
+
+# ifdef MPROTECT_VDB
+    static void GC_gww_read_dirty(void)
+# else
+    void GC_read_dirty(void)
+# endif
+  {
+    word i;
+
+    BZERO(GC_grungy_pages, sizeof(GC_grungy_pages));
+
+    for (i = 0; i != GC_n_heap_sects; ++i) {
+      ULONG_PTR count;
+
+      do {
+        PVOID * pages, * pages_end;
+        DWORD page_size;
+
+        pages = gww_buf;
+        count = GC_GWW_BUF_LEN;
+        /*
+        * GetWriteWatch is documented as returning non-zero when it fails,
+        * but the documentation doesn't explicitly say why it would fail or
+        * what its behaviour will be if it fails.
+       * It does appear to fail, at least on recent W2K instances, if
+       * the underlying memory was not allocated with the appropriate
+       * flag.  This is common if GC_enable_incremental is called
+       * shortly after GC initialization.  To avoid modifying the
+       * interface, we silently work around such a failure, it it only
+       * affects the initial (small) heap allocation.
+       * If there are more dirty
+        * pages than will fit in the buffer, this is not treated as a
+        * failure; we must check the page count in the loop condition.
+       * Since each partial call will reset the status of some
+       * pages, this should eventually terminate even in the overflow
+       * case.
+        */
+        if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET,
+                               GC_heap_sects[i].hs_start,
+                               GC_heap_sects[i].hs_bytes,
+                               pages,
+                               &count,
+                               &page_size) != 0) {
+          static int warn_count = 0;
+          unsigned j;
+          struct hblk * start = (struct hblk *)GC_heap_sects[i].hs_start;
+          static struct hblk *last_warned = 0;
+          size_t nblocks = divHBLKSZ(GC_heap_sects[i].hs_bytes);
+
+          if ( i != 0 && last_warned != start && warn_count++ < 5) {
+            last_warned = start;
+            WARN(
+              "GC_gww_read_dirty unexpectedly failed at %p: "
+              "Falling back to marking all pages dirty\n", start);
+          }
+          for (j = 0; j < nblocks; ++j) {
+              word hash = PHT_HASH(start + j);
+              set_pht_entry_from_index(GC_grungy_pages, hash);
+          }
+          count = 1;  /* Done with this section. */
+        } else /* succeeded */{
+          pages_end = pages + count;
+          while (pages != pages_end) {
+            struct hblk * h = (struct hblk *) *pages++;
+            struct hblk * h_end = (struct hblk *) ((char *) h + page_size);
+            do
+              set_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h));
+            while (++h < h_end);
+          }
+        }
+      } while (count == GC_GWW_BUF_LEN);
+      /* FIXME: It's unclear from Microsoft's documentation if this loop  */
+      /* is useful.  We suspect the call just fails if the buffer fills          */
+      /* up.  But that should still be handled correctly.                */
+    }
+
+    GC_or_pages(GC_written_pages, GC_grungy_pages);
+  }
+
+# ifdef MPROTECT_VDB
+    static GC_bool GC_gww_page_was_dirty(struct hblk * h)
+# else
+    GC_bool GC_page_was_dirty(struct hblk * h)
+# endif
+  {
+    return HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h));
+  }
+
+# ifdef MPROTECT_VDB
+    static GC_bool GC_gww_page_was_ever_dirty(struct hblk * h)
+# else
+    GC_bool GC_page_was_ever_dirty(struct hblk * h)
+# endif
+  {
+    return HDR(h) == 0 || get_pht_entry_from_index(GC_written_pages, PHT_HASH(h));
+  }
+
+# ifndef MPROTECT_VDB
+    /*ARGSUSED*/
+    void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree)
+    {}
+# endif
+
+# endif /* GWW_VDB */
+
 # ifdef DEFAULT_VDB
 
-/* All of the following assume the allocation lock is held, and        */
-/* signals are disabled.                                       */
+/* All of the following assume the allocation lock is held.    */
 
 /* The client asserts that unallocated pages in the heap are never     */
 /* written.                                                            */
 
 /* Initialize virtual dirty bit implementation.                        */
-void GC_dirty_init()
+void GC_dirty_init(void)
 {
-#   ifdef PRINTSTATS
-      GC_printf0("Initializing DEFAULT_VDB...\n");
-#   endif
+    if (GC_print_stats == VERBOSE)
+      GC_log_printf("Initializing DEFAULT_VDB...\n");
     GC_dirty_maintained = TRUE;
 }
 
 /* Retrieve system dirty bits for heap to a local buffer.      */
 /* Restore the systems notion of which pages are dirty.                */
-void GC_read_dirty()
+void GC_read_dirty(void)
 {}
 
 /* Is the HBLKSIZE sized page at h marked dirty in the local buffer?   */
 /* If the actual page size is different, this returns TRUE if any      */
 /* of the pages overlapping h are dirty.  This routine may err on the  */
-/* side of labelling pages as dirty (and this implementation does).    */
+/* side of labeling pages as dirty (and this implementation does).     */
 /*ARGSUSED*/
-GC_bool GC_page_was_dirty(h)
-struct hblk *h;
+GC_bool GC_page_was_dirty(struct hblk *h)
 {
     return(TRUE);
 }
@@ -2138,19 +2434,11 @@ struct hblk *h;
  
 /* Could any valid GC heap pointer ever have been written to this page?        */
 /*ARGSUSED*/
-GC_bool GC_page_was_ever_dirty(h)
-struct hblk *h;
+GC_bool GC_page_was_ever_dirty(struct hblk *h)
 {
     return(TRUE);
 }
 
-/* Reset the n pages starting at h to "was never dirty" status.        */
-void GC_is_fresh(h, n)
-struct hblk *h;
-word n;
-{
-}
-
 /* A call that:                                                */
 /* I) hints that [h, h+nblocks) is about to be written.        */
 /* II) guarantees that protection is removed.          */
@@ -2159,15 +2447,67 @@ word n;
 /* pointer-free system call buffers in the heap are    */
 /* not protected.                                      */
 /*ARGSUSED*/
-void GC_remove_protection(h, nblocks, is_ptrfree)
-struct hblk *h;
-word nblocks;
-GC_bool is_ptrfree;
+void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree)
 {
 }
 
 # endif /* DEFAULT_VDB */
 
+# ifdef MANUAL_VDB
+
+/* Initialize virtual dirty bit implementation.                        */
+void GC_dirty_init(void)
+{
+    if (GC_print_stats == VERBOSE)
+      GC_log_printf("Initializing MANUAL_VDB...\n");
+    /* GC_dirty_pages and GC_grungy_pages are already cleared. */
+    GC_dirty_maintained = TRUE;
+}
+
+/* Retrieve system dirty bits for heap to a local buffer.      */
+/* Restore the systems notion of which pages are dirty.                */
+void GC_read_dirty(void)
+{
+    BCOPY((word *)GC_dirty_pages, GC_grungy_pages,
+          (sizeof GC_dirty_pages));
+    BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages));
+}
+
+/* Is the HBLKSIZE sized page at h marked dirty in the local buffer?   */
+/* If the actual page size is different, this returns TRUE if any      */
+/* of the pages overlapping h are dirty.  This routine may err on the  */
+/* side of labeling pages as dirty (and this implementation does).     */
+GC_bool GC_page_was_dirty(struct hblk *h)
+{
+    register word index;
+    
+    index = PHT_HASH(h);
+    return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index));
+}
+/* Could any valid GC heap pointer ever have been written to this page?        */
+/*ARGSUSED*/
+GC_bool GC_page_was_ever_dirty(struct hblk *h)
+{
+    /* FIXME - implement me.   */
+    return(TRUE);
+}
+
+/* Mark the page containing p as dirty.  Logically, this dirties the   */
+/* entire object.                                                      */
+void GC_dirty(ptr_t p)
+{
+    word index = PHT_HASH(p);
+    async_set_pht_entry_from_index(GC_dirty_pages, index);
+}
+
+/*ARGSUSED*/
+void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree)
+{
+}
+
+# endif /* MANUAL_VDB */
+
 
 # ifdef MPROTECT_VDB
 
@@ -2185,7 +2525,6 @@ GC_bool is_ptrfree;
  * heap, and do even that only if we are on a platform on which those
  * are not protected.  Another alternative is to wrap system calls
  * (see example for read below), but the current implementation holds
- * a lock across blocking calls, making it problematic for multithreaded
  * applications. 
  * We assume the page size is a multiple of HBLKSIZE.
  * We prefer them to be the same.  We avoid protecting POINTERFREE
@@ -2216,12 +2555,12 @@ GC_bool is_ptrfree;
        decrease the likelihood of some of the problems described below. */
     #include <mach/vm_map.h>
     static mach_port_t GC_task_self;
-    #define PROTECT(addr,len) \
+#   define PROTECT(addr,len) \
         if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
                 FALSE,VM_PROT_READ) != KERN_SUCCESS) { \
             ABORT("vm_portect failed"); \
         }
-    #define UNPROTECT(addr,len) \
+#   define UNPROTECT(addr,len) \
         if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
                 FALSE,VM_PROT_READ|VM_PROT_WRITE) != KERN_SUCCESS) { \
             ABORT("vm_portect failed"); \
@@ -2236,8 +2575,7 @@ GC_bool is_ptrfree;
 #   define PROTECT(addr, len) \
          if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READ, \
                              &protect_junk)) { \
-           DWORD last_error = GetLastError(); \
-           GC_printf1("Last error code: %lx\n", last_error); \
+           GC_printf("Last error code: %lx\n", (long)GetLastError()); \
            ABORT("VirtualProtect failed"); \
          }
 #   define UNPROTECT(addr, len) \
@@ -2248,88 +2586,35 @@ GC_bool is_ptrfree;
 # endif /* !DARWIN */
 # endif /* MSWIN32 || MSWINCE || DARWIN */
 
-#if defined(SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
-    typedef void (* SIG_PF)();
-#endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
-
-#if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \
-    || defined(HURD)
-# ifdef __STDC__
-    typedef void (* SIG_PF)(int);
-# else
-    typedef void (* SIG_PF)();
-# endif
-#endif /* SUNOS5SIGS || OSF1 || LINUX || HURD */
-
 #if defined(MSWIN32)
-    typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF;
+    typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR;
 #   undef SIG_DFL
-#   define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1)
-#endif
-#if defined(MSWINCE)
-    typedef LONG (WINAPI *SIG_PF)(struct _EXCEPTION_POINTERS *);
+#   define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER)((signed_word)-1)
+#elif defined(MSWINCE)
+    typedef LONG (WINAPI *SIG_HNDLR_PTR)(struct _EXCEPTION_POINTERS *);
 #   undef SIG_DFL
-#   define SIG_DFL (SIG_PF) (-1)
+#   define SIG_DFL (SIG_HNDLR_PTR) (-1)
+#elif defined(DARWIN)
+    typedef void (* SIG_HNDLR_PTR)();
+#else
+    typedef void (* SIG_HNDLR_PTR)(int, siginfo_t *, void *);
+    typedef void (* PLAIN_HNDLR_PTR)(int);
 #endif
 
-#if defined(IRIX5) || defined(OSF1) || defined(HURD)
-    typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
-#endif /* IRIX5 || OSF1 || HURD */
-
-#if defined(SUNOS5SIGS)
-# if defined(HPUX) || defined(FREEBSD)
-#   define SIGINFO_T siginfo_t
-# else
-#   define SIGINFO_T struct siginfo
-# endif
-# ifdef __STDC__
-    typedef void (* REAL_SIG_PF)(int, SIGINFO_T *, void *);
-# else
-    typedef void (* REAL_SIG_PF)();
-# endif
-#endif /* SUNOS5SIGS */
-
-#if defined(LINUX)
-#   if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
-      typedef struct sigcontext s_c;
-#   else  /* glibc < 2.2 */
-#     include <linux/version.h>
-#     if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(ARM32)
-        typedef struct sigcontext s_c;
-#     else
-        typedef struct sigcontext_struct s_c;
-#     endif
-#   endif  /* glibc < 2.2 */
-#   if defined(ALPHA) || defined(M68K)
-      typedef void (* REAL_SIG_PF)(int, int, s_c *);
-#   else
-#     if defined(IA64) || defined(HP_PA) || defined(X86_64)
-        typedef void (* REAL_SIG_PF)(int, siginfo_t *, s_c *);
-       /* FIXME:                                                 */
-       /* According to SUSV3, the last argument should have type */
-       /* void * or ucontext_t *                                 */
-#     else
-        typedef void (* REAL_SIG_PF)(int, s_c);
-#     endif
+#if defined(__GLIBC__)
+#   if __GLIBC__ < 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ < 2
+#      error glibc too old?
 #   endif
-#   ifdef ALPHA
-    /* Retrieve fault address from sigcontext structure by decoding    */
-    /* instruction.                                                    */
-    char * get_fault_addr(s_c *sc) {
-        unsigned instr;
-       word faultaddr;
-
-       instr = *((unsigned *)(sc->sc_pc));
-       faultaddr = sc->sc_regs[(instr >> 16) & 0x1f];
-       faultaddr += (word) (((int)instr << 16) >> 16);
-       return (char *)faultaddr;
-    }
-#   endif /* !ALPHA */
-# endif /* LINUX */
+#endif
 
 #ifndef DARWIN
-SIG_PF GC_old_bus_handler;
-SIG_PF GC_old_segv_handler;    /* Also old MSWIN32 ACCESS_VIOLATION filter */
+STATIC SIG_HNDLR_PTR GC_old_segv_handler;
+                       /* Also old MSWIN32 ACCESS_VIOLATION filter */
+#if !defined(MSWIN32) && !defined(MSWINCE)
+STATIC SIG_HNDLR_PTR GC_old_bus_handler;
+STATIC GC_bool GC_old_bus_handler_used_si;
+STATIC GC_bool GC_old_segv_handler_used_si;
+#endif
 #endif /* !DARWIN */
 
 #if defined(THREADS)
@@ -2340,25 +2625,27 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
 /* safe fallback algorithm of setting all bits in the word.            */
 /* Contention should be very rare, so we do the minimum to handle it   */
 /* correctly.                                                          */
-#ifdef GC_TEST_AND_SET_DEFINED
-  static VOLATILE unsigned int fault_handler_lock = 0;
-  void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
-    while (GC_test_and_set(&fault_handler_lock)) {}
+#ifdef AO_HAVE_test_and_set_acquire
+  volatile AO_TS_t GC_fault_handler_lock = 0;
+  void async_set_pht_entry_from_index(volatile page_hash_table db, size_t index) {
+    while (AO_test_and_set_acquire(&GC_fault_handler_lock) == AO_TS_SET) {}
     /* Could also revert to set_pht_entry_from_index_safe if initial   */
     /* GC_test_and_set fails.                                          */
     set_pht_entry_from_index(db, index);
-    GC_clear(&fault_handler_lock);
+    AO_CLEAR(&GC_fault_handler_lock);
   }
-#else /* !GC_TEST_AND_SET_DEFINED */
-  /* THIS IS INCORRECT! The dirty bit vector may be temporarily wrong, */
+#else /* !AO_have_test_and_set_acquire */
+# error No test_and_set operation: Introduces a race.
+  /* THIS WOULD BE INCORRECT!                                          */
+  /* The dirty bit vector may be temporarily wrong,                    */
   /* just before we notice the conflict and correct it. We may end up   */
   /* looking at it while it's wrong.  But this requires contention     */
   /* exactly when a GC is triggered, which seems far less likely to    */
   /* fail than the old code, which had no reported failures.  Thus we  */
   /* leave it this way while we think of something better, or support  */
   /* GC_test_and_set on the remaining platforms.                       */
-  static VOLATILE word currently_updating = 0;
-  void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
+  static volatile word currently_updating = 0;
+  void async_set_pht_entry_from_index(volatile page_hash_table db, size_t index) {
     unsigned int update_dummy;
     currently_updating = (word)(&update_dummy);
     set_pht_entry_from_index(db, index);
@@ -2374,187 +2661,77 @@ SIG_PF GC_old_segv_handler;    /* Also old MSWIN32 ACCESS_VIOLATION filter */
        /* returning us to a safe state, though not soon enough.        */
     }
   }
-#endif /* !GC_TEST_AND_SET_DEFINED */
+#endif /* !AO_HAVE_test_and_set_acquire */
 #else /* !THREADS */
 # define async_set_pht_entry_from_index(db, index) \
        set_pht_entry_from_index(db, index)
 #endif /* !THREADS */
 
-/*ARGSUSED*/
-#if !defined(DARWIN)
-# if defined (SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
-    void GC_write_fault_handler(sig, code, scp, addr)
-    int sig, code;
-    struct sigcontext *scp;
-    char * addr;
-#   ifdef SUNOS4
-#     define SIG_OK (sig == SIGSEGV || sig == SIGBUS)
-#     define CODE_OK (FC_CODE(code) == FC_PROT \
-                   || (FC_CODE(code) == FC_OBJERR \
-                      && FC_ERRNO(code) == FC_PROT))
-#   endif
-#   ifdef FREEBSD
-#     define SIG_OK (sig == SIGBUS)
-#     define CODE_OK TRUE
-#   endif
-# endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
+#ifdef CHECKSUMS
+  void GC_record_fault(struct hblk * h);
+       /* From checksums.c */
+#endif
 
-# if defined(IRIX5) || defined(OSF1) || defined(HURD)
+#if !defined(DARWIN)
 #   include <errno.h>
-    void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
-#   ifdef OSF1
+#   if defined(FREEBSD)
+#     define SIG_OK TRUE
+#     define CODE_OK (si -> si_code == BUS_PAGE_FAULT)
+#   elif defined(OSF1)
 #     define SIG_OK (sig == SIGSEGV)
-#     define CODE_OK (code == 2 /* experimentally determined */)
-#   endif
-#   ifdef IRIX5
+#     define CODE_OK (si -> si_code == 2 /* experimentally determined */)
+#   elif defined(IRIX5)
 #     define SIG_OK (sig == SIGSEGV)
-#     define CODE_OK (code == EACCES)
-#   endif
-#   ifdef HURD
+#     define CODE_OK (si -> si_code == EACCES)
+#   elif defined(HURD)
 #     define SIG_OK (sig == SIGBUS || sig == SIGSEGV)  
 #     define CODE_OK  TRUE
-#   endif
-# endif /* IRIX5 || OSF1 || HURD */
-
-# if defined(LINUX)
-#   if defined(ALPHA) || defined(M68K)
-      void GC_write_fault_handler(int sig, int code, s_c * sc)
-#   else
-#     if defined(IA64) || defined(HP_PA) || defined(X86_64)
-        void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp)
-#     else
-#       if defined(ARM32)
-          void GC_write_fault_handler(int sig, int a2, int a3, int a4, s_c sc)
-#       else
-          void GC_write_fault_handler(int sig, s_c sc)
-#       endif
-#     endif
-#   endif
-#   define SIG_OK (sig == SIGSEGV)
-#   define CODE_OK TRUE
+#   elif defined(LINUX)
+#     define SIG_OK (sig == SIGSEGV)
+#     define CODE_OK TRUE
        /* Empirically c.trapno == 14, on IA32, but is that useful?     */
        /* Should probably consider alignment issues on other           */
        /* architectures.                                               */
-# endif /* LINUX */
-
-# if defined(SUNOS5SIGS)
-#  ifdef __STDC__
-    void GC_write_fault_handler(int sig, SIGINFO_T *scp, void * context)
-#  else
-    void GC_write_fault_handler(sig, scp, context)
-    int sig;
-    SIGINFO_T *scp;
-    void * context;
-#  endif
-#   ifdef HPUX
+#   elif defined(HPUX)
 #     define SIG_OK (sig == SIGSEGV || sig == SIGBUS)
-#     define CODE_OK (scp -> si_code == SEGV_ACCERR) \
-                    || (scp -> si_code == BUS_ADRERR) \
-                    || (scp -> si_code == BUS_UNKNOWN) \
-                    || (scp -> si_code == SEGV_UNKNOWN) \
-                    || (scp -> si_code == BUS_OBJERR)
-#   else
-#     ifdef FREEBSD
-#       define SIG_OK (sig == SIGBUS)
-#       define CODE_OK (scp -> si_code == BUS_PAGE_FAULT)
-#     else
-#       define SIG_OK (sig == SIGSEGV)
-#       define CODE_OK (scp -> si_code == SEGV_ACCERR)
-#     endif
+#     define CODE_OK (si -> si_code == SEGV_ACCERR) \
+                    || (si -> si_code == BUS_ADRERR) \
+                    || (si -> si_code == BUS_UNKNOWN) \
+                    || (si -> si_code == SEGV_UNKNOWN) \
+                    || (si -> si_code == BUS_OBJERR)
+#   elif defined(SUNOS5SIGS)
+#     define SIG_OK (sig == SIGSEGV)
+#     define CODE_OK (si -> si_code == SEGV_ACCERR)
+#   elif defined(MSWIN32) || defined(MSWINCE)
+#     define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode \
+                    == STATUS_ACCESS_VIOLATION)
+#     define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] \
+                     == 1) /* Write fault */
 #   endif    
-# endif /* SUNOS5SIGS */
 
 # if defined(MSWIN32) || defined(MSWINCE)
     LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info)
-#   define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode == \
-                       STATUS_ACCESS_VIOLATION)
-#   define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1)
-                       /* Write fault */
+# else
+#   include <ucontext.h>
+    /*ARGSUSED*/
+    STATIC void GC_write_fault_handler(int sig, siginfo_t *si, void *raw_sc)
 # endif /* MSWIN32 || MSWINCE */
 {
-    register unsigned i;
-#   if defined(HURD) 
-       char *addr = (char *) code;
-#   endif
-#   ifdef IRIX5
-       char * addr = (char *) (size_t) (scp -> sc_badvaddr);
-#   endif
-#   if defined(OSF1) && defined(ALPHA)
-       char * addr = (char *) (scp -> sc_traparg_a0);
-#   endif
-#   ifdef SUNOS5SIGS
-       char * addr = (char *) (scp -> si_addr);
-#   endif
-#   ifdef LINUX
-#     if defined(I386)
-       char * addr = (char *) (sc.cr2);
-#     else
-#      if defined(M68K)
-          char * addr = NULL;
-
-         struct sigcontext *scp = (struct sigcontext *)(sc);
-
-         int format = (scp->sc_formatvec >> 12) & 0xf;
-         unsigned long *framedata = (unsigned long *)(scp + 1); 
-         unsigned long ea;
-
-         if (format == 0xa || format == 0xb) {
-               /* 68020/030 */
-               ea = framedata[2];
-         } else if (format == 7) {
-               /* 68040 */
-               ea = framedata[3];
-               if (framedata[1] & 0x08000000) {
-                       /* correct addr on misaligned access */
-                       ea = (ea+4095)&(~4095);
-               }
-         } else if (format == 4) {
-               /* 68060 */
-               ea = framedata[0];
-               if (framedata[1] & 0x08000000) {
-                       /* correct addr on misaligned access */
-                       ea = (ea+4095)&(~4095);
-               }
-         }     
-         addr = (char *)ea;
-#      else
-#        ifdef ALPHA
-            char * addr = get_fault_addr(sc);
-#        else
-#          if defined(IA64) || defined(HP_PA) || defined(X86_64)
-             char * addr = si -> si_addr;
-             /* I believe this is claimed to work on all platforms for */
-             /* Linux 2.3.47 and later.  Hopefully we don't have to    */
-             /* worry about earlier kernels on IA64.                   */
-#          else
-#             if defined(POWERPC)
-                char * addr = (char *) (sc.regs->dar);
-#            else
-#               if defined(ARM32)
-                  char * addr = (char *)sc.fault_address;
-#               else
-#                if defined(CRIS)
-                   char * addr = (char *)sc.regs.csraddr;
-#                else
-                   --> architecture not supported
-#                endif
-#               endif
-#            endif
-#          endif
-#        endif
-#      endif
-#     endif
-#   endif
-#   if defined(MSWIN32) || defined(MSWINCE)
+#   if !defined(MSWIN32) && !defined(MSWINCE)
+       char *addr = si -> si_addr;
+#   else
        char * addr = (char *) (exc_info -> ExceptionRecord
                                -> ExceptionInformation[1]);
-#      define sig SIGSEGV
 #   endif
+    unsigned i;
     
     if (SIG_OK && CODE_OK) {
         register struct hblk * h =
                        (struct hblk *)((word)addr & ~(GC_page_size-1));
         GC_bool in_allocd_block;
+#      ifdef CHECKSUMS
+         GC_record_fault(h);
+#      endif /* CHECKSUMS */
         
 #      ifdef SUNOS5SIGS
            /* Address is only within the correct physical page.        */
@@ -2573,53 +2750,44 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
            /* sequence, which often depends on SA_SIGINFO.     */
 
            /* Heap blocks now begin and end on page boundaries */
-            SIG_PF old_handler;
-            
-            if (sig == SIGSEGV) {
+            SIG_HNDLR_PTR old_handler;
+
+#          if defined(MSWIN32) || defined(MSWINCE)
                old_handler = GC_old_segv_handler;
-            } else {
-                old_handler = GC_old_bus_handler;
-            }
-            if (old_handler == SIG_DFL) {
+#          else
+               GC_bool used_si;
+
+               if (sig == SIGSEGV) {
+                  old_handler = GC_old_segv_handler;
+                  used_si = GC_old_segv_handler_used_si;
+               } else {
+                  old_handler = GC_old_bus_handler;
+                  used_si = GC_old_bus_handler_used_si;
+               }
+#          endif
+            
+            if (old_handler == (SIG_HNDLR_PTR)SIG_DFL) {
 #              if !defined(MSWIN32) && !defined(MSWINCE)
-                   GC_err_printf1("Segfault at 0x%lx\n", addr);
+                   GC_err_printf("Segfault at %p\n", addr);
                     ABORT("Unexpected bus error or segmentation fault");
 #              else
                    return(EXCEPTION_CONTINUE_SEARCH);
 #              endif
             } else {
-#              if defined (SUNOS4) \
-                    || (defined(FREEBSD) && !defined(SUNOS5SIGS))
-                   (*old_handler) (sig, code, scp, addr);
-                   return;
-#              endif
-#              if defined (SUNOS5SIGS)
-                    /*
-                     * FIXME: For FreeBSD, this code should check if the 
-                     * old signal handler used the traditional BSD style and
-                     * if so call it using that style.
-                     */
-                   (*(REAL_SIG_PF)old_handler) (sig, scp, context);
-                   return;
-#              endif
-#              if defined (LINUX)
-#                  if defined(ALPHA) || defined(M68K)
-                       (*(REAL_SIG_PF)old_handler) (sig, code, sc);
-#                  else 
-#                    if defined(IA64) || defined(HP_PA) || defined(X86_64)
-                       (*(REAL_SIG_PF)old_handler) (sig, si, scp);
-#                    else
-                       (*(REAL_SIG_PF)old_handler) (sig, sc);
-#                    endif
-#                  endif
-                   return;
-#              endif
-#              if defined (IRIX5) || defined(OSF1) || defined(HURD)
-                   (*(REAL_SIG_PF)old_handler) (sig, code, scp);
-                   return;
-#              endif
-#              ifdef MSWIN32
+                /*
+                 * FIXME: This code should probably check if the 
+                 * old signal handler used the traditional style and
+                 * if so call it using that style.
+                 */
+#              if defined(MSWIN32) || defined(MSWINCE)
                    return((*old_handler)(exc_info));
+#              else
+                   if (used_si)
+                     ((SIG_HNDLR_PTR)old_handler) (sig, si, raw_sc);
+                   else
+                     /* FIXME: should pass nonstandard args as well. */
+                     ((PLAIN_HNDLR_PTR)old_handler) (sig);
+                   return;
 #              endif
             }
         }
@@ -2636,14 +2804,10 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
        /* and then to have the thread stopping code set the dirty      */
        /* flag, if necessary.                                          */
         for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
-            register int index = PHT_HASH(h+i);
+            size_t index = PHT_HASH(h+i);
             
             async_set_pht_entry_from_index(GC_dirty_pages, index);
         }
-#      if defined(OSF1)
-           /* These reset the signal handler each time by default. */
-           signal(SIGSEGV, (SIG_PF) GC_write_fault_handler);
-#      endif
        /* The write may not take place before dirty bits are read.     */
        /* But then we'll fault again ...                               */
 #      if defined(MSWIN32) || defined(MSWINCE)
@@ -2655,7 +2819,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
 #if defined(MSWIN32) || defined(MSWINCE)
     return EXCEPTION_CONTINUE_SEARCH;
 #else
-    GC_err_printf1("Segfault at 0x%lx\n", addr);
+    GC_err_printf("Segfault at %p\n", addr);
     ABORT("Unexpected bus error or segmentation fault");
 #endif
 }
@@ -2667,24 +2831,26 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
  * starting at h are no longer protected.  If is_ptrfree is false,
  * also ensure that they will subsequently appear to be dirty.
  */
-void GC_remove_protection(h, nblocks, is_ptrfree)
-struct hblk *h;
-word nblocks;
-GC_bool is_ptrfree;
+void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree)
 {
     struct hblk * h_trunc;  /* Truncated to page boundary */
     struct hblk * h_end;    /* Page boundary following block end */
     struct hblk * current;
-    GC_bool found_clean;
     
+#   if defined(GWW_VDB)
+      if (GC_GWW_AVAILABLE()) return;
+#   endif
     if (!GC_dirty_maintained) return;
     h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1));
     h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size-1)
                            & ~(GC_page_size-1));
-    found_clean = FALSE;
+    if (h_end == h_trunc + 1 &&
+        get_pht_entry_from_index(GC_dirty_pages, PHT_HASH(h_trunc))) {
+       /* already marked dirty, and hence unprotected. */
+       return;
+    }
     for (current = h_trunc; current < h_end; ++current) {
-        int index = PHT_HASH(current);
-            
+        size_t index = PHT_HASH(current);
         if (!is_ptrfree || current < h || current >= h + nblocks) {
             async_set_pht_entry_from_index(GC_dirty_pages, index);
         }
@@ -2693,21 +2859,12 @@ GC_bool is_ptrfree;
 }
 
 #if !defined(DARWIN)
-void GC_dirty_init()
+void GC_dirty_init(void)
 {
-#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \
-       defined(OSF1) || defined(HURD)
+#   if !defined(MSWIN32) && !defined(MSWINCE)
       struct sigaction act, oldact;
-      /* We should probably specify SA_SIGINFO for Linux, and handle   */
-      /* the different architectures more uniformly.                   */
-#     if defined(IRIX5) || defined(LINUX) && !defined(X86_64) \
-        || defined(OSF1) || defined(HURD)
-       act.sa_flags    = SA_RESTART;
-        act.sa_handler  = (SIG_PF)GC_write_fault_handler;
-#     else
-       act.sa_flags    = SA_RESTART | SA_SIGINFO;
-        act.sa_sigaction = GC_write_fault_handler;
-#     endif
+      act.sa_flags     = SA_RESTART | SA_SIGINFO;
+      act.sa_sigaction = GC_write_fault_handler;
       (void)sigemptyset(&act.sa_mask);
 #     ifdef SIG_SUSPEND
         /* Arrange to postpone SIG_SUSPEND while we're in a write fault        */
@@ -2715,42 +2872,16 @@ void GC_dirty_init()
         /* stopping the world for GC.                                  */
         (void)sigaddset(&act.sa_mask, SIG_SUSPEND);
 #     endif /* SIG_SUSPEND */
-#    endif
-#   ifdef PRINTSTATS
-       GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n");
 #   endif
+    if (GC_print_stats == VERBOSE)
+       GC_log_printf(
+               "Initializing mprotect virtual dirty bit implementation\n");
     GC_dirty_maintained = TRUE;
     if (GC_page_size % HBLKSIZE != 0) {
-        GC_err_printf0("Page size not multiple of HBLKSIZE\n");
+        GC_err_printf("Page size not multiple of HBLKSIZE\n");
         ABORT("Page size not multiple of HBLKSIZE");
     }
-#   if defined(SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
-      GC_old_bus_handler = signal(SIGBUS, GC_write_fault_handler);
-      if (GC_old_bus_handler == SIG_IGN) {
-        GC_err_printf0("Previously ignored bus error!?");
-        GC_old_bus_handler = SIG_DFL;
-      }
-      if (GC_old_bus_handler != SIG_DFL) {
-#      ifdef PRINTSTATS
-          GC_err_printf0("Replaced other SIGBUS handler\n");
-#      endif
-      }
-#   endif
-#   if defined(SUNOS4)
-      GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler);
-      if (GC_old_segv_handler == SIG_IGN) {
-        GC_err_printf0("Previously ignored segmentation violation!?");
-        GC_old_segv_handler = SIG_DFL;
-      }
-      if (GC_old_segv_handler != SIG_DFL) {
-#      ifdef PRINTSTATS
-          GC_err_printf0("Replaced other SIGSEGV handler\n");
-#      endif
-      }
-#   endif
-#   if (defined(SUNOS5SIGS) && !defined(FREEBSD)) || defined(IRIX5) \
-       || defined(LINUX) || defined(OSF1) || defined(HURD)
-      /* SUNOS5SIGS includes HPUX */
+#   if !defined(MSWIN32) && !defined(MSWINCE)
 #     if defined(GC_IRIX_THREADS)
        sigaction(SIGSEGV, 0, &oldact);
        sigaction(SIGSEGV, &act, 0);
@@ -2760,47 +2891,50 @@ void GC_dirty_init()
          if (res != 0) ABORT("Sigaction failed");
        }
 #     endif
-#     if defined(_sigargs) || defined(HURD) || !defined(SA_SIGINFO)
-       /* This is Irix 5.x, not 6.x.  Irix 5.x does not have   */
-       /* sa_sigaction.                                        */
-       GC_old_segv_handler = oldact.sa_handler;
-#     else /* Irix 6.x or SUNOS5SIGS or LINUX */
-        if (oldact.sa_flags & SA_SIGINFO) {
-          GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction);
-        } else {
-          GC_old_segv_handler = oldact.sa_handler;
-        }
-#     endif
-      if (GC_old_segv_handler == SIG_IGN) {
-            GC_err_printf0("Previously ignored segmentation violation!?");
-            GC_old_segv_handler = SIG_DFL;
+      if (oldact.sa_flags & SA_SIGINFO) {
+        GC_old_segv_handler = oldact.sa_sigaction;
+       GC_old_segv_handler_used_si = TRUE;
+      } else {
+        GC_old_segv_handler = (SIG_HNDLR_PTR)oldact.sa_handler;
+       GC_old_segv_handler_used_si = FALSE;
       }
-      if (GC_old_segv_handler != SIG_DFL) {
-#       ifdef PRINTSTATS
-         GC_err_printf0("Replaced other SIGSEGV handler\n");
-#       endif
+      if (GC_old_segv_handler == (SIG_HNDLR_PTR)SIG_IGN) {
+       GC_err_printf("Previously ignored segmentation violation!?\n");
+       GC_old_segv_handler = (SIG_HNDLR_PTR)SIG_DFL;
+      }
+      if (GC_old_segv_handler != (SIG_HNDLR_PTR)SIG_DFL) {
+       if (GC_print_stats == VERBOSE)
+         GC_log_printf("Replaced other SIGSEGV handler\n");
       }
-#   endif /* (SUNOS5SIGS && !FREEBSD) || IRIX5 || LINUX || OSF1 || HURD */
 #   if defined(HPUX) || defined(LINUX) || defined(HURD) \
       || (defined(FREEBSD) && defined(SUNOS5SIGS))
       sigaction(SIGBUS, &act, &oldact);
-      GC_old_bus_handler = oldact.sa_handler;
-      if (GC_old_bus_handler == SIG_IGN) {
-            GC_err_printf0("Previously ignored bus error!?");
-            GC_old_bus_handler = SIG_DFL;
+      if (oldact.sa_flags & SA_SIGINFO) {
+        GC_old_bus_handler = oldact.sa_sigaction;
+       GC_old_bus_handler_used_si = TRUE;
+      } else {
+        GC_old_bus_handler = (SIG_HNDLR_PTR)oldact.sa_handler;
+       GC_old_bus_handler_used_si = FALSE;
       }
-      if (GC_old_bus_handler != SIG_DFL) {
-#       ifdef PRINTSTATS
-         GC_err_printf0("Replaced other SIGBUS handler\n");
-#       endif
+      if (GC_old_bus_handler == (SIG_HNDLR_PTR)SIG_IGN) {
+            GC_err_printf("Previously ignored bus error!?\n");
+            GC_old_bus_handler = (SIG_HNDLR_PTR)SIG_DFL;
+      }
+      if (GC_old_bus_handler != (SIG_HNDLR_PTR)SIG_DFL) {
+       if (GC_print_stats == VERBOSE)
+         GC_log_printf("Replaced other SIGBUS handler\n");
       }
 #   endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */
+#   endif /* ! MS windows */
+#   if defined(GWW_VDB)
+      if (GC_gww_dirty_init())
+        return;
+#   endif
 #   if defined(MSWIN32)
       GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
       if (GC_old_segv_handler != NULL) {
-#      ifdef PRINTSTATS
-          GC_err_printf0("Replaced other UnhandledExceptionFilter\n");
-#      endif
+       if (GC_print_stats)
+          GC_log_printf("Replaced other UnhandledExceptionFilter\n");
       } else {
           GC_old_segv_handler = SIG_DFL;
       }
@@ -2808,7 +2942,7 @@ void GC_dirty_init()
 }
 #endif /* !DARWIN */
 
-int GC_incremental_protection_needs()
+GC_API int GC_CALL GC_incremental_protection_needs(void)
 {
     if (GC_page_size == HBLKSIZE) {
        return GC_PROTECTS_POINTER_HEAP;
@@ -2822,10 +2956,10 @@ int GC_incremental_protection_needs()
 #define IS_PTRFREE(hhdr) ((hhdr)->hb_descr == 0)
 
 #define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size - 1))
-void GC_protect_heap()
+STATIC void GC_protect_heap(void)
 {
     ptr_t start;
-    word len;
+    size_t len;
     struct hblk * current;
     struct hblk * current_start;  /* Start of block to be protected. */
     struct hblk * limit;
@@ -2884,19 +3018,30 @@ void GC_protect_heap()
 
 /* We assume that either the world is stopped or its OK to lose dirty  */
 /* bits while this is happenning (as in GC_enable_incremental).                */
-void GC_read_dirty()
+void GC_read_dirty(void)
 {
+#   if defined(GWW_VDB)
+      if (GC_GWW_AVAILABLE()) {
+        GC_gww_read_dirty();
+        return;
+      }
+#   endif
     BCOPY((word *)GC_dirty_pages, GC_grungy_pages,
           (sizeof GC_dirty_pages));
     BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages));
     GC_protect_heap();
 }
 
-GC_bool GC_page_was_dirty(h)
-struct hblk * h;
+GC_bool GC_page_was_dirty(struct hblk *h)
 {
-    register word index = PHT_HASH(h);
+    register word index;
     
+#   if defined(GWW_VDB)
+      if (GC_GWW_AVAILABLE())
+        return GC_gww_page_was_dirty(h);
+#   endif
+
+    index = PHT_HASH(h);
     return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index));
 }
 
@@ -2908,17 +3053,20 @@ struct hblk * h;
  * On other systems, SET_LOCK_HOLDER and friends must be suitably defined.
  */
 
+#if 0
 static GC_bool syscall_acquired_lock = FALSE;  /* Protected by GC lock. */
  
-void GC_begin_syscall()
+void GC_begin_syscall(void)
 {
+    /* FIXME: Resurrecting this code would require fixing the  */
+    /* test, which can spuriously return TRUE.                 */
     if (!I_HOLD_LOCK()) {
        LOCK();
        syscall_acquired_lock = TRUE;
     }
 }
 
-void GC_end_syscall()
+void GC_end_syscall(void)
 {
     if (syscall_acquired_lock) {
        syscall_acquired_lock = FALSE;
@@ -2926,9 +3074,7 @@ void GC_end_syscall()
     }
 }
 
-void GC_unprotect_range(addr, len)
-ptr_t addr;
-word len;
+void GC_unprotect_range(ptr_t addr, word len)
 {
     struct hblk * start_block;
     struct hblk * end_block;
@@ -2953,7 +3099,6 @@ word len;
              ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
 }
 
-#if 0
 
 /* We no longer wrap read by default, since that was causing too many  */
 /* problems.  It is preferred that the client instead avoids writing   */
@@ -2970,20 +3115,9 @@ word len;
 /* make sure that input is available.                                    */
 /* Another, preferred alternative is to ensure that system calls never           */
 /* write to the protected heap (see above).                              */
-# if defined(__STDC__) && !defined(SUNOS4)
-#   include <unistd.h>
-#   include <sys/uio.h>
-    ssize_t read(int fd, void *buf, size_t nbyte)
-# else
-#   ifndef LINT
-      int read(fd, buf, nbyte)
-#   else
-      int GC_read(fd, buf, nbyte)
-#   endif
-    int fd;
-    char *buf;
-    int nbyte;
-# endif
+# include <unistd.h>
+# include <sys/uio.h>
+ssize_t read(int fd, void *buf, size_t nbyte)
 {
     int result;
     
@@ -3042,20 +3176,15 @@ word len;
 #endif /* 0 */
 
 /*ARGSUSED*/
-GC_bool GC_page_was_ever_dirty(h)
-struct hblk *h;
+GC_bool GC_page_was_ever_dirty(struct hblk *h)
 {
+#   if defined(GWW_VDB)
+      if (GC_GWW_AVAILABLE())
+        return GC_gww_page_was_ever_dirty(h);
+#   endif
     return(TRUE);
 }
 
-/* Reset the n pages starting at h to "was never dirty" status.        */
-/*ARGSUSED*/
-void GC_is_fresh(h, n)
-struct hblk *h;
-word n;
-{
-}
-
 # endif /* MPROTECT_VDB */
 
 # ifdef PROC_VDB
@@ -3065,7 +3194,7 @@ word n;
  */
  
 /*
- * This implementaion assumes a Solaris 2.X like /proc pseudo-file-system
+ * This implementation assumes a Solaris 2.X like /proc pseudo-file-system
  * from which we can read page modified bits.  This facility is far from
  * optimal (e.g. we would like to get the info for only some of the
  * address space), but it avoids intercepting system calls.
@@ -3080,54 +3209,28 @@ word n;
 #include <sys/stat.h>
 
 #define INITIAL_BUF_SZ 16384
-word GC_proc_buf_size = INITIAL_BUF_SZ;
-char *GC_proc_buf;
-
-#ifdef GC_SOLARIS_THREADS
-/* We don't have exact sp values for threads.  So we count on  */
-/* occasionally declaring stack pages to be fresh.  Thus we    */
-/* need a real implementation of GC_is_fresh.  We can't clear  */
-/* entries in GC_written_pages, since that would declare all   */
-/* pages with the given hash address to be fresh.              */
-#   define MAX_FRESH_PAGES 8*1024      /* Must be power of 2 */
-    struct hblk ** GC_fresh_pages;     /* A direct mapped cache.       */
-                                       /* Collisions are dropped.      */
-
-#   define FRESH_PAGE_SLOT(h) (divHBLKSZ((word)(h)) & (MAX_FRESH_PAGES-1))
-#   define ADD_FRESH_PAGE(h) \
-       GC_fresh_pages[FRESH_PAGE_SLOT(h)] = (h)
-#   define PAGE_IS_FRESH(h) \
-       (GC_fresh_pages[FRESH_PAGE_SLOT(h)] == (h) && (h) != 0)
-#endif
+STATIC word GC_proc_buf_size = INITIAL_BUF_SZ;
+STATIC char *GC_proc_buf;
 
-/* Add all pages in pht2 to pht1 */
-void GC_or_pages(pht1, pht2)
-page_hash_table pht1, pht2;
-{
-    register int i;
-    
-    for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i];
-}
+STATIC int GC_proc_fd;
 
-int GC_proc_fd;
-
-void GC_dirty_init()
+void GC_dirty_init(void)
 {
     int fd;
     char buf[30];
 
     GC_dirty_maintained = TRUE;
-    if (GC_words_allocd != 0 || GC_words_allocd_before_gc != 0) {
+    if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) {
        register int i;
     
         for (i = 0; i < PHT_SIZE; i++) GC_written_pages[i] = (word)(-1);
-#       ifdef PRINTSTATS
-           GC_printf1("Allocated words:%lu:all pages may have been written\n",
-                      (unsigned long)
-                               (GC_words_allocd + GC_words_allocd_before_gc));
-#      endif       
+       if (GC_print_stats == VERBOSE)
+           GC_log_printf(
+                     "Allocated bytes:%lu:all pages may have been written\n",
+                     (unsigned long)
+                               (GC_bytes_allocd + GC_bytes_allocd_before_gc));
     }
-    sprintf(buf, "/proc/%d", getpid());
+    sprintf(buf, "/proc/%ld", (long)getpid());
     fd = open(buf, O_RDONLY);
     if (fd < 0) {
        ABORT("/proc open failed");
@@ -3139,33 +3242,17 @@ void GC_dirty_init()
        ABORT("/proc ioctl failed");
     }
     GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size);
-#   ifdef GC_SOLARIS_THREADS
-       GC_fresh_pages = (struct hblk **)
-         GC_scratch_alloc(MAX_FRESH_PAGES * sizeof (struct hblk *));
-       if (GC_fresh_pages == 0) {
-           GC_err_printf0("No space for fresh pages\n");
-           EXIT();
-       }
-       BZERO(GC_fresh_pages, MAX_FRESH_PAGES * sizeof (struct hblk *));
-#   endif
 }
 
 /* Ignore write hints. They don't help us here.        */
 /*ARGSUSED*/
-void GC_remove_protection(h, nblocks, is_ptrfree)
-struct hblk *h;
-word nblocks;
-GC_bool is_ptrfree;
+void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree)
 {
 }
 
-#ifdef GC_SOLARIS_THREADS
-#   define READ(fd,buf,nbytes) syscall(SYS_read, fd, buf, nbytes)
-#else
-#   define READ(fd,buf,nbytes) read(fd, buf, nbytes)
-#endif
+# define READ(fd,buf,nbytes) read(fd, buf, nbytes)
 
-void GC_read_dirty()
+void GC_read_dirty(void)
 {
     unsigned long ps, np;
     int nmaps;
@@ -3174,16 +3261,14 @@ void GC_read_dirty()
     char * bufp;
     ptr_t current_addr, limit;
     int i;
-int dummy;
 
     BZERO(GC_grungy_pages, (sizeof GC_grungy_pages));
     
     bufp = GC_proc_buf;
     if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
-#      ifdef PRINTSTATS
-            GC_printf1("/proc read failed: GC_proc_buf_size = %lu\n",
-                      GC_proc_buf_size);
-#      endif       
+       if (GC_print_stats)
+            GC_log_printf("/proc read failed: GC_proc_buf_size = %lu\n",
+                         (unsigned long)GC_proc_buf_size);
         {
             /* Retry with larger buffer. */
             word new_size = 2 * GC_proc_buf_size;
@@ -3198,10 +3283,6 @@ int dummy;
                 /* Punt:       */
                memset(GC_grungy_pages, 0xff, sizeof (page_hash_table));
                memset(GC_written_pages, 0xff, sizeof(page_hash_table));
-#              ifdef GC_SOLARIS_THREADS
-                   BZERO(GC_fresh_pages,
-                         MAX_FRESH_PAGES * sizeof (struct hblk *)); 
-#              endif
                return;
             }
         }
@@ -3228,15 +3309,6 @@ int dummy;
                        register word index = PHT_HASH(h);
                        
                        set_pht_entry_from_index(GC_grungy_pages, index);
-#                      ifdef GC_SOLARIS_THREADS
-                         {
-                           register int slot = FRESH_PAGE_SLOT(h);
-                           
-                           if (GC_fresh_pages[slot] == h) {
-                               GC_fresh_pages[slot] = 0;
-                           }
-                         }
-#                      endif
                        h++;
                    }
                }
@@ -3246,63 +3318,22 @@ int dummy;
        }
     /* Update GC_written_pages. */
         GC_or_pages(GC_written_pages, GC_grungy_pages);
-#   ifdef GC_SOLARIS_THREADS
-      /* Make sure that old stacks are considered completely clean     */
-      /* unless written again.                                         */
-       GC_old_stacks_are_fresh();
-#   endif
 }
 
 #undef READ
 
-GC_bool GC_page_was_dirty(h)
-struct hblk *h;
+GC_bool GC_page_was_dirty(struct hblk *h)
 {
     register word index = PHT_HASH(h);
-    register GC_bool result;
     
-    result = get_pht_entry_from_index(GC_grungy_pages, index);
-#   ifdef GC_SOLARIS_THREADS
-       if (result && PAGE_IS_FRESH(h)) result = FALSE;
-       /* This happens only if page was declared fresh since   */
-       /* the read_dirty call, e.g. because it's in an unused  */
-       /* thread stack.  It's OK to treat it as clean, in      */
-       /* that case.  And it's consistent with                 */
-       /* GC_page_was_ever_dirty.                              */
-#   endif
-    return(result);
+    return get_pht_entry_from_index(GC_grungy_pages, index);
 }
 
-GC_bool GC_page_was_ever_dirty(h)
-struct hblk *h;
+GC_bool GC_page_was_ever_dirty(struct hblk *h)
 {
     register word index = PHT_HASH(h);
-    register GC_bool result;
-    
-    result = get_pht_entry_from_index(GC_written_pages, index);
-#   ifdef GC_SOLARIS_THREADS
-       if (result && PAGE_IS_FRESH(h)) result = FALSE;
-#   endif
-    return(result);
-}
-
-/* Caller holds allocation lock.       */
-void GC_is_fresh(h, n)
-struct hblk *h;
-word n;
-{
-
-    register word index;
     
-#   ifdef GC_SOLARIS_THREADS
-      register word i;
-      
-      if (GC_fresh_pages != 0) {
-        for (i = 0; i < n; i++) {
-          ADD_FRESH_PAGE(h + i);
-        }
-      }
-#   endif
+    return get_pht_entry_from_index(GC_written_pages, index);
 }
 
 # endif /* PROC_VDB */
@@ -3319,7 +3350,7 @@ PCR_VD_DB  GC_grungy_bits[NPAGES];
 ptr_t GC_vd_base;      /* Address corresponding to GC_grungy_bits[0]   */
                        /* HBLKSIZE aligned.                            */
 
-void GC_dirty_init()
+void GC_dirty_init(void)
 {
     GC_dirty_maintained = TRUE;
     /* For the time being, we assume the heap generally grows up */
@@ -3333,7 +3364,7 @@ void GC_dirty_init()
     }
 }
 
-void GC_read_dirty()
+void GC_read_dirty(void)
 {
     /* lazily enable dirty bits on newly added heap sects */
     {
@@ -3353,8 +3384,7 @@ void GC_read_dirty()
     }
 }
 
-GC_bool GC_page_was_dirty(h)
-struct hblk *h;
+GC_bool GC_page_was_dirty(struct hblk *h)
 {
     if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) {
        return(TRUE);
@@ -3363,10 +3393,7 @@ struct hblk *h;
 }
 
 /*ARGSUSED*/
-void GC_remove_protection(h, nblocks, is_ptrfree)
-struct hblk *h;
-word nblocks;
-GC_bool is_ptrfree;
+void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree)
 {
     PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE);
     PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE);
@@ -3379,15 +3406,16 @@ GC_bool is_ptrfree;
    code:
       1. Apple's mach/xnu documentation
       2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
-         omnigroup's macosx-dev list. 
-         www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html
+         omnigroup's macosx-dev list.
+         www.omnigroup.com/mailman/archive/macosx-dev/2000-June/014178.html
       3. macosx-nat.c from Apple's GDB source code.
 */
-   
+
 /* The bug that caused all this trouble should now be fixed. This should
    eventually be removed if all goes well. */
-/* define BROKEN_EXCEPTION_HANDLING */
-    
+
+/* #define BROKEN_EXCEPTION_HANDLING */
+
 #include <mach/mach.h>
 #include <mach/mach_error.h>
 #include <mach/thread_status.h>
@@ -3395,21 +3423,29 @@ GC_bool is_ptrfree;
 #include <mach/task.h>
 #include <pthread.h>
 
+extern void GC_darwin_register_mach_handler_thread(mach_port_t);
+
 /* These are not defined in any header, although they are documented */
-extern boolean_t exc_server(mach_msg_header_t *,mach_msg_header_t *);
-extern kern_return_t exception_raise(
-    mach_port_t,mach_port_t,mach_port_t,
-    exception_type_t,exception_data_t,mach_msg_type_number_t);
-extern kern_return_t exception_raise_state(
-    mach_port_t,mach_port_t,mach_port_t,
-    exception_type_t,exception_data_t,mach_msg_type_number_t,
-    thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
-    thread_state_t,mach_msg_type_number_t*);
-extern kern_return_t exception_raise_state_identity(
-    mach_port_t,mach_port_t,mach_port_t,
-    exception_type_t,exception_data_t,mach_msg_type_number_t,
-    thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
-    thread_state_t,mach_msg_type_number_t*);
+extern boolean_t
+exc_server(mach_msg_header_t *, mach_msg_header_t *);
+
+extern kern_return_t
+exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t,
+               exception_data_t, mach_msg_type_number_t);
+
+extern kern_return_t
+exception_raise_state(mach_port_t, mach_port_t, mach_port_t, exception_type_t,
+                     exception_data_t, mach_msg_type_number_t,
+                     thread_state_flavor_t*, thread_state_t,
+                     mach_msg_type_number_t, thread_state_t,
+                     mach_msg_type_number_t*);
+
+extern kern_return_t
+exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t,
+                              exception_type_t, exception_data_t,
+                              mach_msg_type_number_t, thread_state_flavor_t*,
+                              thread_state_t, mach_msg_type_number_t,
+                              thread_state_t, mach_msg_type_number_t*);
 
 
 #define MAX_EXCEPTION_PORTS 16
@@ -3451,63 +3487,57 @@ typedef enum {
 GC_mprotect_state_t GC_mprotect_state;
 
 /* The following should ONLY be called when the world is stopped  */
-static void GC_mprotect_thread_notify(mach_msg_id_t id) {
-    struct {
-        GC_msg_t msg;
-        mach_msg_trailer_t trailer;
-    } buf;
-    mach_msg_return_t r;
-    /* remote, local */
-    buf.msg.head.msgh_bits = 
-        MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0);
-    buf.msg.head.msgh_size = sizeof(buf.msg);
-    buf.msg.head.msgh_remote_port = GC_ports.exception;
-    buf.msg.head.msgh_local_port = MACH_PORT_NULL;
-    buf.msg.head.msgh_id = id;
-            
-    r = mach_msg(
-        &buf.msg.head,
-        MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_LARGE,
-        sizeof(buf.msg),
-        sizeof(buf),
-        GC_ports.reply,
-        MACH_MSG_TIMEOUT_NONE,
-        MACH_PORT_NULL);
-    if(r != MACH_MSG_SUCCESS)
-       ABORT("mach_msg failed in GC_mprotect_thread_notify");
-    if(buf.msg.head.msgh_id != ID_ACK)
-        ABORT("invalid ack in GC_mprotect_thread_notify");
+static void GC_mprotect_thread_notify(mach_msg_id_t id)
+{
+
+  struct {
+    GC_msg_t msg;
+    mach_msg_trailer_t trailer;
+  } buf;
+
+  mach_msg_return_t r;
+  /* remote, local */
+  buf.msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
+  buf.msg.head.msgh_size = sizeof(buf.msg);
+  buf.msg.head.msgh_remote_port = GC_ports.exception;
+  buf.msg.head.msgh_local_port = MACH_PORT_NULL;
+  buf.msg.head.msgh_id = id;
+
+  r = mach_msg(&buf.msg.head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_LARGE,
+              sizeof(buf.msg), sizeof(buf), GC_ports.reply,
+              MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+  if(r != MACH_MSG_SUCCESS)
+    ABORT("mach_msg failed in GC_mprotect_thread_notify");
+  if(buf.msg.head.msgh_id != ID_ACK)
+    ABORT("invalid ack in GC_mprotect_thread_notify");
 }
 
 /* Should only be called by the mprotect thread */
-static void GC_mprotect_thread_reply() {
-    GC_msg_t msg;
-    mach_msg_return_t r;
-    /* remote, local */
-    msg.head.msgh_bits = 
-        MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0);
-    msg.head.msgh_size = sizeof(msg);
-    msg.head.msgh_remote_port = GC_ports.reply;
-    msg.head.msgh_local_port = MACH_PORT_NULL;
-    msg.head.msgh_id = ID_ACK;
-            
-    r = mach_msg(
-        &msg.head,
-        MACH_SEND_MSG,
-        sizeof(msg),
-        0,
-        MACH_PORT_NULL,
-        MACH_MSG_TIMEOUT_NONE,
-        MACH_PORT_NULL);
-    if(r != MACH_MSG_SUCCESS)
-       ABORT("mach_msg failed in GC_mprotect_thread_reply");
+static void GC_mprotect_thread_reply(void)
+{
+
+  GC_msg_t msg;
+  mach_msg_return_t r;
+  /* remote, local */
+  msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
+  msg.head.msgh_size = sizeof(msg);
+  msg.head.msgh_remote_port = GC_ports.reply;
+  msg.head.msgh_local_port = MACH_PORT_NULL;
+  msg.head.msgh_id = ID_ACK;
+
+  r = mach_msg(&msg.head, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL,
+              MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+  if(r != MACH_MSG_SUCCESS)
+    ABORT("mach_msg failed in GC_mprotect_thread_reply");
 }
 
-void GC_mprotect_stop() {
-    GC_mprotect_thread_notify(ID_STOP);
+void GC_mprotect_stop(void)
+{
+  GC_mprotect_thread_notify(ID_STOP);
 }
-void GC_mprotect_resume() {
-    GC_mprotect_thread_notify(ID_RESUME);
+void GC_mprotect_resume(void)
+{
+  GC_mprotect_thread_notify(ID_RESUME);
 }
 
 #else /* !THREADS */
@@ -3515,429 +3545,417 @@ void GC_mprotect_resume() {
 #define GC_mprotect_state GC_MP_NORMAL
 #endif
 
-static void *GC_mprotect_thread(void *arg) {
-    mach_msg_return_t r;
-    /* These two structures contain some private kernel data. We don't need to
-       access any of it so we don't bother defining a proper struct. The
-       correct definitions are in the xnu source code. */
-    struct {
-        mach_msg_header_t head;
-        char data[256];
-    } reply;
-    struct {
-        mach_msg_header_t head;
-        mach_msg_body_t msgh_body;
-        char data[1024];
-    } msg;
-
-    mach_msg_id_t id;
-
-    GC_darwin_register_mach_handler_thread(mach_thread_self());
-    
-    for(;;) {
-        r = mach_msg(
-            &msg.head,
-            MACH_RCV_MSG|MACH_RCV_LARGE|
-                (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0),
-            0,
-            sizeof(msg),
-            GC_ports.exception,
-            GC_mprotect_state == GC_MP_DISCARDING ? 0 : MACH_MSG_TIMEOUT_NONE,
-            MACH_PORT_NULL);
-        
-        id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1;
-        
-#if defined(THREADS)
-        if(GC_mprotect_state == GC_MP_DISCARDING) {
-            if(r == MACH_RCV_TIMED_OUT) {
-                GC_mprotect_state = GC_MP_STOPPED;
-                GC_mprotect_thread_reply();
-                continue;
-            }
-            if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME))
-                ABORT("out of order mprotect thread request");
-        }
-#endif
-        
-        if(r != MACH_MSG_SUCCESS) {
-            GC_err_printf2("mach_msg failed with %d %s\n", 
-                (int)r,mach_error_string(r));
-            ABORT("mach_msg failed");
-        }
-        
-        switch(id) {
-#if defined(THREADS)
-            case ID_STOP:
-                if(GC_mprotect_state != GC_MP_NORMAL)
-                    ABORT("Called mprotect_stop when state wasn't normal");
-                GC_mprotect_state = GC_MP_DISCARDING;
-                break;
-            case ID_RESUME:
-                if(GC_mprotect_state != GC_MP_STOPPED)
-                    ABORT("Called mprotect_resume when state wasn't stopped");
-                GC_mprotect_state = GC_MP_NORMAL;
-                GC_mprotect_thread_reply();
-                break;
-#endif /* THREADS */
-            default:
-                   /* Handle the message (calls catch_exception_raise) */
-               if(!exc_server(&msg.head,&reply.head))
-                    ABORT("exc_server failed");
-                /* Send the reply */
-                r = mach_msg(
-                    &reply.head,
-                    MACH_SEND_MSG,
-                    reply.head.msgh_size,
-                    0,
-                    MACH_PORT_NULL,
-                    MACH_MSG_TIMEOUT_NONE,
-                    MACH_PORT_NULL);
-               if(r != MACH_MSG_SUCCESS) {
-                       /* This will fail if the thread dies, but the thread shouldn't
-                          die... */
-                       #ifdef BROKEN_EXCEPTION_HANDLING
-                       GC_err_printf2(
-                        "mach_msg failed with %d %s while sending exc reply\n",
-                        (int)r,mach_error_string(r));
-               #else
-                       ABORT("mach_msg failed while sending exception reply");
-               #endif
-               }
-        } /* switch */
-    } /* for(;;) */
+static void *GC_mprotect_thread(void *arg)
+{
+  mach_msg_return_t r;
+  /* These two structures contain some private kernel data. We don't need to
+     access any of it so we don't bother defining a proper struct. The
+     correct definitions are in the xnu source code. */
+  struct {
+    mach_msg_header_t head;
+    char data[256];
+  } reply;
+  struct {
+    mach_msg_header_t head;
+    mach_msg_body_t msgh_body;
+    char data[1024];
+  } msg;
+
+  mach_msg_id_t id;
+
+  GC_darwin_register_mach_handler_thread(mach_thread_self());
+
+  for(;;) {
+    r = mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE |
+                (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0),
+                0, sizeof(msg), GC_ports.exception,
+                GC_mprotect_state == GC_MP_DISCARDING ? 0
+                : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+    id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1;
+
+#   if defined(THREADS)
+      if(GC_mprotect_state == GC_MP_DISCARDING) {
+       if(r == MACH_RCV_TIMED_OUT) {
+         GC_mprotect_state = GC_MP_STOPPED;
+         GC_mprotect_thread_reply();
+         continue;
+       }
+       if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME))
+         ABORT("out of order mprotect thread request");
+      }
+#   endif /* THREADS */
+
+    if(r != MACH_MSG_SUCCESS) {
+      GC_err_printf("mach_msg failed with %d %s\n", (int)r,
+                   mach_error_string(r));
+      ABORT("mach_msg failed");
+    }
+
+    switch(id) {
+#     if defined(THREADS)
+        case ID_STOP:
+         if(GC_mprotect_state != GC_MP_NORMAL)
+           ABORT("Called mprotect_stop when state wasn't normal");
+         GC_mprotect_state = GC_MP_DISCARDING;
+         break;
+        case ID_RESUME:
+         if(GC_mprotect_state != GC_MP_STOPPED)
+           ABORT("Called mprotect_resume when state wasn't stopped");
+         GC_mprotect_state = GC_MP_NORMAL;
+         GC_mprotect_thread_reply();
+         break;
+#     endif /* THREADS */
+        default:
+         /* Handle the message (calls catch_exception_raise) */
+         if(!exc_server(&msg.head, &reply.head))
+           ABORT("exc_server failed");
+         /* Send the reply */
+         r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0,
+                      MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+                      MACH_PORT_NULL);
+         if(r != MACH_MSG_SUCCESS) {
+           /* This will fail if the thread dies, but the thread */
+           /* shouldn't die... */
+#           ifdef BROKEN_EXCEPTION_HANDLING
+             GC_err_printf("mach_msg failed with %d %s while sending "
+                           "exc reply\n", (int)r,mach_error_string(r));
+#           else
+             ABORT("mach_msg failed while sending exception reply");
+#           endif
+         }
+    } /* switch */
+  } /* for(;;) */
     /* NOT REACHED */
-    return NULL;
+  return NULL;
 }
 
 /* All this SIGBUS code shouldn't be necessary. All protection faults should
-   be going throught the mach exception handler. However, it seems a SIGBUS is
+   be going through the mach exception handler. However, it seems a SIGBUS is
    occasionally sent for some unknown reason. Even more odd, it seems to be
    meaningless and safe to ignore. */
 #ifdef BROKEN_EXCEPTION_HANDLING
 
-typedef void (* SIG_PF)();
-static SIG_PF GC_old_bus_handler;
-
 /* Updates to this aren't atomic, but the SIGBUSs seem pretty rare.
    Even if this doesn't get updated property, it isn't really a problem */
 static int GC_sigbus_count;
 
-static void GC_darwin_sigbus(int num,siginfo_t *sip,void *context) {
-    if(num != SIGBUS) ABORT("Got a non-sigbus signal in the sigbus handler");
-    
-    /* Ugh... some seem safe to ignore, but too many in a row probably means
-       trouble. GC_sigbus_count is reset for each mach exception that is
-       handled */
-    if(GC_sigbus_count >= 8) {
-        ABORT("Got more than 8 SIGBUSs in a row!");
-    } else {
-        GC_sigbus_count++;
-        GC_err_printf0("GC: WARNING: Ignoring SIGBUS.\n");
-    }
+static void GC_darwin_sigbus(int num, siginfo_t *sip, void *context)
+{
+  if(num != SIGBUS)
+    ABORT("Got a non-sigbus signal in the sigbus handler");
+
+  /* Ugh... some seem safe to ignore, but too many in a row probably means
+     trouble. GC_sigbus_count is reset for each mach exception that is
+     handled */
+  if(GC_sigbus_count >= 8) {
+    ABORT("Got more than 8 SIGBUSs in a row!");
+  } else {
+    GC_sigbus_count++;
+    WARN("Ignoring SIGBUS.\n", 0);
+  }
 }
 #endif /* BROKEN_EXCEPTION_HANDLING */
 
-void GC_dirty_init() {
-    kern_return_t r;
-    mach_port_t me;
-    pthread_t thread;
-    pthread_attr_t attr;
-    exception_mask_t mask;
-    
-#   ifdef PRINTSTATS
-        GC_printf0("Inititalizing mach/darwin mprotect virtual dirty bit "
-            "implementation\n");
-#   endif  
-#      ifdef BROKEN_EXCEPTION_HANDLING
-        GC_err_printf0("GC: WARNING: Enabling workarounds for various darwin "
-            "exception handling bugs.\n");
-#      endif
-    GC_dirty_maintained = TRUE;
-    if (GC_page_size % HBLKSIZE != 0) {
-        GC_err_printf0("Page size not multiple of HBLKSIZE\n");
-        ABORT("Page size not multiple of HBLKSIZE");
-    }
-    
-    GC_task_self = me = mach_task_self();
-    
-    r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.exception);
-    if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (exception port)");
-    
-    r = mach_port_insert_right(me,GC_ports.exception,GC_ports.exception,
-       MACH_MSG_TYPE_MAKE_SEND);
-    if(r != KERN_SUCCESS)
-       ABORT("mach_port_insert_right failed (exception port)");
-
-    #if defined(THREADS)
-        r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.reply);
-        if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (reply port)");
-    #endif
-
-    /* The exceptions we want to catch */  
-    mask = EXC_MASK_BAD_ACCESS;
-
-    r = task_get_exception_ports(
-        me,
-        mask,
-        GC_old_exc_ports.masks,
-        &GC_old_exc_ports.count,
-        GC_old_exc_ports.ports,
-        GC_old_exc_ports.behaviors,
-        GC_old_exc_ports.flavors
-    );
-    if(r != KERN_SUCCESS) ABORT("task_get_exception_ports failed");
-        
-    r = task_set_exception_ports(
-        me,
-        mask,
-        GC_ports.exception,
-        EXCEPTION_DEFAULT,
-        MACHINE_THREAD_STATE
-    );
-    if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed");
-
-    if(pthread_attr_init(&attr) != 0) ABORT("pthread_attr_init failed");
-    if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) != 0) 
-        ABORT("pthread_attr_setdetachedstate failed");
-
-#      undef pthread_create
-    /* This will call the real pthread function, not our wrapper */
-    if(pthread_create(&thread,&attr,GC_mprotect_thread,NULL) != 0)
-        ABORT("pthread_create failed");
-    pthread_attr_destroy(&attr);
-    
-    /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */
-    #ifdef BROKEN_EXCEPTION_HANDLING 
+void GC_dirty_init(void)
+{
+  kern_return_t r;
+  mach_port_t me;
+  pthread_t thread;
+  pthread_attr_t attr;
+  exception_mask_t mask;
+
+  if (GC_print_stats == VERBOSE)
+    GC_log_printf("Initializing mach/darwin mprotect virtual dirty bit "
+                 "implementation\n");
+# ifdef BROKEN_EXCEPTION_HANDLING
+    WARN("Enabling workarounds for various darwin "
+        "exception handling bugs.\n", 0);
+# endif
+  GC_dirty_maintained = TRUE;
+  if (GC_page_size % HBLKSIZE != 0) {
+    GC_err_printf("Page size not multiple of HBLKSIZE\n");
+    ABORT("Page size not multiple of HBLKSIZE");
+  }
+
+  GC_task_self = me = mach_task_self();
+
+  r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.exception);
+  if(r != KERN_SUCCESS)
+    ABORT("mach_port_allocate failed (exception port)");
+
+  r = mach_port_insert_right(me, GC_ports.exception, GC_ports.exception,
+                            MACH_MSG_TYPE_MAKE_SEND);
+  if(r != KERN_SUCCESS)
+    ABORT("mach_port_insert_right failed (exception port)");
+
+#  if defined(THREADS)
+     r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.reply);
+     if(r != KERN_SUCCESS)
+       ABORT("mach_port_allocate failed (reply port)");
+#  endif
+
+  /* The exceptions we want to catch */
+  mask = EXC_MASK_BAD_ACCESS;
+
+  r = task_get_exception_ports(me, mask, GC_old_exc_ports.masks,
+                              &GC_old_exc_ports.count, GC_old_exc_ports.ports,
+                              GC_old_exc_ports.behaviors,
+                              GC_old_exc_ports.flavors);
+  if(r != KERN_SUCCESS)
+    ABORT("task_get_exception_ports failed");
+
+  r = task_set_exception_ports(me, mask, GC_ports.exception, EXCEPTION_DEFAULT,
+                              GC_MACH_THREAD_STATE);
+  if(r != KERN_SUCCESS)
+    ABORT("task_set_exception_ports failed");
+  if(pthread_attr_init(&attr) != 0)
+    ABORT("pthread_attr_init failed");
+  if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
+    ABORT("pthread_attr_setdetachedstate failed");
+
+# undef pthread_create
+  /* This will call the real pthread function, not our wrapper */
+  if(pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0)
+    ABORT("pthread_create failed");
+  pthread_attr_destroy(&attr);
+
+  /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */
+# ifdef BROKEN_EXCEPTION_HANDLING
     {
-        struct sigaction sa, oldsa;
-        sa.sa_handler = (SIG_PF)GC_darwin_sigbus;
-        sigemptyset(&sa.sa_mask);
-        sa.sa_flags = SA_RESTART|SA_SIGINFO;
-        if(sigaction(SIGBUS,&sa,&oldsa) < 0) ABORT("sigaction");
-        GC_old_bus_handler = (SIG_PF)oldsa.sa_handler;
-        if (GC_old_bus_handler != SIG_DFL) {
-#              ifdef PRINTSTATS
-                GC_err_printf0("Replaced other SIGBUS handler\n");
-#              endif
-        }
+      struct sigaction sa, oldsa;
+      sa.sa_handler = (SIG_HNDLR_PTR)GC_darwin_sigbus;
+      sigemptyset(&sa.sa_mask);
+      sa.sa_flags = SA_RESTART|SA_SIGINFO;
+      if(sigaction(SIGBUS, &sa, &oldsa) < 0)
+       ABORT("sigaction");
+      if ((SIG_HNDLR_PTR)oldsa.sa_handler != SIG_DFL) {
+       if (GC_print_stats == VERBOSE)
+         GC_err_printf("Replaced other SIGBUS handler\n");
+      }
     }
-    #endif /* BROKEN_EXCEPTION_HANDLING  */
+#  endif /* BROKEN_EXCEPTION_HANDLING  */
 }
+
 /* The source code for Apple's GDB was used as a reference for the exception
-   forwarding code. This code is similar to be GDB code only because there is 
+   forwarding code. This code is similar to be GDB code only because there is
    only one way to do it. */
-static kern_return_t GC_forward_exception(
-        mach_port_t thread,
-        mach_port_t task,
-        exception_type_t exception,
-        exception_data_t data,
-        mach_msg_type_number_t data_count
-) {
-    int i;
-    kern_return_t r;
-    mach_port_t port;
-    exception_behavior_t behavior;
-    thread_state_flavor_t flavor;
-    
-    thread_state_t thread_state;
-    mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
-        
-    for(i=0;i<GC_old_exc_ports.count;i++)
-        if(GC_old_exc_ports.masks[i] & (1 << exception))
-            break;
-    if(i==GC_old_exc_ports.count) ABORT("No handler for exception!");
-    
-    port = GC_old_exc_ports.ports[i];
-    behavior = GC_old_exc_ports.behaviors[i];
-    flavor = GC_old_exc_ports.flavors[i];
-
-    if(behavior != EXCEPTION_DEFAULT) {
-        r = thread_get_state(thread,flavor,thread_state,&thread_state_count);
-        if(r != KERN_SUCCESS)
-            ABORT("thread_get_state failed in forward_exception");
-    }
-    
-    switch(behavior) {
-        case EXCEPTION_DEFAULT:
-            r = exception_raise(port,thread,task,exception,data,data_count);
-            break;
-        case EXCEPTION_STATE:
-            r = exception_raise_state(port,thread,task,exception,data,
-                data_count,&flavor,thread_state,thread_state_count,
-                thread_state,&thread_state_count);
-            break;
-        case EXCEPTION_STATE_IDENTITY:
-            r = exception_raise_state_identity(port,thread,task,exception,data,
-                data_count,&flavor,thread_state,thread_state_count,
-                thread_state,&thread_state_count);
-            break;
-        default:
-            r = KERN_FAILURE; /* make gcc happy */
-            ABORT("forward_exception: unknown behavior");
-            break;
-    }
-    
-    if(behavior != EXCEPTION_DEFAULT) {
-        r = thread_set_state(thread,flavor,thread_state,thread_state_count);
-        if(r != KERN_SUCCESS)
-            ABORT("thread_set_state failed in forward_exception");
+static kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task,
+                                         exception_type_t exception,
+                                         exception_data_t data,
+                                         mach_msg_type_number_t data_count)
+{
+  unsigned int i;
+  kern_return_t r;
+  mach_port_t port;
+  exception_behavior_t behavior;
+  thread_state_flavor_t flavor;
+
+  thread_state_t thread_state = NULL;
+  mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
+
+  for(i=0; i < GC_old_exc_ports.count; i++)
+    if(GC_old_exc_ports.masks[i] & (1 << exception))
+      break;
+  if(i==GC_old_exc_ports.count)
+    ABORT("No handler for exception!");
+
+  port = GC_old_exc_ports.ports[i];
+  behavior = GC_old_exc_ports.behaviors[i];
+  flavor = GC_old_exc_ports.flavors[i];
+
+  if(behavior != EXCEPTION_DEFAULT) {
+    r = thread_get_state(thread, flavor, thread_state, &thread_state_count);
+    if(r != KERN_SUCCESS)
+      ABORT("thread_get_state failed in forward_exception");
     }
-    
-    return r;
+
+  switch(behavior) {
+    case EXCEPTION_DEFAULT:
+      r = exception_raise(port, thread, task, exception, data, data_count);
+      break;
+    case EXCEPTION_STATE:
+      r = exception_raise_state(port, thread, task, exception, data, data_count,
+                               &flavor, thread_state, thread_state_count,
+                               thread_state, &thread_state_count);
+      break;
+    case EXCEPTION_STATE_IDENTITY:
+      r = exception_raise_state_identity(port, thread, task, exception, data,
+                                        data_count, &flavor, thread_state,
+                                        thread_state_count, thread_state,
+                                        &thread_state_count);
+      break;
+    default:
+      r = KERN_FAILURE; /* make gcc happy */
+      ABORT("forward_exception: unknown behavior");
+      break;
+  }
+
+  if(behavior != EXCEPTION_DEFAULT) {
+    r = thread_set_state(thread, flavor, thread_state, thread_state_count);
+    if(r != KERN_SUCCESS)
+      ABORT("thread_set_state failed in forward_exception");
+  }
+
+  return r;
 }
 
-#define FWD() GC_forward_exception(thread,task,exception,code,code_count)
+#define FWD() GC_forward_exception(thread, task, exception, code, code_count)
 
 /* This violates the namespace rules but there isn't anything that can be done
    about it. The exception handling stuff is hard coded to call this */
 kern_return_t
-catch_exception_raise(
-   mach_port_t exception_port,mach_port_t thread,mach_port_t task,
-   exception_type_t exception,exception_data_t code,
-   mach_msg_type_number_t code_count
-) {
-    kern_return_t r;
-    char *addr;
-    struct hblk *h;
-    int i;
-#   if defined(POWERPC)
-#     if CPP_WORDSZ == 32
-        thread_state_flavor_t flavor = PPC_EXCEPTION_STATE;
-        mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE_COUNT;
-        ppc_exception_state_t exc_state;
-#     else
-        thread_state_flavor_t flavor = PPC_EXCEPTION_STATE64;
-        mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE64_COUNT;
-        ppc_exception_state64_t exc_state;
-#     endif
-#   elif defined(I386)
-        thread_state_flavor_t flavor = i386_EXCEPTION_STATE;
-        mach_msg_type_number_t exc_state_count = i386_EXCEPTION_STATE_COUNT;
-        i386_exception_state_t exc_state;
+catch_exception_raise(mach_port_t exception_port, mach_port_t thread,
+                     mach_port_t task, exception_type_t exception,
+                     exception_data_t code, mach_msg_type_number_t code_count)
+{
+  kern_return_t r;
+  char *addr;
+  struct hblk *h;
+  unsigned int i;
+# if defined(POWERPC)
+#   if CPP_WORDSZ == 32
+      thread_state_flavor_t flavor = PPC_EXCEPTION_STATE;
+      mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE_COUNT;
+      ppc_exception_state_t exc_state;
+#   else
+      thread_state_flavor_t flavor = PPC_EXCEPTION_STATE64;
+      mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE64_COUNT;
+      ppc_exception_state64_t exc_state;
+#   endif
+# elif defined(I386) || defined(X86_64)
+#   if CPP_WORDSZ == 32
+      thread_state_flavor_t flavor = x86_EXCEPTION_STATE32;
+      mach_msg_type_number_t exc_state_count = x86_EXCEPTION_STATE32_COUNT;
+      x86_exception_state32_t exc_state;
 #   else
-#      error FIXME for non-ppc/x86 darwin
+      thread_state_flavor_t flavor = x86_EXCEPTION_STATE64;
+      mach_msg_type_number_t exc_state_count = x86_EXCEPTION_STATE64_COUNT;
+      x86_exception_state64_t exc_state;
 #   endif
+# else
+#   error FIXME for non-ppc/x86 darwin
+# endif
 
-    
-    if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) {
-        #ifdef DEBUG_EXCEPTION_HANDLING
-        /* We aren't interested, pass it on to the old handler */
-        GC_printf3("Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
-            exception,
-            code_count > 0 ? code[0] : -1,
-            code_count > 1 ? code[1] : -1); 
-        #endif
-        return FWD();
-    }
 
-    r = thread_get_state(thread,flavor,
-        (natural_t*)&exc_state,&exc_state_count);
-    if(r != KERN_SUCCESS) {
-        /* The thread is supposed to be suspended while the exception handler
-           is called. This shouldn't fail. */
-        #ifdef BROKEN_EXCEPTION_HANDLING
-            GC_err_printf0("thread_get_state failed in "
-                "catch_exception_raise\n");
-            return KERN_SUCCESS;
-        #else
-            ABORT("thread_get_state failed in catch_exception_raise");
-        #endif
-    }
-    
+  if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) {
+#   ifdef DEBUG_EXCEPTION_HANDLING
+      /* We aren't interested, pass it on to the old handler */
+      GC_printf("Exception: 0x%x Code: 0x%x 0x%x in catch....\n", exception,
+               code_count > 0 ? code[0] : -1, code_count > 1 ? code[1] : -1);
+#   endif
+    return FWD();
+  }
+
+  r = thread_get_state(thread, flavor, (natural_t*)&exc_state,
+                      &exc_state_count);
+  if(r != KERN_SUCCESS) {
+    /* The thread is supposed to be suspended while the exception handler
+       is called. This shouldn't fail. */
+#   ifdef BROKEN_EXCEPTION_HANDLING
+      GC_err_printf("thread_get_state failed in catch_exception_raise\n");
+      return KERN_SUCCESS;
+#   else
+      ABORT("thread_get_state failed in catch_exception_raise");
+#   endif
+  }
+
     /* This is the address that caused the fault */
-#if defined(POWERPC)
-    addr = (char*) exc_state.dar;
-#elif defined (I386)
-    addr = (char*) exc_state.faultvaddr;
-#else
+# if defined(POWERPC)
+    addr = (char*) exc_state. THREAD_FLD(dar);
+# elif defined (I386) || defined (X86_64)
+    addr = (char*) exc_state. THREAD_FLD(faultvaddr);
+# else
 #   error FIXME for non POWERPC/I386
-#endif
-        
+# endif
+
     if((HDR(addr)) == 0) {
-        /* Ugh... just like the SIGBUS problem above, it seems we get a bogus 
-           KERN_PROTECTION_FAILURE every once and a while. We wait till we get
-           a bunch in a row before doing anything about it. If a "real" fault 
-           ever occurres it'll just keep faulting over and over and we'll hit
-           the limit pretty quickly. */
-        #ifdef BROKEN_EXCEPTION_HANDLING
-            static char *last_fault;
-            static int last_fault_count;
-            
-            if(addr != last_fault) {
-                last_fault = addr;
-                last_fault_count = 0;
-            }
-            if(++last_fault_count < 32) {
-                if(last_fault_count == 1)
-                    GC_err_printf1(
-                        "GC: WARNING: Ignoring KERN_PROTECTION_FAILURE at %p\n",
-                        addr);
-                return KERN_SUCCESS;
-            }
-            
-            GC_err_printf1("Unexpected KERN_PROTECTION_FAILURE at %p\n",addr);
-            /* Can't pass it along to the signal handler because that is
-               ignoring SIGBUS signals. We also shouldn't call ABORT here as
-               signals don't always work too well from the exception handler. */
-            GC_err_printf0("Aborting\n");
-            exit(EXIT_FAILURE);
-        #else /* BROKEN_EXCEPTION_HANDLING */
-            /* Pass it along to the next exception handler 
-               (which should call SIGBUS/SIGSEGV) */
-            return FWD();
-        #endif /* !BROKEN_EXCEPTION_HANDLING */
+      /* Ugh... just like the SIGBUS problem above, it seems we get a bogus
+        KERN_PROTECTION_FAILURE every once and a while. We wait till we get
+        a bunch in a row before doing anything about it. If a "real" fault
+        ever occurs it'll just keep faulting over and over and we'll hit
+        the limit pretty quickly. */
+#     ifdef BROKEN_EXCEPTION_HANDLING
+        static char *last_fault;
+       static int last_fault_count;
+
+       if(addr != last_fault) {
+         last_fault = addr;
+         last_fault_count = 0;
+       }
+       if(++last_fault_count < 32) {
+         if(last_fault_count == 1)
+           WARN("Ignoring KERN_PROTECTION_FAILURE at %p\n", addr);
+         return KERN_SUCCESS;
+       }
+
+       GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p\n",addr);
+       /* Can't pass it along to the signal handler because that is
+          ignoring SIGBUS signals. We also shouldn't call ABORT here as
+          signals don't always work too well from the exception handler. */
+       GC_err_printf("Aborting\n");
+       exit(EXIT_FAILURE);
+#     else /* BROKEN_EXCEPTION_HANDLING */
+       /* Pass it along to the next exception handler
+          (which should call SIGBUS/SIGSEGV) */
+       return FWD();
+#     endif /* !BROKEN_EXCEPTION_HANDLING */
     }
 
-    #ifdef BROKEN_EXCEPTION_HANDLING
-        /* Reset the number of consecutive SIGBUSs */
-        GC_sigbus_count = 0;
-    #endif
-    
+#   ifdef BROKEN_EXCEPTION_HANDLING
+      /* Reset the number of consecutive SIGBUSs */
+      GC_sigbus_count = 0;
+#   endif
+
     if(GC_mprotect_state == GC_MP_NORMAL) { /* common case */
-        h = (struct hblk*)((word)addr & ~(GC_page_size-1));
-        UNPROTECT(h, GC_page_size);    
-        for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
-            register int index = PHT_HASH(h+i);
-            async_set_pht_entry_from_index(GC_dirty_pages, index);
-        }
+      h = (struct hblk*)((word)addr & ~(GC_page_size-1));
+      UNPROTECT(h, GC_page_size);
+      for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
+       register int index = PHT_HASH(h+i);
+       async_set_pht_entry_from_index(GC_dirty_pages, index);
+      }
     } else if(GC_mprotect_state == GC_MP_DISCARDING) {
-        /* Lie to the thread for now. No sense UNPROTECT()ing the memory
-           when we're just going to PROTECT() it again later. The thread
-           will just fault again once it resumes */
+      /* Lie to the thread for now. No sense UNPROTECT()ing the memory
+        when we're just going to PROTECT() it again later. The thread
+        will just fault again once it resumes */
     } else {
-        /* Shouldn't happen, i don't think */
-        GC_printf0("KERN_PROTECTION_FAILURE while world is stopped\n");
-        return FWD();
+      /* Shouldn't happen, i don't think */
+      GC_printf("KERN_PROTECTION_FAILURE while world is stopped\n");
+      return FWD();
     }
     return KERN_SUCCESS;
 }
 #undef FWD
 
 /* These should never be called, but just in case...  */
-kern_return_t catch_exception_raise_state(mach_port_name_t exception_port,
-    int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
-    int flavor, thread_state_t old_state, int old_stateCnt,
-    thread_state_t new_state, int new_stateCnt)
+kern_return_t
+catch_exception_raise_state(mach_port_name_t exception_port, int exception,
+                           exception_data_t code,
+                           mach_msg_type_number_t codeCnt, int flavor,
+                           thread_state_t old_state, int old_stateCnt,
+                           thread_state_t new_state, int new_stateCnt)
 {
-    ABORT("catch_exception_raise_state");
-    return(KERN_INVALID_ARGUMENT);
+  ABORT("catch_exception_raise_state");
+  return(KERN_INVALID_ARGUMENT);
 }
-kern_return_t catch_exception_raise_state_identity(
-    mach_port_name_t exception_port, mach_port_t thread, mach_port_t task,
-    int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
-    int flavor, thread_state_t old_state, int old_stateCnt, 
-    thread_state_t new_state, int new_stateCnt)
+
+kern_return_t
+catch_exception_raise_state_identity(mach_port_name_t exception_port,
+                                    mach_port_t thread, mach_port_t task,
+                                    int exception, exception_data_t code,
+                                    mach_msg_type_number_t codeCnt, int flavor,
+                                    thread_state_t old_state, int old_stateCnt,
+                                    thread_state_t new_state, int new_stateCnt)
 {
-    ABORT("catch_exception_raise_state_identity");
-    return(KERN_INVALID_ARGUMENT);
+  ABORT("catch_exception_raise_state_identity");
+  return(KERN_INVALID_ARGUMENT);
 }
 
 
 #endif /* DARWIN && MPROTECT_VDB */
 
 # ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS
-  int GC_incremental_protection_needs()
+  GC_API int GC_CALL GC_incremental_protection_needs(void)
   {
     return GC_PROTECTS_NONE;
   }
@@ -3976,27 +3994,17 @@ kern_return_t catch_exception_raise_state_identity(
        long    fr_argd[6];
        long    fr_argx[0];
      };
+#  elif defined (DRSNX)
+#    include <sys/sparc/frame.h>
+#  elif defined(OPENBSD)
+#    include <frame.h>
+#  elif defined(FREEBSD) || defined(NETBSD)
+#    include <machine/frame.h>
 #  else
-#    if defined(SUNOS4)
-#      include <machine/frame.h>
-#    else
-#      if defined (DRSNX)
-#       include <sys/sparc/frame.h>
-#      else
-#       if defined(OPENBSD)
-#         include <frame.h>
-#       else
-#         if defined(FREEBSD) || defined(NETBSD)
-#           include <machine/frame.h>
-#         else
-#           include <sys/frame.h>
-#         endif
-#       endif
-#      endif
-#    endif
+#    include <sys/frame.h>
 #  endif
 #  if NARGS > 6
-       --> We only know how to to get the first 6 arguments
+#    error We only know how to to get the first 6 arguments
 #  endif
 #endif /* SPARC */
 
@@ -4011,7 +4019,11 @@ kern_return_t catch_exception_raise_state_identity(
 #endif /* NEED_CALLINFO */
 
 #if defined(GC_HAVE_BUILTIN_BACKTRACE)
-# include <execinfo.h>
+# ifdef _MSC_VER
+#  include "private/msvc_dbg.h"
+# else
+#  include <execinfo.h>
+# endif
 #endif
 
 #ifdef SAVE_CALL_CHAIN
@@ -4029,8 +4041,7 @@ kern_return_t catch_exception_raise_state_identity(
   GC_in_save_callers = FALSE;
 #endif
 
-void GC_save_callers (info) 
-struct callinfo info[NFRAMES];
+void GC_save_callers (struct callinfo info[NFRAMES]) 
 {
   void * tmp_info[NFRAMES + 1];
   int npcs, i;
@@ -4046,7 +4057,7 @@ struct callinfo info[NFRAMES];
     }
     GC_in_save_callers = TRUE;
 # endif
-  GC_ASSERT(sizeof(struct callinfo) == sizeof(void *));
+  GC_STATIC_ASSERT(sizeof(struct callinfo) == sizeof(void *));
   npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES);
   BCOPY(tmp_info+IGNORE_FRAMES, info, (npcs - IGNORE_FRAMES) * sizeof(void *));
   for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0;
@@ -4071,8 +4082,7 @@ struct callinfo info[NFRAMES];
 #   define BIAS 0
 #endif
 
-void GC_save_callers (info) 
-struct callinfo info[NFRAMES];
+void GC_save_callers (struct callinfo info[NFRAMES]) 
 {
   struct frame *frame;
   struct frame *fp;
@@ -4108,8 +4118,7 @@ struct callinfo info[NFRAMES];
 #ifdef NEED_CALLINFO
 
 /* Print info to stderr.  We do NOT hold the allocation lock */
-void GC_print_callers (info)
-struct callinfo info[NFRAMES];
+void GC_print_callers (struct callinfo info[NFRAMES])
 {
     register int i;
     static int reentry_count = 0;
@@ -4122,9 +4131,9 @@ struct callinfo info[NFRAMES];
     UNLOCK();
     
 #   if NFRAMES == 1
-      GC_err_printf0("\tCaller at allocation:\n");
+      GC_err_printf("\tCaller at allocation:\n");
 #   else
-      GC_err_printf0("\tCall chain at allocation:\n");
+      GC_err_printf("\tCall chain at allocation:\n");
 #   endif
     for (i = 0; i < NFRAMES && !stop ; i++) {
        if (info[i].ci_pc == 0) break;
@@ -4132,19 +4141,19 @@ struct callinfo info[NFRAMES];
        {
          int j;
 
-         GC_err_printf0("\t\targs: ");
+         GC_err_printf("\t\targs: ");
          for (j = 0; j < NARGS; j++) {
-           if (j != 0) GC_err_printf0(", ");
-           GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]),
+           if (j != 0) GC_err_printf(", ");
+           GC_err_printf("%d (0x%X)", ~(info[i].ci_arg[j]),
                                        ~(info[i].ci_arg[j]));
          }
-         GC_err_printf0("\n");
+         GC_err_printf("\n");
        }
 #      endif
         if (reentry_count > 1) {
            /* We were called during an allocation during       */
            /* a previous GC_print_callers call; punt.          */
-           GC_err_printf1("\t\t##PC##= 0x%lx\n", info[i].ci_pc);
+           GC_err_printf("\t\t##PC##= 0x%lx\n", info[i].ci_pc);
            continue;
        }
        {
@@ -4219,8 +4228,8 @@ struct callinfo info[NFRAMES];
                if (result_buf[result_len - 1] == '\n') --result_len;
                result_buf[result_len] = 0;
                if (result_buf[0] == '?'
-                   || result_buf[result_len-2] == ':' 
-                      && result_buf[result_len-1] == '0') {
+                   || (result_buf[result_len-2] == ':' 
+                       && result_buf[result_len-1] == '0')) {
                    pclose(pipe);
                    goto out;
                }
@@ -4244,7 +4253,7 @@ struct callinfo info[NFRAMES];
                out:;
            }
 #        endif /* LINUX */
-         GC_err_printf1("\t\t%s\n", name);
+         GC_err_printf("\t\t%s\n", name);
 #        if defined(GC_HAVE_BUILTIN_BACKTRACE) \
             && !defined(GC_BACKTRACE_SYMBOLS_BROKEN)
            free(sym_name);  /* May call GC_free; that's OK */
@@ -4271,11 +4280,11 @@ static word dump_maps(char *maps)
     return 1;
 }
 
-void GC_print_address_map()
+void GC_print_address_map(void)
 {
-    GC_err_printf0("---------- Begin address map ----------\n");
-    GC_apply_to_maps(dump_maps);
-    GC_err_printf0("---------- End address map ----------\n");
+    GC_err_printf("---------- Begin address map ----------\n");
+    dump_maps(GC_get_maps());
+    GC_err_printf("---------- End address map ----------\n");
 }
 
 #endif