[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.2 and 1.8

version 1.2, 2000/04/10 08:31:31 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 17  static int inside_critical_section;
Line 14  static int inside_critical_section;
  * modified is included with the above copyright notice.   * modified is included with the above copyright notice.
  */   */
   
 # include "gc_priv.h"  # include "private/gc_priv.h"
   
 # if defined(LINUX) && !defined(POWERPC)  # if defined(LINUX) && !defined(POWERPC)
 #   include <linux/version.h>  #   include <linux/version.h>
Line 48  static int inside_critical_section;
Line 45  static int inside_critical_section;
 #     endif /* 2 <= __GLIBC__ */  #     endif /* 2 <= __GLIBC__ */
 #   endif  #   endif
 # endif  # endif
 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS)  # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
       && !defined(MSWINCE)
 #   include <sys/types.h>  #   include <sys/types.h>
 #   if !defined(MSWIN32) && !defined(SUNOS4)  #   if !defined(MSWIN32) && !defined(SUNOS4)
 #       include <unistd.h>  #       include <unistd.h>
Line 56  static int inside_critical_section;
Line 54  static int inside_critical_section;
 # endif  # endif
   
 # include <stdio.h>  # include <stdio.h>
 # include <signal.h>  # if defined(MSWINCE)
   #   define SIGSEGV 0 /* value is irrelevant */
   # else
   #   include <signal.h>
   # endif
   
 /* 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
   
 # if (defined(SUNOS4) & defined(DYNAMIC_LOADING)) && !defined(PCR)  # if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
 #   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>
 #   define NEED_FIND_LIMIT  #  if !defined(PCR)
 # endif  #    define NEED_FIND_LIMIT
   #  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
 # include <proto/exec.h>  # define GC_AMIGA_DEF
 # include <proto/dos.h>  # include "AmigaOS.c"
 # include <dos/dosextens.h>  # undef GC_AMIGA_DEF
 # include <workbench/startup.h>  
 #endif  #endif
   
 #ifdef MSWIN32  #if defined(MSWIN32) || defined(MSWINCE)
 # define WIN32_LEAN_AND_MEAN  # define WIN32_LEAN_AND_MEAN
 # define NOSERVICE  # define NOSERVICE
 # include <windows.h>  # include <windows.h>
Line 115  static int inside_critical_section;
Line 115  static int inside_critical_section;
 # include <sys/types.h>  # include <sys/types.h>
 # include <sys/mman.h>  # include <sys/mman.h>
 # include <sys/stat.h>  # include <sys/stat.h>
   #endif
   
   #ifdef UNIX_LIKE
 # 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 127  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 casuse problems with     */    /* Apparently necessary for djgpp 2.01.  May cause problems with      */
   /* other versions.                                                    */    /* other versions.                                                    */
   typedef long unsigned int caddr_t;    typedef long unsigned int caddr_t;
 #endif  #endif
Line 145  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(POWERPC) || defined(SPARC) || defined(ALPHA) \  #if defined(LINUX) && \
                        || defined(IA64))      (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)
   /* 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      */
   /* for recent Linux versions.  This seems to be the easiest way to    */    /* for recent Linux versions.  This seems to be the easiest way to    */
   /* cover all versions.                                                */    /* cover all versions.                                                */
   ptr_t GC_data_start;  
   
   extern char * GC_copyright[];  /* Any data symbol would do. */  # 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
       extern int __data_start[];
   #   pragma weak data_start
       extern int data_start[];
   # endif /* LINUX */
     extern int _end[];
   
     ptr_t GC_data_start;
   
   void GC_init_linux_data_start()    void GC_init_linux_data_start()
   {    {
     extern ptr_t GC_find_limit();      extern ptr_t GC_find_limit();
   
     GC_data_start = GC_find_limit((ptr_t)GC_copyright, FALSE);  #   ifdef LINUX
         /* Try the easy approaches first: */
         if ((ptr_t)__data_start != 0) {
             GC_data_start = (ptr_t)(__data_start);
             return;
         }
         if ((ptr_t)data_start != 0) {
             GC_data_start = (ptr_t)(data_start);
             return;
         }
   #   endif /* LINUX */
       GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
   }    }
 #endif  #endif
   
   # 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;
   
     void GC_init_netbsd_elf()
     {
       extern ptr_t GC_find_limit();
       extern char **environ;
           /* This may need to be environ, without the underscore, for     */
           /* some versions.                                               */
       GC_data_start = GC_find_limit((ptr_t)&environ, FALSE);
     }
   #endif
   
 # ifdef OS2  # ifdef OS2
   
 # include <stddef.h>  # include <stddef.h>
Line 267  void GC_enable_signals(void)
Line 495  void GC_enable_signals(void)
 # else  # else
   
 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \  #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW)        && !defined(MSWINCE) \
         && !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 326  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 341  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 358  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 366  void GC_enable_signals()
Line 580  void GC_enable_signals()
 /* Find the page size */  /* Find the page size */
 word GC_page_size;  word GC_page_size;
   
 # ifdef MSWIN32  # if defined(MSWIN32) || defined(MSWINCE)
   void GC_setpagesize()    void GC_setpagesize()
   {    {
     SYSTEM_INFO sysinfo;      GetSystemInfo(&GC_sysinfo);
       GC_page_size = GC_sysinfo.dwPageSize;
     GetSystemInfo(&sysinfo);  
     GC_page_size = sysinfo.dwPageSize;  
   }    }
   
 # else  # else
Line 397  word GC_page_size;
Line 609  word GC_page_size;
  * With threads, GC_mark_roots needs to know how to do this.   * With threads, GC_mark_roots needs to know how to do this.
  * Called with allocator lock held.   * Called with allocator lock held.
  */   */
 # ifdef MSWIN32  # if defined(MSWIN32) || defined(MSWINCE)
 # define is_writable(prot) ((prot) == PAGE_READWRITE \  # define is_writable(prot) ((prot) == PAGE_READWRITE \
                             || (prot) == PAGE_WRITECOPY \                              || (prot) == PAGE_WRITECOPY \
                             || (prot) == PAGE_EXECUTE_READWRITE \                              || (prot) == PAGE_EXECUTE_READWRITE \
Line 434  ptr_t GC_get_stack_base()
Line 646  ptr_t GC_get_stack_base()
 }  }
   
   
 # else  # endif /* MS Windows */
   
   # ifdef BEOS
   # include <kernel/OS.h>
   ptr_t GC_get_stack_base(){
           thread_info th;
           get_thread_info(find_thread(NULL),&th);
           return th.stack_end;
   }
   # endif /* BEOS */
   
   
 # ifdef OS2  # ifdef OS2
   
 ptr_t GC_get_stack_base()  ptr_t GC_get_stack_base()
