OSDN Git Service

2003-01-06 Dale Johannesen <dalej@apple.com>
[pf3gnuchains/gcc-fork.git] / boehm-gc / os_dep.c
index cc816df..82386ec 100644 (file)
@@ -84,7 +84,7 @@
 #   include <setjmp.h>
 #endif
 
-#ifdef FREEBSD
+#if defined(FREEBSD) && defined(I386)
 #  include <machine/trap.h>
 #endif
 
 
 # ifdef LINUX
 #   pragma weak __data_start
-    extern int __data_start;
+    extern int __data_start[];
 #   pragma weak data_start
-    extern int data_start;
+    extern int data_start[];
 # endif /* LINUX */
-  extern int _end;
+  extern int _end[];
 
   ptr_t GC_data_start;
 
 
 #   ifdef LINUX
       /* Try the easy approaches first:        */
-      if (&__data_start != 0) {
-         GC_data_start = (ptr_t)(&__data_start);
+      if (__data_start != 0) {
+         GC_data_start = (ptr_t)__data_start;
          return;
       }
-      if (&data_start != 0) {
-         GC_data_start = (ptr_t)(&data_start);
+      if (data_start != 0) {
+         GC_data_start = (ptr_t)data_start;
          return;
       }
 #   endif /* LINUX */
-    GC_data_start = GC_find_limit((ptr_t)(&_end), FALSE);
+    GC_data_start = GC_find_limit((ptr_t)_end, FALSE);
   }
 #endif
 
@@ -333,7 +333,8 @@ void GC_enable_signals(void)
 
 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
       && !defined(MSWINCE) \
-      && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW)
+      && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
+      && !defined(NOSYS) && !defined(ECOS)
 
 #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
        /* Use the traditional BSD interface */
@@ -516,7 +517,7 @@ ptr_t GC_get_stack_base()
 #   undef GC_AMIGA_SB
 # endif /* AMIGA */
 
-# if defined(NEED_FIND_LIMIT) || (defined(UNIX_LIKE) && !defined(ECOS))
+# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
 
 #   ifdef __STDC__
        typedef void (*handler)(int);
@@ -540,8 +541,7 @@ ptr_t GC_get_stack_base()
       handler h;
 #   endif
     {
-# ifndef ECOS
-#      if defined(SUNOS5SIGS) || defined(IRIX5)  \
+#     if defined(SUNOS5SIGS) || defined(IRIX5)  \
         || defined(OSF1) || defined(HURD)
          struct sigaction      act;
 
@@ -579,7 +579,6 @@ ptr_t GC_get_stack_base()
            old_bus_handler = signal(SIGBUS, h);
 #        endif
 #      endif
-# endif /* ECOS */
     }
 # endif /* NEED_FIND_LIMIT || UNIX_LIKE */
 
@@ -602,21 +601,19 @@ ptr_t GC_get_stack_base()
     
     void GC_reset_fault_handler()
     {
-# ifndef ECOS
-#       if defined(SUNOS5SIGS) || defined(IRIX5) \
-          || defined(OSF1) || defined(HURD)
-         (void) sigaction(SIGSEGV, &old_segv_act, 0);
-#        if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
-            || defined(HPUX) || defined(HURD)
-             (void) sigaction(SIGBUS, &old_bus_act, 0);
-#        endif
-#       else
-         (void) signal(SIGSEGV, old_segv_handler);
-#        ifdef SIGBUS
-           (void) signal(SIGBUS, old_bus_handler);
-#        endif
-#       endif
-# endif /* ECOS */
+#     if defined(SUNOS5SIGS) || defined(IRIX5) \
+        || defined(OSF1) || defined(HURD)
+       (void) sigaction(SIGSEGV, &old_segv_act, 0);
+#      if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
+          || defined(HPUX) || defined(HURD)
+           (void) sigaction(SIGBUS, &old_bus_act, 0);
+#      endif
+#      else
+       (void) signal(SIGSEGV, old_segv_handler);
+#      ifdef SIGBUS
+         (void) signal(SIGBUS, old_bus_handler);
+#      endif
+#     endif
     }
 
     /* Return the first nonaddressible location > p (up) or    */
