Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / libgc / os_dep.c
index e224af79d833c81125fccb8ee865ed8948f47486..191e4b4ed7d06a8ea02f73a38ea84b941fc758c5 100644 (file)
@@ -26,7 +26,7 @@
 #     define __KERNEL__
 #     include <asm/signal.h>
 #     undef __KERNEL__
-#   else
+#   elif defined(__GLIBC__)
       /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */
       /* struct sigcontext.  libc6 (glibc2) uses "struct sigcontext" in     */
       /* prototypes, so we have to include the top-level sigcontext.h to    */
 # endif
 
 # include <stdio.h>
-# if defined(MSWINCE)
+# if defined(MSWINCE) || defined (SN_TARGET_PS3)
 #   define SIGSEGV 0 /* value is irrelevant */
 # else
 #   include <signal.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.                                                 */
 
@@ -80,7 +84,7 @@
 #   define NEED_FIND_LIMIT
 # endif
 
-#if defined(FREEBSD) && defined(I386)
+#if defined(FREEBSD) && (defined(I386) || defined(powerpc) || defined(__powerpc__))
 #  include <machine/trap.h>
 #  if !defined(PCR)
 #    define NEED_FIND_LIMIT
 # include <errno.h>
 #endif
 
-#ifdef UNIX_LIKE
+#if defined( UNIX_LIKE ) || defined(NACL)
 # include <fcntl.h>
 #endif
 
@@ -250,30 +254,11 @@ word GC_apply_to_maps(word (*fn)(char *))
 //  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
 //  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
 //  start    end      prot          maj_dev
-//  0        9        18            32
-//  
-//  For 64 bit ABIs:
-//  0       17       34            56
 //
-//  The parser is called with a pointer to the entry and the return value
-//  is either NULL or is advanced to the next entry(the byte after the
-//  trailing '\n'.)
+//  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.
 //
-#if CPP_WORDSZ == 32
-# define OFFSET_MAP_START   0
-# define OFFSET_MAP_END     9
-# define OFFSET_MAP_PROT   18
-# define OFFSET_MAP_MAJDEV 32
-# define ADDR_WIDTH        8
-#endif
-
-#if CPP_WORDSZ == 64
-# define OFFSET_MAP_START   0
-# define OFFSET_MAP_END    17
-# define OFFSET_MAP_PROT   34
-# define OFFSET_MAP_MAJDEV 56
-# define ADDR_WIDTH       16
-#endif
 
 /*
  * Assign various fields of the first line in buf_ptr to *start, *end,
@@ -282,37 +267,46 @@ word GC_apply_to_maps(word (*fn)(char *))
 char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
                                 char *prot_buf, unsigned int *maj_dev)
 {
-    int i;
-    char *tok;
+    char *start_start, *end_start, *prot_start, *maj_dev_start;
+    char *p;
+    char *endp;
 
     if (buf_ptr == NULL || *buf_ptr == '\0') {
         return NULL;
     }
 
-    memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4);
-                               /* do the protections first. */
+    p = buf_ptr;
+    while (isspace(*p)) ++p;
+    start_start = p;
+    GC_ASSERT(isxdigit(*start_start));
+    *start = 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;
+    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 all of this if it's not writable. */
-
-        tok = buf_ptr;
-        buf_ptr[OFFSET_MAP_START+ADDR_WIDTH] = '\0';
-        *start = strtoul(tok, NULL, 16);
-
-        tok = buf_ptr+OFFSET_MAP_END;
-        buf_ptr[OFFSET_MAP_END+ADDR_WIDTH] = '\0';
-        *end = strtoul(tok, NULL, 16);
-
-        buf_ptr += OFFSET_MAP_MAJDEV;
-        tok = buf_ptr;
-        while (*buf_ptr != ':') buf_ptr++;
-        *buf_ptr++ = '\0';
-        *maj_dev = strtoul(tok, NULL, 16);
+    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 */
+          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);
     }
 
-    while (*buf_ptr && *buf_ptr++ != '\n');
+    while (*p && *p++ != '\n');
 
-    return buf_ptr;
+    return p;
 }
 
 #endif /* Need to parse /proc/self/maps. */    