Line 450  ptr_t GC_get_stack_base()
Line 672  ptr_t GC_get_stack_base()
     return((ptr_t)(ptib -> tib_pstacklimit));      return((ptr_t)(ptib -> tib_pstacklimit));
 }  }
   
 # else  # endif /* OS2 */
   
 # ifdef AMIGA  # ifdef AMIGA
   #   define GC_AMIGA_SB
   #   include "AmigaOS.c"
   #   undef GC_AMIGA_SB
   # endif /* AMIGA */
   
 ptr_t GC_get_stack_base()  # if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
 {  
     struct Process *proc = (struct Process*)SysBase->ThisTask;  
   
     /* Reference: Amiga Guru Book Pages: 42,567,574 */  
     if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS  
         && proc->pr_CLI != NULL) {  
         /* first ULONG is StackSize */  
         /*longPtr = proc->pr_ReturnAddr;  
         size = longPtr[0];*/  
   
         return (char *)proc->pr_ReturnAddr + sizeof(ULONG);  
     } else {  
         return (char *)proc->pr_Task.tc_SPUpper;  
     }  
 }  
   
 #if 0 /* old version */  
 ptr_t GC_get_stack_base()  
 {  
     extern struct WBStartup *_WBenchMsg;  
     extern long __base;  
     extern long __stack;  
     struct Task *task;  
     struct Process *proc;  
     struct CommandLineInterface *cli;  
     long size;  
   
     if ((task = FindTask(0)) == 0) {  
         GC_err_puts("Cannot find own task structure\n");  
         ABORT("task missing");  
     }  
     proc = (struct Process *)task;  
     cli = BADDR(proc->pr_CLI);  
   
     if (_WBenchMsg != 0 || cli == 0) {  
         size = (char *)task->tc_SPUpper - (char *)task->tc_SPLower;  
     } else {  
         size = cli->cli_DefaultStack * 4;  
     }  
     return (ptr_t)(__base + GC_max(size, __stack));  
 }  
 #endif /* 0 */  
   
 # else /* !AMIGA, !OS2, ... */  
   
 # ifdef NEED_FIND_LIMIT  
   /* Some tools to implement HEURISTIC2 */  
 #   define MIN_PAGE_SIZE 256    /* Smallest conceivable page size, bytes */  
     /* static */ jmp_buf GC_jmp_buf;  
   
     /*ARGSUSED*/  
     void GC_fault_handler(sig)  
     int sig;  
     {  
         longjmp(GC_jmp_buf, 1);  
     }  
   
 #   ifdef __STDC__  #   ifdef __STDC__
         typedef void (*handler)(int);          typedef void (*handler)(int);
 #   else  #   else
         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
         static handler old_segv_handler, old_bus_handler;          static handler old_segv_handler, old_bus_handler;
 #   endif  #   endif
   
     void GC_setup_temporary_fault_handler()  #   ifdef __STDC__
         void GC_set_and_save_fault_handler(handler h)
   #   else
         void GC_set_and_save_fault_handler(h)
         handler h;
   #   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        = GC_fault_handler;            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, GC_fault_handler);            old_segv_handler = signal(SIGSEGV, h);
 #         ifdef SIGBUS  #         ifdef SIGBUS
             old_bus_handler = signal(SIGBUS, GC_fault_handler);              old_bus_handler = signal(SIGBUS, h);
 #         endif  #         endif
 #       endif  #       endif
     }      }
   # endif /* NEED_FIND_LIMIT || UNIX_LIKE */
   
   # ifdef NEED_FIND_LIMIT
     /* Some tools to implement HEURISTIC2 */
   #   define MIN_PAGE_SIZE 256    /* Smallest conceivable page size, bytes */
       /* static */ jmp_buf GC_jmp_buf;
   
       /*ARGSUSED*/
       void GC_fault_handler(sig)
       int sig;
       {
           longjmp(GC_jmp_buf, 1);
       }
   
       void GC_setup_temporary_fault_handler()
       {
           GC_set_and_save_fault_handler(GC_fault_handler);
       }
   
     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 581  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 614  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/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/<pid>/stat                    */                          /* field in /proc/self/stat                     */
   
   # pragma weak __libc_stack_end
     extern ptr_t __libc_stack_end;
   
   # 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
       extern ptr_t __libc_ia64_register_backing_store_base;
   
       ptr_t GC_get_register_stack_base(void)
       {
         if (0 != &__libc_ia64_register_backing_store_base
             && 0 != __libc_ia64_register_backing_store_base) {
           /* Glibc 2.2.4 has a bug such that for dynamically linked       */
           /* executables __libc_ia64_register_backing_store_base is       */
           /* defined but uninitialized during constructor calls.          */
           /* Hence we check for both nonzero address and value.           */
           return __libc_ia64_register_backing_store_base;
         } else {
           word result = backing_store_base_from_proc();
           if (0 == result) {
             /* 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;
         }
       }
   # endif
   
   ptr_t GC_linux_stack_base(void)    ptr_t GC_linux_stack_base(void)
   {    {
     char buf[50];      /* We read the stack base value from /proc/self/stat.  We do this   */
     FILE *f;      /* using direct I/O system calls in order to avoid calling malloc   */
       /* in case REDIRECT_MALLOC is defined.                              */
   #   define STAT_BUF_SIZE 4096
   #   define STAT_READ read
             /* Should probably call the real read, if read is wrapped.    */
       char stat_buf[STAT_BUF_SIZE];
       int f;
     char c;      char c;
     word result = 0;      word result = 0;
     int i;      size_t i, buf_offset = 0;
   
     sprintf(buf, "/proc/%d/stat", getpid());      /* First try the easy way.  This should work for glibc 2.2  */
     f = fopen(buf, "r");        if (0 != &__libc_stack_end) {
     if (NULL == f) ABORT("Couldn't open /proc/<pid>/stat");  #       ifdef IA64
     c = getc(f);            /* 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);
       if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
           ABORT("Couldn't read /proc/self/stat");
       }
       c = stat_buf[buf_offset++];
     /* Skip the required number of fields.  This number is hopefully    */      /* Skip the required number of fields.  This number is hopefully    */
     /* constant across all Linux implementations.                       */      /* constant across all Linux implementations.                       */
       for (i = 0; i < STAT_SKIP; ++i) {        for (i = 0; i < STAT_SKIP; ++i) {
         while (isspace(c)) c = getc(f);          while (isspace(c)) c = stat_buf[buf_offset++];
         while (!isspace(c)) c = getc(f);          while (!isspace(c)) c = stat_buf[buf_offset++];
       }        }
     while (isspace(c)) c = getc(f);      while (isspace(c)) c = stat_buf[buf_offset++];
     while (isdigit(c)) {      while (isdigit(c)) {
       result *= 10;        result *= 10;
       result += c - '0';        result += c - '0';
       c = getc(f);        c = stat_buf[buf_offset++];
     }      }
       close(f);
     if (result < 0x10000000) ABORT("Absurd stack bottom value");      if (result < 0x10000000) ABORT("Absurd stack bottom value");
     return (ptr_t)result;      return (ptr_t)result;
   }    }
   
 #endif /* LINUX_STACKBOTTOM */  #endif /* LINUX_STACKBOTTOM */
   
   #ifdef FREEBSD_STACKBOTTOM
   
   /* This uses an undocumented sysctl call, but at least one expert       */
   /* believes it will stay.                                               */
   
   #include <unistd.h>
   #include <sys/types.h>
   #include <sys/sysctl.h>
   
     ptr_t GC_freebsd_stack_base(void)
     {
       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);
   
       if (r) ABORT("Error getting stack base");
   
       return base;
     }
   
   #endif /* FREEBSD_STACKBOTTOM */
   
   #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
       && !defined(MSWINCE) && !defined(OS2)
   
 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 672  ptr_t GC_get_stack_base()
Line 991  ptr_t GC_get_stack_base()
 #       ifdef LINUX_STACKBOTTOM  #       ifdef LINUX_STACKBOTTOM
            result = GC_linux_stack_base();             result = GC_linux_stack_base();
 #       endif  #       endif
   #       ifdef FREEBSD_STACKBOTTOM
              result = GC_freebsd_stack_base();
   #       endif
 #       ifdef HEURISTIC2  #       ifdef HEURISTIC2
 #           ifdef STACK_GROWS_DOWN  #           ifdef STACK_GROWS_DOWN
                 result = GC_find_limit((ptr_t)(&dummy), TRUE);                  result = GC_find_limit((ptr_t)(&dummy), TRUE);
Line 699  ptr_t GC_get_stack_base()
Line 1021  ptr_t GC_get_stack_base()
 #   endif /* STACKBOTTOM */  #   endif /* STACKBOTTOM */
 }  }
   
 # endif /* ! AMIGA */  # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */
 # endif /* ! OS2 */  
 # endif /* ! MSWIN32 */  
   
 /*  /*
  * Register static data segment(s) as roots.   * Register static data segment(s) as roots.
Line 801  void GC_register_data_segments()
Line 1121  void GC_register_data_segments()
     }      }
 }  }
   
 # else  # else /* !OS2 */
   
   # if defined(MSWIN32) || defined(MSWINCE)
   
 # ifdef MSWIN32  # ifdef MSWIN32
   /* 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               */
   /* returns correct results for all addresses between a and start.     */    /* returns correct results for all addresses between a and start.     */
   /* Assumes VirtualQuery returns correct information for start.        */    /* Assumes VirtualQuery returns correct information for start.        */
   ptr_t GC_least_described_address(ptr_t start)    ptr_t GC_least_described_address(ptr_t start)
   {    {
     MEMORY_BASIC_INFORMATION buf;      MEMORY_BASIC_INFORMATION buf;
     SYSTEM_INFO sysinfo;  
     DWORD result;      DWORD result;
     LPVOID limit;      LPVOID limit;
     ptr_t p;      ptr_t p;
     LPVOID q;      LPVOID q;
   
     GetSystemInfo(&sysinfo);      limit = GC_sysinfo.lpMinimumApplicationAddress;
     limit = sysinfo.lpMinimumApplicationAddress;  
     p = (ptr_t)((word)start & ~(GC_page_size - 1));      p = (ptr_t)((word)start & ~(GC_page_size - 1));
     for (;;) {      for (;;) {
         q = (LPVOID)(p - GC_page_size);          q = (LPVOID)(p - GC_page_size);
Line 850  void GC_register_data_segments()
Line 1167  void GC_register_data_segments()
     }      }
     return(p);      return(p);
   }    }
   # 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();
          register DWORD result = VirtualQuery(malloc(1), &buf, sizeof(buf));           last_gc_no = GC_gc_no;
   
          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
   void GC_register_root_section(ptr_t static_root)    void GC_register_root_section(ptr_t static_root)
   {    {
       MEMORY_BASIC_INFORMATION buf;        MEMORY_BASIC_INFORMATION buf;
       SYSTEM_INFO sysinfo;  
       DWORD result;        DWORD result;
       DWORD protect;        DWORD protect;
       LPVOID p;        LPVOID p;
       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);
       GetSystemInfo(&sysinfo);        while (p < GC_sysinfo.lpMaximumApplicationAddress) {
       while (p < sysinfo.lpMaximumApplicationAddress) {  
         result = VirtualQuery(p, &buf, sizeof(buf));          result = VirtualQuery(p, &buf, sizeof(buf));
         if (result != sizeof(buf) || buf.AllocationBase == 0          if (result != sizeof(buf) || buf.AllocationBase == 0
             || GC_is_heap_base(buf.AllocationBase)) break;              || GC_is_heap_base(buf.AllocationBase)) break;
Line 912  void GC_register_data_segments()
Line 1299  void GC_register_data_segments()
       }        }
       if (base != limit) GC_add_roots_inner(base, limit, FALSE);        if (base != limit) GC_add_roots_inner(base, limit, FALSE);
   }    }
   #endif
   
   void GC_register_data_segments()    void GC_register_data_segments()
   {    {
   #     ifdef MSWIN32
       static char dummy;        static char dummy;
   
       GC_register_root_section((ptr_t)(&dummy));        GC_register_root_section((ptr_t)(&dummy));
   }  
 # else  
 # ifdef AMIGA  
   
    void GC_register_data_segments()  
    {  
      struct Process     *proc;  
      struct CommandLineInterface *cli;  
      BPTR myseglist;  
      ULONG *data;  
   
      int        num;  
   
   
 #    ifdef __GNUC__  
         ULONG dataSegSize;  
         GC_bool found_segment = FALSE;  
         extern char __data_size[];  
   
         dataSegSize=__data_size+8;  
         /* Can`t find the Location of __data_size, because  
            it`s possible that is it, inside the segment. */  
   
 #     endif  #     endif
   
         proc= (struct Process*)SysBase->ThisTask;  
   
         /* Reference: Amiga Guru Book Pages: 538ff,565,573  
                      and XOper.asm */  
         if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) {  
           if (proc->pr_CLI == NULL) {  
             myseglist = proc->pr_SegList;  
           } else {  
             /* ProcLoaded       'Loaded as a command: '*/  
             cli = BADDR(proc->pr_CLI);  
             myseglist = cli->cli_Module;  
           }  
         } else {  
           ABORT("Not a Process.");  
         }  
   
         if (myseglist == NULL) {  
             ABORT("Arrrgh.. can't find segments, aborting");  
         }  
   
         /* xoper hunks Shell Process */  
   
         num=0;  
         for (data = (ULONG *)BADDR(myseglist); data != NULL;  
              data = (ULONG *)BADDR(data[0])) {  
           if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) ||  
               ((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) {  
 #             ifdef __GNUC__  
                 if (dataSegSize == data[-1]) {  
                   found_segment = TRUE;  
                 }  
 #             endif  
               GC_add_roots_inner((char *)&data[1],  
                                  ((char *)&data[1]) + data[-1], FALSE);  
           }  
           ++num;  
         } /* for */  
 #       ifdef __GNUC__  
            if (!found_segment) {  
              ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library");  
            }  
 #       endif  
   }    }
   
 #if 0 /* old version */  # else /* !OS2 && !Windows */
   void GC_register_data_segments()  
   {  
     extern struct WBStartup *_WBenchMsg;  
     struct Process      *proc;  
     struct CommandLineInterface *cli;  
     BPTR myseglist;  
     ULONG *data;  
   
     if ( _WBenchMsg != 0 ) {  # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
         if ((myseglist = _WBenchMsg->sm_Segment) == 0) {        || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
             GC_err_puts("No seglist from workbench\n");  ptr_t GC_SysVGetDataStart(max_page_size, etext_addr)
             return;  
         }  
     } else {  
         if ((proc = (struct Process *)FindTask(0)) == 0) {  
             GC_err_puts("Cannot find process structure\n");  
             return;  
         }  
         if ((cli = BADDR(proc->pr_CLI)) == 0) {  
             GC_err_puts("No CLI\n");  
             return;  
         }  
         if ((myseglist = cli->cli_Module) == 0) {  
             GC_err_puts("No seglist from CLI\n");  
             return;  
         }  
     }  
   
     for (data = (ULONG *)BADDR(myseglist); data != 0;  
          data = (ULONG *)BADDR(data[0])) {  
 #        ifdef AMIGA_SKIP_SEG  
            if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) ||  
            ((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) {  
 #        else  
            {  
 #        endif /* AMIGA_SKIP_SEG */  
           GC_add_roots_inner((char *)&data[1],  
                              ((char *)&data[1]) + data[-1], FALSE);  
          }  
     }  
   }  
 #endif /* old version */  
   
   
 # else  
   
 # if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR)  
 char * GC_SysVGetDataStart(max_page_size, etext_addr)  
 int max_page_size;  int max_page_size;
 int * etext_addr;  int * etext_addr;
 {  {
Line 1061  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
   
   #  define GC_AMIGA_DS
   #  include "AmigaOS.c"
   #  undef GC_AMIGA_DS
   
   #else /* !OS2 && !Windows && !AMIGA */
   
 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 1083  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 1127  void GC_register_data_segments()
Line 1446  void GC_register_data_segments()
 }  }
   
 # endif  /* ! AMIGA */  # endif  /* ! AMIGA */
 # endif  /* ! MSWIN32 */  # endif  /* ! MSWIN32 && ! MSWINCE*/
 # endif  /* ! OS2 */  # endif  /* ! OS2 */
   
 /*  /*
  * Auxiliary routines for obtaining memory from OS.   * Auxiliary routines for obtaining memory from OS.
  */   */
   
 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \  # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
         && !defined(MSWIN32) && !defined(MACOS) && !defined(DOS4GW)          && !defined(MSWIN32) && !defined(MSWINCE) \
           && !defined(MACOS) && !defined(DOS4GW)
   
 # ifdef SUNOS4  # ifdef SUNOS4
     extern caddr_t sbrk();      extern caddr_t sbrk();
Line 1146  void GC_register_data_segments()
Line 1466  void GC_register_data_segments()
 #   define SBRK_ARG_T int  #   define SBRK_ARG_T int
 # endif  # endif
   
   
 # ifdef RS6000  # ifdef RS6000
 /* The compiler seems to generate speculative reads one past the end of */  /* The compiler seems to generate speculative reads one past the end of */
 /* an allocated object.  Hence we need to make sure that the page       */  /* an allocated object.  Hence we need to make sure that the page       */
Line 1178  word bytes;
Line 1499  word bytes;
 #else  /* Not RS6000 */  #else  /* Not RS6000 */
   
 #if defined(USE_MMAP)  #if defined(USE_MMAP)
 /* Tested only under IRIX5 and Solaris 2 */  /* Tested only under Linux, IRIX5 and Solaris 2 */
   
 #ifdef USE_MMAP_FIXED  #ifdef USE_MMAP_FIXED
 #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE  #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE
Line 1188  word bytes;
Line 1509  word bytes;
 #   define GC_MMAP_FLAGS MAP_PRIVATE  #   define GC_MMAP_FLAGS MAP_PRIVATE
 #endif  #endif
   
   #ifndef HEAP_START
   #   define HEAP_START 0
   #endif
   
 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));
   #   if !defined(LINUX)
         if (last_addr == 0) {
           /* Oops.  We got the end of the address space.  This isn't      */
           /* usable by arbitrary C code, since one-past-end pointers      */
           /* don't work, so we discard it and try again.                  */
           munmap(result, (size_t)(-GC_page_size) - (size_t)result);
                           /* Leave last page mapped, so we can't repeat. */
           return GC_unix_get_mem(bytes);
         }
   #   else
         GC_ASSERT(last_addr != 0);
   #   endif
     return((ptr_t)result);      return((ptr_t)result);
 }  }
   
