OSDN Git Service

* os_dep.c: Fix i686-apple-darwin9 builds.
[pf3gnuchains/gcc-fork.git] / boehm-gc / os_dep.c
index 2defc71..3081105 100644 (file)
 #   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.                                                 */
 
 #   define NEED_FIND_LIMIT
 # endif
 
-#if defined(FREEBSD) && defined(I386)
+#if defined(FREEBSD) && (defined(I386) || defined(X86_64) || 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
 # include <sys/uio.h>
 # include <malloc.h>   /* for locking */
 #endif
-#ifdef USE_MMAP
+#if defined(USE_MMAP) || defined(USE_MUNMAP)
+# ifndef USE_MMAP
+    --> USE_MUNMAP requires USE_MMAP
+# endif
 # include <sys/types.h>
 # include <sys/mman.h>
 # include <sys/stat.h>
+# include <errno.h>
 #endif
 
 #ifdef UNIX_LIKE
 # include <fcntl.h>
-#endif
-
-#if defined(SUNOS5SIGS) || defined (HURD) || defined(LINUX)
-# ifdef SUNOS5SIGS
+# if defined(SUNOS5SIGS) && !defined(FREEBSD)
 #  include <sys/siginfo.h>
 # endif
-# undef setjmp
-# undef longjmp
-# define setjmp(env) sigsetjmp(env, 1)
-# define longjmp(env, val) siglongjmp(env, val)
-# define jmp_buf sigjmp_buf
+  /* 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
@@ -183,45 +198,41 @@ ssize_t GC_repeat_read(int fd, char *buf, size_t count)
 /*
  * 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
+ * ahead of time.
  */
 
 word GC_apply_to_maps(word (*fn)(char *))
 {
     int f;
     int result;
-    int maps_size;
-    char maps_temp[32768];
-    char *maps_buf;
-
-    /* Read /proc/self/maps    */
-        /* Note that we may not allocate, and thus can't use stdio.    */
-        f = open("/proc/self/maps", O_RDONLY);
-        if (-1 == f) return 0;
-       /* stat() doesn't work for /proc/self/maps, so we have to
-          read it to find out how large it is... */
-       maps_size = 0;
+    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;
+
+    /* Read /proc/self/maps, growing maps_buf as necessary.    */
+        /* Note that we may not allocate conventionally, and   */
+        /* thus can't use stdio.                               */
        do {
-           result = GC_repeat_read(f, maps_temp, sizeof(maps_temp));
-           if (result <= 0) return 0;
-           maps_size += result;
-       } while (result == sizeof(maps_temp));
-
-       if (maps_size > sizeof(maps_temp)) {
-           /* If larger than our buffer, close and re-read it. */
-           close(f);
+           if (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);
+             if (maps_buf == 0) return 0;
+           }
            f = open("/proc/self/maps", O_RDONLY);
            if (-1 == f) return 0;
-           maps_buf = alloca(maps_size);
-           if (NULL == maps_buf) return 0;
-           result = GC_repeat_read(f, maps_buf, maps_size);
-           if (result <= 0) return 0;
-       } else {
-           /* Otherwise use the fixed size buffer */
-           maps_buf = maps_temp;
-       }
-
-       close(f);
-        maps_buf[result] = '\0';
+           maps_size = 0;
+           do {
+               result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
+               if (result <= 0) return 0;
+               maps_size += result;
+           } while (result == maps_buf_sz-1);
+           close(f);
+       } while (maps_size >= maps_buf_sz);
+        maps_buf[maps_size] = '\0';
        
     /* Apply fn to result. */
        return fn(maps_buf);
@@ -238,30 +249,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,
@@ -270,37 +262,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. */    
@@ -352,7 +353,8 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
 # endif /* ECOS_GC_MEMORY_SIZE */
 
 // setjmp() function, as described in ANSI para 7.6.1.1
-#define setjmp( __env__ )  hal_setjmp( __env__ )
+#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
@@ -688,9 +690,11 @@ ptr_t GC_get_stack_base()
        typedef void (*handler)();
 #   endif
 
-#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) || defined(HURD)
+#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
+    || defined(HURD) || defined(NETBSD)
        static struct sigaction old_segv_act;
-#      if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) || defined(HURD)
+#      if defined(IRIX5) || defined(HPUX) \
+       || defined(HURD) || defined(NETBSD)
            static struct sigaction old_bus_act;
 #      endif
 #   else