@@ -342,6 +336,13 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
   {
     extern ptr_t GC_find_limit();
 
+       if (GC_no_dls)
+               /* 
+                * Not needed, avoids the SIGSEGV caused by GC_find_limit which
+                * complicates debugging.
+                */
+               return;
+
 #   ifdef LINUX
       /* Try the easy approaches first:        */
       if ((ptr_t)__data_start != 0) {
@@ -391,7 +392,7 @@ static void *tiny_sbrk(ptrdiff_t increment)
 #define sbrk tiny_sbrk
 # endif /* ECOS */
 
-#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
+#if defined(NETBSD) && defined(__ELF__)
   ptr_t GC_data_start;
 
   void GC_init_netbsd_elf()
@@ -404,6 +405,102 @@ static void *tiny_sbrk(ptrdiff_t increment)
   }
 #endif
 
+#if defined(OPENBSD)
+  static struct sigaction old_segv_act;
+  sigjmp_buf GC_jmp_buf_openbsd;
+
+# if defined(GC_OPENBSD_THREADS)
+#   include <sys/syscall.h>
+    sigset_t __syscall(quad_t, ...);
+# endif
+
+  /*
+   * Dont use GC_find_limit() because siglongjmp out of the
+   * signal handler by-passes our userland pthreads lib, leaving
+   * SIGSEGV and SIGPROF masked. Instead use this custom one
+   * that works-around the issues.
+   */
+
+    /*ARGSUSED*/
+    void GC_fault_handler_openbsd(int sig)
+    {
+       siglongjmp(GC_jmp_buf_openbsd, 1);
+    }
+
+    /* Return the first nonaddressible location > p or bound   */
+    /* Requires allocation lock.                               */
+    ptr_t GC_find_limit_openbsd(ptr_t p, ptr_t bound)
+    {
+        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 with the               */
+               /* allocation lock held.                                */
+        struct sigaction act;
+       size_t pgsz = (size_t)sysconf(_SC_PAGESIZE);
+
+       GC_ASSERT(I_HOLD_LOCK());
+
+        act.sa_handler = GC_fault_handler_openbsd;
+        sigemptyset(&act.sa_mask);
+        act.sa_flags = SA_NODEFER | SA_RESTART;
+        sigaction(SIGSEGV, &act, &old_segv_act);
+
+       if (sigsetjmp(GC_jmp_buf_openbsd, 1) == 0) {
+           result = (ptr_t)(((word)(p)) & ~(pgsz-1));
+           for (;;) {
+               result += pgsz;
+               if (result >= bound) {
+                   result = bound;
+                   break;
+               }
+               GC_noop1((word)(*result));
+           }
+       }
+
+# if defined(GC_OPENBSD_THREADS)
+       /* due to the siglongjump we need to manually unmask SIGPROF */
+       __syscall(SYS_sigprocmask, SIG_UNBLOCK, sigmask(SIGPROF));
+# endif
+
+       sigaction(SIGSEGV, &old_segv_act, 0);
+
+       return(result);
+    }
+
+    /* Return first addressable location > p or bound */
+    /* Requires allocation lock. */
+    ptr_t GC_skip_hole_openbsd(ptr_t p, ptr_t bound)
+    {
+        static volatile ptr_t result;
+        struct sigaction act;
+       size_t pgsz = (size_t)sysconf(_SC_PAGESIZE);
+       static volatile int firstpass;
+
+       GC_ASSERT(I_HOLD_LOCK());
+
+        act.sa_handler = GC_fault_handler_openbsd;
+        sigemptyset(&act.sa_mask);
+        act.sa_flags = SA_NODEFER | SA_RESTART;
+        sigaction(SIGSEGV, &act, &old_segv_act);
+
+       firstpass = 1;
+       result = (ptr_t)(((word)(p)) & ~(pgsz-1));
+       if (sigsetjmp(GC_jmp_buf_openbsd, 1) != 0 || firstpass) {
+           firstpass = 0;
+           result += pgsz;
+           if (result >= bound) {
+               result = bound;
+           } else
+               GC_noop1((word)(*result));
+        }
+
+       sigaction(SIGSEGV, &old_segv_act, 0);
+
+       return(result);
+    }
+#endif
+
 # ifdef OS2
 
 # include <stddef.h>
@@ -510,7 +607,7 @@ void GC_enable_signals(void)
 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
       && !defined(MSWINCE) \
       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
-      && !defined(NOSYS) && !defined(ECOS)
+      && !defined(NOSYS) && !defined(ECOS) && !defined(SN_TARGET_PS3)
 
 #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
        /* Use the traditional BSD interface */
@@ -521,6 +618,12 @@ void GC_enable_signals(void)
          /* longjmp implementations.  Most systems appear not to have  */
          /* a signal 32.                                               */
 #      define SIGSETMASK(old, new) (old) = sigsetmask(new)
+#   elif defined(NACL)
+       /* We don't use signals in NaCl. */
+#      define SIGSET_T int
+#      define SIG_DEL(set, signal)
+#      define SIG_FILL(set)
+#      define SIGSETMASK(old, new)
 #   else
        /* Use POSIX/SYSV interface     */
 #      define SIGSET_T sigset_t
@@ -704,8 +807,8 @@ ptr_t GC_get_stack_base()
 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
     || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
        static struct sigaction old_segv_act;
-#      if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \
-       || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
+#      if defined(IRIX5) || defined(HPUX) \
+       || defined(HURD) || defined(NETBSD)
            static struct sigaction old_bus_act;
 #      endif
 #   else
@@ -737,10 +840,12 @@ 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(_sigargs) /* Irix 5.x, not 6.x */ \
-                  || defined(HPUX) || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
+#              if defined(IRIX5) \
+                  || defined(HPUX) || defined(HURD) || defined(NETBSD)
                    /* 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.         */
@@ -778,8 +883,8 @@ ptr_t GC_get_stack_base()
 #       if defined(SUNOS5SIGS) || defined(IRIX5) \
           || defined(OSF1) || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
          (void) sigaction(SIGSEGV, &old_segv_act, 0);
-#        if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
-            || defined(HPUX) || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
+#        if defined(IRIX5) \
+            || defined(HPUX) || defined(HURD) || defined(NETBSD)
              (void) sigaction(SIGBUS, &old_bus_act, 0);
 #        endif
 #       else
@@ -859,13 +964,14 @@ ptr_t GC_get_stack_base()
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <ctype.h>
 
 # define STAT_SKIP 27   /* Number of fields preceding startstack       */
                        /* field in /proc/self/stat                     */
 
+#ifdef USE_LIBC_PRIVATES
 # pragma weak __libc_stack_end
   extern ptr_t __libc_stack_end;
+#endif
 
 # ifdef IA64
     /* Try to read the backing store base from /proc/self/maps.        */
@@ -895,33 +1001,38 @@ ptr_t GC_get_stack_base()
         return GC_apply_to_maps(backing_store_base_from_maps);
     }
 