Line 1259  void * os2_alloc(size_t bytes)
Line 1606  void * os2_alloc(size_t bytes)
 # endif /* OS2 */  # endif /* OS2 */
   
   
   # if defined(MSWIN32) || defined(MSWINCE)
   SYSTEM_INFO GC_sysinfo;
   # 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)
 word bytes;  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 1288  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;
         }          }
     }      }
 }  }
   # endif
   
   #ifdef AMIGA
   # define GC_AMIGA_AM
   # include "AmigaOS.c"
   # undef GC_AMIGA_AM
   #endif
   
   
   # ifdef MSWINCE
   word GC_n_heap_bases = 0;
   
   ptr_t GC_wince_get_mem(bytes)
   word bytes;
   {
       ptr_t result;
       word i;
   
       /* Round up allocation size to multiple of page size */
       bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1);
   
       /* Try to find reserved, uncommitted pages */
       for (i = 0; i < GC_n_heap_bases; i++) {
           if (((word)(-(signed_word)GC_heap_lengths[i])
                & (GC_sysinfo.dwAllocationGranularity-1))
               >= bytes) {
               result = GC_heap_bases[i] + GC_heap_lengths[i];
               break;
           }
       }
   
       if (i == GC_n_heap_bases) {
           /* Reserve more pages */
           word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
                            & ~(GC_sysinfo.dwAllocationGranularity-1);
           /* If we ever support MPROTECT_VDB here, we will probably need to       */
           /* ensure that res_bytes is strictly > bytes, so that VirtualProtect    */
           /* never spans regions.  It seems to be OK for a VirtualFree argument   */
           /* to span regions, so we should be OK for now.                         */
           result = (ptr_t) VirtualAlloc(NULL, res_bytes,
                                         MEM_RESERVE | MEM_TOP_DOWN,
                                         PAGE_EXECUTE_READWRITE);
           if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
               /* If I read the documentation correctly, this can  */
               /* only happen if HBLKSIZE > 64k or not a power of 2.       */
           if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
           GC_heap_bases[GC_n_heap_bases] = result;
           GC_heap_lengths[GC_n_heap_bases] = 0;
           GC_n_heap_bases++;
       }
   
       /* Commit pages */
       result = (ptr_t) VirtualAlloc(result, bytes,
                                     MEM_COMMIT,
                                     PAGE_EXECUTE_READWRITE);
       if (result != NULL) {
           if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
           GC_heap_lengths[i] += bytes;
       }
   
       return(result);
   }
 # endif  # endif
   
 #ifdef USE_MUNMAP  #ifdef USE_MUNMAP
   
 /* For now, this only works on some Unix-like systems.  If you  */  /* For now, this only works on Win32/WinCE and some Unix-like   */
 /* have something else, don't define USE_MUNMAP.                */  /* systems.  If you have something else, don't define           */
   /* USE_MUNMAP.                                                  */
 /* We assume ANSI C to support this feature.                    */  /* We assume ANSI C to support this feature.                    */
   
   #if !defined(MSWIN32) && !defined(MSWINCE)
   
 #include <unistd.h>  #include <unistd.h>
 #include <sys/mman.h>  #include <sys/mman.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <sys/types.h>  #include <sys/types.h>
 #include <fcntl.h>  
   
   #endif
   
 /* Compute a page aligned starting address for the unmap        */  /* Compute a page aligned starting address for the unmap        */
 /* operation on a block of size bytes starting at start.        */  /* operation on a block of size bytes starting at start.        */
 /* Return 0 if the block is too small to make this feasible.    */  /* Return 0 if the block is too small to make this feasible.    */