@@ -704,21 +708,17 @@ ptr_t GC_get_stack_base()
       handler h;
 #   endif
     {
-#     if defined(SUNOS5SIGS) || defined(IRIX5)  \
-        || defined(OSF1) || defined(HURD)
+#      if defined(SUNOS5SIGS) || defined(IRIX5)  \
+        || defined(OSF1) || defined(HURD) || defined(NETBSD)
          struct sigaction      act;
 
          act.sa_handler        = h;
-#        ifdef SUNOS5SIGS
+#        if 0 /* Was necessary for Solaris 2.3 and very temporary      */
+              /* NetBSD bugs.                                          */
             act.sa_flags          = SA_RESTART | SA_NODEFER;
 #         else
             act.sa_flags          = SA_RESTART;
 #        endif
-          /* The presence of SA_NODEFER represents yet another gross    */
-          /* hack.  Under Solaris 2.3, siglongjmp doesn't appear to     */
-          /* interact correctly with -lthread.  We hide the confusion   */
-          /* by making sure that signal handling doesn't affect the     */
-          /* signal mask.                                               */
 
          (void) sigemptyset(&act.sa_mask);
 #        ifdef GC_IRIX_THREADS
@@ -726,10 +726,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)
+#              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.         */
@@ -748,13 +750,13 @@ ptr_t GC_get_stack_base()
 # ifdef NEED_FIND_LIMIT
   /* Some tools to implement HEURISTIC2        */
 #   define MIN_PAGE_SIZE 256   /* Smallest conceivable page size, bytes */
-    /* static */ jmp_buf GC_jmp_buf;
+    /* static */ JMP_BUF GC_jmp_buf;
     
     /*ARGSUSED*/
     void GC_fault_handler(sig)
     int sig;
     {
-        longjmp(GC_jmp_buf, 1);
+        LONGJMP(GC_jmp_buf, 1);
     }
 
     void GC_setup_temporary_fault_handler()
@@ -764,19 +766,19 @@ ptr_t GC_get_stack_base()
     
     void GC_reset_fault_handler()
     {
-#     if defined(SUNOS5SIGS) || defined(IRIX5) \
-        || defined(OSF1) || defined(HURD)
-       (void) sigaction(SIGSEGV, &old_segv_act, 0);
-#      if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
-          || defined(HPUX) || defined(HURD)
-           (void) sigaction(SIGBUS, &old_bus_act, 0);
-#      endif
-#      else
-       (void) signal(SIGSEGV, old_segv_handler);
-#      ifdef SIGBUS
-         (void) signal(SIGBUS, old_bus_handler);
-#      endif
-#     endif
+#       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)
+             (void) sigaction(SIGBUS, &old_bus_act, 0);
+#        endif
+#       else
+         (void) signal(SIGSEGV, old_segv_handler);
+#        ifdef SIGBUS
+           (void) signal(SIGBUS, old_bus_handler);
+#        endif
+#       endif
     }
 
     /* Return the first nonaddressible location > p (up) or    */
@@ -786,31 +788,31 @@ ptr_t GC_get_stack_base()
     ptr_t p;
     GC_bool up;
     {
-      static VOLATILE ptr_t result;
-               /* Needs to be static, since otherwise it may not be    */
-               /* preserved across the longjmp.  Can safely be         */
-               /* static since it's only called once, with the         */
-               /* allocation lock held.                                */
-
-
-      GC_setup_temporary_fault_handler();
-      if (setjmp(GC_jmp_buf) == 0) {
-       result = (ptr_t)(((word)(p))
-                        & ~(MIN_PAGE_SIZE-1));
-       for (;;) {
-         if (up) {
-           result += MIN_PAGE_SIZE;
-         } else {
-           result -= MIN_PAGE_SIZE;
-         }
-         GC_noop1((word)(*result));
+        static VOLATILE ptr_t result;
+               /* Needs to be static, since otherwise it may not be    */
+               /* preserved across the longjmp.  Can safely be         */
+               /* static since it's only called once, with the         */
+               /* allocation lock held.                                */
+
+
+       GC_setup_temporary_fault_handler();
+       if (SETJMP(GC_jmp_buf) == 0) {
+           result = (ptr_t)(((word)(p))
+                             & ~(MIN_PAGE_SIZE-1));
+           for (;;) {
+               if (up) {
+                   result += MIN_PAGE_SIZE;
+               } else {
+                   result -= MIN_PAGE_SIZE;
+               }
+               GC_noop1((word)(*result));
+           }
        }
-      }
-      GC_reset_fault_handler();
-      if (!up) {
-       result += MIN_PAGE_SIZE;
-      }
-      return(result);
+       GC_reset_fault_handler();
+       if (!up) {
+           result += MIN_PAGE_SIZE;
+       }
+       return(result);
     }
 # endif
 
@@ -821,17 +823,41 @@ ptr_t GC_get_stack_base()
   }
 #endif
 
+#ifdef HPUX_STACKBOTTOM
+
+#include <sys/param.h>
+#include <sys/pstat.h>
+
+  ptr_t GC_get_register_stack_base(void)
+  {
+    struct pst_vm_status vm_status;
+
+    int i = 0;
+    while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) {
+      if (vm_status.pst_type == PS_RSESTACK) {
+        return (ptr_t) vm_status.pst_vaddr;
+      }
+    }
+
+    /* old way to get the register stackbottom */
+    return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1)
+                   & ~(BACKING_STORE_ALIGNMENT - 1));
+  }
+
+#endif /* HPUX_STACK_BOTTOM */
+
 #ifdef LINUX_STACKBOTTOM
 
 #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.        */
@@ -861,30 +887,33 @@ 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
 
@@ -903,7 +932,12 @@ ptr_t GC_get_stack_base()
     size_t i, buf_offset = 0;
 
     /* First try the easy way.  This should work for glibc 2.2 */
-      if (0 != &__libc_stack_end) {
+    /* This fails in a prelinked ("prelink" command) executable */
+    /* since the correct value of __libc_stack_end never       */
+    /* becomes visible to us.  The second test works around    */
+    /* this.                                                   */  
+#   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        */
          /* low while the initialization code is running.              */
@@ -912,9 +946,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
     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");
@@ -1137,12 +1181,15 @@ void GC_register_data_segments()
        /* 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.  */
   
   void GC_init_win32()
   {
     /* if we're running under win32s, assume that no DLLs will be loaded */
     DWORD v = GetVersion();
-    GC_no_win32_dlls |= ((v & 0x80000000) && (v & 0xff) <= 3);
+    GC_wnt = !(v & 0x80000000);
+    GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3);
   }
 
   /* Return the smallest address a such that VirtualQuery              */
@@ -1328,7 +1375,7 @@ int * etext_addr;
     /* max_page_size to &etext if &etext is at a page boundary */
     
     GC_setup_temporary_fault_handler();
-    if (setjmp(GC_jmp_buf) == 0) {
+    if (SETJMP(GC_jmp_buf) == 0) {
        /* Try writing to the address.  */
        *result = *result;
         GC_reset_fault_handler();
@@ -1345,7 +1392,7 @@ int * etext_addr;
 }
 # endif
 
-# if defined(FREEBSD) && defined(I386) && !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    */
@@ -1362,7 +1409,7 @@ int * etext_addr;
                              & ~((word)max_page_size - 1);
     VOLATILE ptr_t result = (ptr_t)text_end;
     GC_setup_temporary_fault_handler();
-    if (setjmp(GC_jmp_buf) == 0) {
+    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)
@@ -1466,7 +1513,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.                                */
@@ -1497,8 +1544,7 @@ word bytes;
 
 #else  /* Not RS6000 */
 
-#if defined(USE_MMAP)
-/* Tested only under Linux, IRIX5 and Solaris 2 */
+#if defined(USE_MMAP) || defined(USE_MUNMAP)
 
 #ifdef USE_MMAP_FIXED
 #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE
@@ -1508,6 +1554,23 @@ word bytes;
 #   define GC_MMAP_FLAGS MAP_PRIVATE
 #endif
 
+#ifdef USE_MMAP_ANON
+# define zero_fd -1
+# if defined(MAP_ANONYMOUS)
+#   define OPT_MAP_ANON MAP_ANONYMOUS
+# else
+#   define OPT_MAP_ANON MAP_ANON
+# endif
+#else
+  static int zero_fd;
+# 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
 #endif
@@ -1520,23 +1583,17 @@ word bytes;
 
 #   ifndef USE_MMAP_ANON
       static GC_bool initialized = FALSE;
-      static int fd;
 
       if (!initialized) {
-         fd = open("/dev/zero", O_RDONLY);
-         fcntl(fd, F_SETFD, FD_CLOEXEC);
+         zero_fd = open("/dev/zero", O_RDONLY);
+         fcntl(zero_fd, F_SETFD, FD_CLOEXEC);
          initialized = TRUE;
       }
 #   endif
 
     if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
-#   ifdef USE_MMAP_ANON
-      result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
-                   GC_MMAP_FLAGS | MAP_ANON, -1, 0/* offset */);
-#   else
-      result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
-                   GC_MMAP_FLAGS, fd, 0/* offset */);
-#   endif
+    result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+                 GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */);
     if (result == MAP_FAILED) return(0);
     last_addr = (ptr_t)result + bytes + GC_page_size - 1;
     last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