-#   pragma weak __libc_ia64_register_backing_store_base
-    extern ptr_t __libc_ia64_register_backing_store_base;
+#   ifdef USE_LIBC_PRIVATES
+#     pragma weak __libc_ia64_register_backing_store_base
+      extern ptr_t __libc_ia64_register_backing_store_base;
+#   endif
 
     ptr_t GC_get_register_stack_base(void)
     {
-      if (0 != &__libc_ia64_register_backing_store_base
-         && 0 != __libc_ia64_register_backing_store_base) {
-       /* Glibc 2.2.4 has a bug such that for dynamically linked       */
-       /* executables __libc_ia64_register_backing_store_base is       */
-       /* defined but uninitialized during constructor calls.          */
-       /* Hence we check for both nonzero address and value.           */
-       return __libc_ia64_register_backing_store_base;
-      } else {
-       word result = backing_store_base_from_proc();
-       if (0 == result) {
+#     ifdef USE_LIBC_PRIVATES
+        if (0 != &__libc_ia64_register_backing_store_base
+           && 0 != __libc_ia64_register_backing_store_base) {
+         /* Glibc 2.2.4 has a bug such that for dynamically linked     */
+         /* executables __libc_ia64_register_backing_store_base is     */
+         /* defined but uninitialized during constructor calls.        */
+         /* Hence we check for both nonzero address and value.         */
+         return __libc_ia64_register_backing_store_base;
+        }
+#     endif
+      word 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); 
-       }
-       return (ptr_t)result;
       }
+      return (ptr_t)result;
     }
 # endif
 