Line 1332  ptr_t GC_unmap_end(ptr_t start, word bytes)
Line 1763  ptr_t GC_unmap_end(ptr_t start, word bytes)
     return end_addr;      return end_addr;
 }  }
   
   /* Under Win32/WinCE we commit (map) and decommit (unmap)       */
   /* memory using VirtualAlloc and VirtualFree.  These functions  */
   /* work on individual allocations of virtual memory, made       */
   /* previously using VirtualAlloc with the MEM_RESERVE flag.     */
   /* The ranges we need to (de)commit may span several of these   */
   /* allocations; therefore we use VirtualQuery to check          */
   /* allocation lengths, and split up the range as necessary.     */
   
 /* We assume that GC_remap is called on exactly the same range  */  /* We assume that GC_remap is called on exactly the same range  */
 /* as a previous call to GC_unmap.  It is safe to consistently  */  /* as a previous call to GC_unmap.  It is safe to consistently  */
 /* round the endpoints in both places.                          */  /* round the endpoints in both places.                          */
Line 1341  void GC_unmap(ptr_t start, word bytes)
Line 1780  void GC_unmap(ptr_t start, word bytes)
     ptr_t end_addr = GC_unmap_end(start, bytes);      ptr_t end_addr = GC_unmap_end(start, bytes);
     word len = end_addr - start_addr;      word len = end_addr - start_addr;
     if (0 == start_addr) return;      if (0 == start_addr) return;
     if (munmap(start_addr, len) != 0) ABORT("munmap failed");  #   if defined(MSWIN32) || defined(MSWINCE)
     GC_unmapped_bytes += len;        while (len != 0) {
             MEMORY_BASIC_INFORMATION mem_info;
             GC_word free_len;
             if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
                 != sizeof(mem_info))
                 ABORT("Weird VirtualQuery result");
             free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
             if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
                 ABORT("VirtualFree failed");
             GC_unmapped_bytes += free_len;
             start_addr += free_len;
             len -= free_len;
         }
   #   else
         if (munmap(start_addr, len) != 0) ABORT("munmap failed");
         GC_unmapped_bytes += len;
   #   endif
 }  }
   
   