@@ -1794,7 +1851,15 @@ void GC_unmap(ptr_t start, word bytes)
          len -= free_len;
       }
 #   else
-      if (munmap(start_addr, len) != 0) ABORT("munmap failed");
+      /* We immediately remap it to prevent an intervening mmap from   */
+      /* accidentally grabbing the same address space.                 */
+      {
+       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
 }
@@ -1802,13 +1867,13 @@ void GC_unmap(ptr_t start, word bytes)
 
 void GC_remap(ptr_t start, word bytes)
 {
-    static int zero_descr = -1;
     ptr_t start_addr = GC_unmap_start(start, bytes);
     ptr_t end_addr = GC_unmap_end(start, bytes);
     word len = end_addr - start_addr;
-    ptr_t result;
 
 #   if defined(MSWIN32) || defined(MSWINCE)
+      ptr_t result;
+
       if (0 == start_addr) return;
       while (len != 0) {
           MEMORY_BASIC_INFORMATION mem_info;
@@ -1828,13 +1893,17 @@ void GC_remap(ptr_t start, word bytes)
          len -= alloc_len;
       }
 #   else
-      if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);
-      fcntl(zero_descr, F_SETFD, FD_CLOEXEC);
+      /* It was already remapped with PROT_NONE. */
+      int result; 
+
       if (0 == start_addr) return;
-      result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
-                   MAP_FIXED | MAP_PRIVATE, zero_descr, 0);
-      if (result != start_addr) {
-         ABORT("mmap remapping failed");
+      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);
+         ABORT("Mprotect remapping failed");
       }
       GC_unmapped_bytes -= len;
 #   endif
@@ -2170,9 +2239,9 @@ GC_bool is_ptrfree;
 # endif /* !DARWIN */
 # endif /* MSWIN32 || MSWINCE || DARWIN */
 
-#if defined(SUNOS4) || defined(FREEBSD)
+#if defined(SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
     typedef void (* SIG_PF)();
-#endif /* SUNOS4 || FREEBSD */
+#endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
 
 #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \
     || defined(HURD)
@@ -2199,13 +2268,13 @@ GC_bool is_ptrfree;
 #endif /* IRIX5 || OSF1 || HURD */
 
 #if defined(SUNOS5SIGS)
-# ifdef HPUX
-#   define SIGINFO __siginfo
+# if defined(HPUX) || defined(FREEBSD)
+#   define SIGINFO_T siginfo_t
 # else
-#   define SIGINFO siginfo
+#   define SIGINFO_T struct siginfo
 # endif
 # ifdef __STDC__
-    typedef void (* REAL_SIG_PF)(int, struct SIGINFO *, void *);
+    typedef void (* REAL_SIG_PF)(int, SIGINFO_T *, void *);
 # else
     typedef void (* REAL_SIG_PF)();
 # endif
@@ -2225,8 +2294,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
@@ -2301,7 +2373,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
 
 /*ARGSUSED*/
 #if !defined(DARWIN)
-# if defined (SUNOS4) || defined(FREEBSD)
+# if defined (SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
     void GC_write_fault_handler(sig, code, scp, addr)
     int sig, code;
     struct sigcontext *scp;
@@ -2314,9 +2386,9 @@ 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 */
+# endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
 
 # if defined(IRIX5) || defined(OSF1) || defined(HURD)
 #   include <errno.h>
@@ -2339,7 +2411,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)
@@ -2358,11 +2430,11 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
 
 # if defined(SUNOS5SIGS)
 #  ifdef __STDC__
-    void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context)
+    void GC_write_fault_handler(int sig, SIGINFO_T *scp, void * context)
 #  else
     void GC_write_fault_handler(sig, scp, context)
     int sig;
-    struct SIGINFO *scp;
+    SIGINFO_T *scp;
     void * context;
 #  endif
 #   ifdef HPUX