@@ -625,39 +622,41 @@ ptr_t GC_get_stack_base()
     ptr_t p;
     GC_bool up;
     {
-# ifndef ECOS
-        static VOLATILE ptr_t result;
-               /* Needs to be static, since otherwise it may not be    */
-               /* preserved across the longjmp.  Can safely be         */
-               /* static since it's only called once, with the         */
-               /* allocation lock held.                                */
-
-
-       GC_setup_temporary_fault_handler();
-       if (setjmp(GC_jmp_buf) == 0) {
-           result = (ptr_t)(((word)(p))
-                             & ~(MIN_PAGE_SIZE-1));
-           for (;;) {
-               if (up) {
-                   result += MIN_PAGE_SIZE;
-               } else {
-                   result -= MIN_PAGE_SIZE;
-               }
-               GC_noop1((word)(*result));
-           }
-       }
-       GC_reset_fault_handler();
-       if (!up) {
+      static VOLATILE ptr_t result;
+               /* Needs to be static, since otherwise it may not be    */
+               /* preserved across the longjmp.  Can safely be         */
+               /* static since it's only called once, with the         */
+               /* allocation lock held.                                */
+
+
+      GC_setup_temporary_fault_handler();
+      if (setjmp(GC_jmp_buf) == 0) {
+       result = (ptr_t)(((word)(p))
+                        & ~(MIN_PAGE_SIZE-1));
+       for (;;) {
+         if (up) {
            result += MIN_PAGE_SIZE;
-       }
-       return(result);
-# else /* ECOS */
-       abort();
-# endif /* ECOS */
+         } else {
+           result -= MIN_PAGE_SIZE;
+         }
+         GC_noop1((word)(*result));
+       }
+      }
+      GC_reset_fault_handler();
+      if (!up) {
+       result += MIN_PAGE_SIZE;
+      }
+      return(result);
     }
 # endif
 
-# ifndef ECOS
+# if defined(ECOS) || defined(NOSYS)
+ptr_t GC_get_stack_base()
+{
+  return STACKBOTTOM;
+}
+
+#else
 
 #ifdef LINUX_STACKBOTTOM
 
@@ -748,20 +747,20 @@ ptr_t GC_get_stack_base()
 
   ptr_t GC_freebsd_stack_base(void)
   {
-    int nm[2] = { CTL_KERN, KERN_USRSTACK}, base, len, r;
-    
-    len = sizeof(int);
-    r = sysctl(nm, 2, &base, &len, NULL, 0);
+    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 (ptr_t)base;
+    return base;
   }
 
 #endif /* FREEBSD_STACKBOTTOM */
 
 #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
-    && !defined(MSWINCE) && !defined(OS2) && !defined(ECOS)
+    && !defined(MSWINCE) && !defined(OS2)
 
 ptr_t GC_get_stack_base()
 {
@@ -815,7 +814,7 @@ ptr_t GC_get_stack_base()
        return(result);
 #   endif /* STACKBOTTOM */
 }
-# endif /* ECOS */
+# endif /* NOSYS ECOS */
 
 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */
 
@@ -929,19 +928,17 @@ void GC_register_data_segments()
   /* all real work is done by GC_register_dynamic_libraries.  Under    */
   /* win32s, we cannot find the data segments associated with dll's.   */
   /* We rgister the main data segment here.                            */
-  GC_bool GC_win32s = FALSE;   /* We're running under win32s.  */
-  
-  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);
-  }
+#  ifdef __GCC__
+  GC_bool GC_no_win32_dlls = TRUE;      /* GCC can't do SEH, so we can't use VirtualQuery */
+#  else
+  GC_bool GC_no_win32_dlls = FALSE;     
+#  endif
   
   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              */
@@ -1009,7 +1006,7 @@ void GC_register_data_segments()
       char * base;
       char * limit, * new_limit;
     