Line 1354  void GC_remap(ptr_t start, word bytes)
Line 1809  void GC_remap(ptr_t start, word bytes)
     word len = end_addr - start_addr;      word len = end_addr - start_addr;
     ptr_t result;      ptr_t result;
   
     if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);  #   if defined(MSWIN32) || defined(MSWINCE)
     if (0 == start_addr) return;        if (0 == start_addr) return;
     result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,        while (len != 0) {
                   MAP_FIXED | MAP_PRIVATE, zero_descr, 0);            MEMORY_BASIC_INFORMATION mem_info;
     if (result != start_addr) {            GC_word alloc_len;
         ABORT("mmap remapping failed");            if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
     }                != sizeof(mem_info))
     GC_unmapped_bytes -= len;                ABORT("Weird VirtualQuery result");
             alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
             result = VirtualAlloc(start_addr, alloc_len,
                                   MEM_COMMIT,
                                   PAGE_EXECUTE_READWRITE);
             if (result != start_addr) {
                 ABORT("VirtualAlloc remapping failed");
             }
             GC_unmapped_bytes -= alloc_len;
             start_addr += alloc_len;
             len -= alloc_len;
         }
   #   else
         if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);
         fcntl(zero_descr, F_SETFD, FD_CLOEXEC);
         if (0 == start_addr) return;
         result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
                       MAP_FIXED | MAP_PRIVATE, zero_descr, 0);
         if (result != start_addr) {
             ABORT("mmap remapping failed");
         }
         GC_unmapped_bytes -= len;
   #   endif
 }  }
   
 /* Two adjacent blocks have already been unmapped and are about to      */  /* Two adjacent blocks have already been unmapped and are about to      */
Line 1382  void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t sta
Line 1859  void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t sta
     if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);      if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
     if (0 == start_addr) return;      if (0 == start_addr) return;
     len = end_addr - start_addr;      len = end_addr - start_addr;
     if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");  #   if defined(MSWIN32) || defined(MSWINCE)
     GC_unmapped_bytes += len;        while (len != 0) {
             MEMORY_BASIC_INFORMATION mem_info;
             GC_word free_len;
             if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
                 != sizeof(mem_info))
                 ABORT("Weird VirtualQuery result");
             free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
             if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
                 ABORT("VirtualFree failed");
             GC_unmapped_bytes += free_len;
             start_addr += free_len;
             len -= free_len;
         }
   #   else
         if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
         GC_unmapped_bytes += len;
   #   endif
 }  }
   
 #endif /* USE_MUNMAP */  #endif /* USE_MUNMAP */
   
 /* Routine for pushing any additional roots.  In THREADS        */  /* Routine for pushing any additional roots.  In THREADS        */
 /* environment, this is also responsible for marking from       */  /* environment, this is also responsible for marking from       */
 /* thread stacks.  In the SRC_M3 case, it also handles          */  /* thread stacks.                                               */
 /* global variables.                                            */  
 #ifndef THREADS  #ifndef THREADS
 void (*GC_push_other_roots)() = 0;  void (*GC_push_other_roots)() = 0;
 #else /* THREADS */  #else /* THREADS */