@@ -2373,9 +2445,14 @@ SIG_PF GC_old_segv_handler;      /* Also old MSWIN32 ACCESS_VIOLATION filter */
                     || (scp -> si_code == SEGV_UNKNOWN) \
                     || (scp -> si_code == BUS_OBJERR)
 #   else
-#     define SIG_OK (sig == SIGSEGV)
-#     define CODE_OK (scp -> si_code == SEGV_ACCERR)
-#   endif
+#     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
+#   endif    
 # endif /* SUNOS5SIGS */
 
 # if defined(MSWIN32) || defined(MSWINCE)
@@ -2400,7 +2477,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)
@@ -2435,7 +2512,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    */
@@ -2447,7 +2524,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
@@ -2478,6 +2559,10 @@ SIG_PF GC_old_segv_handler;      /* Also old MSWIN32 ACCESS_VIOLATION filter */
            in_allocd_block = (HDR(addr) != 0);
 #      endif
         if (!in_allocd_block) {
+           /* FIXME - We should make sure that we invoke the   */
+           /* old handler with the appropriate calling         */
+           /* sequence, which often depends on SA_SIGINFO.     */
+
            /* Heap blocks now begin and end on page boundaries */
             SIG_PF old_handler;
             
@@ -2494,11 +2579,17 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
                    return(EXCEPTION_CONTINUE_SEARCH);
 #              endif
             } else {
-#              if defined (SUNOS4) || defined(FREEBSD)
+#              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
@@ -2506,7 +2597,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);
@@ -2600,7 +2691,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
@@ -2623,7 +2715,7 @@ void GC_dirty_init()
         GC_err_printf0("Page size not multiple of HBLKSIZE\n");
         ABORT("Page size not multiple of HBLKSIZE");
     }
-#   if defined(SUNOS4) || defined(FREEBSD)
+#   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!?");
@@ -2647,13 +2739,13 @@ void GC_dirty_init()
 #      endif
       }
 #   endif
-#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) \
-       || defined(OSF1) || defined(HURD)
+#   if (defined(SUNOS5SIGS) && !defined(FREEBSD)) || defined(IRIX5) \
+       || defined(LINUX) || defined(OSF1) || defined(HURD)
       /* SUNOS5SIGS includes HPUX */
 #     if defined(GC_IRIX_THREADS)
        sigaction(SIGSEGV, 0, &oldact);
        sigaction(SIGSEGV, &act, 0);
-#     else
+#     else 
        {
          int res = sigaction(SIGSEGV, &act, &oldact);
          if (res != 0) ABORT("Sigaction failed");
@@ -2679,8 +2771,9 @@ void GC_dirty_init()
          GC_err_printf0("Replaced other SIGSEGV handler\n");
 #       endif
       }
-#   endif
-#   if defined(HPUX) || defined(LINUX) || defined(HURD)
+#   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) {
@@ -2692,7 +2785,7 @@ void GC_dirty_init()
          GC_err_printf0("Replaced other SIGBUS handler\n");
 #       endif
       }
-#   endif /* HPUX || LINUX || HURD */
+#   endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */
 #   if defined(MSWIN32)
       GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
       if (GC_old_segv_handler != NULL) {
@@ -2977,7 +3070,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;
 
@@ -3091,7 +3184,7 @@ int dummy;
                 GC_proc_buf = bufp = new_buf;
                 GC_proc_buf_size = new_size;
             }