-      if (!GC_win32s) return;
+      if (!GC_no_win32_dlls) return;
       p = base = limit = GC_least_described_address(static_root);
       while (p < GC_sysinfo.lpMaximumApplicationAddress) {
         result = VirtualQuery(p, &buf, sizeof(buf));
@@ -1102,6 +1099,9 @@ void GC_register_data_segments()
        GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
 #     else
        GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
+#       if defined(DATASTART2)
+         GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), FALSE);
+#       endif
 #     endif
 #   endif
 #   if !defined(PCR) && (defined(NEXT) || defined(MACOSX))
@@ -1300,8 +1300,14 @@ void * os2_alloc(size_t bytes)
 SYSTEM_INFO GC_sysinfo;
 # endif
 
-
 # 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;
 
 ptr_t GC_win32_get_mem(bytes)
@@ -1309,7 +1315,7 @@ word bytes;
 {
     ptr_t result;
 
-    if (GC_win32s) {
+    if (GLOBAL_ALLOC_TEST) {
        /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE.    */
        /* There are also unconfirmed rumors of other           */
        /* problems, so we dodge the issue.                     */
@@ -1330,7 +1336,7 @@ word bytes;
 
 void GC_win32_free_heap ()
 {
-    if (GC_win32s) {
+    if (GC_no_win32_dlls) {
        while (GC_n_heap_bases > 0) {
            GlobalFree (GC_heap_bases[--GC_n_heap_bases]);
            GC_heap_bases[GC_n_heap_bases] = 0;
@@ -1743,11 +1749,18 @@ word n;
 {
 }
 
-/* A call hints that h is about to be written. */
-/* May speed up some dirty bit implementations.        */
+/* A call that:                                                */
+/* 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*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
 {
 }
 
@@ -1850,12 +1863,16 @@ struct hblk *h;
 # endif
 #endif
 #if defined(LINUX)
-#   include <linux/version.h>
-#   if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(IA64)
+#   if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
       typedef struct sigcontext s_c;
-#   else
-      typedef struct sigcontext_struct s_c;
-#   endif
+#   else  /* glibc < 2.2 */
+#     include <linux/version.h>
+#     if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA)
+        typedef struct sigcontext s_c;
+#     else
+        typedef struct sigcontext_struct s_c;
+#     endif
+#   endif  /* glibc < 2.2 */
 #   if defined(ALPHA) || defined(M68K)
       typedef void (* REAL_SIG_PF)(int, int, s_c *);
 #   else
@@ -2157,7 +2174,7 @@ SIG_PF GC_old_segv_handler;       /* Also old MSWIN32 ACCESS_VIOLATION filter */
        char * addr = (char *) (scp -> si_addr);
 #   endif
 #   ifdef LINUX
-#     ifdef I386
+#     if defined(I386) || defined (X86_64)
        char * addr = (char *) (sc.cr2);
 #     else
 #      if defined(M68K)
@@ -2321,29 +2338,33 @@ SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
 
 /*
  * We hold the allocation lock.  We expect block h to be written
- * shortly.
+ * shortly.  Ensure that all pages cvontaining 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;
+word nblocks;
+GC_bool is_ptrfree;
 {
-    register struct hblk * h_trunc;
-    register unsigned i;
-    register GC_bool found_clean;
+    struct hblk * h_trunc;  /* Truncated to page boundary */
+    struct hblk * h_end;    /* Page boundary following block end */
+    struct hblk * current;
+    GC_bool found_clean;
     
     if (!GC_dirty_maintained) return;
     h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1));
+    h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size-1)
+                           & ~(GC_page_size-1));
     found_clean = FALSE;
-    for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
-        register int index = PHT_HASH(h_trunc+i);
+    for (current = h_trunc; current < h_end; ++current) {
+        int index = PHT_HASH(current);
             
-        if (!get_pht_entry_from_index(GC_dirty_pages, index)) {
-            found_clean = TRUE;
+        if (!is_ptrfree || current < h || current >= h + nblocks) {
             async_set_pht_entry_from_index(GC_dirty_pages, index);
         }
     }
-    if (found_clean) {
-       UNPROTECT(h_trunc, GC_page_size);
-    }
+    UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc);
 }
 
 void GC_dirty_init()
@@ -2462,18 +2483,77 @@ void GC_dirty_init()
 #   endif
 }
 
+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()
 {
     ptr_t start;
     word len;
+    struct hblk * current;
+    struct hblk * current_start;  /* Start of block to be protected. */
+    struct hblk * limit;
     unsigned i;
-    
+    GC_bool protect_all = 
+         (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP));
     for (i = 0; i < GC_n_heap_sects; i++) {
         start = GC_heap_sects[i].hs_start;
         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);
+         }
+       }
     }
 }
 