Line 1418  PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any
Line 1910  PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any
 }  }
   
   
 void GC_default_push_other_roots()  void GC_default_push_other_roots GC_PROTO((void))
 {  {
     /* Traverse data allocated by previous memory managers.             */      /* Traverse data allocated by previous memory managers.             */
         {          {
Line 1446  void GC_default_push_other_roots()
Line 1938  void GC_default_push_other_roots()
     --> misconfigured      --> misconfigured
 # endif  # endif
   
   void GC_push_thread_structures GC_PROTO((void))
   {
       /* Not our responsibibility. */
   }
   
 extern void ThreadF__ProcessStacks();  extern void ThreadF__ProcessStacks();
   
Line 1463  int dummy3;
Line 1959  int dummy3;
 {  {
     word q = *p;      word q = *p;
   
     if ((ptr_t)(q) >= GC_least_plausible_heap_addr      GC_PUSH_ONE_STACK(q, p);
          && (ptr_t)(q) < GC_greatest_plausible_heap_addr) {  
          GC_push_one_checked(q,FALSE);  
     }  
 }  }
   
 /* M3 set equivalent to RTHeap.TracedRefTypes */  /* M3 set equivalent to RTHeap.TracedRefTypes */
 typedef struct { int elts[1]; }  RefTypeSet;  typedef struct { int elts[1]; }  RefTypeSet;
 RefTypeSet GC_TracedRefTypes = {{0x1}};  RefTypeSet GC_TracedRefTypes = {{0x1}};
   
 /* From finalize.c */  void GC_default_push_other_roots GC_PROTO((void))
 extern void GC_push_finalizer_structures();  
   
 /* From stubborn.c: */  
 # ifdef STUBBORN_ALLOC  
     extern GC_PTR * GC_changing_list_start;  
 # endif  
   
   
 void GC_default_push_other_roots()  
 {  {
     /* Use the M3 provided routine for finding static roots.    */      /* Use the M3 provided routine for finding static roots.     */
     /* This is a bit dubious, since it presumes no C roots.     */      /* This is a bit dubious, since it presumes no C roots.      */
     /* We handle the collector roots explicitly.                */      /* We handle the collector roots explicitly in GC_push_roots */
        {          RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes);
 #        ifdef STUBBORN_ALLOC  
            GC_push_one(GC_changing_list_start);  
 #        endif  
          GC_push_finalizer_structures();  
          RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes);  
        }  
         if (GC_words_allocd > 0) {          if (GC_words_allocd > 0) {
             ThreadF__ProcessStacks(GC_push_thread_stack);              ThreadF__ProcessStacks(GC_push_thread_stack);
         }          }
Line 1503  void GC_default_push_other_roots()
Line 1981  void GC_default_push_other_roots()
   
 # 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(IRIX_JDK_THREADS) || defined(HPUX_THREADS)  
   
 extern void GC_push_all_stacks();  extern void GC_push_all_stacks();
   
 void GC_default_push_other_roots()  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_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.
  * We plan to eventaually implement four strategies for doing so:   * We plan to eventually implement four strategies for doing so:
  * DEFAULT_VDB: A simple dummy implementation that treats every page   * DEFAULT_VDB: A simple dummy implementation that treats every page
  *              as possibly dirty.  This makes incremental collection   *              as possibly dirty.  This makes incremental collection
  *              useless, but the implementation is still correct.   *              useless, but the implementation is still correct.
Line 1540  void (*GC_push_other_roots)() = GC_default_push_other_
Line 2017  void (*GC_push_other_roots)() = GC_default_push_other_
  *              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 1554  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 1595  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 1615  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.  
  */   */
   
 # ifndef MSWIN32  # if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN)
   
 #   include <sys/mman.h>  #   include <sys/mman.h>
 #   include <signal.h>  #   include <signal.h>
Line 1644  struct hblk *h;
Line 2134  struct hblk *h;
   
 # else  # else
   
 #   include <signal.h>  # 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
   #     include <signal.h>
   #   endif
   
     static DWORD protect_junk;      static DWORD protect_junk;
 #   define PROTECT(addr, len) \  #   define PROTECT(addr, len) \
Line 1659  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)  
   #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
 #   define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1)  #   define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1)
 #endif  #endif
   #if defined(MSWINCE)
       typedef LONG (WINAPI *SIG_PF)(struct _EXCEPTION_POINTERS *);
   #   undef SIG_DFL
   #   define SIG_DFL (SIG_PF) (-1)
   #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 1692  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
 #     if defined(IA64)  #     if defined(IA64) || defined(HP_PA)
         typedef void (* REAL_SIG_PF)(int, siginfo_t *, s_c *);          typedef void (* REAL_SIG_PF)(int, siginfo_t *, s_c *);
 #     else  #     else
         typedef void (* REAL_SIG_PF)(int, s_c);          typedef void (* REAL_SIG_PF)(int, s_c);
Line 1722  struct hblk *h;
Line 2245  struct hblk *h;
         return (char *)faultaddr;          return (char *)faultaddr;
     }      }
 #   endif /* !ALPHA */  #   endif /* !ALPHA */
 # endif  # endif /* LINUX */
   
   #ifndef DARWIN
 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 */
   
   #if defined(THREADS)
   /* 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       */
   /* test-and-set spin lock if we know how to do that.  Otherwise we      */
   /* check whether we are already in the handler and use the dumb but     */
   /* safe fallback algorithm of setting all bits in the word.             */
   /* Contention should be very rare, so we do the minimum to handle it    */
   /* correctly.                                                           */
   #ifdef GC_TEST_AND_SET_DEFINED
     static VOLATILE unsigned int fault_handler_lock = 0;
     void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
       while (GC_test_and_set(&fault_handler_lock)) {}
       /* Could also revert to set_pht_entry_from_index_safe if initial    */
       /* GC_test_and_set fails.                                           */
       set_pht_entry_from_index(db, index);
       GC_clear(&fault_handler_lock);
     }
   #else /* !GC_TEST_AND_SET_DEFINED */
     /* THIS IS INCORRECT! The dirty bit vector may be temporarily wrong,  */
     /* just before we notice the conflict and correct it. We may end up   */
     /* looking at it while it's wrong.  But this requires contention      */
     /* exactly when a GC is triggered, which seems far less likely to     */
     /* fail than the old code, which had no reported failures.  Thus we   */
     /* leave it this way while we think of something better, or support   */
     /* GC_test_and_set on the remaining platforms.                        */
     static VOLATILE word currently_updating = 0;
     void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
       unsigned int update_dummy;
       currently_updating = (word)(&update_dummy);
       set_pht_entry_from_index(db, index);
       /* If we get contention in the 10 or so instruction window here,    */
       /* and we get stopped by a GC between the two updates, we lose!     */
       if (currently_updating != (word)(&update_dummy)) {
           set_pht_entry_from_index_safe(db, index);
           /* We claim that if two threads concurrently try to update the  */
           /* dirty bit vector, the first one to execute UPDATE_START      */
           /* will see it changed when UPDATE_END is executed.  (Note that */
           /* &update_dummy must differ in two distinct threads.)  It      */
           /* will then execute set_pht_entry_from_index_safe, thus        */
           /* returning us to a safe state, though not soon enough.        */
       }
     }
   #endif /* !GC_TEST_AND_SET_DEFINED */
   #else /* !THREADS */
   # define async_set_pht_entry_from_index(db, index) \
           set_pht_entry_from_index(db, index)
   #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 1743  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)
 #   else  #   else
 #     if defined(IA64)  #     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 1770  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 1791  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(MSWIN32)  
   # 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 == \
                         EXCEPTION_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 1811  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)
           char * addr = NULL;            char * addr = NULL;
   
           struct sigcontext *scp = (struct sigcontext *)(&sc);            struct sigcontext *scp = (struct sigcontext *)(sc);
   
           int format = (scp->sc_formatvec >> 12) & 0xf;            int format = (scp->sc_formatvec >> 12) & 0xf;
           unsigned long *framedata = (unsigned long *)(scp + 1);            unsigned long *framedata = (unsigned long *)(scp + 1);
