Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / libgc / os_dep.c
index ef80a9d093ec2dfd0965a28965177628804ac8ba..191e4b4ed7d06a8ea02f73a38ea84b941fc758c5 100644 (file)
@@ -26,7 +26,7 @@
 #     define __KERNEL__
 #     include <asm/signal.h>
 #     undef __KERNEL__
-#   else
+#   elif defined(__GLIBC__)
       /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */
       /* struct sigcontext.  libc6 (glibc2) uses "struct sigcontext" in     */
       /* prototypes, so we have to include the top-level sigcontext.h to    */
@@ -54,7 +54,7 @@
 # endif
 
 # include <stdio.h>
-# if defined(MSWINCE)
+# if defined(MSWINCE) || defined (SN_TARGET_PS3)
 #   define SIGSEGV 0 /* value is irrelevant */
 # else
 #   include <signal.h>
 # include <errno.h>
 #endif
 
-#ifdef UNIX_LIKE
+#if defined( UNIX_LIKE ) || defined(NACL)
 # include <fcntl.h>
 #endif
 
@@ -336,6 +336,13 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
   {
     extern ptr_t GC_find_limit();
 
+       if (GC_no_dls)
+               /* 
+                * Not needed, avoids the SIGSEGV caused by GC_find_limit which
+                * complicates debugging.
+                */
+               return;
+
 #   ifdef LINUX
       /* Try the easy approaches first:        */
       if ((ptr_t)__data_start != 0) {
@@ -385,7 +392,7 @@ static void *tiny_sbrk(ptrdiff_t increment)
 #define sbrk tiny_sbrk
 # endif /* ECOS */
 
-#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
+#if defined(NETBSD) && defined(__ELF__)
   ptr_t GC_data_start;
 
   void GC_init_netbsd_elf()
@@ -398,6 +405,102 @@ static void *tiny_sbrk(ptrdiff_t increment)
   }
 #endif
 
+#if defined(OPENBSD)
+  static struct sigaction old_segv_act;
+  sigjmp_buf GC_jmp_buf_openbsd;
+
+# if defined(GC_OPENBSD_THREADS)
+#   include <sys/syscall.h>
+    sigset_t __syscall(quad_t, ...);
+# endif
+
+  /*
+   * Dont use GC_find_limit() because siglongjmp out of the
+   * signal handler by-passes our userland pthreads lib, leaving
+   * SIGSEGV and SIGPROF masked. Instead use this custom one
+   * that works-around the issues.
+   */
+
+    /*ARGSUSED*/
+    void GC_fault_handler_openbsd(int sig)
+    {
+       siglongjmp(GC_jmp_buf_openbsd, 1);
+    }
+
+    /* Return the first nonaddressible location > p or bound   */
+    /* Requires allocation lock.                               */
+    ptr_t GC_find_limit_openbsd(ptr_t p, ptr_t bound)
+    {
+        static volatile ptr_t result;
+               /* Safer if static, since otherwise it may not be       */
+               /* preserved across the longjmp.  Can safely be         */
+               /* static since it's only called with the               */
+               /* allocation lock held.                                */
+        struct sigaction act;
+       size_t pgsz = (size_t)sysconf(_SC_PAGESIZE);
+
+       GC_ASSERT(I_HOLD_LOCK());
+
+        act.sa_handler = GC_fault_handler_openbsd;
+        sigemptyset(&act.sa_mask);
+        act.sa_flags = SA_NODEFER | SA_RESTART;
+        sigaction(SIGSEGV, &act, &old_segv_act);
+
+       if (sigsetjmp(GC_jmp_buf_openbsd, 1) == 0) {
+           result = (ptr_t)(((word)(p)) & ~(pgsz-1));
+           for (;;) {
+               result += pgsz;
+               if (result >= bound) {
+                   result = bound;
+                   break;
+               }
+               GC_noop1((word)(*result));
+           }
+       }
+
+# if defined(GC_OPENBSD_THREADS)
+       /* due to the siglongjump we need to manually unmask SIGPROF */
+       __syscall(SYS_sigprocmask, SIG_UNBLOCK, sigmask(SIGPROF));
+# endif
+
+       sigaction(SIGSEGV, &old_segv_act, 0);
+
+       return(result);
+    }
+
+    /* Return first addressable location > p or bound */
+    /* Requires allocation lock. */
+    ptr_t GC_skip_hole_openbsd(ptr_t p, ptr_t bound)
+    {
+        static volatile ptr_t result;
+        struct sigaction act;
+       size_t pgsz = (size_t)sysconf(_SC_PAGESIZE);
+       static volatile int firstpass;
+
+       GC_ASSERT(I_HOLD_LOCK());
+
+        act.sa_handler = GC_fault_handler_openbsd;
+        sigemptyset(&act.sa_mask);
+        act.sa_flags = SA_NODEFER | SA_RESTART;
+        sigaction(SIGSEGV, &act, &old_segv_act);
+
+       firstpass = 1;
+       result = (ptr_t)(((word)(p)) & ~(pgsz-1));
+       if (sigsetjmp(GC_jmp_buf_openbsd, 1) != 0 || firstpass) {
+           firstpass = 0;
+           result += pgsz;
+           if (result >= bound) {
+               result = bound;
+           } else
+               GC_noop1((word)(*result));
+        }
+
+       sigaction(SIGSEGV, &old_segv_act, 0);
+
+       return(result);
+    }
+#endif
+
 # ifdef OS2
 
 # include <stddef.h>
@@ -504,7 +607,7 @@ void GC_enable_signals(void)
 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
       && !defined(MSWINCE) \
       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
-      && !defined(NOSYS) && !defined(ECOS)
+      && !defined(NOSYS) && !defined(ECOS) && !defined(SN_TARGET_PS3)
 
 #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
        /* Use the traditional BSD interface */
@@ -515,6 +618,12 @@ void GC_enable_signals(void)
          /* longjmp implementations.  Most systems appear not to have  */
          /* a signal 32.                                               */
 #      define SIGSETMASK(old, new) (old) = sigsetmask(new)
+#   elif defined(NACL)
+       /* We don't use signals in NaCl. */
+#      define SIGSET_T int
+#      define SIG_DEL(set, signal)
+#      define SIG_FILL(set)
+#      define SIGSETMASK(old, new)
 #   else
        /* Use POSIX/SYSV interface     */
 #      define SIGSET_T sigset_t
@@ -1014,7 +1123,8 @@ void *GC_set_stackbottom = NULL;
 #endif /* FREEBSD_STACKBOTTOM */
 
 #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
-    && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS)
+    && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS) \
+    && !defined(GC_OPENBSD_THREADS)
 
 ptr_t GC_get_stack_base()
 {
@@ -1074,6 +1184,25 @@ ptr_t GC_get_stack_base()
 
 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS */
 
+#if defined(GC_OPENBSD_THREADS)
+
+/* Find the stack using pthread_stackseg_np() */
+
+# include <sys/signal.h>
+# include <pthread.h>
+# include <pthread_np.h>
+        
+#define HAVE_GET_STACK_BASE
+
+ptr_t GC_get_stack_base()
+{
+    stack_t stack;
+    pthread_stackseg_np(pthread_self(), &stack);
+    return stack.ss_sp;
+}
+
+#endif /* GC_OPENBSD_THREADS */
+
 /*
  * Register static data segment(s) as roots.
  * If more data segments are added later then they need to be registered
@@ -1396,7 +1525,7 @@ int * etext_addr;
 }
 # endif
 
-# if defined(FREEBSD) && (defined(I386) || defined(powerpc) || defined(__powerpc__)) && !defined(PCR)
+# if defined(FREEBSD) && (defined(I386) || defined(powerpc) || defined(__powerpc__) ||  defined(__x86_64__)) && !defined(PCR)
 /* Its unclear whether this should be identical to the above, or       */
 /* whether it should apply to non-X86 architectures.                   */
 /* For now we don't assume that there is always an empty page after    */
@@ -1438,6 +1567,31 @@ int * etext_addr;
 
 #else /* !OS2 && !Windows && !AMIGA */
 
+#if defined(OPENBSD)
+
+/*
+ * Depending on arch alignment there can be multiple holes
+ * between DATASTART & DATAEND. Scan from DATASTART - DATAEND
+ * and register each region.
+ */
+void GC_register_data_segments(void)
+{
+  ptr_t region_start, region_end;
+
+  region_start = DATASTART;
+
+  for(;;) {
+    region_end = GC_find_limit_openbsd(region_start, DATAEND);
+    GC_add_roots_inner(region_start, region_end, FALSE);
+    if (region_end < DATAEND)
+       region_start = GC_skip_hole_openbsd(region_end, DATAEND);
+    else
+       break;
+  }
+}
+
+# else /* !OS2 && !Windows && !AMIGA && !OPENBSD */
+
 void GC_register_data_segments()
 {
 #   if !defined(PCR) && !defined(SRC_M3) && !defined(MACOS)
@@ -1449,8 +1603,10 @@ void GC_register_data_segments()
        /* hanging from it.  We're on thin ice here ...                 */
         extern caddr_t sbrk();
 
+       GC_ASSERT(DATASTART);
        GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
 #     else
+       GC_ASSERT(DATASTART);
        GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
 #       if defined(DATASTART2)
          GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), FALSE);