-            if (syscall(SYS_read, GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
+            if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
                 WARN("Insufficient space for /proc read\n", 0);
                 /* Punt:       */
                memset(GC_grungy_pages, 0xff, sizeof (page_hash_table));
@@ -3278,7 +3371,7 @@ GC_bool is_ptrfree;
       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
+         www.omnigroup.com/mailman/archive/macosx-dev/2000-June/014178.html
       3. macosx-nat.c from Apple's GDB source code.
 */
    
@@ -3312,6 +3405,15 @@ extern kern_return_t exception_raise_state_identity(
 
 #define MAX_EXCEPTION_PORTS 16
 
+#if defined (HAS_PPC_THREAD_STATE___R0) ||     \
+    defined (HAS_PPC_THREAD_STATE64___R0) ||   \
+    defined (HAS_X86_THREAD_STATE32___EAX) ||  \
+    defined (HAS_X86_THREAD_STATE64___RAX)
+#  define THREAD_FLD(x) __ ## x
+#else
+#  define THREAD_FLD(x) x
+#endif
+
 static struct {
     mach_msg_type_number_t count;
     exception_mask_t      masks[MAX_EXCEPTION_PORTS];
@@ -3429,6 +3531,8 @@ static void *GC_mprotect_thread(void *arg) {
     } msg;
 
     mach_msg_id_t id;
+
+    GC_darwin_register_mach_handler_thread(mach_thread_self());
     
     for(;;) {
         r = mach_msg(
@@ -3588,7 +3692,7 @@ void GC_dirty_init() {
         mask,
         GC_ports.exception,
         EXCEPTION_DEFAULT,
-        MACHINE_THREAD_STATE
+        GC_MACH_THREAD_STATE
     );
     if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed");
 
@@ -3636,7 +3740,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++)
@@ -3697,13 +3801,29 @@ 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) || 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
+       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 darwin
-#endif
+#   endif
 
     
     if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) {
@@ -3732,7 +3852,13 @@ catch_exception_raise(
     }
     
     /* This is the address that caused the fault */
+#if defined(POWERPC)
     addr = (char*) exc_state.dar;
+#elif defined (I386) || defined (X86_64)
+    addr = (char*) exc_state. THREAD_FLD(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 
@@ -3863,10 +3989,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
@@ -3886,12 +4016,24 @@ kern_return_t catch_exception_raise_state_identity(
 
 #endif /* NEED_CALLINFO */
 
+#if defined(GC_HAVE_BUILTIN_BACKTRACE)
+# include <execinfo.h>
+#endif
+
 #ifdef SAVE_CALL_CHAIN
 
 #if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \
     && defined(GC_HAVE_BUILTIN_BACKTRACE)
 
-#include <execinfo.h>
+#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];
@@ -3902,15 +4044,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
@@ -3968,6 +4121,8 @@ struct callinfo info[NFRAMES];
     static int reentry_count = 0;
     GC_bool stop = FALSE;
 
+    /* FIXME: This should probably use a different lock, so that we    */
+    /* become callable with or without the allocation lock.            */
     LOCK();
       ++reentry_count;
     UNLOCK();
@@ -4002,7 +4157,8 @@ struct callinfo info[NFRAMES];
 #        ifdef LINUX
            FILE *pipe;
 #        endif
-#        if defined(GC_HAVE_BUILTIN_BACKTRACE)
+#        if defined(GC_HAVE_BUILTIN_BACKTRACE) \
+            && !defined(GC_BACKTRACE_SYMBOLS_BROKEN)
            char **sym_name =
              backtrace_symbols((void **)(&(info[i].ci_pc)), 1);
            char *name = sym_name[0];
@@ -4021,6 +4177,9 @@ struct callinfo info[NFRAMES];
 #              define RESULT_SZ 200
                static char result_buf[RESULT_SZ];
                size_t result_len;
+               char *old_preload;
+#              define PRELOAD_SZ 200
+               char preload_buf[PRELOAD_SZ];
                static GC_bool found_exe_name = FALSE;
                static GC_bool will_fail = FALSE;
                int ret_code;
@@ -4042,7 +4201,20 @@ struct callinfo info[NFRAMES];
                /* isn't time critical.                                 */
                sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name,
                                 (unsigned long)info[i].ci_pc);
+               old_preload = getenv ("LD_PRELOAD");
+               if (0 != old_preload) {
+                 if (strlen (old_preload) >= PRELOAD_SZ) {
+                   will_fail = TRUE;
+                   goto out;
+                 }
+                 strcpy (preload_buf, old_preload);
+                 unsetenv ("LD_PRELOAD");
+               }
                pipe = popen(cmd_buf, "r");
+               if (0 != old_preload
+                   && 0 != setenv ("LD_PRELOAD", preload_buf, 0)) {
+                 WARN("Failed to reset LD_PRELOAD\n", 0);
+               }
                if (pipe == NULL
                    || (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe))
                       == 0) {
@@ -4079,7 +4251,8 @@ struct callinfo info[NFRAMES];
            }
 #        endif /* LINUX */
          GC_err_printf1("\t\t%s\n", name);
-#        if defined(GC_HAVE_BUILTIN_BACKTRACE)
+#        if defined(GC_HAVE_BUILTIN_BACKTRACE) \
+            && !defined(GC_BACKTRACE_SYMBOLS_BROKEN)
            free(sym_name);  /* May call GC_free; that's OK */
 #         endif
        }