Line 1829  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2419  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
           } else if (format == 7) {            } else if (format == 7) {
                 /* 68040 */                  /* 68040 */
                 ea = framedata[3];                  ea = framedata[3];
                   if (framedata[1] & 0x08000000) {
                           /* correct addr on misaligned access */
                           ea = (ea+4095)&(~4095);
                   }
           } else if (format == 4) {            } else if (format == 4) {
                 /* 68060 */                  /* 68060 */
                 ea = framedata[0];                  ea = framedata[0];
Line 1842  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2436  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #         ifdef ALPHA  #         ifdef ALPHA
             char * addr = get_fault_addr(sc);              char * addr = get_fault_addr(sc);
 #         else  #         else
 #           ifdef IA64  #           if defined(IA64) || defined(HP_PA)
               char * addr = si -> si_addr;                char * addr = si -> si_addr;
                 /* I believe this is claimed to work on all platforms for */
                 /* Linux 2.3.47 and later.  Hopefully we don't have to    */
                 /* worry about earlier kernels on IA64.                   */
 #           else  #           else
 #             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(MSWIN32)  #   if defined(MSWIN32) || defined(MSWINCE)
         char * addr = (char *) (exc_info -> ExceptionRecord          char * addr = (char *) (exc_info -> ExceptionRecord
                                 -> ExceptionInformation[1]);                                  -> ExceptionInformation[1]);
 #       define sig SIGSEGV  #       define sig SIGSEGV
Line 1887  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2488  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
                 old_handler = GC_old_bus_handler;                  old_handler = GC_old_bus_handler;
             }              }
             if (old_handler == SIG_DFL) {              if (old_handler == SIG_DFL) {
 #               ifndef MSWIN32  #               if !defined(MSWIN32) && !defined(MSWINCE)
                     GC_err_printf1("Segfault at 0x%lx\n", addr);                      GC_err_printf1("Segfault at 0x%lx\n", addr);
                     ABORT("Unexpected bus error or segmentation fault");                      ABORT("Unexpected bus error or segmentation fault");
 #               else  #               else
Line 1906  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2507  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #                   if defined(ALPHA) || defined(M68K)  #                   if defined(ALPHA) || defined(M68K)
                         (*(REAL_SIG_PF)old_handler) (sig, code, sc);                          (*(REAL_SIG_PF)old_handler) (sig, code, sc);
 #                   else  #                   else
 #                     if defined(IA64)  #                     if defined(IA64) || defined(HP_PA)
                         (*(REAL_SIG_PF)old_handler) (sig, si, scp);                          (*(REAL_SIG_PF)old_handler) (sig, si, scp);
 #                     else  #                     else
                         (*(REAL_SIG_PF)old_handler) (sig, sc);                          (*(REAL_SIG_PF)old_handler) (sig, sc);
Line 1914  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
Line 1923  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
Line 2524  SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS
 #               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);
   
             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
         /* The write may not take place before dirty bits are read.     */          /* The write may not take place before dirty bits are read.     */
         /* But then we'll fault again ...                               */          /* But then we'll fault again ...                               */
 #       ifdef MSWIN32  #       if defined(MSWIN32) || defined(MSWINCE)
             return(EXCEPTION_CONTINUE_EXECUTION);              return(EXCEPTION_CONTINUE_EXECUTION);
 #       else  #       else
             return;              return;
 #       endif  #       endif
     }      }
 #ifdef MSWIN32  #if defined(MSWIN32) || defined(MSWINCE)
     return EXCEPTION_CONTINUE_SEARCH;      return EXCEPTION_CONTINUE_SEARCH;
 #else  #else
     GC_err_printf1("Segfault at 0x%lx\n", addr);      GC_err_printf1("Segfault at 0x%lx\n", addr);
     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);
             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) || \
     struct sigaction    act, oldact;         defined(OSF1) || defined(HURD)
 #   ifdef IRIX5        struct sigaction  act, oldact;
         /* We should probably specify SA_SIGINFO for Linux, and handle    */
         /* the different architectures more uniformly.                    */
   #     if defined(IRIX5) || defined(LINUX) || defined(OSF1) || defined(HURD)
         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);
 #endif  #     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
 #   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 2009  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 2021  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(IRIX_JDK_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 2048  void GC_dirty_init()
Line 2680  void GC_dirty_init()
           GC_err_printf0("Replaced other SIGSEGV handler\n");            GC_err_printf0("Replaced other SIGSEGV handler\n");
 #       endif  #       endif
       }        }
 #     ifdef HPUX  #   endif
           sigaction(SIGBUS, &act, &oldact);  #   if defined(HPUX) || defined(LINUX) || defined(HURD)
           GC_old_bus_handler = oldact.sa_handler;        sigaction(SIGBUS, &act, &oldact);
           if (GC_old_segv_handler != SIG_DFL) {        GC_old_bus_handler = oldact.sa_handler;
 #           ifdef PRINTSTATS        if (GC_old_bus_handler == SIG_IGN) {
               GC_err_printf0("Replaced other SIGBUS handler\n");               GC_err_printf0("Previously ignored bus error!?");
 #           endif               GC_old_bus_handler = SIG_DFL;
           }        }
 #     endif        if (GC_old_bus_handler != SIG_DFL) {
 #    endif  #       ifdef PRINTSTATS
             GC_err_printf0("Replaced other SIGBUS handler\n");
   #       endif
         }
   #   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 2069  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 2110  struct hblk * h;
Line 2806  struct hblk * h;
  * happens to work.   * happens to work.
  * On other systems, SET_LOCK_HOLDER and friends must be suitably defined.   * On other systems, SET_LOCK_HOLDER and friends must be suitably defined.
  */   */
   
   static GC_bool syscall_acquired_lock = FALSE;   /* Protected by GC lock. */
   
 void GC_begin_syscall()  void GC_begin_syscall()
 {  {
     if (!I_HOLD_LOCK()) LOCK();      if (!I_HOLD_LOCK()) {
           LOCK();
           syscall_acquired_lock = TRUE;
       }
 }  }
   
 void GC_end_syscall()  void GC_end_syscall()
 {  {
     if (!I_HOLD_LOCK()) UNLOCK();      if (syscall_acquired_lock) {
           syscall_acquired_lock = FALSE;
           UNLOCK();
       }
 }  }
   
 void GC_unprotect_range(addr, len)  void GC_unprotect_range(addr, len)
Line 2130  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 2142  word len;
Line 2846  word len;
     for (h = start_block; h <= end_block; h++) {      for (h = start_block; h <= end_block; h++) {
         register word index = PHT_HASH(h);          register word index = PHT_HASH(h);
   
         set_pht_entry_from_index(GC_dirty_pages, index);          async_set_pht_entry_from_index(GC_dirty_pages, index);
     }      }
     UNPROTECT(start_block,      UNPROTECT(start_block,
               ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);                ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
 }  }
   
 #ifndef MSWIN32  #if 0
 /* Replacement for UNIX system call.     */  
 /* Other calls that write to the heap    */  /* We no longer wrap read by default, since that was causing too many   */
 /* should be handled similarly.          */  /* problems.  It is preferred that the client instead avoids writing    */
   /* 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>
     ssize_t read(int fd, void *buf, size_t nbyte)      ssize_t read(int fd, void *buf, size_t nbyte)
 # else  # else
 #   ifndef LINT  #   ifndef LINT
Line 2170  word len;
Line 2888  word len;
   
     GC_begin_syscall();      GC_begin_syscall();
     GC_unprotect_range(buf, (word)nbyte);      GC_unprotect_range(buf, (word)nbyte);
 #   ifdef IRIX5  #   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.                             */
           /* On Linux, we have to be careful with the linuxthreads        */
           /* read interception.                                           */
         {          {
             struct iovec iov;              struct iovec iov;
   
Line 2182  word len;
Line 2902  word len;
             result = readv(fd, &iov, 1);              result = readv(fd, &iov, 1);
         }          }
 #   else  #   else
         result = syscall(SYS_read, fd, buf, nbyte);  #     if defined(HURD)
           result = __read(fd, buf, nbyte);
   #     else
           /* The two zero args at the end of this list are because one
              IA-64 syscall() implementation actually requires six args
              to be passed, even though they aren't always used. */
           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 */  #endif /* !MSWIN32 && !MSWINCE && !GC_LINUX_THREADS */
   
   #if defined(GC_USE_LD_WRAP) && !defined(THREADS)
       /* We use the GNU ld call wrapping facility.                        */
       /* This requires that the linker be invoked with "--wrap read".     */
       /* This can be done by passing -Wl,"--wrap read" to gcc.            */
       /* I'm not sure that this actually wraps whatever version of read   */
       /* is called by stdio.  That code also mentions __read.             */
   #   include <unistd.h>
       ssize_t __wrap_read(int fd, void *buf, size_t nbyte)
       {
           int result;
   
           GC_begin_syscall();
           GC_unprotect_range(buf, (word)nbyte);
           result = __real_read(fd, buf, nbyte);
           GC_end_syscall();
           return(result);
       }
   
       /* We should probably also do this for __read, or whatever stdio    */
       /* actually calls.                                                  */
   #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 2226  word n;
Line 2977  word n;
 #include <sys/syscall.h>  #include <sys/syscall.h>
 #include <sys/procfs.h>  #include <sys/procfs.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <fcntl.h>  
   
 #define INITIAL_BUF_SZ 4096  #define INITIAL_BUF_SZ 4096
 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 2283  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 2300  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 2344  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 2374  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 2392  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 2408  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 2426  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 2440  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 2509  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.
  */   */
 #if defined(SPARC) && !defined(LINUX)  
 #   if defined(SUNOS4)  /* I suspect the following works for most X86 *nix variants, so         */
 #     include <machine/frame.h>  /* long as the frame pointer is explicitly stored.  In the case of gcc, */
 #   else  /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is.  */
 #     if defined (DRSNX)  #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)
 #       include <sys/sparc/frame.h>  #   include <features.h>
 #     else  
 #        if defined(OPENBSD)      struct frame {
 #          include <frame.h>          struct frame *fr_savfp;
 #        else          long    fr_savpc;
 #          include <sys/frame.h>          long    fr_arg[NARGS];  /* All the arguments go here.   */
 #        endif      };
 #     endif  #endif
 #   endif  
 #   if NARGS > 6  #if defined(SPARC)
   #  if defined(LINUX)
   #    include <features.h>
   
        struct frame {
           long    fr_local[8];
           long    fr_arg[6];
           struct frame *fr_savfp;
           long    fr_savpc;
   #       ifndef __arch64__
             char  *fr_stret;
   #       endif
           long    fr_argd[6];
           long    fr_argx[0];
        };
   #  else
   #    if defined(SUNOS4)
   #      include <machine/frame.h>
   #    else
   #      if defined (DRSNX)
   #        include <sys/sparc/frame.h>
   #      else
   #        if defined(OPENBSD) || defined(NETBSD)
   #          include <frame.h>
   #        else
   #          include <sys/frame.h>
   #        endif
   #      endif
   #    endif
   #  endif
   #  if NARGS > 6
         --> We only know how to to get the first 6 arguments          --> We only know how to to get the first 6 arguments
 #   endif  #  endif
   #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 OPENBSD  #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)
 #  define FR_SAVFP fr_fp  #  define FR_SAVFP fr_fp
 #  define FR_SAVPC fr_pc  #  define FR_SAVPC fr_pc
 #else  #else
Line 2552  struct hblk *h;
Line 3922  struct hblk *h;
 #  define FR_SAVPC fr_savpc  #  define FR_SAVPC fr_savpc
 #endif  #endif
   
   #if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9))
   #   define BIAS 2047
   #else
   #   define BIAS 0
   #endif
   
 void GC_save_callers (info)  void GC_save_callers (info)
 struct callinfo info[NFRAMES];  struct callinfo info[NFRAMES];
 {  {
   struct frame *frame;    struct frame *frame;
   struct frame *fp;    struct frame *fp;
   int nframes = 0;    int nframes = 0;
   word GC_save_regs_in_stack();  # ifdef I386
       /* We assume this is turned on only with gcc as the compiler. */
   frame = (struct frame *) GC_save_regs_in_stack ();      asm("movl %%ebp,%0" : "=r"(frame));
       fp = frame;
   # else
       frame = (struct frame *) GC_save_regs_in_stack ();
       fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);
   #endif
   
   for (fp = frame -> FR_SAVFP; fp != 0 && nframes < NFRAMES;     for (; (!(fp HOTTER_THAN frame) && !(GC_stackbottom HOTTER_THAN (ptr_t)fp)
        fp = fp -> FR_SAVFP, nframes++) {             && (nframes < NFRAMES));
          fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), 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 */
 #endif /* SPARC */  
   
   #ifdef NEED_CALLINFO
   
   /* Print info to stderr.  We do NOT hold the allocation lock */
   void GC_print_callers (info)
   struct callinfo info[NFRAMES];
   {
       register int i;
       static int reentry_count = 0;
       GC_bool stop = FALSE;
   
       LOCK();
         ++reentry_count;
       UNLOCK();
   
   #   if NFRAMES == 1
         GC_err_printf0("\tCaller at allocation:\n");
   #   else
         GC_err_printf0("\tCall chain at allocation:\n");
   #   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
           }
       }
       LOCK();
         --reentry_count;
       UNLOCK();
   }
   
   #endif /* NEED_CALLINFO */
   
   
   
   #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
   
   /* Dump /proc/self/maps to GC_stderr, to enable looking up names for
      addresses in FIND_LEAK output. */
   
   static word dump_maps(char *maps)
   {
       GC_err_write(maps, strlen(maps));
       return 1;
   }
   
   void GC_print_address_map()
   {
       GC_err_printf0("---------- Begin address map ----------\n");
       GC_apply_to_maps(dump_maps);
       GC_err_printf0("---------- End address map ----------\n");
   }
   
   #endif
   
   

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

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