+void *GC_set_stackbottom = NULL;
+
   ptr_t GC_linux_stack_base(void)
   {
     /* We read the stack base value from /proc/self/stat.  We do this  */
@@ -941,7 +1052,7 @@ ptr_t GC_get_stack_base()
     /* since the correct value of __libc_stack_end never       */
     /* becomes visible to us.  The second test works around    */
     /* this.                                                   */  
-#if USE_LIBC_PRIVATE_SYMBOLS
+#   ifdef USE_LIBC_PRIVATES
       if (0 != &__libc_stack_end && 0 != __libc_stack_end ) {
 #       ifdef IA64
          /* Some versions of glibc set the address 16 bytes too        */
@@ -951,10 +1062,19 @@ ptr_t GC_get_stack_base()
          } /* Otherwise it's not safe to add 16 bytes and we fall      */
            /* back to using /proc.                                     */
 #      else 
+#      ifdef SPARC
+         /* Older versions of glibc for 64-bit Sparc do not set
+          * this variable correctly, it gets set to either zero
+          * or one.
+          */
+         if (__libc_stack_end != (ptr_t) (unsigned long)0x1)
+           return __libc_stack_end;
+#      else
          return __libc_stack_end;
+#      endif
 #      endif
       }
-#endif
+#   endif
     f = open("/proc/self/stat", O_RDONLY);
     if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
        ABORT("Couldn't read /proc/self/stat");
@@ -1003,7 +1123,8 @@ 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(GC_OPENBSD_THREADS)
 
 ptr_t GC_get_stack_base()
 {
@@ -1063,6 +1184,25 @@ ptr_t GC_get_stack_base()
 
 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS */
 
+#if defined(GC_OPENBSD_THREADS)
+
+/* Find the stack using pthread_stackseg_np() */
+
+# include <sys/signal.h>
+# include <pthread.h>
+# include <pthread_np.h>
+        
+#define HAVE_GET_STACK_BASE
+
+ptr_t GC_get_stack_base()
+{
+    stack_t stack;
+    pthread_stackseg_np(pthread_self(), &stack);
+    return stack.ss_sp;
+}
+
+#endif /* GC_OPENBSD_THREADS */
+
 /*
  * Register static data segment(s) as roots.
  * If more data segments are added later then they need to be registered
@@ -1385,7 +1525,7 @@ int * etext_addr;
 }
 # endif
 
-# if defined(FREEBSD) && defined(I386) && !defined(PCR)
+# if defined(FREEBSD) && (defined(I386) || defined(powerpc) || defined(__powerpc__) ||  defined(__x86_64__)) && !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    */
@@ -1427,6 +1567,31 @@ int * etext_addr;
 
 #else /* !OS2 && !Windows && !AMIGA */
 
+#if defined(OPENBSD)
+
+/*
+ * Depending on arch alignment there can be multiple holes
+ * between DATASTART & DATAEND. Scan from DATASTART - DATAEND
+ * and register each region.
+ */
+void GC_register_data_segments(void)
+{
+  ptr_t region_start, region_end;
+
+  region_start = DATASTART;
+
+  for(;;) {
+    region_end = GC_find_limit_openbsd(region_start, DATAEND);
+    GC_add_roots_inner(region_start, region_end, FALSE);
+    if (region_end < DATAEND)
+       region_start = GC_skip_hole_openbsd(region_end, DATAEND);
+    else
+       break;
+  }
+}
+
+# else /* !OS2 && !Windows && !AMIGA && !OPENBSD */
+
 void GC_register_data_segments()
 {
 #   if !defined(PCR) && !defined(SRC_M3) && !defined(MACOS)
@@ -1438,8 +1603,10 @@ void GC_register_data_segments()
        /* hanging from it.  We're on thin ice here ...                 */
         extern caddr_t sbrk();
 
+       GC_ASSERT(DATASTART);
        GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
 #     else
+       GC_ASSERT(DATASTART);
        GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
 #       if defined(DATASTART2)
          GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), FALSE);
@@ -1484,6 +1651,7 @@ void GC_register_data_segments()
     /* change.                                                         */
 }
 
+# endif  /* ! OPENBSD */
 # endif  /* ! AMIGA */
 # endif  /* ! MSWIN32 && ! MSWINCE*/
 # endif  /* ! OS2 */