@@ -1495,6 +1651,7 @@ void GC_register_data_segments()
     /* change.                                                         */
 }
 
+# endif  /* ! OPENBSD */
 # endif  /* ! AMIGA */
 # endif  /* ! MSWIN32 && ! MSWINCE*/
 # endif  /* ! OS2 */
@@ -1505,7 +1662,7 @@ void GC_register_data_segments()
 
 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
        && !defined(MSWIN32) && !defined(MSWINCE) \
-       && !defined(MACOS) && !defined(DOS4GW)
+       && !defined(MACOS) && !defined(DOS4GW) && !defined(SN_TARGET_PS3)
 
 # ifdef SUNOS4
     extern caddr_t sbrk();
@@ -1617,6 +1774,8 @@ word bytes;
 #   else
       GC_ASSERT(last_addr != 0);
 #   endif
+         if (((word)result % HBLKSIZE) != 0)
+                 ABORT ("GC_unix_get_mem: Memory returned by mmap is not aligned to HBLKSIZE.");
     return((ptr_t)result);
 }
 
@@ -1914,8 +2073,21 @@ void GC_remap(ptr_t start, word bytes)
       int result; 
 
       if (0 == start_addr) return;
+#ifdef NACL
+      {
+       /* NaCl doesn't expose mprotect, but mmap should work fine */
+       void * mmap_result;
+        mmap_result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+                     MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
+                     zero_fd, 0/* offset */);
+        if (mmap_result != (void *)start_addr) ABORT("mmap as mprotect failed");
+        /* Fake the return value as if mprotect succeeded. */
+        result = 0;
+      }
+#else /* NACL */
       result = mprotect(start_addr, len,
                        PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
+#endif /* NACL */
       if (result != 0) {
          GC_err_printf3(
                "Mprotect failed at 0x%lx (length %ld) with errno %ld\n",
@@ -1959,7 +2131,14 @@ void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
          len -= free_len;
       }
 #   else
-      if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
+      if (len != 0) {
+        /* Immediately remap as above. */
+        void * result;
+        result = mmap(start_addr, len, PROT_NONE,
+                      MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
+                      zero_fd, 0/* offset */);
+        if (result != (void *)start_addr) ABORT("mmap(...PROT_NONE...) failed");
+      }
       GC_unmapped_bytes += len;
 #   endif
 }
@@ -2077,6 +2256,16 @@ void GC_default_push_other_roots GC_PROTO((void))
 }
 
 # endif /* GC_SOLARIS_THREADS || GC_PTHREADS */
+#ifdef SN_TARGET_PS3
+void GC_default_push_other_roots GC_PROTO((void))
+{
+       printf ("WARNING WARNING WARNING\nGC_default_push_other_roots is not implemented\n");
+}
+void GC_push_thread_structures GC_PROTO((void))
+{
+       printf ("WARNING WARNING WARNING\nGC_default_push_thread_structures is not implemented\n");
+}
+#endif
 
 void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
 
@@ -3700,7 +3889,7 @@ void GC_dirty_init() {
         mask,
         GC_ports.exception,
         EXCEPTION_DEFAULT,
-        MACHINE_THREAD_STATE
+        GC_MACH_THREAD_STATE_FLAVOR
     );
     if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed");