[BACK]Return to os_dep.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib2 / asir2000 / gc

Diff for /OpenXM_contrib2/asir2000/gc/os_dep.c between version 1.4 and 1.8

version 1.4, 2001/04/20 07:39:19 version 1.8, 2004/02/13 05:48:35
Line 1 
Line 1 
 int ox_usr1_sent, ox_int_received, critical_when_signal;  
 static int inside_critical_section;  
   
 /*  /*
  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers   * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
  * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.   * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
Line 66  static int inside_critical_section;
Line 63  static int inside_critical_section;
 /* Blatantly OS dependent routines, except for those that are related   */  /* Blatantly OS dependent routines, except for those that are related   */
 /* to dynamic loading.                                                  */  /* to dynamic loading.                                                  */
   
 # if !defined(THREADS) && !defined(STACKBOTTOM) && defined(HEURISTIC2)  # if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START)
 #   define NEED_FIND_LIMIT  #   define NEED_FIND_LIMIT
 # endif  # endif
   
 # if defined(IRIX_THREADS) || defined(HPUX_THREADS)  # if !defined(STACKBOTTOM) && defined(HEURISTIC2)
 #   define NEED_FIND_LIMIT  #   define NEED_FIND_LIMIT
 # endif  # endif
   
Line 78  static int inside_critical_section;
Line 75  static int inside_critical_section;
 #   define NEED_FIND_LIMIT  #   define NEED_FIND_LIMIT
 # endif  # endif
   
 # if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR)  # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
         || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
 #   define NEED_FIND_LIMIT  #   define NEED_FIND_LIMIT
 # endif  # endif
   
 # if defined(LINUX) && \  #if defined(FREEBSD) && defined(I386)
      (defined(POWERPC) || defined(SPARC) || defined(ALPHA) || defined(IA64) \  #  include <machine/trap.h>
       || defined(MIPS))  #  if !defined(PCR)
 #   define NEED_FIND_LIMIT  #    define NEED_FIND_LIMIT
 # endif  #  endif
   #endif
   
 #ifdef NEED_FIND_LIMIT  #ifdef NEED_FIND_LIMIT
 #   include <setjmp.h>  #   include <setjmp.h>
 #endif  #endif
   
 #ifdef FREEBSD  
 #  include <machine/trap.h>  
 #endif  
   
 #ifdef AMIGA  #ifdef AMIGA
 # define GC_AMIGA_DEF  # define GC_AMIGA_DEF
 # include "AmigaOS.c"  # include "AmigaOS.c"
Line 126  static int inside_critical_section;
Line 121  static int inside_critical_section;
 # include <fcntl.h>  # include <fcntl.h>
 #endif  #endif
   
 #ifdef SUNOS5SIGS  #if defined(SUNOS5SIGS) || defined (HURD) || defined(LINUX)
 # include <sys/siginfo.h>  # ifdef SUNOS5SIGS
   #  include <sys/siginfo.h>
   # endif
 # undef setjmp  # undef setjmp
 # undef longjmp  # undef longjmp
 # define setjmp(env) sigsetjmp(env, 1)  # define setjmp(env) sigsetjmp(env, 1)
Line 135  static int inside_critical_section;
Line 132  static int inside_critical_section;
 # define jmp_buf sigjmp_buf  # define jmp_buf sigjmp_buf
 #endif  #endif
   
   #ifdef DARWIN
   /* for get_etext and friends */
   #include <mach-o/getsect.h>
   #endif
   
 #ifdef DJGPP  #ifdef DJGPP
   /* Apparently necessary for djgpp 2.01.  May cause problems with      */    /* Apparently necessary for djgpp 2.01.  May cause problems with      */
   /* other versions.                                                    */    /* other versions.                                                    */
Line 153  static int inside_critical_section;
Line 155  static int inside_critical_section;
 # define OPT_PROT_EXEC 0  # define OPT_PROT_EXEC 0
 #endif  #endif
   
   #if defined(LINUX) && \
       (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))
   
   /* 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.                                                                */
   
   #define READ read
   
   /* Repeatedly perform a read call until the buffer is filled or */
   /* we encounter EOF.                                            */
   ssize_t GC_repeat_read(int fd, char *buf, size_t count)
   {
       ssize_t num_read = 0;
       ssize_t result;
   
       while (num_read < count) {
           result = READ(fd, buf + num_read, count - num_read);
           if (result < 0) return result;
           if (result == 0) break;
           num_read += result;
       }
       return num_read;
   }
   
   /*
    * Apply fn to a buffer containing the contents of /proc/self/maps.
    * Return the result of fn or, if we failed, 0.
    */
   
   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;
           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);
               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';
   
       /* Apply fn to result. */
           return fn(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
   //  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'.)
   //
   #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,
    * *prot_buf and *maj_dev.  Only *prot_buf may be set for unwritable maps.
    */
   char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
                                   char *prot_buf, unsigned int *maj_dev)
   {
       int i;
       char *tok;
   
       if (buf_ptr == NULL || *buf_ptr == '\0') {
           return NULL;
       }
   
       memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4);
                                   /* do the protections first. */
       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);
       }
   
       while (*buf_ptr && *buf_ptr++ != '\n');
   
       return buf_ptr;
   }
   
   #endif /* Need to parse /proc/self/maps. */
   
 #if defined(SEARCH_FOR_DATA_START)  #if defined(SEARCH_FOR_DATA_START)
   /* The I386 case can be handled without a search.  The Alpha case     */    /* The I386 case can be handled without a search.  The Alpha case     */
   /* used to be handled differently as well, but the rules changed      */    /* used to be handled differently as well, but the rules changed      */
Line 160  static int inside_critical_section;
Line 312  static int inside_critical_section;
   /* cover all versions.                                                */    /* cover all versions.                                                */
   
 # ifdef LINUX  # ifdef LINUX
       /* 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. */
       /* We assume gcc-compatible pragmas.        */
 #   pragma weak __data_start  #   pragma weak __data_start
     extern int __data_start;      extern int __data_start[];
 #   pragma weak data_start  #   pragma weak data_start
     extern int data_start;      extern int data_start[];
 # endif /* LINUX */  # endif /* LINUX */
   extern int _end;    extern int _end[];
   
   ptr_t GC_data_start;    ptr_t GC_data_start;
   
Line 175  static int inside_critical_section;
Line 332  static int inside_critical_section;
   
 #   ifdef LINUX  #   ifdef LINUX
       /* Try the easy approaches first: */        /* Try the easy approaches first: */
       if (&__data_start != 0) {        if ((ptr_t)__data_start != 0) {
           GC_data_start = (ptr_t)(&__data_start);            GC_data_start = (ptr_t)(__data_start);
           return;            return;
       }        }
       if (&data_start != 0) {        if ((ptr_t)data_start != 0) {
           GC_data_start = (ptr_t)(&data_start);            GC_data_start = (ptr_t)(data_start);
           return;            return;
       }        }
 #   endif /* LINUX */  #   endif /* LINUX */
     GC_data_start = GC_find_limit((ptr_t)(&_end), FALSE);      GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
   }    }
 #endif  #endif
   
 #if defined(NETBSD) && defined(__ELF__)  # ifdef ECOS
   
   # ifndef ECOS_GC_MEMORY_SIZE
   # define ECOS_GC_MEMORY_SIZE (448 * 1024)
   # endif /* ECOS_GC_MEMORY_SIZE */
   
   // setjmp() function, as described in ANSI para 7.6.1.1
   #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.
   static char memory[ECOS_GC_MEMORY_SIZE];
   static char *brk = memory;
   
   static void *tiny_sbrk(ptrdiff_t increment)
   {
     void *p = brk;
   
     brk += increment;
   
     if (brk >  memory + sizeof memory)
       {
         brk -= increment;
         return NULL;
       }
   
     return p;
   }
   #define sbrk tiny_sbrk
   # endif /* ECOS */
   
   #if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
   ptr_t GC_data_start;    ptr_t GC_data_start;
   
   void GC_init_netbsd_elf()    void GC_init_netbsd_elf()
Line 306  void GC_enable_signals(void)
Line 496  void GC_enable_signals(void)
   
 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \  #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
       && !defined(MSWINCE) \        && !defined(MSWINCE) \
       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW)        && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
         && !defined(NOSYS) && !defined(ECOS)
   
 #   if defined(sigmask) && !defined(UTS4)  #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
         /* Use the traditional BSD interface */          /* Use the traditional BSD interface */
 #       define SIGSET_T int  #       define SIGSET_T int
 #       define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))  #       define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
Line 365  void GC_disable_signals()
Line 556  void GC_disable_signals()
         GC_sig_disabled++;          GC_sig_disabled++;
 #   endif  #   endif
     SIGSETMASK(old_mask,new_mask);      SIGSETMASK(old_mask,new_mask);
         if ( critical_when_signal )  
                 inside_critical_section = 1;  
         else {  
                 inside_critical_section = 0;  
                 critical_when_signal = 1;  
         }  
 }  }
   
 void GC_enable_signals()  void GC_enable_signals()
Line 380  void GC_enable_signals()
Line 565  void GC_enable_signals()
         GC_sig_disabled--;          GC_sig_disabled--;
 #   endif  #   endif
     SIGSETMASK(dummy,old_mask);      SIGSETMASK(dummy,old_mask);
         if ( !inside_critical_section ) {  
                 critical_when_signal = 0;  
                 if ( ox_usr1_sent ) {  
                         ox_usr1_sent = 0; ox_usr1_handler();  
                 }  
                 if ( ox_int_received ) {  
                         ox_int_received = 0; int_handler();  
                 }  
         } else  
                 inside_critical_section = 0;  
 }  }
   
 #  endif  /* !PCR */  #  endif  /* !PCR */
Line 397  void GC_enable_signals()
Line 572  void GC_enable_signals()
 # endif /*!OS/2 */  # endif /*!OS/2 */
   
 /* Ivan Demakov: simplest way (to me) */  /* Ivan Demakov: simplest way (to me) */
 #ifdef DOS4GW  #if defined (DOS4GW)
   void GC_disable_signals() { }    void GC_disable_signals() { }
   void GC_enable_signals() { }    void GC_enable_signals() { }
 #endif  #endif
Line 513  ptr_t GC_get_stack_base()
Line 688  ptr_t GC_get_stack_base()
         typedef void (*handler)();          typedef void (*handler)();
 #   endif  #   endif
   
 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1)  #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) || defined(HURD)
         static struct sigaction old_segv_act;          static struct sigaction old_segv_act;
 #       if defined(_sigargs) || defined(HPUX) /* !Irix6.x */  #       if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) || defined(HURD)
             static struct sigaction old_bus_act;              static struct sigaction old_bus_act;
 #       endif  #       endif
 #   else  #   else
Line 529  ptr_t GC_get_stack_base()
Line 704  ptr_t GC_get_stack_base()
       handler h;        handler h;
 #   endif  #   endif
     {      {
 #       if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1)  #       if defined(SUNOS5SIGS) || defined(IRIX5)  \
           || defined(OSF1) || defined(HURD)
           struct sigaction      act;            struct sigaction      act;
   
           act.sa_handler        = h;            act.sa_handler        = h;
           act.sa_flags          = SA_RESTART | SA_NODEFER;  #         ifdef SUNOS5SIGS
               act.sa_flags          = SA_RESTART | SA_NODEFER;
   #         else
               act.sa_flags          = SA_RESTART;
   #         endif
           /* The presence of SA_NODEFER represents yet another gross    */            /* The presence of SA_NODEFER represents yet another gross    */
           /* hack.  Under Solaris 2.3, siglongjmp doesn't appear to     */            /* hack.  Under Solaris 2.3, siglongjmp doesn't appear to     */
           /* interact correctly with -lthread.  We hide the confusion   */            /* interact correctly with -lthread.  We hide the confusion   */
Line 541  ptr_t GC_get_stack_base()
Line 721  ptr_t GC_get_stack_base()
           /* signal mask.                                               */            /* signal mask.                                               */
   
           (void) sigemptyset(&act.sa_mask);            (void) sigemptyset(&act.sa_mask);
 #         ifdef IRIX_THREADS  #         ifdef GC_IRIX_THREADS
                 /* Older versions have a bug related to retrieving and  */                  /* Older versions have a bug related to retrieving and  */
                 /* and setting a handler at the same time.              */                  /* and setting a handler at the same time.              */
                 (void) sigaction(SIGSEGV, 0, &old_segv_act);                  (void) sigaction(SIGSEGV, 0, &old_segv_act);
Line 549  ptr_t GC_get_stack_base()
Line 729  ptr_t GC_get_stack_base()
 #         else  #         else
                 (void) sigaction(SIGSEGV, &act, &old_segv_act);                  (void) sigaction(SIGSEGV, &act, &old_segv_act);
 #               if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \  #               if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
                    || defined(HPUX)                     || defined(HPUX) || defined(HURD)
                     /* Under Irix 5.x or HP/UX, we may get SIGBUS.      */                      /* Under Irix 5.x or HP/UX, we may get SIGBUS.      */
                     /* Pthreads doesn't exist under Irix 5.x, so we     */                      /* Pthreads doesn't exist under Irix 5.x, so we     */
                     /* don't have to worry in the threads case.         */                      /* don't have to worry in the threads case.         */
                     (void) sigaction(SIGBUS, &act, &old_bus_act);                      (void) sigaction(SIGBUS, &act, &old_bus_act);
 #               endif  #               endif
 #         endif /* IRIX_THREADS */  #         endif /* GC_IRIX_THREADS */
 #       else  #       else
           old_segv_handler = signal(SIGSEGV, h);            old_segv_handler = signal(SIGSEGV, h);
 #         ifdef SIGBUS  #         ifdef SIGBUS
Line 584  ptr_t GC_get_stack_base()
Line 764  ptr_t GC_get_stack_base()
   
     void GC_reset_fault_handler()      void GC_reset_fault_handler()
     {      {
 #       if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1)  #       if defined(SUNOS5SIGS) || defined(IRIX5) \
              || defined(OSF1) || defined(HURD)
           (void) sigaction(SIGSEGV, &old_segv_act, 0);            (void) sigaction(SIGSEGV, &old_segv_act, 0);
 #         if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \  #         if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
              || defined(HPUX)               || defined(HPUX) || defined(HURD)
               (void) sigaction(SIGBUS, &old_bus_act, 0);                (void) sigaction(SIGBUS, &old_bus_act, 0);
 #         endif  #         endif
 #       else  #       else
Line 599  ptr_t GC_get_stack_base()
Line 780  ptr_t GC_get_stack_base()
     }      }
   
     /* Return the first nonaddressible location > p (up) or     */      /* Return the first nonaddressible location > p (up) or     */
     /* the smallest location q s.t. [q,p] is addressible (!up). */      /* 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 GC_find_limit(p, up)
     ptr_t p;      ptr_t p;
     GC_bool up;      GC_bool up;
Line 632  ptr_t GC_get_stack_base()
Line 814  ptr_t GC_get_stack_base()
     }      }
 # endif  # endif
   
   #if defined(ECOS) || defined(NOSYS)
     ptr_t GC_get_stack_base()
     {
       return STACKBOTTOM;
     }
   #endif
   
 #ifdef LINUX_STACKBOTTOM  #ifdef LINUX_STACKBOTTOM
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/stat.h>  #include <sys/stat.h>
   #include <ctype.h>
   
 # define STAT_SKIP 27   /* Number of fields preceding startstack        */  # define STAT_SKIP 27   /* Number of fields preceding startstack        */
                         /* field in /proc/self/stat                     */                          /* field in /proc/self/stat                     */
Line 644  ptr_t GC_get_stack_base()
Line 834  ptr_t GC_get_stack_base()
   extern ptr_t __libc_stack_end;    extern ptr_t __libc_stack_end;
   
 # ifdef IA64  # 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);
       }
   
 #   pragma weak __libc_ia64_register_backing_store_base  #   pragma weak __libc_ia64_register_backing_store_base
     extern ptr_t __libc_ia64_register_backing_store_base;      extern ptr_t __libc_ia64_register_backing_store_base;
   
     ptr_t GC_get_register_stack_base(void)      ptr_t GC_get_register_stack_base(void)
     {      {
       if (0 != &__libc_ia64_register_backing_store_base) {        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;          return __libc_ia64_register_backing_store_base;
       } else {        } else {
         word result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;          word result = backing_store_base_from_proc();
         result += BACKING_STORE_ALIGNMENT - 1;          if (0 == result) {
         result &= ~(BACKING_STORE_ALIGNMENT - 1);            /* 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;
       }        }
     }      }
Line 666  ptr_t GC_get_stack_base()
Line 894  ptr_t GC_get_stack_base()
     /* using direct I/O system calls in order to avoid calling malloc   */      /* using direct I/O system calls in order to avoid calling malloc   */
     /* in case REDIRECT_MALLOC is defined.                              */      /* in case REDIRECT_MALLOC is defined.                              */
 #   define STAT_BUF_SIZE 4096  #   define STAT_BUF_SIZE 4096
 #   if defined(GC_USE_LD_WRAP)  #   define STAT_READ read
 #       define STAT_READ __real_read            /* Should probably call the real read, if read is wrapped.    */
 #   else  
 #       define STAT_READ read  
 #   endif  
     char stat_buf[STAT_BUF_SIZE];      char stat_buf[STAT_BUF_SIZE];
     int f;      int f;
     char c;      char c;
Line 679  ptr_t GC_get_stack_base()
Line 904  ptr_t GC_get_stack_base()
   
     /* First try the easy way.  This should work for glibc 2.2  */      /* First try the easy way.  This should work for glibc 2.2  */
       if (0 != &__libc_stack_end) {        if (0 != &__libc_stack_end) {
         return __libc_stack_end;  #       ifdef 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
             return __libc_stack_end;
   #       endif
       }        }
     f = open("/proc/self/stat", O_RDONLY);      f = open("/proc/self/stat", O_RDONLY);
     if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {      if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
Line 716  ptr_t GC_get_stack_base()
Line 950  ptr_t GC_get_stack_base()
   
   ptr_t GC_freebsd_stack_base(void)    ptr_t GC_freebsd_stack_base(void)
   {    {
     int nm[2] = { CTL_KERN, KERN_USRSTACK}, base, len, r;      int nm[2] = {CTL_KERN, KERN_USRSTACK};
       ptr_t base;
       size_t len = sizeof(ptr_t);
       int r = sysctl(nm, 2, &base, &len, NULL, 0);
   
     len = sizeof(int);  
     r = sysctl(nm, 2, &base, &len, NULL, 0);  
   
     if (r) ABORT("Error getting stack base");      if (r) ABORT("Error getting stack base");
   
     return (ptr_t)base;      return base;
   }    }
   
 #endif /* FREEBSD_STACKBOTTOM */  #endif /* FREEBSD_STACKBOTTOM */
Line 733  ptr_t GC_get_stack_base()
Line 967  ptr_t GC_get_stack_base()
   
 ptr_t GC_get_stack_base()  ptr_t GC_get_stack_base()
 {  {
   #   if defined(HEURISTIC1) || defined(HEURISTIC2) || \
          defined(LINUX_STACKBOTTOM) || defined(FREEBSD_STACKBOTTOM)
     word dummy;      word dummy;
     ptr_t result;      ptr_t result;
   #   endif
   
 #   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)  #   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
   
Line 892  void GC_register_data_segments()
Line 1129  void GC_register_data_segments()
   /* Unfortunately, we have to handle win32s very differently from NT,  */    /* Unfortunately, we have to handle win32s very differently from NT,  */
   /* Since VirtualQuery has very different semantics.  In particular,   */    /* Since VirtualQuery has very different semantics.  In particular,   */
   /* under win32s a VirtualQuery call on an unmapped page returns an    */    /* under win32s a VirtualQuery call on an unmapped page returns an    */
   /* invalid result.  Under GC_register_data_segments is a noop and     */    /* invalid result.  Under NT, GC_register_data_segments is a noop and */
   /* all real work is done by GC_register_dynamic_libraries.  Under     */    /* all real work is done by GC_register_dynamic_libraries.  Under     */
   /* win32s, we cannot find the data segments associated with dll's.    */    /* win32s, we cannot find the data segments associated with dll's.    */
   /* We rgister the main data segment here.                             */    /* We register the main data segment here.                            */
   GC_bool GC_win32s = FALSE;    /* We're running under win32s.  */    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_is_win32s()  
   {  
       DWORD v = GetVersion();  
   
       /* Check that this is not NT, and Windows major version <= 3      */  
       return ((v & 0x80000000) && (v & 0xff) <= 3);  
   }  
   
   void GC_init_win32()    void GC_init_win32()
   {    {
       GC_win32s = GC_is_win32s();      /* 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);
   }    }
   
   /* Return the smallest address a such that VirtualQuery               */    /* Return the smallest address a such that VirtualQuery               */
Line 934  void GC_register_data_segments()
Line 1168  void GC_register_data_segments()
     return(p);      return(p);
   }    }
 # endif  # endif
   
   # ifndef REDIRECT_MALLOC
     /* We maintain a linked list of AllocationBase values that we know    */
     /* correspond to malloc heap sections.  Currently this is only called */
     /* during a GC.  But there is some hope that for long running         */
     /* programs we will eventually see most heap sections.                */
   
     /* In the long run, it would be more reliable to occasionally walk    */
     /* the malloc heap with HeapWalk on the default heap.  But that       */
     /* apparently works only for NT-based Windows.                        */
   
     /* In the long run, a better data structure would also be nice ...    */
     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)
     {
       struct GC_malloc_heap_list *q = GC_malloc_heap_l;
   
       while (0 != q) {
         if (q -> allocation_base == p) return TRUE;
         q = q -> next;
       }
       return FALSE;
     }
   
     void *GC_get_allocation_base(void *p)
     {
       MEMORY_BASIC_INFORMATION buf;
       DWORD 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.     */
   
     void GC_add_current_malloc_heap()
     {
       struct GC_malloc_heap_list *new_l =
                    malloc(sizeof(struct GC_malloc_heap_list));
       void * candidate = GC_get_allocation_base(new_l);
   
       if (new_l == 0) return;
       if (GC_is_malloc_heap_base(candidate)) {
         /* Try a little harder to find malloc heap.                       */
           size_t req_size = 10000;
           do {
             void *p = malloc(req_size);
             if (0 == p) { free(new_l); return; }
             candidate = GC_get_allocation_base(p);
             free(p);
             req_size *= 2;
           } while (GC_is_malloc_heap_base(candidate)
                    && req_size < GC_max_root_size/10 && req_size < 500000);
           if (GC_is_malloc_heap_base(candidate)) {
             free(new_l); return;
           }
       }
   #   ifdef CONDPRINT
         if (GC_print_stats)
             GC_printf1("Found new system malloc AllocationBase at 0x%lx\n",
                        candidate);
   #   endif
       new_l -> allocation_base = candidate;
       new_l -> next = GC_malloc_heap_l;
       GC_malloc_heap_l = new_l;
     }
   # endif /* REDIRECT_MALLOC */
   
   /* Is p the start of either the malloc heap, or of one of our */    /* Is p the start of either the malloc heap, or of one of our */
   /* heap sections?                                             */    /* heap sections?                                             */
   GC_bool GC_is_heap_base (ptr_t p)    GC_bool GC_is_heap_base (ptr_t p)
   {    {
   
      register unsigned i;       unsigned i;
   
 #    ifndef REDIRECT_MALLOC  #    ifndef REDIRECT_MALLOC
        static ptr_t malloc_heap_pointer = 0;         static word last_gc_no = -1;
   
        if (0 == malloc_heap_pointer) {         if (last_gc_no != GC_gc_no) {
          MEMORY_BASIC_INFORMATION buf;           GC_add_current_malloc_heap();
          void *pTemp = malloc( 1 );           last_gc_no = GC_gc_no;
          register DWORD result = VirtualQuery(pTemp, &buf, sizeof(buf));  
   
          free( pTemp );  
   
   
          if (result != sizeof(buf)) {  
              ABORT("Weird VirtualQuery result");  
          }  
          malloc_heap_pointer = (ptr_t)(buf.AllocationBase);  
        }         }
        if (p == malloc_heap_pointer) return(TRUE);         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  #    endif
      for (i = 0; i < GC_n_heap_bases; i++) {       for (i = 0; i < GC_n_heap_bases; i++) {
          if (GC_heap_bases[i] == p) return(TRUE);           if (GC_heap_bases[i] == p) return TRUE;
      }       }
      return(FALSE);       return FALSE ;
   }    }
   
 # ifdef MSWIN32  # ifdef MSWIN32
Line 976  void GC_register_data_segments()
Line 1276  void GC_register_data_segments()
       char * base;        char * base;
       char * limit, * new_limit;        char * limit, * new_limit;
   
       if (!GC_win32s) return;        if (!GC_no_win32_dlls) return;
       p = base = limit = GC_least_described_address(static_root);        p = base = limit = GC_least_described_address(static_root);
       while (p < GC_sysinfo.lpMaximumApplicationAddress) {        while (p < GC_sysinfo.lpMaximumApplicationAddress) {
         result = VirtualQuery(p, &buf, sizeof(buf));          result = VirtualQuery(p, &buf, sizeof(buf));
Line 1013  void GC_register_data_segments()
Line 1313  void GC_register_data_segments()
   
 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \  # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)        || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
 char * GC_SysVGetDataStart(max_page_size, etext_addr)  ptr_t GC_SysVGetDataStart(max_page_size, etext_addr)
 int max_page_size;  int max_page_size;
 int * etext_addr;  int * etext_addr;
 {  {
Line 1039  int * etext_addr;
Line 1339  int * etext_addr;
         /* string constants in the text segment, but after etext.       */          /* string constants in the text segment, but after etext.       */
         /* Use plan B.  Note that we now know there is a gap between    */          /* Use plan B.  Note that we now know there is a gap between    */
         /* text and data segments, so plan A bought us something.       */          /* text and data segments, so plan A bought us something.       */
         result = (char *)GC_find_limit((ptr_t)(DATAEND) - MIN_PAGE_SIZE, FALSE);          result = (char *)GC_find_limit((ptr_t)(DATAEND), FALSE);
     }      }
     return((char *)result);      return((ptr_t)result);
 }  }
 # endif  # endif
   
   // # if defined(FREEBSD) && defined(I386) && !defined(PCR)
   # if defined(FREEBSD) && ( defined(I386) || 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     */
   /* 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;
   {
       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)
                                 & ~((word)max_page_size - 1);
       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;
           GC_reset_fault_handler();
       } else {
           GC_reset_fault_handler();
           /* As above, we go to plan B    */
           result = GC_find_limit((ptr_t)(DATAEND), FALSE);
       }
       return(result);
   }
   
   # endif
   
   
 #ifdef AMIGA  #ifdef AMIGA
   
 #  define GC_AMIGA_DS  #  define GC_AMIGA_DS
Line 1056  int * etext_addr;
Line 1390  int * etext_addr;
   
 void GC_register_data_segments()  void GC_register_data_segments()
 {  {
 #   if !defined(PCR) && !defined(SRC_M3) && !defined(NEXT) && !defined(MACOS) \  #   if !defined(PCR) && !defined(SRC_M3) && !defined(MACOS)
        && !defined(MACOSX)  #     if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS)
 #     if defined(REDIRECT_MALLOC) && defined(SOLARIS_THREADS)  
         /* As of Solaris 2.3, the Solaris threads implementation        */          /* As of Solaris 2.3, the Solaris threads implementation        */
         /* allocates the data structure for the initial thread with     */          /* allocates the data structure for the initial thread with     */
         /* sbrk at process startup.  It needs to be scanned, so that    */          /* sbrk at process startup.  It needs to be scanned, so that    */
Line 1069  void GC_register_data_segments()
Line 1402  void GC_register_data_segments()
         GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);          GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
 #     else  #     else
         GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);          GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
   #       if defined(DATASTART2)
            GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), FALSE);
   #       endif
 #     endif  #     endif
 #   endif  #   endif
 #   if !defined(PCR) && (defined(NEXT) || defined(MACOSX))  
       GC_add_roots_inner(DATASTART, (char *) get_end(), FALSE);  
 #   endif  
 #   if defined(MACOS)  #   if defined(MACOS)
     {      {
 #   if defined(THINK_C)  #   if defined(THINK_C)
Line 1183  word bytes;
Line 1516  word bytes;
 ptr_t GC_unix_get_mem(bytes)  ptr_t GC_unix_get_mem(bytes)
 word bytes;  word bytes;
 {  {
     static GC_bool initialized = FALSE;  
     static int fd;  
     void *result;      void *result;
     static ptr_t last_addr = HEAP_START;      static ptr_t last_addr = HEAP_START;
   
     if (!initialized) {  #   ifndef USE_MMAP_ANON
         fd = open("/dev/zero", O_RDONLY);        static GC_bool initialized = FALSE;
         initialized = TRUE;        static int fd;
     }  
         if (!initialized) {
             fd = open("/dev/zero", O_RDONLY);
             fcntl(fd, F_SETFD, FD_CLOEXEC);
             initialized = TRUE;
         }
   #   endif
   
     if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");      if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
     result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,  #   ifdef USE_MMAP_ANON
                   GC_MMAP_FLAGS, fd, 0/* offset */);        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
     if (result == MAP_FAILED) return(0);      if (result == MAP_FAILED) return(0);
     last_addr = (ptr_t)result + bytes + GC_page_size - 1;      last_addr = (ptr_t)result + bytes + GC_page_size - 1;
     last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));      last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
Line 1267  void * os2_alloc(size_t bytes)
Line 1610  void * os2_alloc(size_t bytes)
 SYSTEM_INFO GC_sysinfo;  SYSTEM_INFO GC_sysinfo;
 # endif  # endif
   
   
 # ifdef MSWIN32  # ifdef MSWIN32
   
   # ifdef USE_GLOBAL_ALLOC
   #   define GLOBAL_ALLOC_TEST 1
   # else
   #   define GLOBAL_ALLOC_TEST GC_no_win32_dlls
   # endif
   
 word GC_n_heap_bases = 0;  word GC_n_heap_bases = 0;
   
 ptr_t GC_win32_get_mem(bytes)  ptr_t GC_win32_get_mem(bytes)
Line 1276  word bytes;
Line 1625  word bytes;
 {  {
     ptr_t result;      ptr_t result;
   
     if (GC_win32s) {      if (GLOBAL_ALLOC_TEST) {
         /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE.    */          /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE.    */
         /* There are also unconfirmed rumors of other           */          /* There are also unconfirmed rumors of other           */
         /* problems, so we dodge the issue.                     */          /* problems, so we dodge the issue.                     */
         result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);          result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
         result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));          result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));
     } else {      } else {
         result = (ptr_t) VirtualAlloc(NULL, bytes,          /* VirtualProtect only works on regions returned by a   */
           /* single VirtualAlloc call.  Thus we allocate one      */
           /* extra page, which will prevent merging of blocks     */
           /* in separate regions, and eliminate any temptation    */
           /* to call VirtualProtect on a range spanning regions.  */
           /* This wastes a small amount of memory, and risks      */
           /* increased fragmentation.  But better alternatives    */
           /* would require effort.                                */
           result = (ptr_t) VirtualAlloc(NULL, bytes + 1,
                                       MEM_COMMIT | MEM_RESERVE,                                        MEM_COMMIT | MEM_RESERVE,
                                       PAGE_EXECUTE_READWRITE);                                        PAGE_EXECUTE_READWRITE);
     }      }
Line 1297  word bytes;
Line 1654  word bytes;
   
 void GC_win32_free_heap ()  void GC_win32_free_heap ()
 {  {
     if (GC_win32s) {      if (GC_no_win32_dlls) {
         while (GC_n_heap_bases > 0) {          while (GC_n_heap_bases > 0) {
             GlobalFree (GC_heap_bases[--GC_n_heap_bases]);              GlobalFree (GC_heap_bases[--GC_n_heap_bases]);
             GC_heap_bases[GC_n_heap_bases] = 0;              GC_heap_bases[GC_n_heap_bases] = 0;
Line 1339  word bytes;
Line 1696  word bytes;
         /* Reserve more pages */          /* Reserve more pages */
         word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)          word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
                          & ~(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.                         */
         result = (ptr_t) VirtualAlloc(NULL, res_bytes,          result = (ptr_t) VirtualAlloc(NULL, res_bytes,
                                       MEM_RESERVE | MEM_TOP_DOWN,                                        MEM_RESERVE | MEM_TOP_DOWN,
                                       PAGE_EXECUTE_READWRITE);                                        PAGE_EXECUTE_READWRITE);
Line 1469  void GC_remap(ptr_t start, word bytes)
Line 1830  void GC_remap(ptr_t start, word bytes)
       }        }
 #   else  #   else
       if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);        if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);
         fcntl(zero_descr, F_SETFD, FD_CLOEXEC);
       if (0 == start_addr) return;        if (0 == start_addr) return;
       result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,        result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
                     MAP_FIXED | MAP_PRIVATE, zero_descr, 0);                      MAP_FIXED | MAP_PRIVATE, zero_descr, 0);
Line 1619  void GC_default_push_other_roots GC_PROTO((void))
Line 1981  void GC_default_push_other_roots GC_PROTO((void))
   
 # endif /* SRC_M3 */  # endif /* SRC_M3 */
   
 # if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \  # if defined(GC_SOLARIS_THREADS) || defined(GC_PTHREADS) || \
      || defined(IRIX_THREADS) || defined(LINUX_THREADS) \       defined(GC_WIN32_THREADS)
      || defined(HPUX_THREADS)  
   
 extern void GC_push_all_stacks();  extern void GC_push_all_stacks();
   
Line 1630  void GC_default_push_other_roots GC_PROTO((void))
Line 1991  void GC_default_push_other_roots GC_PROTO((void))
     GC_push_all_stacks();      GC_push_all_stacks();
 }  }
   
 # endif /* SOLARIS_THREADS || ... */  # endif /* GC_SOLARIS_THREADS || GC_PTHREADS */
   
 void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;  void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
   
 #endif  #endif /* THREADS */
   
 /*  /*
  * Routines for accessing dirty  bits on virtual pages.   * Routines for accessing dirty  bits on virtual pages.
Line 1656  void (*GC_push_other_roots) GC_PROTO((void)) = GC_defa
Line 2017  void (*GC_push_other_roots) GC_PROTO((void)) = GC_defa
  *              make sure that other system calls are similarly protected   *              make sure that other system calls are similarly protected
  *              or write only to the stack.   *              or write only to the stack.
  */   */
   
 GC_bool GC_dirty_maintained = FALSE;  GC_bool GC_dirty_maintained = FALSE;
   
 # ifdef DEFAULT_VDB  # ifdef DEFAULT_VDB
Line 1670  GC_bool GC_dirty_maintained = FALSE;
Line 2030  GC_bool GC_dirty_maintained = FALSE;
 /* Initialize virtual dirty bit implementation.                 */  /* Initialize virtual dirty bit implementation.                 */
 void GC_dirty_init()  void GC_dirty_init()
 {  {
   #   ifdef PRINTSTATS
         GC_printf0("Initializing DEFAULT_VDB...\n");
   #   endif
     GC_dirty_maintained = TRUE;      GC_dirty_maintained = TRUE;
 }  }
   
Line 1711  word n;
Line 2074  word n;
 {  {
 }  }
   
 /* A call hints that h is about to be written.  */  /* A call that:                                         */
 /* May speed up some dirty bit implementations. */  /* I) hints that [h, h+nblocks) is about to be written. */
   /* II) guarantees that protection is removed.           */
   /* (I) may speed up some dirty bit implementations.     */
   /* (II) may be essential if we need to ensure that      */
   /* pointer-free system call buffers in the heap are     */
   /* not protected.                                       */
 /*ARGSUSED*/  /*ARGSUSED*/
 void GC_write_hint(h)  void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;  struct hblk *h;
   word nblocks;
   GC_bool is_ptrfree;
 {  {
 }  }
   
Line 1731  struct hblk *h;
Line 2101  struct hblk *h;
 /*  /*
  * This implementation maintains dirty bits itself by catching write   * This implementation maintains dirty bits itself by catching write
  * faults and keeping track of them.  We assume nobody else catches   * faults and keeping track of them.  We assume nobody else catches
  * SIGBUS or SIGSEGV.  We assume no write faults occur in system calls   * SIGBUS or SIGSEGV.  We assume no write faults occur in system calls.
  * except as a result of a read system call.  This means clients must   * This means that clients must ensure that system calls don't write
  * either ensure that system calls do not touch the heap, or must   * to the write-protected heap.  Probably the best way to do this is to
  * provide their own wrappers analogous to the one for read.   * ensure that system calls write at most to POINTERFREE objects in the
    * 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 assume the page size is a multiple of HBLKSIZE.
  * This implementation is currently SunOS 4.X and IRIX 5.X specific, though we   * We prefer them to be the same.  We avoid protecting POINTERFREE
  * tried to use portable code where easily possible.  It is known   * objects only if they are the same.
  * not to work under a number of other systems.  
  */   */
   
 # if !defined(MSWIN32) && !defined(MSWINCE)  # if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN)
   
 #   include <sys/mman.h>  #   include <sys/mman.h>
 #   include <signal.h>  #   include <signal.h>
Line 1760  struct hblk *h;
Line 2134  struct hblk *h;
   
 # else  # else
   
   # ifdef DARWIN
       /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to
          decrease the likelihood of some of the problems described below. */
       #include <mach/vm_map.h>
       extern mach_port_t GC_task_self;
       #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) \
           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"); \
           }
   # else
   
 #   ifndef MSWINCE  #   ifndef MSWINCE
 #     include <signal.h>  #     include <signal.h>
 #   endif  #   endif
Line 1777  struct hblk *h;
Line 2168  struct hblk *h;
                               &protect_junk)) { \                                &protect_junk)) { \
             ABORT("un-VirtualProtect failed"); \              ABORT("un-VirtualProtect failed"); \
           }            }
   # endif /* !DARWIN */
 # endif  # endif /* MSWIN32 || MSWINCE || DARWIN */
   
 #if defined(SUNOS4) || defined(FREEBSD)  #if defined(SUNOS4) || defined(FREEBSD)
     typedef void (* SIG_PF)();      typedef void (* SIG_PF)();
 #endif  #endif /* SUNOS4 || FREEBSD */
 #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) || defined(MACOSX)  
   #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \
       || defined(HURD)
 # ifdef __STDC__  # ifdef __STDC__
     typedef void (* SIG_PF)(int);      typedef void (* SIG_PF)(int);
 # else  # else
     typedef void (* SIG_PF)();      typedef void (* SIG_PF)();
 # endif  # endif
 #endif  #endif /* SUNOS5SIGS || OSF1 || LINUX || HURD */
   
 #if defined(MSWIN32)  #if defined(MSWIN32)
     typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF;      typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF;
 #   undef SIG_DFL  #   undef SIG_DFL
Line 1801  struct hblk *h;
Line 2195  struct hblk *h;
 #   define SIG_DFL (SIG_PF) (-1)  #   define SIG_DFL (SIG_PF) (-1)
 #endif  #endif
   
 #if defined(IRIX5) || defined(OSF1)  #if defined(IRIX5) || defined(OSF1) || defined(HURD)
     typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);      typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
 #endif  #endif /* IRIX5 || OSF1 || HURD */
   
 #if defined(SUNOS5SIGS)  #if defined(SUNOS5SIGS)
 # ifdef HPUX  # ifdef HPUX
 #   define SIGINFO __siginfo  #   define SIGINFO __siginfo
Line 1815  struct hblk *h;
Line 2210  struct hblk *h;
 # else  # else
     typedef void (* REAL_SIG_PF)();      typedef void (* REAL_SIG_PF)();
 # endif  # endif
 #endif  #endif /* SUNOS5SIGS */
   
 #if defined(LINUX)  #if defined(LINUX)
 #   include <linux/version.h>  #   if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
 #   if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(IA64)  
       typedef struct sigcontext s_c;        typedef struct sigcontext s_c;
 #   else  #   else  /* glibc < 2.2 */
       typedef struct sigcontext_struct s_c;  #     include <linux/version.h>
 #   endif  #     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)  #   if defined(ALPHA) || defined(M68K)
       typedef void (* REAL_SIG_PF)(int, int, s_c *);        typedef void (* REAL_SIG_PF)(int, int, s_c *);
 #   else  #   else
Line 1845  struct hblk *h;
Line 2245  struct hblk *h;
         return (char *)faultaddr;          return (char *)faultaddr;
     }      }
 #   endif /* !ALPHA */  #   endif /* !ALPHA */
 # endif  # endif /* LINUX */
   
 # if defined(MACOSX) /* Should also test for PowerPC? */  #ifndef DARWIN
     typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);  
   
 /* Decodes the machine instruction which was responsible for the sending of the  
    SIGBUS signal. Sadly this is the only way to find the faulting address because  
    the signal handler doesn't get it directly from the kernel (although it is  
    available on the Mach level, but droppped by the BSD personality before it  
    calls our signal handler...)  
    This code should be able to deal correctly with all PPCs starting from the  
    601 up to and including the G4s (including Velocity Engine). */  
 #define EXTRACT_OP1(iw)     (((iw) & 0xFC000000) >> 26)  
 #define EXTRACT_OP2(iw)     (((iw) & 0x000007FE) >> 1)  
 #define EXTRACT_REGA(iw)    (((iw) & 0x001F0000) >> 16)  
 #define EXTRACT_REGB(iw)    (((iw) & 0x03E00000) >> 21)  
 #define EXTRACT_REGC(iw)    (((iw) & 0x0000F800) >> 11)  
 #define EXTRACT_DISP(iw)    ((short *) &(iw))[1]  
   
 static char *get_fault_addr(struct sigcontext *scp)  
 {  
    unsigned int   instr = *((unsigned int *) scp->sc_ir);  
    unsigned int * regs = &((unsigned int *) scp->sc_regs)[2];  
    int            disp = 0, tmp;  
    unsigned int   baseA = 0, baseB = 0;  
    unsigned int   addr, alignmask = 0xFFFFFFFF;  
   
 #ifdef GC_DEBUG_DECODER  
    GC_err_printf1("Instruction: 0x%lx\n", instr);  
    GC_err_printf1("Opcode 1: d\n", (int)EXTRACT_OP1(instr));  
 #endif  
    switch(EXTRACT_OP1(instr)) {  
       case 38:   /* stb */  
       case 39:   /* stbu */  
       case 54:   /* stfd */  
       case 55:   /* stfdu */  
       case 52:   /* stfs */  
       case 53:   /* stfsu */  
       case 44:   /* sth */  
       case 45:   /* sthu */  
       case 47:   /* stmw */  
       case 36:   /* stw */  
       case 37:   /* stwu */  
             tmp = EXTRACT_REGA(instr);  
             if(tmp > 0)  
                baseA = regs[tmp];  
             disp = EXTRACT_DISP(instr);  
             break;  
       case 31:  
 #ifdef GC_DEBUG_DECODER  
             GC_err_printf1("Opcode 2: %d\n", (int)EXTRACT_OP2(instr));  
 #endif  
             switch(EXTRACT_OP2(instr)) {  
                case 86:    /* dcbf */  
                case 54:    /* dcbst */  
                case 1014:  /* dcbz */  
                case 247:   /* stbux */  
                case 215:   /* stbx */  
                case 759:   /* stfdux */  
                case 727:   /* stfdx */  
                case 983:   /* stfiwx */  
                case 695:   /* stfsux */  
                case 663:   /* stfsx */  
                case 918:   /* sthbrx */  
                case 439:   /* sthux */  
                case 407:   /* sthx */  
                case 661:   /* stswx */  
                case 662:   /* stwbrx */  
                case 150:   /* stwcx. */  
                case 183:   /* stwux */  
                case 151:   /* stwx */  
                case 135:   /* stvebx */  
                case 167:   /* stvehx */  
                case 199:   /* stvewx */  
                case 231:   /* stvx */  
                case 487:   /* stvxl */  
                      tmp = EXTRACT_REGA(instr);  
                      if(tmp > 0)  
                         baseA = regs[tmp];  
                         baseB = regs[EXTRACT_REGC(instr)];  
                         /* determine Altivec alignment mask */  
                         switch(EXTRACT_OP2(instr)) {  
                            case 167:   /* stvehx */  
                                  alignmask = 0xFFFFFFFE;  
                                  break;  
                            case 199:   /* stvewx */  
                                  alignmask = 0xFFFFFFFC;  
                                  break;  
                            case 231:   /* stvx */  
                                  alignmask = 0xFFFFFFF0;  
                                  break;  
                            case 487:  /* stvxl */  
                                  alignmask = 0xFFFFFFF0;  
                                  break;  
                         }  
                         break;  
                case 725:   /* stswi */  
                      tmp = EXTRACT_REGA(instr);  
                      if(tmp > 0)  
                         baseA = regs[tmp];  
                         break;  
                default:   /* ignore instruction */  
 #ifdef GC_DEBUG_DECODER  
                      GC_err_printf("Ignored by inner handler\n");  
 #endif  
                      return NULL;  
                     break;  
             }  
             break;  
       default:   /* ignore instruction */  
 #ifdef GC_DEBUG_DECODER  
             GC_err_printf("Ignored by main handler\n");  
 #endif  
             return NULL;  
             break;  
    }  
   
    addr = (baseA + baseB) + disp;  
   addr &= alignmask;  
 #ifdef GC_DEBUG_DECODER  
    GC_err_printf1("BaseA: %d\n", baseA);  
    GC_err_printf1("BaseB: %d\n", baseB);  
    GC_err_printf1("Disp:  %d\n", disp);  
    GC_err_printf1("Address: %d\n", addr);  
 #endif  
    return (char *)addr;  
 }  
 #endif /* MACOSX */  
   
 SIG_PF GC_old_bus_handler;  SIG_PF GC_old_bus_handler;
 SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */  SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
   #endif /* !DARWIN */
   
 #ifdef THREADS  #if defined(THREADS)
 /* We need to lock around the bitmap update in the write fault handler  */  /* We need to lock around the bitmap update in the write fault handler  */
 /* in order to avoid the risk of losing a bit.  We do this with a       */  /* in order to avoid the risk of losing a bit.  We do this with a       */
 /* test-and-set spin lock if we know how to do that.  Otherwise we      */  /* test-and-set spin lock if we know how to do that.  Otherwise we      */
Line 1988  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2263  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #ifdef GC_TEST_AND_SET_DEFINED  #ifdef GC_TEST_AND_SET_DEFINED
   static VOLATILE unsigned int fault_handler_lock = 0;    static VOLATILE unsigned int fault_handler_lock = 0;
   void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {    void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
     while (GC_test_and_set(&fault_handler_lock));      while (GC_test_and_set(&fault_handler_lock)) {}
     /* Could also revert to set_pht_entry_from_index_safe if initial    */      /* Could also revert to set_pht_entry_from_index_safe if initial    */
     /* GC_test_and_set fails.                                           */      /* GC_test_and_set fails.                                           */
     set_pht_entry_from_index(db, index);      set_pht_entry_from_index(db, index);
Line 2026  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2301  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #endif /* !THREADS */  #endif /* !THREADS */
   
 /*ARGSUSED*/  /*ARGSUSED*/
   #if !defined(DARWIN)
 # if defined (SUNOS4) || defined(FREEBSD)  # if defined (SUNOS4) || defined(FREEBSD)
     void GC_write_fault_handler(sig, code, scp, addr)      void GC_write_fault_handler(sig, code, scp, addr)
     int sig, code;      int sig, code;
Line 2041  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2317  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #     define SIG_OK (sig == SIGBUS)  #     define SIG_OK (sig == SIGBUS)
 #     define CODE_OK (code == BUS_PAGE_FAULT)  #     define CODE_OK (code == BUS_PAGE_FAULT)
 #   endif  #   endif
 # endif  # endif /* SUNOS4 || FREEBSD */
 # if defined(IRIX5) || defined(OSF1)  
   # if defined(IRIX5) || defined(OSF1) || defined(HURD)
 #   include <errno.h>  #   include <errno.h>
     void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)      void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
 #   define SIG_OK (sig == SIGSEGV)  
 #   ifdef OSF1  #   ifdef OSF1
   #     define SIG_OK (sig == SIGSEGV)
 #     define CODE_OK (code == 2 /* experimentally determined */)  #     define CODE_OK (code == 2 /* experimentally determined */)
 #   endif  #   endif
 #   ifdef IRIX5  #   ifdef IRIX5
   #     define SIG_OK (sig == SIGSEGV)
 #     define CODE_OK (code == EACCES)  #     define CODE_OK (code == EACCES)
 #   endif  #   endif
 # endif  #   ifdef HURD
   #     define SIG_OK (sig == SIGBUS || sig == SIGSEGV)
   #     define CODE_OK  TRUE
   #   endif
   # endif /* IRIX5 || OSF1 || HURD */
   
 # if defined(LINUX)  # if defined(LINUX)
 #   if defined(ALPHA) || defined(M68K)  #   if defined(ALPHA) || defined(M68K)
       void GC_write_fault_handler(int sig, int code, s_c * sc)        void GC_write_fault_handler(int sig, int code, s_c * sc)
Line 2060  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2343  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #     if defined(IA64) || defined(HP_PA)  #     if defined(IA64) || defined(HP_PA)
         void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp)          void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp)
 #     else  #     else
         void GC_write_fault_handler(int sig, s_c sc)  #       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
 #   endif  #   endif
 #   define SIG_OK (sig == SIGSEGV)  #   define SIG_OK (sig == SIGSEGV)
Line 2068  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2355  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
         /* Empirically c.trapno == 14, on IA32, but is that useful?     */          /* Empirically c.trapno == 14, on IA32, but is that useful?     */
         /* Should probably consider alignment issues on other           */          /* Should probably consider alignment issues on other           */
         /* architectures.                                               */          /* architectures.                                               */
 # endif  # endif /* LINUX */
   
 # if defined(SUNOS5SIGS)  # if defined(SUNOS5SIGS)
 #  ifdef __STDC__  #  ifdef __STDC__
     void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context)      void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context)
Line 2089  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2377  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #     define SIG_OK (sig == SIGSEGV)  #     define SIG_OK (sig == SIGSEGV)
 #     define CODE_OK (scp -> si_code == SEGV_ACCERR)  #     define CODE_OK (scp -> si_code == SEGV_ACCERR)
 #   endif  #   endif
 # endif  # endif /* SUNOS5SIGS */
   
 # if defined(MACOSX)  
     void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)  
 #   define SIG_OK (sig == SIGBUS)  
 #   define CODE_OK (code == 0 /* experimentally determined */)  
 # endif  
   
 # if defined(MSWIN32) || defined(MSWINCE)  # if defined(MSWIN32) || defined(MSWINCE)
     LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info)      LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info)
 #   define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode == \  #   define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode == \
                         STATUS_ACCESS_VIOLATION)                          STATUS_ACCESS_VIOLATION)
 #   define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1)  #   define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1)
                         /* Write fault */                          /* Write fault */
 # endif  # endif /* MSWIN32 || MSWINCE */
 {  {
     register unsigned i;      register unsigned i;
   #   if defined(HURD)
           char *addr = (char *) code;
   #   endif
 #   ifdef IRIX5  #   ifdef IRIX5
         char * addr = (char *) (size_t) (scp -> sc_badvaddr);          char * addr = (char *) (size_t) (scp -> sc_badvaddr);
 #   endif  #   endif
Line 2116  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2401  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
         char * addr = (char *) (scp -> si_addr);          char * addr = (char *) (scp -> si_addr);
 #   endif  #   endif
 #   ifdef LINUX  #   ifdef LINUX
 #     ifdef I386  #     if defined(I386) || defined (X86_64)
         char * addr = (char *) (sc.cr2);          char * addr = (char *) (sc.cr2);
 #     else  #     else
 #       if defined(M68K)  #       if defined(M68K)
Line 2160  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2445  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #             if defined(POWERPC)  #             if defined(POWERPC)
                 char * addr = (char *) (sc.regs->dar);                  char * addr = (char *) (sc.regs->dar);
 #             else  #             else
                 --> architecture not supported  #               if defined(ARM32)
                     char * addr = (char *)sc.fault_address;
   #               else
                     --> architecture not supported
   #               endif
 #             endif  #             endif
 #           endif  #           endif
 #         endif  #         endif
 #       endif  #       endif
 #     endif  #     endif
 #   endif  #   endif
 #   if defined(MACOSX)  
         char * addr = get_fault_addr(scp);  
 #   endif  
 #   if defined(MSWIN32) || defined(MSWINCE)  #   if defined(MSWIN32) || defined(MSWINCE)
         char * addr = (char *) (exc_info -> ExceptionRecord          char * addr = (char *) (exc_info -> ExceptionRecord
                                 -> ExceptionInformation[1]);                                  -> ExceptionInformation[1]);
Line 2229  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2515  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #                   endif  #                   endif
                     return;                      return;
 #               endif  #               endif
 #               if defined (IRIX5) || defined(OSF1)  #               if defined (IRIX5) || defined(OSF1) || defined(HURD)
                     (*(REAL_SIG_PF)old_handler) (sig, code, scp);                      (*(REAL_SIG_PF)old_handler) (sig, code, scp);
                     return;                      return;
 #               endif  #               endif
 #               ifdef MACOSX  
                     (*(REAL_SIG_PF)old_handler) (sig, code, scp);  
 #               endif  
 #               ifdef MSWIN32  #               ifdef MSWIN32
                     return((*old_handler)(exc_info));                      return((*old_handler)(exc_info));
 #               endif  #               endif
             }              }
         }          }
           UNPROTECT(h, GC_page_size);
           /* We need to make sure that no collection occurs between       */
           /* the UNPROTECT and the setting of the dirty bit.  Otherwise   */
           /* a write by a third thread might go unnoticed.  Reversing     */
           /* the order is just as bad, since we would end up unprotecting */
           /* a page in a GC cycle during which it's not marked.           */
           /* Currently we do this by disabling the thread stopping        */
           /* signals while this handler is running.  An alternative might */
           /* be to record the fact that we're about to unprotect, or      */
           /* have just unprotected a page in the GC's thread structure,   */
           /* and then to have the thread stopping code set the dirty      */
           /* flag, if necessary.                                          */
         for (i = 0; i < divHBLKSZ(GC_page_size); i++) {          for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
             register int index = PHT_HASH(h+i);              register int index = PHT_HASH(h+i);
   
             async_set_pht_entry_from_index(GC_dirty_pages, index);              async_set_pht_entry_from_index(GC_dirty_pages, index);
         }          }
         UNPROTECT(h, GC_page_size);  #       if defined(OSF1)
 #       if defined(OSF1) || defined(LINUX)  
             /* These reset the signal handler each time by default. */              /* These reset the signal handler each time by default. */
             signal(SIGSEGV, (SIG_PF) GC_write_fault_handler);              signal(SIGSEGV, (SIG_PF) GC_write_fault_handler);
 #       endif  #       endif
Line 2266  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2560  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
     ABORT("Unexpected bus error or segmentation fault");      ABORT("Unexpected bus error or segmentation fault");
 #endif  #endif
 }  }
   #endif /* !DARWIN */
   
 /*  /*
  * We hold the allocation lock.  We expect block h to be written   * We hold the allocation lock.  We expect block h to be written
  * shortly.   * shortly.  Ensure that all pages containing any part of the n hblks
    * starting at h are no longer protected.  If is_ptrfree is false,
    * also ensure that they will subsequently appear to be dirty.
  */   */
 void GC_write_hint(h)  void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;  struct hblk *h;
   word nblocks;
   GC_bool is_ptrfree;
 {  {
     register struct hblk * h_trunc;      struct hblk * h_trunc;  /* Truncated to page boundary */
     register unsigned i;      struct hblk * h_end;    /* Page boundary following block end */
     register GC_bool found_clean;      struct hblk * current;
       GC_bool found_clean;
   
     if (!GC_dirty_maintained) return;      if (!GC_dirty_maintained) return;
     h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1));      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;      found_clean = FALSE;
     for (i = 0; i < divHBLKSZ(GC_page_size); i++) {      for (current = h_trunc; current < h_end; ++current) {
         register int index = PHT_HASH(h_trunc+i);          int index = PHT_HASH(current);
   
         if (!get_pht_entry_from_index(GC_dirty_pages, index)) {          if (!is_ptrfree || current < h || current >= h + nblocks) {
             found_clean = TRUE;  
             async_set_pht_entry_from_index(GC_dirty_pages, index);              async_set_pht_entry_from_index(GC_dirty_pages, index);
         }          }
     }      }
     if (found_clean) {      UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc);
         UNPROTECT(h_trunc, GC_page_size);  
     }  
 }  }
   
   #if !defined(DARWIN)
 void GC_dirty_init()  void GC_dirty_init()
 {  {
 #   if defined(SUNOS5SIGS) || defined(IRIX5) /* || defined(OSF1) */  #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \
          defined(OSF1) || defined(HURD)
       struct sigaction  act, oldact;        struct sigaction  act, oldact;
 #     ifdef IRIX5        /* We should probably specify SA_SIGINFO for Linux, and handle    */
         /* the different architectures more uniformly.                    */
   #     if defined(IRIX5) || defined(LINUX) || defined(OSF1) || defined(HURD)
         act.sa_flags    = SA_RESTART;          act.sa_flags    = SA_RESTART;
         act.sa_handler  = GC_write_fault_handler;          act.sa_handler  = (SIG_PF)GC_write_fault_handler;
 #     else  #     else
         act.sa_flags    = SA_RESTART | SA_SIGINFO;          act.sa_flags    = SA_RESTART | SA_SIGINFO;
         act.sa_sigaction = GC_write_fault_handler;          act.sa_sigaction = GC_write_fault_handler;
 #     endif  #     endif
       (void)sigemptyset(&act.sa_mask);        (void)sigemptyset(&act.sa_mask);
   #     ifdef SIG_SUSPEND
           /* Arrange to postpone SIG_SUSPEND while we're in a write fault */
           /* handler.  This effectively makes the handler atomic w.r.t.   */
           /* stopping the world for GC.                                   */
           (void)sigaddset(&act.sa_mask, SIG_SUSPEND);
   #     endif /* SIG_SUSPEND */
 #    endif  #    endif
 #   if defined(MACOSX)  
       struct sigaction act, oldact;  
   
       act.sa_flags = SA_RESTART;  
       act.sa_handler = GC_write_fault_handler;  
       sigemptyset(&act.sa_mask);  
 #   endif  
 #   ifdef PRINTSTATS  #   ifdef PRINTSTATS
         GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n");          GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n");
 #   endif  #   endif
Line 2334  void GC_dirty_init()
Line 2636  void GC_dirty_init()
 #       endif  #       endif
       }        }
 #   endif  #   endif
 #   if defined(OSF1) || defined(SUNOS4) || defined(LINUX)  #   if defined(SUNOS4)
       GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler);        GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler);
       if (GC_old_segv_handler == SIG_IGN) {        if (GC_old_segv_handler == SIG_IGN) {
         GC_err_printf0("Previously ignored segmentation violation!?");          GC_err_printf0("Previously ignored segmentation violation!?");
Line 2346  void GC_dirty_init()
Line 2648  void GC_dirty_init()
 #       endif  #       endif
       }        }
 #   endif  #   endif
 #   if defined(SUNOS5SIGS) || defined(IRIX5)  #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) \
 #     if defined(IRIX_THREADS)         || defined(OSF1) || defined(HURD)
         /* SUNOS5SIGS includes HPUX */
   #     if defined(GC_IRIX_THREADS)
         sigaction(SIGSEGV, 0, &oldact);          sigaction(SIGSEGV, 0, &oldact);
         sigaction(SIGSEGV, &act, 0);          sigaction(SIGSEGV, &act, 0);
 #     else  #     else
         sigaction(SIGSEGV, &act, &oldact);          {
             int res = sigaction(SIGSEGV, &act, &oldact);
             if (res != 0) ABORT("Sigaction failed");
           }
 #     endif  #     endif
 #     if defined(_sigargs)  #     if defined(_sigargs) || defined(HURD) || !defined(SA_SIGINFO)
         /* This is Irix 5.x, not 6.x.  Irix 5.x does not have   */          /* This is Irix 5.x, not 6.x.  Irix 5.x does not have   */
         /* sa_sigaction.                                        */          /* sa_sigaction.                                        */
         GC_old_segv_handler = oldact.sa_handler;          GC_old_segv_handler = oldact.sa_handler;
 #     else /* Irix 6.x or SUNOS5SIGS */  #     else /* Irix 6.x or SUNOS5SIGS or LINUX */
         if (oldact.sa_flags & SA_SIGINFO) {          if (oldact.sa_flags & SA_SIGINFO) {
           GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction);            GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction);
         } else {          } else {
Line 2374  void GC_dirty_init()
Line 2681  void GC_dirty_init()
 #       endif  #       endif
       }        }
 #   endif  #   endif
 #   if defined(MACOSX) || defined(HPUX)  #   if defined(HPUX) || defined(LINUX) || defined(HURD)
       sigaction(SIGBUS, &act, &oldact);        sigaction(SIGBUS, &act, &oldact);
       GC_old_bus_handler = oldact.sa_handler;        GC_old_bus_handler = oldact.sa_handler;
       if (GC_old_bus_handler == SIG_IGN) {        if (GC_old_bus_handler == SIG_IGN) {
Line 2386  void GC_dirty_init()
Line 2693  void GC_dirty_init()
           GC_err_printf0("Replaced other SIGBUS handler\n");            GC_err_printf0("Replaced other SIGBUS handler\n");
 #       endif  #       endif
       }        }
 #   endif /* MACOS || HPUX */  #   endif /* HPUX || LINUX || HURD */
 #   if defined(MSWIN32)  #   if defined(MSWIN32)
       GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);        GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
       if (GC_old_segv_handler != NULL) {        if (GC_old_segv_handler != NULL) {
Line 2398  void GC_dirty_init()
Line 2705  void GC_dirty_init()
       }        }
 #   endif  #   endif
 }  }
   #endif /* !DARWIN */
   
   int GC_incremental_protection_needs()
   {
       if (GC_page_size == HBLKSIZE) {
           return GC_PROTECTS_POINTER_HEAP;
       } else {
           return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP;
       }
   }
   
   #define HAVE_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()  void GC_protect_heap()
 {  {
     ptr_t start;      ptr_t start;
     word len;      word len;
       struct hblk * current;
       struct hblk * current_start;  /* Start of block to be protected. */
       struct hblk * limit;
     unsigned i;      unsigned i;
       GC_bool protect_all =
             (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP));
     for (i = 0; i < GC_n_heap_sects; i++) {      for (i = 0; i < GC_n_heap_sects; i++) {
         start = GC_heap_sects[i].hs_start;          start = GC_heap_sects[i].hs_start;
         len = GC_heap_sects[i].hs_bytes;          len = GC_heap_sects[i].hs_bytes;
         PROTECT(start, len);          if (protect_all) {
             PROTECT(start, len);
           } else {
             GC_ASSERT(PAGE_ALIGNED(len))
             GC_ASSERT(PAGE_ALIGNED(start))
             current_start = current = (struct hblk *)start;
             limit = (struct hblk *)(start + len);
             while (current < limit) {
               hdr * hhdr;
               word nhblks;
               GC_bool is_ptrfree;
   
               GC_ASSERT(PAGE_ALIGNED(current));
               GET_HDR(current, hhdr);
               if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
                 /* This can happen only if we're at the beginning of a    */
                 /* heap segment, and a block spans heap segments.         */
                 /* We will handle that block as part of the preceding     */
                 /* segment.                                               */
                 GC_ASSERT(current_start == current);
                 current_start = ++current;
                 continue;
               }
               if (HBLK_IS_FREE(hhdr)) {
                 GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz));
                 nhblks = divHBLKSZ(hhdr -> hb_sz);
                 is_ptrfree = TRUE;        /* dirty on alloc */
               } else {
                 nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz);
                 is_ptrfree = IS_PTRFREE(hhdr);
               }
               if (is_ptrfree) {
                 if (current_start < current) {
                   PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
                 }
                 current_start = (current += nhblks);
               } else {
                 current += nhblks;
               }
             }
             if (current_start < current) {
               PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
             }
           }
     }      }
 }  }
   
Line 2467  word len;
Line 2834  word len;
     register struct hblk *h;      register struct hblk *h;
     ptr_t obj_start;      ptr_t obj_start;
   
     if (!GC_incremental) return;      if (!GC_dirty_maintained) return;
     obj_start = GC_base(addr);      obj_start = GC_base(addr);
     if (obj_start == 0) return;      if (obj_start == 0) return;
     if (GC_base(addr + len - 1) != obj_start) {      if (GC_base(addr + len - 1) != obj_start) {
Line 2485  word len;
Line 2852  word len;
               ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);                ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
 }  }
   
 #if !defined(MSWIN32) && !defined(MSWINCE) && !defined(LINUX_THREADS) \  #if 0
     && !defined(GC_USE_LD_WRAP)  
 /* Replacement for UNIX system call.     */  /* We no longer wrap read by default, since that was causing too many   */
 /* Other calls that write to the heap    */  /* problems.  It is preferred that the client instead avoids writing    */
 /* should be handled similarly.          */  /* to the write-protected heap with a system call.                      */
   /* This still serves as sample code if you do want to wrap system calls.*/
   
   #if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_USE_LD_WRAP)
   /* Replacement for UNIX system call.                                      */
   /* Other calls that write to the heap should be handled similarly.        */
   /* Note that this doesn't work well for blocking reads:  It will hold     */
   /* the allocation lock for the entire duration of the call. Multithreaded */
   /* clients should really ensure that it won't block, either by setting    */
   /* the descriptor nonblocking, or by calling select or poll first, to     */
   /* 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)  # if defined(__STDC__) && !defined(SUNOS4)
 #   include <unistd.h>  #   include <unistd.h>
 #   include <sys/uio.h>  #   include <sys/uio.h>
Line 2509  word len;
Line 2888  word len;
   
     GC_begin_syscall();      GC_begin_syscall();
     GC_unprotect_range(buf, (word)nbyte);      GC_unprotect_range(buf, (word)nbyte);
 #   if defined(IRIX5) || defined(LINUX_THREADS)  #   if defined(IRIX5) || defined(GC_LINUX_THREADS)
         /* Indirect system call may not always be easily available.     */          /* Indirect system call may not always be easily available.     */
         /* We could call _read, but that would interfere with the       */          /* We could call _read, but that would interfere with the       */
         /* libpthread interception of read.                             */          /* libpthread interception of read.                             */
Line 2523  word len;
Line 2902  word len;
             result = readv(fd, &iov, 1);              result = readv(fd, &iov, 1);
         }          }
 #   else  #   else
   #     if defined(HURD)
           result = __read(fd, buf, nbyte);
   #     else
         /* The two zero args at the end of this list are because one          /* The two zero args at the end of this list are because one
            IA-64 syscall() implementation actually requires six args             IA-64 syscall() implementation actually requires six args
            to be passed, even though they aren't always used. */             to be passed, even though they aren't always used. */
         result = syscall(SYS_read, fd, buf, nbyte, 0, 0);          result = syscall(SYS_read, fd, buf, nbyte, 0, 0);
   #     endif /* !HURD */
 #   endif  #   endif
     GC_end_syscall();      GC_end_syscall();
     return(result);      return(result);
 }  }
 #endif /* !MSWIN32 && !MSWINCE && !LINUX_THREADS */  #endif /* !MSWIN32 && !MSWINCE && !GC_LINUX_THREADS */
   
 #ifdef GC_USE_LD_WRAP  #if defined(GC_USE_LD_WRAP) && !defined(THREADS)
     /* We use the GNU ld call wrapping facility.                        */      /* We use the GNU ld call wrapping facility.                        */
     /* This requires that the linker be invoked with "--wrap read".     */      /* This requires that the linker be invoked with "--wrap read".     */
     /* This can be done by passing -Wl,"--wrap read" to gcc.            */      /* This can be done by passing -Wl,"--wrap read" to gcc.            */
Line 2555  word len;
Line 2938  word len;
     /* actually calls.                                                  */      /* actually calls.                                                  */
 #endif  #endif
   
   #endif /* 0 */
   
 /*ARGSUSED*/  /*ARGSUSED*/
 GC_bool GC_page_was_ever_dirty(h)  GC_bool GC_page_was_ever_dirty(h)
 struct hblk *h;  struct hblk *h;
Line 2570  word n;
Line 2955  word n;
 {  {
 }  }
   
 # else /* !MPROTECT_VDB */  
   
 #   ifdef GC_USE_LD_WRAP  
       ssize_t __wrap_read(int fd, void *buf, size_t nbyte)  
       { return __real_read(fd, buf, nbyte); }  
 #   endif  
   
 # endif /* MPROTECT_VDB */  # endif /* MPROTECT_VDB */
   
 # ifdef PROC_VDB  # ifdef PROC_VDB
Line 2604  word n;
Line 2982  word n;
 word GC_proc_buf_size = INITIAL_BUF_SZ;  word GC_proc_buf_size = INITIAL_BUF_SZ;
 char *GC_proc_buf;  char *GC_proc_buf;
   
 #ifdef SOLARIS_THREADS  #ifdef GC_SOLARIS_THREADS
 /* We don't have exact sp values for threads.  So we count on   */  /* We don't have exact sp values for threads.  So we count on   */
 /* occasionally declaring stack pages to be fresh.  Thus we     */  /* occasionally declaring stack pages to be fresh.  Thus we     */
 /* need a real implementation of GC_is_fresh.  We can't clear   */  /* need a real implementation of GC_is_fresh.  We can't clear   */