@@ -1494,7 +1662,7 @@ void GC_register_data_segments()
 
 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
        && !defined(MSWIN32) && !defined(MSWINCE) \
-       && !defined(MACOS) && !defined(DOS4GW)
+       && !defined(MACOS) && !defined(DOS4GW) && !defined(SN_TARGET_PS3)
 
 # ifdef SUNOS4
     extern caddr_t sbrk();
@@ -1506,7 +1674,7 @@ void GC_register_data_segments()
 # endif
 
 
-# ifdef RS6000
+# 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.                                */
@@ -1606,6 +1774,8 @@ word bytes;
 #   else
       GC_ASSERT(last_addr != 0);
 #   endif
+         if (((word)result % HBLKSIZE) != 0)
+                 ABORT ("GC_unix_get_mem: Memory returned by mmap is not aligned to HBLKSIZE.");
     return((ptr_t)result);
 }
 
@@ -1903,8 +2073,21 @@ void GC_remap(ptr_t start, word bytes)
       int result; 
 
       if (0 == start_addr) return;
+#ifdef NACL
+      {
+       /* NaCl doesn't expose mprotect, but mmap should work fine */
+       void * mmap_result;
+        mmap_result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+                     MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
+                     zero_fd, 0/* offset */);
+        if (mmap_result != (void *)start_addr) ABORT("mmap as mprotect failed");
+        /* Fake the return value as if mprotect succeeded. */
+        result = 0;
+      }
+#else /* NACL */
       result = mprotect(start_addr, len,
                        PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
+#endif /* NACL */
       if (result != 0) {
          GC_err_printf3(
                "Mprotect failed at 0x%lx (length %ld) with errno %ld\n",
@@ -1948,7 +2131,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
 }
@@ -2066,6 +2256,16 @@ void GC_default_push_other_roots GC_PROTO((void))
 }
 
 # endif /* GC_SOLARIS_THREADS || GC_PTHREADS */
+#ifdef SN_TARGET_PS3
+void GC_default_push_other_roots GC_PROTO((void))
+{
+       printf ("WARNING WARNING WARNING\nGC_default_push_other_roots is not implemented\n");
+}
+void GC_push_thread_structures GC_PROTO((void))
+{
+       printf ("WARNING WARNING WARNING\nGC_default_push_thread_structures is not implemented\n");
+}
+#endif
 
 void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
 
@@ -2300,8 +2500,11 @@ GC_bool is_ptrfree;
 #   if defined(ALPHA) || defined(M68K)
       typedef void (* REAL_SIG_PF)(int, int, s_c *);
 #   else
-#     if defined(IA64) || defined(HP_PA)
+#     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
@@ -2389,7 +2592,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
 #   endif
 #   ifdef FREEBSD
 #     define SIG_OK (sig == SIGBUS)
-#     define CODE_OK (code == BUS_PAGE_FAULT)
+#     define CODE_OK TRUE
 #   endif
 # endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
 
@@ -2414,7 +2617,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
 #   if defined(ALPHA) || defined(M68K)
       void GC_write_fault_handler(int sig, int code, s_c * sc)
 #   else
-#     if defined(IA64) || defined(HP_PA)
+#     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)
@@ -2480,7 +2683,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
        char * addr = (char *) (scp -> si_addr);
 #   endif
 #   ifdef LINUX
-#     if defined(I386) || defined (X86_64)
+#     if defined(I386)
        char * addr = (char *) (sc.cr2);
 #     else
 #      if defined(M68K)
@@ -2515,7 +2718,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
 #        ifdef ALPHA
             char * addr = get_fault_addr(sc);
 #        else
-#          if defined(IA64) || defined(HP_PA)
+#          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    */
@@ -2527,7 +2730,11 @@ SIG_PF GC_old_segv_handler;      /* Also old MSWIN32 ACCESS_VIOLATION filter */
 #               if defined(ARM32)
                   char * addr = (char *)sc.fault_address;
 #               else
-                 --> architecture not supported
+#                if defined(CRIS)
+                   char * addr = (char *)sc.regs.csraddr;
+#                else
+                   --> architecture not supported
+#                endif
 #               endif
 #            endif
 #          endif
@@ -2596,7 +2803,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
 #                  if defined(ALPHA) || defined(M68K)
                        (*(REAL_SIG_PF)old_handler) (sig, code, sc);
 #                  else 
-#                    if defined(IA64) || defined(HP_PA)
+#                    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);
@@ -2690,7 +2897,8 @@ void GC_dirty_init()
       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(OSF1) || defined(HURD)
+#     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
@@ -3068,7 +3276,7 @@ word n;
 #include <sys/procfs.h>
 #include <sys/stat.h>
 
-#define INITIAL_BUF_SZ 4096
+#define INITIAL_BUF_SZ 16384
 word GC_proc_buf_size = INITIAL_BUF_SZ;
 char *GC_proc_buf;
 
@@ -3403,8 +3611,6 @@ extern kern_return_t exception_raise_state_identity(
 
 #define MAX_EXCEPTION_PORTS 16
 
-static mach_port_t GC_task_self;
-
 static struct {
     mach_msg_type_number_t count;
     exception_mask_t      masks[MAX_EXCEPTION_PORTS];
@@ -3683,7 +3889,7 @@ void GC_dirty_init() {
         mask,
         GC_ports.exception,
         EXCEPTION_DEFAULT,
-        MACHINE_THREAD_STATE
+        GC_MACH_THREAD_STATE_FLAVOR
     );
     if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed");
 
@@ -3731,7 +3937,7 @@ static kern_return_t GC_forward_exception(
     exception_behavior_t behavior;
     thread_state_flavor_t flavor;
     
-    thread_state_data_t thread_state;
+    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++)
@@ -3792,13 +3998,23 @@ catch_exception_raise(
     char *addr;
     struct hblk *h;
     int i;
-#ifdef POWERPC
-    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
+#   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;
+#   else
 #      error FIXME for non-ppc darwin
-#endif
+#   endif
 
     
     if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) {
@@ -3827,7 +4043,13 @@ catch_exception_raise(
     }
     
     /* 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
+#   error FIXME for non POWERPC/I386
+#endif
         
     if((HDR(addr)) == 0) {
         /* Ugh... just like the SIGBUS problem above, it seems we get a bogus 
@@ -3958,10 +4180,14 @@ kern_return_t catch_exception_raise_state_identity(
 #      if defined (DRSNX)
 #       include <sys/sparc/frame.h>
 #      else
-#       if defined(OPENBSD) || defined(NETBSD)
+#       if defined(OPENBSD)
 #         include <frame.h>
 #       else
-#         include <sys/frame.h>
+#         if defined(FREEBSD) || defined(NETBSD)
+#           include <machine/frame.h>
+#         else
+#           include <sys/frame.h>
+#         endif
 #       endif
 #      endif
 #    endif
@@ -3990,6 +4216,16 @@ kern_return_t catch_exception_raise_state_identity(
 #if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \
     && defined(GC_HAVE_BUILTIN_BACKTRACE)
 
+#ifdef REDIRECT_MALLOC
+  /* Deal with possible malloc calls in backtrace by omitting  */
+  /* the infinitely recursing backtrace.                       */
+# ifdef THREADS
+    __thread   /* If your compiler doesn't understand this */
+               /* you could use something like pthread_getspecific.    */
+# endif
+  GC_in_save_callers = FALSE;
+#endif
+
 void GC_save_callers (info) 
 struct callinfo info[NFRAMES];
 {
@@ -3999,15 +4235,26 @@ struct callinfo info[NFRAMES];
   
   /* We retrieve NFRAMES+1 pc values, but discard the first, since it  */
   /* points to our own frame.                                          */
+# ifdef REDIRECT_MALLOC
+    if (GC_in_save_callers) {
+      info[0].ci_pc = (word)(&GC_save_callers);
+      for (i = 1; i < NFRAMES; ++i) info[i].ci_pc = 0;
+      return;
+    }
+    GC_in_save_callers = TRUE;
+# endif
   GC_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;
+# ifdef REDIRECT_MALLOC
+    GC_in_save_callers = FALSE;
+# endif
 }
 
 #else /* No builtin backtrace; do it ourselves */
 
-#if (defined(OPENBSD) || defined(NETBSD)) && defined(SPARC)
+#if (defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD)) && defined(SPARC)
 #  define FR_SAVFP fr_fp
 #  define FR_SAVPC fr_pc
 #else