@@ -2530,7 +2610,7 @@ word len;
     register struct hblk *h;
     ptr_t obj_start;
     
-    if (!GC_incremental) return;
+    if (!GC_dirty_maintained) return;
     obj_start = GC_base(addr);
     if (obj_start == 0) return;
     if (GC_base(addr + len - 1) != obj_start) {
@@ -2548,11 +2628,15 @@ word len;
              ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
 }
 
-#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_LINUX_THREADS) \
+#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(THREADS) \
     && !defined(GC_USE_LD_WRAP)
-/* Replacement for UNIX system call.    */
-/* Other calls that write to the heap   */
-/* should be handled similarly.                 */
+/* 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   */
+/* tha allocation lock for the entur 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.                                   */
 # if defined(__STDC__) && !defined(SUNOS4)
 #   include <unistd.h>
 #   include <sys/uio.h>
@@ -2600,7 +2684,7 @@ word len;
 }
 #endif /* !MSWIN32 && !MSWINCE && !GC_LINUX_THREADS */
 
-#ifdef GC_USE_LD_WRAP
+#if defined(GC_USE_LD_WRAP) && !defined(THREADS)
     /* We use the GNU ld call wrapping facility.                       */
     /* This requires that the linker be invoked with "--wrap read".    */
     /* This can be done by passing -Wl,"--wrap read" to gcc.           */
@@ -2739,8 +2823,10 @@ void GC_dirty_init()
 
 /* Ignore write hints. They don't help us here.        */
 /*ARGSUSED*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
 {
 }
 
@@ -2948,15 +3034,24 @@ struct hblk *h;
 }
 
 /*ARGSUSED*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
 struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
 {
-    PCR_VD_WriteProtectDisable(h, HBLKSIZE);
-    PCR_VD_WriteProtectEnable(h, HBLKSIZE);
+    PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE);
+    PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE);
 }
 
 # endif /* PCR_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.
  * Should probably be in mach_dep.c, but that requires reorganization.
@@ -2966,6 +3061,8 @@ struct hblk *h;
 /* long as the frame pointer is explicitly stored.  In the case of gcc,        */
 /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is. */
 #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)
+#   include <features.h>
+
     struct frame {
        struct frame *fr_savfp;
        long    fr_savpc;
@@ -2975,6 +3072,8 @@ struct hblk *h;
 
 #if defined(SPARC)
 #  if defined(LINUX)
+#    include <features.h>
+
      struct frame {
        long    fr_local[8];
        long    fr_arg[6];
@@ -3010,6 +3109,35 @@ struct hblk *h;
 /* Fill in the pc and argument information for up to NFRAMES of my     */
 /* callers.  Ignore my frame and my callers frame.                     */
 
+#ifdef LINUX
+# include <features.h>
+# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2
+#   define HAVE_BUILTIN_BACKTRACE
+# endif
+#endif
+
+#if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \
+    && defined(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_SAVPC fr_pc
@@ -3056,6 +3184,8 @@ struct callinfo info[NFRAMES];
   if (nframes < NFRAMES) info[nframes].ci_pc = 0;
 }
 
+#endif /* No builtin backtrace */
+
 #endif /* SAVE_CALL_CHAIN */
 
 #if defined(LINUX) && defined(__ELF__) && \