Line 2655  void GC_dirty_init()
Line 3033  void GC_dirty_init()
     }      }
     GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0);      GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0);
     close(fd);      close(fd);
       syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC);
     if (GC_proc_fd < 0) {      if (GC_proc_fd < 0) {
         ABORT("/proc ioctl failed");          ABORT("/proc ioctl failed");
     }      }
     GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size);      GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size);
 #   ifdef SOLARIS_THREADS  #   ifdef GC_SOLARIS_THREADS
         GC_fresh_pages = (struct hblk **)          GC_fresh_pages = (struct hblk **)
           GC_scratch_alloc(MAX_FRESH_PAGES * sizeof (struct hblk *));            GC_scratch_alloc(MAX_FRESH_PAGES * sizeof (struct hblk *));
         if (GC_fresh_pages == 0) {          if (GC_fresh_pages == 0) {
Line 2672  void GC_dirty_init()
Line 3051  void GC_dirty_init()
   
 /* Ignore write hints. They don't help us here. */  /* Ignore write hints. They don't help us here. */
 /*ARGSUSED*/  /*ARGSUSED*/
 void GC_write_hint(h)  void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;  struct hblk *h;
   word nblocks;
   GC_bool is_ptrfree;
 {  {
 }  }
   
 #ifdef SOLARIS_THREADS  #ifdef GC_SOLARIS_THREADS
 #   define READ(fd,buf,nbytes) syscall(SYS_read, fd, buf, nbytes)  #   define READ(fd,buf,nbytes) syscall(SYS_read, fd, buf, nbytes)
 #else  #else
 #   define READ(fd,buf,nbytes) read(fd, buf, nbytes)  #   define READ(fd,buf,nbytes) read(fd, buf, nbytes)
Line 2716  int dummy;
Line 3097  int dummy;
                 /* Punt:        */                  /* Punt:        */
                 memset(GC_grungy_pages, 0xff, sizeof (page_hash_table));                  memset(GC_grungy_pages, 0xff, sizeof (page_hash_table));
                 memset(GC_written_pages, 0xff, sizeof(page_hash_table));                  memset(GC_written_pages, 0xff, sizeof(page_hash_table));
 #               ifdef SOLARIS_THREADS  #               ifdef GC_SOLARIS_THREADS
                     BZERO(GC_fresh_pages,                      BZERO(GC_fresh_pages,
                           MAX_FRESH_PAGES * sizeof (struct hblk *));                            MAX_FRESH_PAGES * sizeof (struct hblk *));
 #               endif  #               endif
Line 2746  int dummy;
Line 3127  int dummy;
                         register word index = PHT_HASH(h);                          register word index = PHT_HASH(h);
   
                         set_pht_entry_from_index(GC_grungy_pages, index);                          set_pht_entry_from_index(GC_grungy_pages, index);
 #                       ifdef SOLARIS_THREADS  #                       ifdef GC_SOLARIS_THREADS
                           {                            {
                             register int slot = FRESH_PAGE_SLOT(h);                              register int slot = FRESH_PAGE_SLOT(h);
   
Line 2764  int dummy;
Line 3145  int dummy;
         }          }
     /* Update GC_written_pages. */      /* Update GC_written_pages. */
         GC_or_pages(GC_written_pages, GC_grungy_pages);          GC_or_pages(GC_written_pages, GC_grungy_pages);
 #   ifdef SOLARIS_THREADS  #   ifdef GC_SOLARIS_THREADS
       /* Make sure that old stacks are considered completely clean      */        /* Make sure that old stacks are considered completely clean      */
       /* unless written again.                                          */        /* unless written again.                                          */
         GC_old_stacks_are_fresh();          GC_old_stacks_are_fresh();
Line 2780  struct hblk *h;
Line 3161  struct hblk *h;
     register GC_bool result;      register GC_bool result;
   
     result = get_pht_entry_from_index(GC_grungy_pages, index);      result = get_pht_entry_from_index(GC_grungy_pages, index);
 #   ifdef SOLARIS_THREADS  #   ifdef GC_SOLARIS_THREADS
         if (result && PAGE_IS_FRESH(h)) result = FALSE;          if (result && PAGE_IS_FRESH(h)) result = FALSE;
         /* This happens only if page was declared fresh since   */          /* This happens only if page was declared fresh since   */
         /* the read_dirty call, e.g. because it's in an unused  */          /* the read_dirty call, e.g. because it's in an unused  */
Line 2798  struct hblk *h;
Line 3179  struct hblk *h;
     register GC_bool result;      register GC_bool result;
   
     result = get_pht_entry_from_index(GC_written_pages, index);      result = get_pht_entry_from_index(GC_written_pages, index);
 #   ifdef SOLARIS_THREADS  #   ifdef GC_SOLARIS_THREADS
         if (result && PAGE_IS_FRESH(h)) result = FALSE;          if (result && PAGE_IS_FRESH(h)) result = FALSE;
 #   endif  #   endif
     return(result);      return(result);
Line 2812  word n;
Line 3193  word n;
   
     register word index;      register word index;
   
 #   ifdef SOLARIS_THREADS  #   ifdef GC_SOLARIS_THREADS
       register word i;        register word i;
   
       if (GC_fresh_pages != 0) {        if (GC_fresh_pages != 0) {
Line 2881  struct hblk *h;
Line 3262  struct hblk *h;
 }  }
   
 /*ARGSUSED*/  /*ARGSUSED*/
 void GC_write_hint(h)  void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;  struct hblk *h;
   word nblocks;
   GC_bool is_ptrfree;
 {  {
     PCR_VD_WriteProtectDisable(h, HBLKSIZE);      PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE);
     PCR_VD_WriteProtectEnable(h, HBLKSIZE);      PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE);
 }  }
   
 # endif /* PCR_VDB */  # endif /* PCR_VDB */
   
   #if defined(MPROTECT_VDB) && defined(DARWIN)
   /* The following sources were used as a *reference* for this exception handling
      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
         3. macosx-nat.c from Apple's GDB source code.
   */
   
   /* There seem to be numerous problems with darwin's mach exception handling.
      I'm pretty sure they are not problems in my code. Search for
      BROKEN_EXCEPTION_HANDLING for more information. */
   #define BROKEN_EXCEPTION_HANDLING
   
   #include <mach/mach.h>
   #include <mach/mach_error.h>
   #include <mach/thread_status.h>
   #include <mach/exception.h>
   #include <mach/task.h>
   #include <pthread.h>
   
   /* 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*);
   
   
   #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];
       exception_handler_t   ports[MAX_EXCEPTION_PORTS];
       exception_behavior_t  behaviors[MAX_EXCEPTION_PORTS];
       thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS];
   } GC_old_exc_ports;
   
   static struct {
       mach_port_t exception;
   #if defined(THREADS)
       mach_port_t reply;
   #endif
   } GC_ports;
   
   typedef struct {
       mach_msg_header_t head;
   } GC_msg_t;
   
   typedef enum {
       GC_MP_NORMAL, GC_MP_DISCARDING, GC_MP_STOPPED
   } GC_mprotect_state_t;
   
   /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field,
      but it isn't  documented. Use the source and see if they
      should be ok. */
   #define ID_STOP 1
   #define ID_RESUME 2
   
   /* These values are only used on the reply port */
   #define ID_ACK 3
   
   #if defined(THREADS)
   
   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");
   }
   
   /* 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");
   }
   
   void GC_mprotect_stop() {
       GC_mprotect_thread_notify(ID_STOP);
   }
   void GC_mprotect_resume() {
       GC_mprotect_thread_notify(ID_RESUME);
   }
   
   #else /* !THREADS */
   /* The compiler should optimize away any GC_mprotect_state computations */
   #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;
   
       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(;;) */
       /* NOT REACHED */
       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
      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");
       }
   }
   #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
       {
           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
           }
       }
       #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
      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_data_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");
       }
   
       return r;
   }
   
   #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;
   #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
   #       error FIXME for non-ppc 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
       }
   
       /* This is the address that caused the fault */
       addr = (char*) exc_state.dar;
   
       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 */
       }
   
       #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);
           }
       } 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 */
       } else {
           /* Shouldn't happen, i don't think */
           GC_printf0("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)
   {
       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)
   {
       ABORT("catch_exception_raise_state_identity");
       return(KERN_INVALID_ARGUMENT);
   }
   
   
   #endif /* DARWIN && MPROTECT_VDB */
   
   # ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS
     int GC_incremental_protection_needs()
     {
       return GC_PROTECTS_NONE;
     }
   # endif /* !HAVE_INCREMENTAL_PROTECTION_NEEDS */
   
 /*  /*
  * Call stack save code for debugging.   * Call stack save code for debugging.
  * Should probably be in mach_dep.c, but that requires reorganization.   * Should probably be in mach_dep.c, but that requires reorganization.
Line 2899  struct hblk *h;
Line 3836  struct hblk *h;
 /* long as the frame pointer is explicitly stored.  In the case of gcc, */  /* long as the frame pointer is explicitly stored.  In the case of gcc, */
 /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is.  */  /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is.  */
 #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)  #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)
   #   include <features.h>
   
     struct frame {      struct frame {
         struct frame *fr_savfp;          struct frame *fr_savfp;
         long    fr_savpc;          long    fr_savpc;
Line 2908  struct hblk *h;
Line 3847  struct hblk *h;
   
 #if defined(SPARC)  #if defined(SPARC)
 #  if defined(LINUX)  #  if defined(LINUX)
   #    include <features.h>
   
      struct frame {       struct frame {
         long    fr_local[8];          long    fr_local[8];
         long    fr_arg[6];          long    fr_arg[6];
Line 2939  struct hblk *h;
Line 3880  struct hblk *h;
 #  endif  #  endif
 #endif /* SPARC */  #endif /* SPARC */
   
 #ifdef SAVE_CALL_CHAIN  #ifdef  NEED_CALLINFO
 /* Fill in the pc and argument information for up to NFRAMES of my      */  /* Fill in the pc and argument information for up to NFRAMES of my      */
 /* callers.  Ignore my frame and my callers frame.                      */  /* callers.  Ignore my frame and my callers frame.                      */
   
   #ifdef LINUX
   #   include <unistd.h>
   #endif
   
   #endif /* NEED_CALLINFO */
   
   #ifdef SAVE_CALL_CHAIN
   
   #if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \
       && defined(GC_HAVE_BUILTIN_BACKTRACE)
   
   #include <execinfo.h>
   
   void GC_save_callers (info)
   struct callinfo info[NFRAMES];
   {
     void * tmp_info[NFRAMES + 1];
     int npcs, i;
   # define IGNORE_FRAMES 1
   
     /* We retrieve NFRAMES+1 pc values, but discard the first, since it   */
     /* points to our own frame.                                           */
     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;
   }
   
   #else /* No builtin backtrace; do it ourselves */
   
 #if (defined(OPENBSD) || defined(NETBSD)) && defined(SPARC)  #if (defined(OPENBSD) || defined(NETBSD)) && defined(SPARC)
 #  define FR_SAVFP fr_fp  #  define FR_SAVFP fr_fp
 #  define FR_SAVPC fr_pc  #  define FR_SAVPC fr_pc
Line 2968  struct callinfo info[NFRAMES];
Line 3939  struct callinfo info[NFRAMES];
     asm("movl %%ebp,%0" : "=r"(frame));      asm("movl %%ebp,%0" : "=r"(frame));
     fp = frame;      fp = frame;
 # else  # else
     word GC_save_regs_in_stack();  
   
     frame = (struct frame *) GC_save_regs_in_stack ();      frame = (struct frame *) GC_save_regs_in_stack ();
     fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);      fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);
 #endif  #endif
Line 2980  struct callinfo info[NFRAMES];
Line 3949  struct callinfo info[NFRAMES];
       register int i;        register int i;
   
       info[nframes].ci_pc = fp->FR_SAVPC;        info[nframes].ci_pc = fp->FR_SAVPC;
       for (i = 0; i < NARGS; i++) {  #     if NARGS > 0
         info[nframes].ci_arg[i] = ~(fp->fr_arg[i]);          for (i = 0; i < NARGS; i++) {
       }            info[nframes].ci_arg[i] = ~(fp->fr_arg[i]);
           }
   #     endif /* NARGS > 0 */
   }    }
   if (nframes < NFRAMES) info[nframes].ci_pc = 0;    if (nframes < NFRAMES) info[nframes].ci_pc = 0;
 }  }
   
   #endif /* No builtin backtrace */
   
 #endif /* SAVE_CALL_CHAIN */  #endif /* SAVE_CALL_CHAIN */
   
 #if defined(LINUX) && defined(__ELF__) && \  #ifdef NEED_CALLINFO
     (!defined(SMALL_CONFIG) || defined(USE_PROC_FOR_LIBRARIES))  
 #ifdef GC_USE_LD_WRAP  
 #   define READ __real_read  
 #else  
 #   define READ read  
 #endif  
   
   /* Print info to stderr.  We do NOT hold the allocation lock */
 /* Repeatedly perform a read call until the buffer is filled or */  void GC_print_callers (info)
 /* we encounter EOF.                                            */  struct callinfo info[NFRAMES];
 ssize_t GC_repeat_read(int fd, char *buf, size_t count)  
 {  {
     ssize_t num_read = 0;      register int i;
     ssize_t result;      static int reentry_count = 0;
       GC_bool stop = FALSE;
   
       LOCK();
         ++reentry_count;
       UNLOCK();
   
     while (num_read < count) {  #   if NFRAMES == 1
         result = READ(fd, buf + num_read, count - num_read);        GC_err_printf0("\tCaller at allocation:\n");
         if (result < 0) return result;  #   else
         if (result == 0) break;        GC_err_printf0("\tCall chain at allocation:\n");
         num_read += result;  #   endif
       for (i = 0; i < NFRAMES && !stop ; i++) {
           if (info[i].ci_pc == 0) break;
   #       if NARGS > 0
           {
             int j;
   
             GC_err_printf0("\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]),
                                           ~(info[i].ci_arg[j]));
             }
             GC_err_printf0("\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);
               continue;
           }
           {
   #         ifdef LINUX
               FILE *pipe;
   #         endif
   #         if defined(GC_HAVE_BUILTIN_BACKTRACE)
               char **sym_name =
                 backtrace_symbols((void **)(&(info[i].ci_pc)), 1);
               char *name = sym_name[0];
   #         else
               char buf[40];
               char *name = buf;
               sprintf(buf, "##PC##= 0x%lx", info[i].ci_pc);
   #         endif
   #         if defined(LINUX) && !defined(SMALL_CONFIG)
               /* Try for a line number. */
               {
   #               define EXE_SZ 100
                   static char exe_name[EXE_SZ];
   #               define CMD_SZ 200
                   char cmd_buf[CMD_SZ];
   #               define RESULT_SZ 200
                   static char result_buf[RESULT_SZ];
                   size_t result_len;
                   static GC_bool found_exe_name = FALSE;
                   static GC_bool will_fail = FALSE;
                   int ret_code;
                   /* Try to get it via a hairy and expensive scheme.      */
                   /* First we get the name of the executable:             */
                   if (will_fail) goto out;
                   if (!found_exe_name) {
                     ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ);
                     if (ret_code < 0 || ret_code >= EXE_SZ
                         || exe_name[0] != '/') {
                       will_fail = TRUE;   /* Dont try again. */
                       goto out;
                     }
                     exe_name[ret_code] = '\0';
                     found_exe_name = TRUE;
                   }
                   /* Then we use popen to start addr2line -e <exe> <addr> */
                   /* There are faster ways to do this, but hopefully this */
                   /* isn't time critical.                                 */
                   sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name,
                                    (unsigned long)info[i].ci_pc);
                   pipe = popen(cmd_buf, "r");
                   if (pipe == NULL
                       || (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe))
                          == 0) {
                     if (pipe != NULL) pclose(pipe);
                     will_fail = TRUE;
                     goto out;
                   }
                   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') {
                       pclose(pipe);
                       goto out;
                   }
                   /* Get rid of embedded newline, if any.  Test for "main" */
                   {
                      char * nl = strchr(result_buf, '\n');
                      if (nl != NULL && nl < result_buf + result_len) {
                        *nl = ':';
                      }
                      if (strncmp(result_buf, "main", nl - result_buf) == 0) {
                        stop = TRUE;
                      }
                   }
                   if (result_len < RESULT_SZ - 25) {
                     /* Add in hex address */
                       sprintf(result_buf + result_len, " [0x%lx]",
                             (unsigned long)info[i].ci_pc);
                   }
                   name = result_buf;
                   pclose(pipe);
                   out:;
               }
   #         endif /* LINUX */
             GC_err_printf1("\t\t%s\n", name);
   #         if defined(GC_HAVE_BUILTIN_BACKTRACE)
               free(sym_name);  /* May call GC_free; that's OK */
   #         endif
           }
     }      }
     return num_read;      LOCK();
         --reentry_count;
       UNLOCK();
 }  }
 #endif /* LINUX && ... */  
   
   #endif /* NEED_CALLINFO */
   
   
   
 #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)  #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
   
 /* Dump /proc/self/maps to GC_stderr, to enable looking up names for  /* Dump /proc/self/maps to GC_stderr, to enable looking up names for
    addresses in FIND_LEAK output. */     addresses in FIND_LEAK output. */
   
   static word dump_maps(char *maps)
   {
       GC_err_write(maps, strlen(maps));
       return 1;
   }
   
 void GC_print_address_map()  void GC_print_address_map()
 {  {
     int f;  
     int result;  
     char maps_temp[32768];  
     GC_err_printf0("---------- Begin address map ----------\n");      GC_err_printf0("---------- Begin address map ----------\n");
         f = open("/proc/self/maps", O_RDONLY);      GC_apply_to_maps(dump_maps);
         if (-1 == f) ABORT("Couldn't open /proc/self/maps");  
         do {  
             result = GC_repeat_read(f, maps_temp, sizeof(maps_temp));  
             if (result <= 0) ABORT("Couldn't read /proc/self/maps");  
             GC_err_write(maps_temp, result);  
         } while (result == sizeof(maps_temp));  
   
     GC_err_printf0("---------- End address map ----------\n");      GC_err_printf0("---------- End address map ----------\n");
 }  }
   

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.8

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>