root/gc/os_dep.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GC_repeat_read
  2. GC_apply_to_maps
  3. GC_parse_map_entry
  4. GC_init_linux_data_start
  5. tiny_sbrk
  6. GC_init_netbsd_elf
  7. GC_disable_signals
  8. GC_enable_signals
  9. GC_disable_signals
  10. GC_enable_signals
  11. GC_disable_signals
  12. GC_enable_signals
  13. GC_setpagesize
  14. GC_setpagesize
  15. GC_setpagesize
  16. GC_get_writable_length
  17. GC_get_stack_base
  18. GC_get_stack_base
  19. GC_get_stack_base
  20. GC_set_and_save_fault_handler
  21. GC_fault_handler
  22. GC_setup_temporary_fault_handler
  23. GC_reset_fault_handler
  24. GC_find_limit
  25. GC_get_stack_base
  26. GC_get_register_stack_base
  27. backing_store_base_from_maps
  28. backing_store_base_from_proc
  29. GC_get_register_stack_base
  30. GC_linux_stack_base
  31. GC_freebsd_stack_base
  32. GC_get_stack_base
  33. GC_register_data_segments
  34. GC_init_win32
  35. GC_least_described_address
  36. GC_is_malloc_heap_base
  37. GC_get_allocation_base
  38. GC_add_current_malloc_heap
  39. GC_is_heap_base
  40. GC_register_root_section
  41. GC_register_data_segments
  42. GC_SysVGetDataStart
  43. GC_FreeBSDGetDataStart
  44. GC_register_data_segments
  45. GC_unix_get_mem
  46. GC_unix_get_mem
  47. GC_unix_get_mem
  48. os2_alloc
  49. GC_win32_get_mem
  50. GC_win32_free_heap
  51. GC_wince_get_mem
  52. GC_unmap_start
  53. GC_unmap_end
  54. GC_unmap
  55. GC_remap
  56. GC_unmap_gap
  57. GC_push_thread_stack
  58. GC_push_old_obj
  59. GC_PROTO
  60. GC_PROTO
  61. GC_push_thread_stack
  62. GC_m3_push_root
  63. GC_PROTO
  64. GC_PROTO
  65. GC_dirty_init
  66. GC_read_dirty
  67. GC_page_was_dirty
  68. GC_page_was_ever_dirty
  69. GC_is_fresh
  70. GC_remove_protection
  71. get_fault_addr
  72. async_set_pht_entry_from_index
  73. async_set_pht_entry_from_index
  74. GC_remove_protection
  75. GC_dirty_init
  76. GC_incremental_protection_needs
  77. GC_protect_heap
  78. GC_read_dirty
  79. GC_page_was_dirty
  80. GC_begin_syscall
  81. GC_end_syscall
  82. GC_unprotect_range
  83. read
  84. __wrap_read
  85. GC_page_was_ever_dirty
  86. GC_is_fresh
  87. GC_or_pages
  88. GC_dirty_init
  89. GC_remove_protection
  90. GC_read_dirty
  91. GC_page_was_dirty
  92. GC_page_was_ever_dirty
  93. GC_is_fresh
  94. GC_dirty_init
  95. GC_read_dirty
  96. GC_page_was_dirty
  97. GC_remove_protection
  98. GC_msg_t
  99. GC_MP_NORMAL
  100. GC_MP_DISCARDING
  101. GC_MP_STOPPED
  102. GC_mprotect_state_t
  103. GC_mprotect_thread_notify
  104. GC_mprotect_thread_reply
  105. GC_mprotect_stop
  106. GC_mprotect_resume
  107. GC_mprotect_thread
  108. GC_darwin_sigbus
  109. GC_dirty_init
  110. GC_forward_exception
  111. catch_exception_raise
  112. catch_exception_raise_state
  113. catch_exception_raise_state_identity
  114. GC_incremental_protection_needs
  115. GC_save_callers
  116. GC_save_callers
  117. GC_print_callers
  118. dump_maps
  119. GC_print_address_map

   1 /*
   2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
   3  * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
   4  * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
   5  * Copyright (c) 1999 by Hewlett-Packard Company.  All rights reserved.
   6  *
   7  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   8  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   9  *
  10  * Permission is hereby granted to use or copy this program
  11  * for any purpose,  provided the above notices are retained on all copies.
  12  * Permission to modify the code and to distribute modified code is granted,
  13  * provided the above notices are retained, and a notice that the code was
  14  * modified is included with the above copyright notice.
  15  */
  16 
  17 # include "private/gc_priv.h"
  18 
  19 # if defined(LINUX) && !defined(POWERPC)
  20 #   include <linux/version.h>
  21 #   if (LINUX_VERSION_CODE <= 0x10400)
  22       /* Ugly hack to get struct sigcontext_struct definition.  Required      */
  23       /* for some early 1.3.X releases.  Will hopefully go away soon. */
  24       /* in some later Linux releases, asm/sigcontext.h may have to   */
  25       /* be included instead.                                         */
  26 #     define __KERNEL__
  27 #     include <asm/signal.h>
  28 #     undef __KERNEL__
  29 #   else
  30       /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */
  31       /* struct sigcontext.  libc6 (glibc2) uses "struct sigcontext" in     */
  32       /* prototypes, so we have to include the top-level sigcontext.h to    */
  33       /* make sure the former gets defined to be the latter if appropriate. */
  34 #     include <features.h>
  35 #     if 2 <= __GLIBC__
  36 #       if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__
  37           /* glibc 2.1 no longer has sigcontext.h.  But signal.h        */
  38           /* has the right declaration for glibc 2.1.                   */
  39 #         include <sigcontext.h>
  40 #       endif /* 0 == __GLIBC_MINOR__ */
  41 #     else /* not 2 <= __GLIBC__ */
  42         /* libc5 doesn't have <sigcontext.h>: go directly with the kernel   */
  43         /* one.  Check LINUX_VERSION_CODE to see which we should reference. */
  44 #       include <asm/sigcontext.h>
  45 #     endif /* 2 <= __GLIBC__ */
  46 #   endif
  47 # endif
  48 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
  49     && !defined(MSWINCE)
  50 #   include <sys/types.h>
  51 #   if !defined(MSWIN32) && !defined(SUNOS4)
  52 #       include <unistd.h>
  53 #   endif
  54 # endif
  55 
  56 # include <stdio.h>
  57 # if defined(MSWINCE)
  58 #   define SIGSEGV 0 /* value is irrelevant */
  59 # else
  60 #   include <signal.h>
  61 # endif
  62 
  63 /* Blatantly OS dependent routines, except for those that are related   */
  64 /* to dynamic loading.                                                  */
  65 
  66 # if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START)
  67 #   define NEED_FIND_LIMIT
  68 # endif
  69 
  70 # if !defined(STACKBOTTOM) && defined(HEURISTIC2)
  71 #   define NEED_FIND_LIMIT
  72 # endif
  73 
  74 # if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
  75 #   define NEED_FIND_LIMIT
  76 # endif
  77 
  78 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
  79       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
  80 #   define NEED_FIND_LIMIT
  81 # endif
  82 
  83 #if defined(FREEBSD) && defined(I386)
  84 #  include <machine/trap.h>
  85 #  if !defined(PCR)
  86 #    define NEED_FIND_LIMIT
  87 #  endif
  88 #endif
  89 
  90 #if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) \
  91     && !defined(NEED_FIND_LIMIT)
  92    /* Used by GC_init_netbsd_elf() below.       */
  93 #  define NEED_FIND_LIMIT
  94 #endif
  95 
  96 #ifdef NEED_FIND_LIMIT
  97 #   include <setjmp.h>
  98 #endif
  99 
 100 #ifdef AMIGA
 101 # define GC_AMIGA_DEF
 102 # include "AmigaOS.c"
 103 # undef GC_AMIGA_DEF
 104 #endif
 105 
 106 #if defined(MSWIN32) || defined(MSWINCE)
 107 # define WIN32_LEAN_AND_MEAN
 108 # define NOSERVICE
 109 # include <windows.h>
 110 #endif
 111 
 112 #ifdef MACOS
 113 # include <Processes.h>
 114 #endif
 115 
 116 #ifdef IRIX5
 117 # include <sys/uio.h>
 118 # include <malloc.h>   /* for locking */
 119 #endif
 120 #if defined(USE_MMAP) || defined(USE_MUNMAP)
 121 # ifndef USE_MMAP
 122     --> USE_MUNMAP requires USE_MMAP
 123 # endif
 124 # include <sys/types.h>
 125 # include <sys/mman.h>
 126 # include <sys/stat.h>
 127 # include <errno.h>
 128 #endif
 129 
 130 #ifdef UNIX_LIKE
 131 # include <fcntl.h>
 132 # if defined(SUNOS5SIGS) && !defined(FREEBSD)
 133 #  include <sys/siginfo.h>
 134 # endif
 135   /* Define SETJMP and friends to be the version that restores  */
 136   /* the signal mask.                                           */
 137 # define SETJMP(env) sigsetjmp(env, 1)
 138 # define LONGJMP(env, val) siglongjmp(env, val)
 139 # define JMP_BUF sigjmp_buf
 140 #else
 141 # define SETJMP(env) setjmp(env)
 142 # define LONGJMP(env, val) longjmp(env, val)
 143 # define JMP_BUF jmp_buf
 144 #endif
 145 
 146 #ifdef DARWIN
 147 /* for get_etext and friends */
 148 #include <mach-o/getsect.h>
 149 #endif
 150 
 151 #ifdef DJGPP
 152   /* Apparently necessary for djgpp 2.01.  May cause problems with      */
 153   /* other versions.                                                    */
 154   typedef long unsigned int caddr_t;
 155 #endif
 156 
 157 #ifdef PCR
 158 # include "il/PCR_IL.h"
 159 # include "th/PCR_ThCtl.h"
 160 # include "mm/PCR_MM.h"
 161 #endif
 162 
 163 #if !defined(NO_EXECUTE_PERMISSION)
 164 # define OPT_PROT_EXEC PROT_EXEC
 165 #else
 166 # define OPT_PROT_EXEC 0
 167 #endif
 168 
 169 #if defined(LINUX) && \
 170     (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))
 171 
 172 /* We need to parse /proc/self/maps, either to find dynamic libraries,  */
 173 /* and/or to find the register backing store base (IA64).  Do it once   */
 174 /* here.                                                                */
 175 
 176 #define READ read
 177 
 178 /* Repeatedly perform a read call until the buffer is filled or */
 179 /* we encounter EOF.                                            */
 180 ssize_t GC_repeat_read(int fd, char *buf, size_t count)
 181 {
 182     ssize_t num_read = 0;
 183     ssize_t result;
 184     
 185     while (num_read < count) {
 186         result = READ(fd, buf + num_read, count - num_read);
 187         if (result < 0) return result;
 188         if (result == 0) break;
 189         num_read += result;
 190     }
 191     return num_read;
 192 }
 193 
 194 /*
 195  * Apply fn to a buffer containing the contents of /proc/self/maps.
 196  * Return the result of fn or, if we failed, 0.
 197  * We currently do nothing to /proc/self/maps other than simply read
 198  * it.  This code could be simplified if we could determine its size
 199  * ahead of time.
 200  */
 201 
 202 word GC_apply_to_maps(word (*fn)(char *))
 203 {
 204     int f;
 205     int result;
 206     size_t maps_size = 4000;  /* Initial guess.         */
 207     static char init_buf[1];
 208     static char *maps_buf = init_buf;
 209     static size_t maps_buf_sz = 1;
 210 
 211     /* Read /proc/self/maps, growing maps_buf as necessary.     */
 212         /* Note that we may not allocate conventionally, and    */
 213         /* thus can't use stdio.                                */
 214         do {
 215             if (maps_size >= maps_buf_sz) {
 216               /* Grow only by powers of 2, since we leak "too small" buffers. */
 217               while (maps_size >= maps_buf_sz) maps_buf_sz *= 2;
 218               maps_buf = GC_scratch_alloc(maps_buf_sz);
 219               if (maps_buf == 0) return 0;
 220             }
 221             f = open("/proc/self/maps", O_RDONLY);
 222             if (-1 == f) return 0;
 223             maps_size = 0;
 224             do {
 225                 result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
 226                 if (result <= 0) return 0;
 227                 maps_size += result;
 228             } while (result == maps_buf_sz-1);
 229             close(f);
 230         } while (maps_size >= maps_buf_sz);
 231         maps_buf[maps_size] = '\0';
 232         
 233     /* Apply fn to result. */
 234         return fn(maps_buf);
 235 }
 236 
 237 #endif /* Need GC_apply_to_maps */
 238 
 239 #if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64))
 240 //
 241 //  GC_parse_map_entry parses an entry from /proc/self/maps so we can
 242 //  locate all writable data segments that belong to shared libraries.
 243 //  The format of one of these entries and the fields we care about
 244 //  is as follows:
 245 //  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
 246 //  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
 247 //  start    end      prot          maj_dev
 248 //  0        9        18            32
 249 //  
 250 //  For 64 bit ABIs:
 251 //  0        17       34            56
 252 //
 253 //  The parser is called with a pointer to the entry and the return value
 254 //  is either NULL or is advanced to the next entry(the byte after the
 255 //  trailing '\n'.)
 256 //
 257 #if CPP_WORDSZ == 32
 258 # define OFFSET_MAP_START   0
 259 # define OFFSET_MAP_END     9
 260 # define OFFSET_MAP_PROT   18
 261 # define OFFSET_MAP_MAJDEV 32
 262 # define ADDR_WIDTH         8
 263 #endif
 264 
 265 #if CPP_WORDSZ == 64
 266 # define OFFSET_MAP_START   0
 267 # define OFFSET_MAP_END    17
 268 # define OFFSET_MAP_PROT   34
 269 # define OFFSET_MAP_MAJDEV 56
 270 # define ADDR_WIDTH        16
 271 #endif
 272 
 273 /*
 274  * Assign various fields of the first line in buf_ptr to *start, *end,
 275  * *prot_buf and *maj_dev.  Only *prot_buf may be set for unwritable maps.
 276  */
 277 char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
 278                                 char *prot_buf, unsigned int *maj_dev)
 279 {
 280     char *tok;
 281 
 282     if (buf_ptr == NULL || *buf_ptr == '\0') {
 283         return NULL;
 284     }
 285 
 286     memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4);
 287                                 /* do the protections first. */
 288     prot_buf[4] = '\0';
 289 
 290     if (prot_buf[1] == 'w') {/* we can skip all of this if it's not writable. */
 291 
 292         tok = buf_ptr;
 293         buf_ptr[OFFSET_MAP_START+ADDR_WIDTH] = '\0';
 294         *start = strtoul(tok, NULL, 16);
 295 
 296         tok = buf_ptr+OFFSET_MAP_END;
 297         buf_ptr[OFFSET_MAP_END+ADDR_WIDTH] = '\0';
 298         *end = strtoul(tok, NULL, 16);
 299 
 300         buf_ptr += OFFSET_MAP_MAJDEV;
 301         tok = buf_ptr;
 302         while (*buf_ptr != ':') buf_ptr++;
 303         *buf_ptr++ = '\0';
 304         *maj_dev = strtoul(tok, NULL, 16);
 305     }
 306 
 307     while (*buf_ptr && *buf_ptr++ != '\n');
 308 
 309     return buf_ptr;
 310 }
 311 
 312 #endif /* Need to parse /proc/self/maps. */     
 313 
 314 #if defined(SEARCH_FOR_DATA_START)
 315   /* The I386 case can be handled without a search.  The Alpha case     */
 316   /* used to be handled differently as well, but the rules changed      */
 317   /* for recent Linux versions.  This seems to be the easiest way to    */
 318   /* cover all versions.                                                */
 319 
 320 # ifdef LINUX
 321     /* Some Linux distributions arrange to define __data_start.  Some   */
 322     /* define data_start as a weak symbol.  The latter is technically   */
 323     /* broken, since the user program may define data_start, in which   */
 324     /* case we lose.  Nonetheless, we try both, prefering __data_start. */
 325     /* We assume gcc-compatible pragmas.        */
 326 #   pragma weak __data_start
 327     extern int __data_start[];
 328 #   pragma weak data_start
 329     extern int data_start[];
 330 # endif /* LINUX */
 331   extern int _end[];
 332 
 333   ptr_t GC_data_start;
 334 
 335   void GC_init_linux_data_start()
 336   {
 337     extern ptr_t GC_find_limit();
 338 
 339 #   ifdef LINUX
 340       /* Try the easy approaches first: */
 341       if ((ptr_t)__data_start != 0) {
 342           GC_data_start = (ptr_t)(__data_start);
 343           return;
 344       }
 345       if ((ptr_t)data_start != 0) {
 346           GC_data_start = (ptr_t)(data_start);
 347           return;
 348       }
 349 #   endif /* LINUX */
 350     GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
 351   }
 352 #endif
 353 
 354 # ifdef ECOS
 355 
 356 # ifndef ECOS_GC_MEMORY_SIZE
 357 # define ECOS_GC_MEMORY_SIZE (448 * 1024)
 358 # endif /* ECOS_GC_MEMORY_SIZE */
 359 
 360 // setjmp() function, as described in ANSI para 7.6.1.1
 361 #undef SETJMP
 362 #define SETJMP( __env__ )  hal_setjmp( __env__ )
 363 
 364 // FIXME: This is a simple way of allocating memory which is
 365 // compatible with ECOS early releases.  Later releases use a more
 366 // sophisticated means of allocating memory than this simple static
 367 // allocator, but this method is at least bound to work.
 368 static char memory[ECOS_GC_MEMORY_SIZE];
 369 static char *brk = memory;
 370 
 371 static void *tiny_sbrk(ptrdiff_t increment)
 372 {
 373   void *p = brk;
 374 
 375   brk += increment;
 376 
 377   if (brk >  memory + sizeof memory)
 378     {
 379       brk -= increment;
 380       return NULL;
 381     }
 382 
 383   return p;
 384 }
 385 #define sbrk tiny_sbrk
 386 # endif /* ECOS */
 387 
 388 #if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
 389   ptr_t GC_data_start;
 390 
 391   void GC_init_netbsd_elf()
 392   {
 393     extern ptr_t GC_find_limit();
 394     extern char **environ;
 395         /* This may need to be environ, without the underscore, for     */
 396         /* some versions.                                               */
 397     GC_data_start = GC_find_limit((ptr_t)&environ, FALSE);
 398   }
 399 #endif
 400 
 401 # ifdef OS2
 402 
 403 # include <stddef.h>
 404 
 405 # if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */
 406 
 407 struct exe_hdr {
 408     unsigned short      magic_number;
 409     unsigned short      padding[29];
 410     long                new_exe_offset;
 411 };
 412 
 413 #define E_MAGIC(x)      (x).magic_number
 414 #define EMAGIC          0x5A4D  
 415 #define E_LFANEW(x)     (x).new_exe_offset
 416 
 417 struct e32_exe {
 418     unsigned char       magic_number[2]; 
 419     unsigned char       byte_order; 
 420     unsigned char       word_order; 
 421     unsigned long       exe_format_level;
 422     unsigned short      cpu;       
 423     unsigned short      os;
 424     unsigned long       padding1[13];
 425     unsigned long       object_table_offset;
 426     unsigned long       object_count;    
 427     unsigned long       padding2[31];
 428 };
 429 
 430 #define E32_MAGIC1(x)   (x).magic_number[0]
 431 #define E32MAGIC1       'L'
 432 #define E32_MAGIC2(x)   (x).magic_number[1]
 433 #define E32MAGIC2       'X'
 434 #define E32_BORDER(x)   (x).byte_order
 435 #define E32LEBO         0
 436 #define E32_WORDER(x)   (x).word_order
 437 #define E32LEWO         0
 438 #define E32_CPU(x)      (x).cpu
 439 #define E32CPU286       1
 440 #define E32_OBJTAB(x)   (x).object_table_offset
 441 #define E32_OBJCNT(x)   (x).object_count
 442 
 443 struct o32_obj {
 444     unsigned long       size;  
 445     unsigned long       base;
 446     unsigned long       flags;  
 447     unsigned long       pagemap;
 448     unsigned long       mapsize; 
 449     unsigned long       reserved;
 450 };
 451 
 452 #define O32_FLAGS(x)    (x).flags
 453 #define OBJREAD         0x0001L
 454 #define OBJWRITE        0x0002L
 455 #define OBJINVALID      0x0080L
 456 #define O32_SIZE(x)     (x).size
 457 #define O32_BASE(x)     (x).base
 458 
 459 # else  /* IBM's compiler */
 460 
 461 /* A kludge to get around what appears to be a header file bug */
 462 # ifndef WORD
 463 #   define WORD unsigned short
 464 # endif
 465 # ifndef DWORD
 466 #   define DWORD unsigned long
 467 # endif
 468 
 469 # define EXE386 1
 470 # include <newexe.h>
 471 # include <exe386.h>
 472 
 473 # endif  /* __IBMC__ */
 474 
 475 # define INCL_DOSEXCEPTIONS
 476 # define INCL_DOSPROCESS
 477 # define INCL_DOSERRORS
 478 # define INCL_DOSMODULEMGR
 479 # define INCL_DOSMEMMGR
 480 # include <os2.h>
 481 
 482 
 483 /* Disable and enable signals during nontrivial allocations     */
 484 
 485 void GC_disable_signals(void)
 486 {
 487     ULONG nest;
 488     
 489     DosEnterMustComplete(&nest);
 490     if (nest != 1) ABORT("nested GC_disable_signals");
 491 }
 492 
 493 void GC_enable_signals(void)
 494 {
 495     ULONG nest;
 496     
 497     DosExitMustComplete(&nest);
 498     if (nest != 0) ABORT("GC_enable_signals");
 499 }
 500 
 501 
 502 # else
 503 
 504 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
 505       && !defined(MSWINCE) \
 506       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
 507       && !defined(NOSYS) && !defined(ECOS)
 508 
 509 #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
 510         /* Use the traditional BSD interface */
 511 #       define SIGSET_T int
 512 #       define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
 513 #       define SIG_FILL(set)  (set) = 0x7fffffff
 514           /* Setting the leading bit appears to provoke a bug in some   */
 515           /* longjmp implementations.  Most systems appear not to have  */
 516           /* a signal 32.                                               */
 517 #       define SIGSETMASK(old, new) (old) = sigsetmask(new)
 518 #   else
 519         /* Use POSIX/SYSV interface     */
 520 #       define SIGSET_T sigset_t
 521 #       define SIG_DEL(set, signal) sigdelset(&(set), (signal))
 522 #       define SIG_FILL(set) sigfillset(&set)
 523 #       define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old))
 524 #   endif
 525 
 526 static GC_bool mask_initialized = FALSE;
 527 
 528 static SIGSET_T new_mask;
 529 
 530 static SIGSET_T old_mask;
 531 
 532 static SIGSET_T dummy;
 533 
 534 #if defined(PRINTSTATS) && !defined(THREADS)
 535 # define CHECK_SIGNALS
 536   int GC_sig_disabled = 0;
 537 #endif
 538 
 539 void GC_disable_signals()
 540 {
 541     if (!mask_initialized) {
 542         SIG_FILL(new_mask);
 543 
 544         SIG_DEL(new_mask, SIGSEGV);
 545         SIG_DEL(new_mask, SIGILL);
 546         SIG_DEL(new_mask, SIGQUIT);
 547 #       ifdef SIGBUS
 548             SIG_DEL(new_mask, SIGBUS);
 549 #       endif
 550 #       ifdef SIGIOT
 551             SIG_DEL(new_mask, SIGIOT);
 552 #       endif
 553 #       ifdef SIGEMT
 554             SIG_DEL(new_mask, SIGEMT);
 555 #       endif
 556 #       ifdef SIGTRAP
 557             SIG_DEL(new_mask, SIGTRAP);
 558 #       endif 
 559         mask_initialized = TRUE;
 560     }
 561 #   ifdef CHECK_SIGNALS
 562         if (GC_sig_disabled != 0) ABORT("Nested disables");
 563         GC_sig_disabled++;
 564 #   endif
 565     SIGSETMASK(old_mask,new_mask);
 566 }
 567 
 568 void GC_enable_signals()
 569 {
 570 #   ifdef CHECK_SIGNALS
 571         if (GC_sig_disabled != 1) ABORT("Unmatched enable");
 572         GC_sig_disabled--;
 573 #   endif
 574     SIGSETMASK(dummy,old_mask);
 575 }
 576 
 577 #  endif  /* !PCR */
 578 
 579 # endif /*!OS/2 */
 580 
 581 /* Ivan Demakov: simplest way (to me) */
 582 #if defined (DOS4GW)
 583   void GC_disable_signals() { }
 584   void GC_enable_signals() { }
 585 #endif
 586 
 587 /* Find the page size */
 588 word GC_page_size;
 589 
 590 # if defined(MSWIN32) || defined(MSWINCE)
 591   void GC_setpagesize()
 592   {
 593     GetSystemInfo(&GC_sysinfo);
 594     GC_page_size = GC_sysinfo.dwPageSize;
 595   }
 596 
 597 # else
 598 #   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) \
 599        || defined(USE_MUNMAP)
 600         void GC_setpagesize()
 601         {
 602             GC_page_size = GETPAGESIZE();
 603         }
 604 #   else
 605         /* It's acceptable to fake it. */
 606         void GC_setpagesize()
 607         {
 608             GC_page_size = HBLKSIZE;
 609         }
 610 #   endif
 611 # endif
 612 
 613 /* 
 614  * Find the base of the stack. 
 615  * Used only in single-threaded environment.
 616  * With threads, GC_mark_roots needs to know how to do this.
 617  * Called with allocator lock held.
 618  */
 619 # if defined(MSWIN32) || defined(MSWINCE)
 620 # define is_writable(prot) ((prot) == PAGE_READWRITE \
 621                             || (prot) == PAGE_WRITECOPY \
 622                             || (prot) == PAGE_EXECUTE_READWRITE \
 623                             || (prot) == PAGE_EXECUTE_WRITECOPY)
 624 /* Return the number of bytes that are writable starting at p.  */
 625 /* The pointer p is assumed to be page aligned.                 */
 626 /* If base is not 0, *base becomes the beginning of the         */
 627 /* allocation region containing p.                              */
 628 word GC_get_writable_length(ptr_t p, ptr_t *base)
 629 {
 630     MEMORY_BASIC_INFORMATION buf;
 631     word result;
 632     word protect;
 633     
 634     result = VirtualQuery(p, &buf, sizeof(buf));
 635     if (result != sizeof(buf)) ABORT("Weird VirtualQuery result");
 636     if (base != 0) *base = (ptr_t)(buf.AllocationBase);
 637     protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE));
 638     if (!is_writable(protect)) {
 639         return(0);
 640     }
 641     if (buf.State != MEM_COMMIT) return(0);
 642     return(buf.RegionSize);
 643 }
 644 
 645 ptr_t GC_get_stack_base()
 646 {
 647     int dummy;
 648     ptr_t sp = (ptr_t)(&dummy);
 649     ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
 650     word size = GC_get_writable_length(trunc_sp, 0);
 651    
 652     return(trunc_sp + size);
 653 }
 654 
 655 
 656 # endif /* MS Windows */
 657 
 658 # ifdef BEOS
 659 # include <kernel/OS.h>
 660 ptr_t GC_get_stack_base(){
 661         thread_info th;
 662         get_thread_info(find_thread(NULL),&th);
 663         return th.stack_end;
 664 }
 665 # endif /* BEOS */
 666 
 667 
 668 # ifdef OS2
 669 
 670 ptr_t GC_get_stack_base()
 671 {
 672     PTIB ptib;
 673     PPIB ppib;
 674     
 675     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
 676         GC_err_printf0("DosGetInfoBlocks failed\n");
 677         ABORT("DosGetInfoBlocks failed\n");
 678     }
 679     return((ptr_t)(ptib -> tib_pstacklimit));
 680 }
 681 
 682 # endif /* OS2 */
 683 
 684 # ifdef AMIGA
 685 #   define GC_AMIGA_SB
 686 #   include "AmigaOS.c"
 687 #   undef GC_AMIGA_SB
 688 # endif /* AMIGA */
 689 
 690 # if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
 691 
 692 #   ifdef __STDC__
 693         typedef void (*handler)(int);
 694 #   else
 695         typedef void (*handler)();
 696 #   endif
 697 
 698 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
 699     || defined(HURD) || defined(NETBSD)
 700         static struct sigaction old_segv_act;
 701 #       if defined(IRIX5) || defined(HPUX) \
 702         || defined(HURD) || defined(NETBSD)
 703             static struct sigaction old_bus_act;
 704 #       endif
 705 #   else
 706         static handler old_segv_handler, old_bus_handler;
 707 #   endif
 708     
 709 #   ifdef __STDC__
 710       void GC_set_and_save_fault_handler(handler h)
 711 #   else
 712       void GC_set_and_save_fault_handler(h)
 713       handler h;
 714 #   endif
 715     {
 716 #       if defined(SUNOS5SIGS) || defined(IRIX5)  \
 717         || defined(OSF1) || defined(HURD) || defined(NETBSD)
 718           struct sigaction      act;
 719 
 720           act.sa_handler        = h;
 721 #         if 0 /* Was necessary for Solaris 2.3 and very temporary      */
 722                /* NetBSD bugs.                                          */
 723             act.sa_flags          = SA_RESTART | SA_NODEFER;
 724 #         else
 725             act.sa_flags          = SA_RESTART;
 726 #         endif
 727 
 728           (void) sigemptyset(&act.sa_mask);
 729 #         ifdef GC_IRIX_THREADS
 730                 /* Older versions have a bug related to retrieving and  */
 731                 /* and setting a handler at the same time.              */
 732                 (void) sigaction(SIGSEGV, 0, &old_segv_act);
 733                 (void) sigaction(SIGSEGV, &act, 0);
 734                 (void) sigaction(SIGBUS, 0, &old_bus_act);
 735                 (void) sigaction(SIGBUS, &act, 0);
 736 #         else
 737                 (void) sigaction(SIGSEGV, &act, &old_segv_act);
 738 #               if defined(IRIX5) \
 739                    || defined(HPUX) || defined(HURD) || defined(NETBSD)
 740                     /* Under Irix 5.x or HP/UX, we may get SIGBUS.      */
 741                     /* Pthreads doesn't exist under Irix 5.x, so we     */
 742                     /* don't have to worry in the threads case.         */
 743                     (void) sigaction(SIGBUS, &act, &old_bus_act);
 744 #               endif
 745 #         endif /* GC_IRIX_THREADS */
 746 #       else
 747           old_segv_handler = signal(SIGSEGV, h);
 748 #         ifdef SIGBUS
 749             old_bus_handler = signal(SIGBUS, h);
 750 #         endif
 751 #       endif
 752     }
 753 # endif /* NEED_FIND_LIMIT || UNIX_LIKE */
 754 
 755 # ifdef NEED_FIND_LIMIT
 756   /* Some tools to implement HEURISTIC2 */
 757 #   define MIN_PAGE_SIZE 256    /* Smallest conceivable page size, bytes */
 758     /* static */ JMP_BUF GC_jmp_buf;
 759     
 760     /*ARGSUSED*/
 761     void GC_fault_handler(sig)
 762     int sig;
 763     {
 764         LONGJMP(GC_jmp_buf, 1);
 765     }
 766 
 767     void GC_setup_temporary_fault_handler()
 768     {
 769         GC_set_and_save_fault_handler(GC_fault_handler);
 770     }
 771     
 772     void GC_reset_fault_handler()
 773     {
 774 #       if defined(SUNOS5SIGS) || defined(IRIX5) \
 775            || defined(OSF1) || defined(HURD) || defined(NETBSD)
 776           (void) sigaction(SIGSEGV, &old_segv_act, 0);
 777 #         if defined(IRIX5) \
 778              || defined(HPUX) || defined(HURD) || defined(NETBSD)
 779               (void) sigaction(SIGBUS, &old_bus_act, 0);
 780 #         endif
 781 #       else
 782           (void) signal(SIGSEGV, old_segv_handler);
 783 #         ifdef SIGBUS
 784             (void) signal(SIGBUS, old_bus_handler);
 785 #         endif
 786 #       endif
 787     }
 788 
 789     /* Return the first nonaddressible location > p (up) or     */
 790     /* the smallest location q s.t. [q,p) is addressable (!up). */
 791     /* We assume that p (up) or p-1 (!up) is addressable.       */
 792     ptr_t GC_find_limit(p, up)
 793     ptr_t p;
 794     GC_bool up;
 795     {
 796         static VOLATILE ptr_t result;
 797                 /* Needs to be static, since otherwise it may not be    */
 798                 /* preserved across the longjmp.  Can safely be         */
 799                 /* static since it's only called once, with the         */
 800                 /* allocation lock held.                                */
 801 
 802 
 803         GC_setup_temporary_fault_handler();
 804         if (SETJMP(GC_jmp_buf) == 0) {
 805             result = (ptr_t)(((word)(p))
 806                               & ~(MIN_PAGE_SIZE-1));
 807             for (;;) {
 808                 if (up) {
 809                     result += MIN_PAGE_SIZE;
 810                 } else {
 811                     result -= MIN_PAGE_SIZE;
 812                 }
 813                 GC_noop1((word)(*result));
 814             }
 815         }
 816         GC_reset_fault_handler();
 817         if (!up) {
 818             result += MIN_PAGE_SIZE;
 819         }
 820         return(result);
 821     }
 822 # endif
 823 
 824 #if defined(ECOS) || defined(NOSYS)
 825   ptr_t GC_get_stack_base()
 826   {
 827     return STACKBOTTOM;
 828   }
 829 #endif
 830 
 831 #ifdef HPUX_STACKBOTTOM
 832 
 833 #include <sys/param.h>
 834 #include <sys/pstat.h>
 835 
 836   ptr_t GC_get_register_stack_base(void)
 837   {
 838     struct pst_vm_status vm_status;
 839 
 840     int i = 0;
 841     while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) {
 842       if (vm_status.pst_type == PS_RSESTACK) {
 843         return (ptr_t) vm_status.pst_vaddr;
 844       }
 845     }
 846 
 847     /* old way to get the register stackbottom */
 848     return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1)
 849                    & ~(BACKING_STORE_ALIGNMENT - 1));
 850   }
 851 
 852 #endif /* HPUX_STACK_BOTTOM */
 853 
 854 #ifdef LINUX_STACKBOTTOM
 855 
 856 #include <sys/types.h>
 857 #include <sys/stat.h>
 858 #include <ctype.h>
 859 
 860 # define STAT_SKIP 27   /* Number of fields preceding startstack        */
 861                         /* field in /proc/self/stat                     */
 862 
 863 #ifdef USE_LIBC_PRIVATES
 864 # pragma weak __libc_stack_end
 865   extern ptr_t __libc_stack_end;
 866 #endif
 867 
 868 # ifdef IA64
 869     /* Try to read the backing store base from /proc/self/maps. */
 870     /* We look for the writable mapping with a 0 major device,  */
 871     /* which is as close to our frame as possible, but below it.*/
 872     static word backing_store_base_from_maps(char *maps)
 873     {
 874       char prot_buf[5];
 875       char *buf_ptr = maps;
 876       word start, end;
 877       unsigned int maj_dev;
 878       word current_best = 0;
 879       word dummy;
 880   
 881       for (;;) {
 882         buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
 883         if (buf_ptr == NULL) return current_best;
 884         if (prot_buf[1] == 'w' && maj_dev == 0) {
 885             if (end < (word)(&dummy) && start > current_best) current_best = start;
 886         }
 887       }
 888       return current_best;
 889     }
 890 
 891     static word backing_store_base_from_proc(void)
 892     {
 893         return GC_apply_to_maps(backing_store_base_from_maps);
 894     }
 895 
 896 #   ifdef USE_LIBC_PRIVATES
 897 #     pragma weak __libc_ia64_register_backing_store_base
 898       extern ptr_t __libc_ia64_register_backing_store_base;
 899 #   endif
 900 
 901     ptr_t GC_get_register_stack_base(void)
 902     {
 903 #     ifdef USE_LIBC_PRIVATES
 904         if (0 != &__libc_ia64_register_backing_store_base
 905             && 0 != __libc_ia64_register_backing_store_base) {
 906           /* Glibc 2.2.4 has a bug such that for dynamically linked     */
 907           /* executables __libc_ia64_register_backing_store_base is     */
 908           /* defined but uninitialized during constructor calls.        */
 909           /* Hence we check for both nonzero address and value.         */
 910           return __libc_ia64_register_backing_store_base;
 911         }
 912 #     endif
 913       word result = backing_store_base_from_proc();
 914       if (0 == result) {
 915           /* Use dumb heuristics.  Works only for default configuration. */
 916           result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
 917           result += BACKING_STORE_ALIGNMENT - 1;
 918           result &= ~(BACKING_STORE_ALIGNMENT - 1);
 919           /* Verify that it's at least readable.  If not, we goofed. */
 920           GC_noop1(*(word *)result); 
 921       }
 922       return (ptr_t)result;
 923     }
 924 # endif
 925 
 926   ptr_t GC_linux_stack_base(void)
 927   {
 928     /* We read the stack base value from /proc/self/stat.  We do this   */
 929     /* using direct I/O system calls in order to avoid calling malloc   */
 930     /* in case REDIRECT_MALLOC is defined.                              */ 
 931 #   define STAT_BUF_SIZE 4096
 932 #   define STAT_READ read
 933           /* Should probably call the real read, if read is wrapped.    */
 934     char stat_buf[STAT_BUF_SIZE];
 935     int f;
 936     char c;
 937     word result = 0;
 938     size_t i, buf_offset = 0;
 939 
 940     /* First try the easy way.  This should work for glibc 2.2  */
 941     /* This fails in a prelinked ("prelink" command) executable */
 942     /* since the correct value of __libc_stack_end never        */
 943     /* becomes visible to us.  The second test works around     */
 944     /* this.                                                    */  
 945 #   ifdef USE_LIBC_PRIVATES
 946       if (0 != &__libc_stack_end && 0 != __libc_stack_end ) {
 947 #       ifdef IA64
 948           /* Some versions of glibc set the address 16 bytes too        */
 949           /* low while the initialization code is running.              */
 950           if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) {
 951             return __libc_stack_end + 0x10;
 952           } /* Otherwise it's not safe to add 16 bytes and we fall      */
 953             /* back to using /proc.                                     */
 954 #       else 
 955 #       ifdef SPARC
 956           /* Older versions of glibc for 64-bit Sparc do not set
 957            * this variable correctly, it gets set to either zero
 958            * or one.
 959            */
 960           if (__libc_stack_end != (ptr_t) (unsigned long)0x1)
 961             return __libc_stack_end;
 962 #       else
 963           return __libc_stack_end;
 964 #       endif
 965 #       endif
 966       }
 967 #   endif
 968     f = open("/proc/self/stat", O_RDONLY);
 969     if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
 970         ABORT("Couldn't read /proc/self/stat");
 971     }
 972     c = stat_buf[buf_offset++];
 973     /* Skip the required number of fields.  This number is hopefully    */
 974     /* constant across all Linux implementations.                       */
 975       for (i = 0; i < STAT_SKIP; ++i) {
 976         while (isspace(c)) c = stat_buf[buf_offset++];
 977         while (!isspace(c)) c = stat_buf[buf_offset++];
 978       }
 979     while (isspace(c)) c = stat_buf[buf_offset++];
 980     while (isdigit(c)) {
 981       result *= 10;
 982       result += c - '0';
 983       c = stat_buf[buf_offset++];
 984     }
 985     close(f);
 986     if (result < 0x10000000) ABORT("Absurd stack bottom value");
 987     return (ptr_t)result;
 988   }
 989 
 990 #endif /* LINUX_STACKBOTTOM */
 991 
 992 #ifdef FREEBSD_STACKBOTTOM
 993 
 994 /* This uses an undocumented sysctl call, but at least one expert       */
 995 /* believes it will stay.                                               */
 996 
 997 #include <unistd.h>
 998 #include <sys/types.h>
 999 #include <sys/sysctl.h>
1000 
1001   ptr_t GC_freebsd_stack_base(void)
1002   {
1003     int nm[2] = {CTL_KERN, KERN_USRSTACK};
1004     ptr_t base;
1005     size_t len = sizeof(ptr_t);
1006     int r = sysctl(nm, 2, &base, &len, NULL, 0);
1007     
1008     if (r) ABORT("Error getting stack base");
1009 
1010     return base;
1011   }
1012 
1013 #endif /* FREEBSD_STACKBOTTOM */
1014 
1015 #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
1016     && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS)
1017 
1018 ptr_t GC_get_stack_base()
1019 {
1020 #   if defined(HEURISTIC1) || defined(HEURISTIC2) || \
1021        defined(LINUX_STACKBOTTOM) || defined(FREEBSD_STACKBOTTOM)
1022     word dummy;
1023     ptr_t result;
1024 #   endif
1025 
1026 #   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
1027 
1028 #   ifdef STACKBOTTOM
1029         return(STACKBOTTOM);
1030 #   else
1031 #       ifdef HEURISTIC1
1032 #          ifdef STACK_GROWS_DOWN
1033              result = (ptr_t)((((word)(&dummy))
1034                                + STACKBOTTOM_ALIGNMENT_M1)
1035                               & ~STACKBOTTOM_ALIGNMENT_M1);
1036 #          else
1037              result = (ptr_t)(((word)(&dummy))
1038                               & ~STACKBOTTOM_ALIGNMENT_M1);
1039 #          endif
1040 #       endif /* HEURISTIC1 */
1041 #       ifdef LINUX_STACKBOTTOM
1042            result = GC_linux_stack_base();
1043 #       endif
1044 #       ifdef FREEBSD_STACKBOTTOM
1045            result = GC_freebsd_stack_base();
1046 #       endif
1047 #       ifdef HEURISTIC2
1048 #           ifdef STACK_GROWS_DOWN
1049                 result = GC_find_limit((ptr_t)(&dummy), TRUE);
1050 #               ifdef HEURISTIC2_LIMIT
1051                     if (result > HEURISTIC2_LIMIT
1052                         && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) {
1053                             result = HEURISTIC2_LIMIT;
1054                     }
1055 #               endif
1056 #           else
1057                 result = GC_find_limit((ptr_t)(&dummy), FALSE);
1058 #               ifdef HEURISTIC2_LIMIT
1059                     if (result < HEURISTIC2_LIMIT
1060                         && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) {
1061                             result = HEURISTIC2_LIMIT;
1062                     }
1063 #               endif
1064 #           endif
1065 
1066 #       endif /* HEURISTIC2 */
1067 #       ifdef STACK_GROWS_DOWN
1068             if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t));
1069 #       endif
1070         return(result);
1071 #   endif /* STACKBOTTOM */
1072 }
1073 
1074 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS */
1075 
1076 /*
1077  * Register static data segment(s) as roots.
1078  * If more data segments are added later then they need to be registered
1079  * add that point (as we do with SunOS dynamic loading),
1080  * or GC_mark_roots needs to check for them (as we do with PCR).
1081  * Called with allocator lock held.
1082  */
1083 
1084 # ifdef OS2
1085 
1086 void GC_register_data_segments()
1087 {
1088     PTIB ptib;
1089     PPIB ppib;
1090     HMODULE module_handle;
1091 #   define PBUFSIZ 512
1092     UCHAR path[PBUFSIZ];
1093     FILE * myexefile;
1094     struct exe_hdr hdrdos;      /* MSDOS header.        */
1095     struct e32_exe hdr386;      /* Real header for my executable */
1096     struct o32_obj seg; /* Currrent segment */
1097     int nsegs;
1098     
1099     
1100     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
1101         GC_err_printf0("DosGetInfoBlocks failed\n");
1102         ABORT("DosGetInfoBlocks failed\n");
1103     }
1104     module_handle = ppib -> pib_hmte;
1105     if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
1106         GC_err_printf0("DosQueryModuleName failed\n");
1107         ABORT("DosGetInfoBlocks failed\n");
1108     }
1109     myexefile = fopen(path, "rb");
1110     if (myexefile == 0) {
1111         GC_err_puts("Couldn't open executable ");
1112         GC_err_puts(path); GC_err_puts("\n");
1113         ABORT("Failed to open executable\n");
1114     }
1115     if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) {
1116         GC_err_puts("Couldn't read MSDOS header from ");
1117         GC_err_puts(path); GC_err_puts("\n");
1118         ABORT("Couldn't read MSDOS header");
1119     }
1120     if (E_MAGIC(hdrdos) != EMAGIC) {
1121         GC_err_puts("Executable has wrong DOS magic number: ");
1122         GC_err_puts(path); GC_err_puts("\n");
1123         ABORT("Bad DOS magic number");
1124     }
1125     if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
1126         GC_err_puts("Seek to new header failed in ");
1127         GC_err_puts(path); GC_err_puts("\n");
1128         ABORT("Bad DOS magic number");
1129     }
1130     if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) {
1131         GC_err_puts("Couldn't read MSDOS header from ");
1132         GC_err_puts(path); GC_err_puts("\n");
1133         ABORT("Couldn't read OS/2 header");
1134     }
1135     if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
1136         GC_err_puts("Executable has wrong OS/2 magic number:");
1137         GC_err_puts(path); GC_err_puts("\n");
1138         ABORT("Bad OS/2 magic number");
1139     }
1140     if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
1141         GC_err_puts("Executable %s has wrong byte order: ");
1142         GC_err_puts(path); GC_err_puts("\n");
1143         ABORT("Bad byte order");
1144     }
1145     if ( E32_CPU(hdr386) == E32CPU286) {
1146         GC_err_puts("GC can't handle 80286 executables: ");
1147         GC_err_puts(path); GC_err_puts("\n");
1148         EXIT();
1149     }
1150     if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
1151               SEEK_SET) != 0) {
1152         GC_err_puts("Seek to object table failed: ");
1153         GC_err_puts(path); GC_err_puts("\n");
1154         ABORT("Seek to object table failed");
1155     }
1156     for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
1157       int flags;
1158       if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) {
1159         GC_err_puts("Couldn't read obj table entry from ");
1160         GC_err_puts(path); GC_err_puts("\n");
1161         ABORT("Couldn't read obj table entry");
1162       }
1163       flags = O32_FLAGS(seg);
1164       if (!(flags & OBJWRITE)) continue;
1165       if (!(flags & OBJREAD)) continue;
1166       if (flags & OBJINVALID) {
1167           GC_err_printf0("Object with invalid pages?\n");
1168           continue;
1169       } 
1170       GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE);
1171     }
1172 }
1173 
1174 # else /* !OS2 */
1175 
1176 # if defined(MSWIN32) || defined(MSWINCE)
1177 
1178 # ifdef MSWIN32
1179   /* Unfortunately, we have to handle win32s very differently from NT,  */
1180   /* Since VirtualQuery has very different semantics.  In particular,   */
1181   /* under win32s a VirtualQuery call on an unmapped page returns an    */
1182   /* invalid result.  Under NT, GC_register_data_segments is a noop and */
1183   /* all real work is done by GC_register_dynamic_libraries.  Under     */
1184   /* win32s, we cannot find the data segments associated with dll's.    */
1185   /* We register the main data segment here.                            */
1186   GC_bool GC_no_win32_dlls = FALSE;      
1187         /* This used to be set for gcc, to avoid dealing with           */
1188         /* the structured exception handling issues.  But we now have   */
1189         /* assembly code to do that right.                              */
1190   
1191   void GC_init_win32()
1192   {
1193     /* if we're running under win32s, assume that no DLLs will be loaded */
1194     DWORD v = GetVersion();
1195     GC_no_win32_dlls |= ((v & 0x80000000) && (v & 0xff) <= 3);
1196   }
1197 
1198   /* Return the smallest address a such that VirtualQuery               */
1199   /* returns correct results for all addresses between a and start.     */
1200   /* Assumes VirtualQuery returns correct information for start.        */
1201   ptr_t GC_least_described_address(ptr_t start)
1202   {  
1203     MEMORY_BASIC_INFORMATION buf;
1204     DWORD result;
1205     LPVOID limit;
1206     ptr_t p;
1207     LPVOID q;
1208     
1209     limit = GC_sysinfo.lpMinimumApplicationAddress;
1210     p = (ptr_t)((word)start & ~(GC_page_size - 1));
1211     for (;;) {
1212         q = (LPVOID)(p - GC_page_size);
1213         if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break;
1214         result = VirtualQuery(q, &buf, sizeof(buf));
1215         if (result != sizeof(buf) || buf.AllocationBase == 0) break;
1216         p = (ptr_t)(buf.AllocationBase);
1217     }
1218     return(p);
1219   }
1220 # endif
1221 
1222 # ifndef REDIRECT_MALLOC
1223   /* We maintain a linked list of AllocationBase values that we know    */
1224   /* correspond to malloc heap sections.  Currently this is only called */
1225   /* during a GC.  But there is some hope that for long running         */
1226   /* programs we will eventually see most heap sections.                */
1227 
1228   /* In the long run, it would be more reliable to occasionally walk    */
1229   /* the malloc heap with HeapWalk on the default heap.  But that       */
1230   /* apparently works only for NT-based Windows.                        */ 
1231 
1232   /* In the long run, a better data structure would also be nice ...    */
1233   struct GC_malloc_heap_list {
1234     void * allocation_base;
1235     struct GC_malloc_heap_list *next;
1236   } *GC_malloc_heap_l = 0;
1237 
1238   /* Is p the base of one of the malloc heap sections we already know   */
1239   /* about?                                                             */
1240   GC_bool GC_is_malloc_heap_base(ptr_t p)
1241   {
1242     struct GC_malloc_heap_list *q = GC_malloc_heap_l;
1243 
1244     while (0 != q) {
1245       if (q -> allocation_base == p) return TRUE;
1246       q = q -> next;
1247     }
1248     return FALSE;
1249   }
1250 
1251   void *GC_get_allocation_base(void *p)
1252   {
1253     MEMORY_BASIC_INFORMATION buf;
1254     DWORD result = VirtualQuery(p, &buf, sizeof(buf));
1255     if (result != sizeof(buf)) {
1256       ABORT("Weird VirtualQuery result");
1257     }
1258     return buf.AllocationBase;
1259   }
1260 
1261   size_t GC_max_root_size = 100000;     /* Appr. largest root size.     */
1262 
1263   void GC_add_current_malloc_heap()
1264   {
1265     struct GC_malloc_heap_list *new_l =
1266                  malloc(sizeof(struct GC_malloc_heap_list));
1267     void * candidate = GC_get_allocation_base(new_l);
1268 
1269     if (new_l == 0) return;
1270     if (GC_is_malloc_heap_base(candidate)) {
1271       /* Try a little harder to find malloc heap.                       */
1272         size_t req_size = 10000;
1273         do {
1274           void *p = malloc(req_size);
1275           if (0 == p) { free(new_l); return; }
1276           candidate = GC_get_allocation_base(p);
1277           free(p);
1278           req_size *= 2;
1279         } while (GC_is_malloc_heap_base(candidate)
1280                  && req_size < GC_max_root_size/10 && req_size < 500000);
1281         if (GC_is_malloc_heap_base(candidate)) {
1282           free(new_l); return;
1283         }
1284     }
1285 #   ifdef CONDPRINT
1286       if (GC_print_stats)
1287           GC_printf1("Found new system malloc AllocationBase at 0x%lx\n",
1288                      candidate);
1289 #   endif
1290     new_l -> allocation_base = candidate;
1291     new_l -> next = GC_malloc_heap_l;
1292     GC_malloc_heap_l = new_l;
1293   }
1294 # endif /* REDIRECT_MALLOC */
1295   
1296   /* Is p the start of either the malloc heap, or of one of our */
1297   /* heap sections?                                             */
1298   GC_bool GC_is_heap_base (ptr_t p)
1299   {
1300      
1301      unsigned i;
1302      
1303 #    ifndef REDIRECT_MALLOC
1304        static word last_gc_no = -1;
1305      
1306        if (last_gc_no != GC_gc_no) {
1307          GC_add_current_malloc_heap();
1308          last_gc_no = GC_gc_no;
1309        }
1310        if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size;
1311        if (GC_is_malloc_heap_base(p)) return TRUE;
1312 #    endif
1313      for (i = 0; i < GC_n_heap_bases; i++) {
1314          if (GC_heap_bases[i] == p) return TRUE;
1315      }
1316      return FALSE ;
1317   }
1318 
1319 # ifdef MSWIN32
1320   void GC_register_root_section(ptr_t static_root)
1321   {
1322       MEMORY_BASIC_INFORMATION buf;
1323       DWORD result;
1324       DWORD protect;
1325       LPVOID p;
1326       char * base;
1327       char * limit, * new_limit;
1328     
1329       if (!GC_no_win32_dlls) return;
1330       p = base = limit = GC_least_described_address(static_root);
1331       while (p < GC_sysinfo.lpMaximumApplicationAddress) {
1332         result = VirtualQuery(p, &buf, sizeof(buf));
1333         if (result != sizeof(buf) || buf.AllocationBase == 0
1334             || GC_is_heap_base(buf.AllocationBase)) break;
1335         new_limit = (char *)p + buf.RegionSize;
1336         protect = buf.Protect;
1337         if (buf.State == MEM_COMMIT
1338             && is_writable(protect)) {
1339             if ((char *)p == limit) {
1340                 limit = new_limit;
1341             } else {
1342                 if (base != limit) GC_add_roots_inner(base, limit, FALSE);
1343                 base = p;
1344                 limit = new_limit;
1345             }
1346         }
1347         if (p > (LPVOID)new_limit /* overflow */) break;
1348         p = (LPVOID)new_limit;
1349       }
1350       if (base != limit) GC_add_roots_inner(base, limit, FALSE);
1351   }
1352 #endif
1353   
1354   void GC_register_data_segments()
1355   {
1356 #     ifdef MSWIN32
1357       static char dummy;
1358       GC_register_root_section((ptr_t)(&dummy));
1359 #     endif
1360   }
1361 
1362 # else /* !OS2 && !Windows */
1363 
1364 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
1365       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
1366 ptr_t GC_SysVGetDataStart(max_page_size, etext_addr)
1367 int max_page_size;
1368 int * etext_addr;
1369 {
1370     word text_end = ((word)(etext_addr) + sizeof(word) - 1)
1371                     & ~(sizeof(word) - 1);
1372         /* etext rounded to word boundary       */
1373     word next_page = ((text_end + (word)max_page_size - 1)
1374                       & ~((word)max_page_size - 1));
1375     word page_offset = (text_end & ((word)max_page_size - 1));
1376     VOLATILE char * result = (char *)(next_page + page_offset);
1377     /* Note that this isnt equivalent to just adding            */
1378     /* max_page_size to &etext if &etext is at a page boundary  */
1379     
1380     GC_setup_temporary_fault_handler();
1381     if (SETJMP(GC_jmp_buf) == 0) {
1382         /* Try writing to the address.  */
1383         *result = *result;
1384         GC_reset_fault_handler();
1385     } else {
1386         GC_reset_fault_handler();
1387         /* We got here via a longjmp.  The address is not readable.     */
1388         /* This is known to happen under Solaris 2.4 + gcc, which place */
1389         /* string constants in the text segment, but after etext.       */
1390         /* Use plan B.  Note that we now know there is a gap between    */
1391         /* text and data segments, so plan A bought us something.       */
1392         result = (char *)GC_find_limit((ptr_t)(DATAEND), FALSE);
1393     }
1394     return((ptr_t)result);
1395 }
1396 # endif
1397 
1398 # if defined(FREEBSD) && defined(I386) && !defined(PCR)
1399 /* Its unclear whether this should be identical to the above, or        */
1400 /* whether it should apply to non-X86 architectures.                    */
1401 /* For now we don't assume that there is always an empty page after     */
1402 /* etext.  But in some cases there actually seems to be slightly more.  */
1403 /* This also deals with holes between read-only data and writable data. */
1404 ptr_t GC_FreeBSDGetDataStart(max_page_size, etext_addr)
1405 int max_page_size;
1406 int * etext_addr;
1407 {
1408     word text_end = ((word)(etext_addr) + sizeof(word) - 1)
1409                      & ~(sizeof(word) - 1);
1410         /* etext rounded to word boundary       */
1411     VOLATILE word next_page = (text_end + (word)max_page_size - 1)
1412                               & ~((word)max_page_size - 1);
1413     VOLATILE ptr_t result = (ptr_t)text_end;
1414     GC_setup_temporary_fault_handler();
1415     if (SETJMP(GC_jmp_buf) == 0) {
1416         /* Try reading at the address.                          */
1417         /* This should happen before there is another thread.   */
1418         for (; next_page < (word)(DATAEND); next_page += (word)max_page_size)
1419             *(VOLATILE char *)next_page;
1420         GC_reset_fault_handler();
1421     } else {
1422         GC_reset_fault_handler();
1423         /* As above, we go to plan B    */
1424         result = GC_find_limit((ptr_t)(DATAEND), FALSE);
1425     }
1426     return(result);
1427 }
1428 
1429 # endif
1430 
1431 
1432 #ifdef AMIGA
1433 
1434 #  define GC_AMIGA_DS
1435 #  include "AmigaOS.c"
1436 #  undef GC_AMIGA_DS
1437 
1438 #else /* !OS2 && !Windows && !AMIGA */
1439 
1440 void GC_register_data_segments()
1441 {
1442 #   if !defined(PCR) && !defined(SRC_M3) && !defined(MACOS)
1443 #     if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS)
1444         /* As of Solaris 2.3, the Solaris threads implementation        */
1445         /* allocates the data structure for the initial thread with     */
1446         /* sbrk at process startup.  It needs to be scanned, so that    */
1447         /* we don't lose some malloc allocated data structures          */
1448         /* hanging from it.  We're on thin ice here ...                 */
1449         extern caddr_t sbrk();
1450 
1451         GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
1452 #     else
1453         GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
1454 #       if defined(DATASTART2)
1455          GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), FALSE);
1456 #       endif
1457 #     endif
1458 #   endif
1459 #   if defined(MACOS)
1460     {
1461 #   if defined(THINK_C)
1462         extern void* GC_MacGetDataStart(void);
1463         /* globals begin above stack and end at a5. */
1464         GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
1465                            (ptr_t)LMGetCurrentA5(), FALSE);
1466 #   else
1467 #     if defined(__MWERKS__)
1468 #       if !__POWERPC__
1469           extern void* GC_MacGetDataStart(void);
1470           /* MATTHEW: Function to handle Far Globals (CW Pro 3) */
1471 #         if __option(far_data)
1472           extern void* GC_MacGetDataEnd(void);
1473 #         endif
1474           /* globals begin above stack and end at a5. */
1475           GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
1476                              (ptr_t)LMGetCurrentA5(), FALSE);
1477           /* MATTHEW: Handle Far Globals */                          
1478 #         if __option(far_data)
1479       /* Far globals follow he QD globals: */
1480           GC_add_roots_inner((ptr_t)LMGetCurrentA5(),
1481                              (ptr_t)GC_MacGetDataEnd(), FALSE);
1482 #         endif
1483 #       else
1484           extern char __data_start__[], __data_end__[];
1485           GC_add_roots_inner((ptr_t)&__data_start__,
1486                              (ptr_t)&__data_end__, FALSE);
1487 #       endif /* __POWERPC__ */
1488 #     endif /* __MWERKS__ */
1489 #   endif /* !THINK_C */
1490     }
1491 #   endif /* MACOS */
1492 
1493     /* Dynamic libraries are added at every collection, since they may  */
1494     /* change.                                                          */
1495 }
1496 
1497 # endif  /* ! AMIGA */
1498 # endif  /* ! MSWIN32 && ! MSWINCE*/
1499 # endif  /* ! OS2 */
1500 
1501 /*
1502  * Auxiliary routines for obtaining memory from OS.
1503  */
1504 
1505 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
1506         && !defined(MSWIN32) && !defined(MSWINCE) \
1507         && !defined(MACOS) && !defined(DOS4GW)
1508 
1509 # ifdef SUNOS4
1510     extern caddr_t sbrk();
1511 # endif
1512 # ifdef __STDC__
1513 #   define SBRK_ARG_T ptrdiff_t
1514 # else
1515 #   define SBRK_ARG_T int
1516 # endif
1517 
1518 
1519 # if 0 && defined(RS6000)  /* We now use mmap */
1520 /* The compiler seems to generate speculative reads one past the end of */
1521 /* an allocated object.  Hence we need to make sure that the page       */
1522 /* following the last heap page is also mapped.                         */
1523 ptr_t GC_unix_get_mem(bytes)
1524 word bytes;
1525 {
1526     caddr_t cur_brk = (caddr_t)sbrk(0);
1527     caddr_t result;
1528     SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
1529     static caddr_t my_brk_val = 0;
1530     
1531     if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
1532     if (lsbs != 0) {
1533         if((caddr_t)(sbrk(GC_page_size - lsbs)) == (caddr_t)(-1)) return(0);
1534     }
1535     if (cur_brk == my_brk_val) {
1536         /* Use the extra block we allocated last time. */
1537         result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
1538         if (result == (caddr_t)(-1)) return(0);
1539         result -= GC_page_size;
1540     } else {
1541         result = (ptr_t)sbrk(GC_page_size + (SBRK_ARG_T)bytes);
1542         if (result == (caddr_t)(-1)) return(0);
1543     }
1544     my_brk_val = result + bytes + GC_page_size; /* Always page aligned */
1545     return((ptr_t)result);
1546 }
1547 
1548 #else  /* Not RS6000 */
1549 
1550 #if defined(USE_MMAP) || defined(USE_MUNMAP)
1551 
1552 #ifdef USE_MMAP_FIXED
1553 #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE
1554         /* Seems to yield better performance on Solaris 2, but can      */
1555         /* be unreliable if something is already mapped at the address. */
1556 #else
1557 #   define GC_MMAP_FLAGS MAP_PRIVATE
1558 #endif
1559 
1560 #ifdef USE_MMAP_ANON
1561 # define zero_fd -1
1562 # if defined(MAP_ANONYMOUS)
1563 #   define OPT_MAP_ANON MAP_ANONYMOUS
1564 # else
1565 #   define OPT_MAP_ANON MAP_ANON
1566 # endif
1567 #else
1568   static int zero_fd;
1569 # define OPT_MAP_ANON 0
1570 #endif 
1571 
1572 #endif /* defined(USE_MMAP) || defined(USE_MUNMAP) */
1573 
1574 #if defined(USE_MMAP)
1575 /* Tested only under Linux, IRIX5 and Solaris 2 */
1576 
1577 #ifndef HEAP_START
1578 #   define HEAP_START 0
1579 #endif
1580 
1581 ptr_t GC_unix_get_mem(bytes)
1582 word bytes;
1583 {
1584     void *result;
1585     static ptr_t last_addr = HEAP_START;
1586 
1587 #   ifndef USE_MMAP_ANON
1588       static GC_bool initialized = FALSE;
1589 
1590       if (!initialized) {
1591           zero_fd = open("/dev/zero", O_RDONLY);
1592           fcntl(zero_fd, F_SETFD, FD_CLOEXEC);
1593           initialized = TRUE;
1594       }
1595 #   endif
1596 
1597     if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
1598     result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
1599                   GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */);
1600     if (result == MAP_FAILED) return(0);
1601     last_addr = (ptr_t)result + bytes + GC_page_size - 1;
1602     last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
1603 #   if !defined(LINUX)
1604       if (last_addr == 0) {
1605         /* Oops.  We got the end of the address space.  This isn't      */
1606         /* usable by arbitrary C code, since one-past-end pointers      */
1607         /* don't work, so we discard it and try again.                  */
1608         munmap(result, (size_t)(-GC_page_size) - (size_t)result);
1609                         /* Leave last page mapped, so we can't repeat. */
1610         return GC_unix_get_mem(bytes);
1611       }
1612 #   else
1613       GC_ASSERT(last_addr != 0);
1614 #   endif
1615     return((ptr_t)result);
1616 }
1617 
1618 #else /* Not RS6000, not USE_MMAP */
1619 ptr_t GC_unix_get_mem(bytes)
1620 word bytes;
1621 {
1622   ptr_t result;
1623 # ifdef IRIX5
1624     /* Bare sbrk isn't thread safe.  Play by malloc rules.      */
1625     /* The equivalent may be needed on other systems as well.   */
1626     __LOCK_MALLOC();
1627 # endif
1628   {
1629     ptr_t cur_brk = (ptr_t)sbrk(0);
1630     SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
1631     
1632     if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
1633     if (lsbs != 0) {
1634         if((ptr_t)sbrk(GC_page_size - lsbs) == (ptr_t)(-1)) return(0);
1635     }
1636     result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
1637     if (result == (ptr_t)(-1)) result = 0;
1638   }
1639 # ifdef IRIX5
1640     __UNLOCK_MALLOC();
1641 # endif
1642   return(result);
1643 }
1644 
1645 #endif /* Not USE_MMAP */
1646 #endif /* Not RS6000 */
1647 
1648 # endif /* UN*X */
1649 
1650 # ifdef OS2
1651 
1652 void * os2_alloc(size_t bytes)
1653 {
1654     void * result;
1655 
1656     if (DosAllocMem(&result, bytes, PAG_EXECUTE | PAG_READ |
1657                                     PAG_WRITE | PAG_COMMIT)
1658                     != NO_ERROR) {
1659         return(0);
1660     }
1661     if (result == 0) return(os2_alloc(bytes));
1662     return(result);
1663 }
1664 
1665 # endif /* OS2 */
1666 
1667 
1668 # if defined(MSWIN32) || defined(MSWINCE)
1669 SYSTEM_INFO GC_sysinfo;
1670 # endif
1671 
1672 # ifdef MSWIN32
1673 
1674 # ifdef USE_GLOBAL_ALLOC
1675 #   define GLOBAL_ALLOC_TEST 1
1676 # else
1677 #   define GLOBAL_ALLOC_TEST GC_no_win32_dlls
1678 # endif
1679 
1680 word GC_n_heap_bases = 0;
1681 
1682 ptr_t GC_win32_get_mem(bytes)
1683 word bytes;
1684 {
1685     ptr_t result;
1686 
1687     if (GLOBAL_ALLOC_TEST) {
1688         /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE.    */
1689         /* There are also unconfirmed rumors of other           */
1690         /* problems, so we dodge the issue.                     */
1691         result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
1692         result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));
1693     } else {
1694         /* VirtualProtect only works on regions returned by a   */
1695         /* single VirtualAlloc call.  Thus we allocate one      */
1696         /* extra page, which will prevent merging of blocks     */
1697         /* in separate regions, and eliminate any temptation    */
1698         /* to call VirtualProtect on a range spanning regions.  */
1699         /* This wastes a small amount of memory, and risks      */
1700         /* increased fragmentation.  But better alternatives    */
1701         /* would require effort.                                */
1702         result = (ptr_t) VirtualAlloc(NULL, bytes + 1,
1703                                       MEM_COMMIT | MEM_RESERVE,
1704                                       PAGE_EXECUTE_READWRITE);
1705     }
1706     if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1707         /* If I read the documentation correctly, this can      */
1708         /* only happen if HBLKSIZE > 64k or not a power of 2.   */
1709     if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
1710     GC_heap_bases[GC_n_heap_bases++] = result;
1711     return(result);                       
1712 }
1713 
1714 void GC_win32_free_heap ()
1715 {
1716     if (GC_no_win32_dlls) {
1717         while (GC_n_heap_bases > 0) {
1718             GlobalFree (GC_heap_bases[--GC_n_heap_bases]);
1719             GC_heap_bases[GC_n_heap_bases] = 0;
1720         }
1721     }
1722 }
1723 # endif
1724 
1725 #ifdef AMIGA
1726 # define GC_AMIGA_AM
1727 # include "AmigaOS.c"
1728 # undef GC_AMIGA_AM
1729 #endif
1730 
1731 
1732 # ifdef MSWINCE
1733 word GC_n_heap_bases = 0;
1734 
1735 ptr_t GC_wince_get_mem(bytes)
1736 word bytes;
1737 {
1738     ptr_t result;
1739     word i;
1740 
1741     /* Round up allocation size to multiple of page size */
1742     bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1);
1743 
1744     /* Try to find reserved, uncommitted pages */
1745     for (i = 0; i < GC_n_heap_bases; i++) {
1746         if (((word)(-(signed_word)GC_heap_lengths[i])
1747              & (GC_sysinfo.dwAllocationGranularity-1))
1748             >= bytes) {
1749             result = GC_heap_bases[i] + GC_heap_lengths[i];
1750             break;
1751         }
1752     }
1753 
1754     if (i == GC_n_heap_bases) {
1755         /* Reserve more pages */
1756         word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
1757                          & ~(GC_sysinfo.dwAllocationGranularity-1);
1758         /* If we ever support MPROTECT_VDB here, we will probably need to       */
1759         /* ensure that res_bytes is strictly > bytes, so that VirtualProtect    */
1760         /* never spans regions.  It seems to be OK for a VirtualFree argument   */
1761         /* to span regions, so we should be OK for now.                         */
1762         result = (ptr_t) VirtualAlloc(NULL, res_bytes,
1763                                       MEM_RESERVE | MEM_TOP_DOWN,
1764                                       PAGE_EXECUTE_READWRITE);
1765         if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1766             /* If I read the documentation correctly, this can  */
1767             /* only happen if HBLKSIZE > 64k or not a power of 2.       */
1768         if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
1769         GC_heap_bases[GC_n_heap_bases] = result;
1770         GC_heap_lengths[GC_n_heap_bases] = 0;
1771         GC_n_heap_bases++;
1772     }
1773 
1774     /* Commit pages */
1775     result = (ptr_t) VirtualAlloc(result, bytes,
1776                                   MEM_COMMIT,
1777                                   PAGE_EXECUTE_READWRITE);
1778     if (result != NULL) {
1779         if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1780         GC_heap_lengths[i] += bytes;
1781     }
1782 
1783     return(result);                       
1784 }
1785 # endif
1786 
1787 #ifdef USE_MUNMAP
1788 
1789 /* For now, this only works on Win32/WinCE and some Unix-like   */
1790 /* systems.  If you have something else, don't define           */
1791 /* USE_MUNMAP.                                                  */
1792 /* We assume ANSI C to support this feature.                    */
1793 
1794 #if !defined(MSWIN32) && !defined(MSWINCE)
1795 
1796 #include <unistd.h>
1797 #include <sys/mman.h>
1798 #include <sys/stat.h>
1799 #include <sys/types.h>
1800 
1801 #endif
1802 
1803 /* Compute a page aligned starting address for the unmap        */
1804 /* operation on a block of size bytes starting at start.        */
1805 /* Return 0 if the block is too small to make this feasible.    */
1806 ptr_t GC_unmap_start(ptr_t start, word bytes)
1807 {
1808     ptr_t result = start;
1809     /* Round start to next page boundary.       */
1810         result += GC_page_size - 1;
1811         result = (ptr_t)((word)result & ~(GC_page_size - 1));
1812     if (result + GC_page_size > start + bytes) return 0;
1813     return result;
1814 }
1815 
1816 /* Compute end address for an unmap operation on the indicated  */
1817 /* block.                                                       */
1818 ptr_t GC_unmap_end(ptr_t start, word bytes)
1819 {
1820     ptr_t end_addr = start + bytes;
1821     end_addr = (ptr_t)((word)end_addr & ~(GC_page_size - 1));
1822     return end_addr;
1823 }
1824 
1825 /* Under Win32/WinCE we commit (map) and decommit (unmap)       */
1826 /* memory using VirtualAlloc and VirtualFree.  These functions  */
1827 /* work on individual allocations of virtual memory, made       */
1828 /* previously using VirtualAlloc with the MEM_RESERVE flag.     */
1829 /* The ranges we need to (de)commit may span several of these   */
1830 /* allocations; therefore we use VirtualQuery to check          */
1831 /* allocation lengths, and split up the range as necessary.     */
1832 
1833 /* We assume that GC_remap is called on exactly the same range  */
1834 /* as a previous call to GC_unmap.  It is safe to consistently  */
1835 /* round the endpoints in both places.                          */
1836 void GC_unmap(ptr_t start, word bytes)
1837 {
1838     ptr_t start_addr = GC_unmap_start(start, bytes);
1839     ptr_t end_addr = GC_unmap_end(start, bytes);
1840     word len = end_addr - start_addr;
1841     if (0 == start_addr) return;
1842 #   if defined(MSWIN32) || defined(MSWINCE)
1843       while (len != 0) {
1844           MEMORY_BASIC_INFORMATION mem_info;
1845           GC_word free_len;
1846           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1847               != sizeof(mem_info))
1848               ABORT("Weird VirtualQuery result");
1849           free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1850           if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
1851               ABORT("VirtualFree failed");
1852           GC_unmapped_bytes += free_len;
1853           start_addr += free_len;
1854           len -= free_len;
1855       }
1856 #   else
1857       /* We immediately remap it to prevent an intervening mmap from    */
1858       /* accidentally grabbing the same address space.                  */
1859       {
1860         void * result;
1861         result = mmap(start_addr, len, PROT_NONE,
1862                       MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
1863                       zero_fd, 0/* offset */);
1864         if (result != (void *)start_addr) ABORT("mmap(...PROT_NONE...) failed");
1865       }
1866       GC_unmapped_bytes += len;
1867 #   endif
1868 }
1869 
1870 
1871 void GC_remap(ptr_t start, word bytes)
1872 {
1873     ptr_t start_addr = GC_unmap_start(start, bytes);
1874     ptr_t end_addr = GC_unmap_end(start, bytes);
1875     word len = end_addr - start_addr;
1876 
1877 #   if defined(MSWIN32) || defined(MSWINCE)
1878       ptr_t result;
1879 
1880       if (0 == start_addr) return;
1881       while (len != 0) {
1882           MEMORY_BASIC_INFORMATION mem_info;
1883           GC_word alloc_len;
1884           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1885               != sizeof(mem_info))
1886               ABORT("Weird VirtualQuery result");
1887           alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1888           result = VirtualAlloc(start_addr, alloc_len,
1889                                 MEM_COMMIT,
1890                                 PAGE_EXECUTE_READWRITE);
1891           if (result != start_addr) {
1892               ABORT("VirtualAlloc remapping failed");
1893           }
1894           GC_unmapped_bytes -= alloc_len;
1895           start_addr += alloc_len;
1896           len -= alloc_len;
1897       }
1898 #   else
1899       /* It was already remapped with PROT_NONE. */
1900       int result; 
1901 
1902       if (0 == start_addr) return;
1903       result = mprotect(start_addr, len,
1904                         PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
1905       if (result != 0) {
1906           GC_err_printf3(
1907                 "Mprotect failed at 0x%lx (length %ld) with errno %ld\n",
1908                 start_addr, len, errno);
1909           ABORT("Mprotect remapping failed");
1910       }
1911       GC_unmapped_bytes -= len;
1912 #   endif
1913 }
1914 
1915 /* Two adjacent blocks have already been unmapped and are about to      */
1916 /* be merged.  Unmap the whole block.  This typically requires          */
1917 /* that we unmap a small section in the middle that was not previously  */
1918 /* unmapped due to alignment constraints.                               */
1919 void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
1920 {
1921     ptr_t start1_addr = GC_unmap_start(start1, bytes1);
1922     ptr_t end1_addr = GC_unmap_end(start1, bytes1);
1923     ptr_t start2_addr = GC_unmap_start(start2, bytes2);
1924     ptr_t end2_addr = GC_unmap_end(start2, bytes2);
1925     ptr_t start_addr = end1_addr;
1926     ptr_t end_addr = start2_addr;
1927     word len;
1928     GC_ASSERT(start1 + bytes1 == start2);
1929     if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2);
1930     if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
1931     if (0 == start_addr) return;
1932     len = end_addr - start_addr;
1933 #   if defined(MSWIN32) || defined(MSWINCE)
1934       while (len != 0) {
1935           MEMORY_BASIC_INFORMATION mem_info;
1936           GC_word free_len;
1937           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1938               != sizeof(mem_info))
1939               ABORT("Weird VirtualQuery result");
1940           free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1941           if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
1942               ABORT("VirtualFree failed");
1943           GC_unmapped_bytes += free_len;
1944           start_addr += free_len;
1945           len -= free_len;
1946       }
1947 #   else
1948       if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
1949       GC_unmapped_bytes += len;
1950 #   endif
1951 }
1952 
1953 #endif /* USE_MUNMAP */
1954 
1955 /* Routine for pushing any additional roots.  In THREADS        */
1956 /* environment, this is also responsible for marking from       */
1957 /* thread stacks.                                               */
1958 #ifndef THREADS
1959 void (*GC_push_other_roots)() = 0;
1960 #else /* THREADS */
1961 
1962 # ifdef PCR
1963 PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy)
1964 {
1965     struct PCR_ThCtl_TInfoRep info;
1966     PCR_ERes result;
1967     
1968     info.ti_stkLow = info.ti_stkHi = 0;
1969     result = PCR_ThCtl_GetInfo(t, &info);
1970     GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi));
1971     return(result);
1972 }
1973 
1974 /* Push the contents of an old object. We treat this as stack   */
1975 /* data only becasue that makes it robust against mark stack    */
1976 /* overflow.                                                    */
1977 PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data)
1978 {
1979     GC_push_all_stack((ptr_t)p, (ptr_t)p + size);
1980     return(PCR_ERes_okay);
1981 }
1982 
1983 
1984 void GC_default_push_other_roots GC_PROTO((void))
1985 {
1986     /* Traverse data allocated by previous memory managers.             */
1987         {
1988           extern struct PCR_MM_ProcsRep * GC_old_allocator;
1989           
1990           if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false,
1991                                                    GC_push_old_obj, 0)
1992               != PCR_ERes_okay) {
1993               ABORT("Old object enumeration failed");
1994           }
1995         }
1996     /* Traverse all thread stacks. */
1997         if (PCR_ERes_IsErr(
1998                 PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0))
1999               || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) {
2000               ABORT("Thread stack marking failed\n");
2001         }
2002 }
2003 
2004 # endif /* PCR */
2005 
2006 # ifdef SRC_M3
2007 
2008 # ifdef ALL_INTERIOR_POINTERS
2009     --> misconfigured
2010 # endif
2011 
2012 void GC_push_thread_structures GC_PROTO((void))
2013 {
2014     /* Not our responsibibility. */
2015 }
2016 
2017 extern void ThreadF__ProcessStacks();
2018 
2019 void GC_push_thread_stack(start, stop)
2020 word start, stop;
2021 {
2022    GC_push_all_stack((ptr_t)start, (ptr_t)stop + sizeof(word));
2023 }
2024 
2025 /* Push routine with M3 specific calling convention. */
2026 GC_m3_push_root(dummy1, p, dummy2, dummy3)
2027 word *p;
2028 ptr_t dummy1, dummy2;
2029 int dummy3;
2030 {
2031     word q = *p;
2032     
2033     GC_PUSH_ONE_STACK(q, p);
2034 }
2035 
2036 /* M3 set equivalent to RTHeap.TracedRefTypes */
2037 typedef struct { int elts[1]; }  RefTypeSet;
2038 RefTypeSet GC_TracedRefTypes = {{0x1}};
2039 
2040 void GC_default_push_other_roots GC_PROTO((void))
2041 {
2042     /* Use the M3 provided routine for finding static roots.     */
2043     /* This is a bit dubious, since it presumes no C roots.      */
2044     /* We handle the collector roots explicitly in GC_push_roots */
2045         RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes);
2046         if (GC_words_allocd > 0) {
2047             ThreadF__ProcessStacks(GC_push_thread_stack);
2048         }
2049         /* Otherwise this isn't absolutely necessary, and we have       */
2050         /* startup ordering problems.                                   */
2051 }
2052 
2053 # endif /* SRC_M3 */
2054 
2055 # if defined(GC_SOLARIS_THREADS) || defined(GC_PTHREADS) || \
2056      defined(GC_WIN32_THREADS)
2057 
2058 extern void GC_push_all_stacks();
2059 
2060 void GC_default_push_other_roots GC_PROTO((void))
2061 {
2062     GC_push_all_stacks();
2063 }
2064 
2065 # endif /* GC_SOLARIS_THREADS || GC_PTHREADS */
2066 
2067 void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
2068 
2069 #endif /* THREADS */
2070 
2071 /*
2072  * Routines for accessing dirty  bits on virtual pages.
2073  * We plan to eventually implement four strategies for doing so:
2074  * DEFAULT_VDB: A simple dummy implementation that treats every page
2075  *              as possibly dirty.  This makes incremental collection
2076  *              useless, but the implementation is still correct.
2077  * PCR_VDB:     Use PPCRs virtual dirty bit facility.
2078  * PROC_VDB:    Use the /proc facility for reading dirty bits.  Only
2079  *              works under some SVR4 variants.  Even then, it may be
2080  *              too slow to be entirely satisfactory.  Requires reading
2081  *              dirty bits for entire address space.  Implementations tend
2082  *              to assume that the client is a (slow) debugger.
2083  * MPROTECT_VDB:Protect pages and then catch the faults to keep track of
2084  *              dirtied pages.  The implementation (and implementability)
2085  *              is highly system dependent.  This usually fails when system
2086  *              calls write to a protected page.  We prevent the read system
2087  *              call from doing so.  It is the clients responsibility to
2088  *              make sure that other system calls are similarly protected
2089  *              or write only to the stack.
2090  */
2091 GC_bool GC_dirty_maintained = FALSE;
2092 
2093 # ifdef DEFAULT_VDB
2094 
2095 /* All of the following assume the allocation lock is held, and */
2096 /* signals are disabled.                                        */
2097 
2098 /* The client asserts that unallocated pages in the heap are never      */
2099 /* written.                                                             */
2100 
2101 /* Initialize virtual dirty bit implementation.                 */
2102 void GC_dirty_init()
2103 {
2104 #   ifdef PRINTSTATS
2105       GC_printf0("Initializing DEFAULT_VDB...\n");
2106 #   endif
2107     GC_dirty_maintained = TRUE;
2108 }
2109 
2110 /* Retrieve system dirty bits for heap to a local buffer.       */
2111 /* Restore the systems notion of which pages are dirty.         */
2112 void GC_read_dirty()
2113 {}
2114 
2115 /* Is the HBLKSIZE sized page at h marked dirty in the local buffer?    */
2116 /* If the actual page size is different, this returns TRUE if any       */
2117 /* of the pages overlapping h are dirty.  This routine may err on the   */
2118 /* side of labelling pages as dirty (and this implementation does).     */
2119 /*ARGSUSED*/
2120 GC_bool GC_page_was_dirty(h)
2121 struct hblk *h;
2122 {
2123     return(TRUE);
2124 }
2125 
2126 /*
2127  * The following two routines are typically less crucial.  They matter
2128  * most with large dynamic libraries, or if we can't accurately identify
2129  * stacks, e.g. under Solaris 2.X.  Otherwise the following default
2130  * versions are adequate.
2131  */
2132  
2133 /* Could any valid GC heap pointer ever have been written to this page? */
2134 /*ARGSUSED*/
2135 GC_bool GC_page_was_ever_dirty(h)
2136 struct hblk *h;
2137 {
2138     return(TRUE);
2139 }
2140 
2141 /* Reset the n pages starting at h to "was never dirty" status. */
2142 void GC_is_fresh(h, n)
2143 struct hblk *h;
2144 word n;
2145 {
2146 }
2147 
2148 /* A call that:                                         */
2149 /* I) hints that [h, h+nblocks) is about to be written. */
2150 /* II) guarantees that protection is removed.           */
2151 /* (I) may speed up some dirty bit implementations.     */
2152 /* (II) may be essential if we need to ensure that      */
2153 /* pointer-free system call buffers in the heap are     */
2154 /* not protected.                                       */
2155 /*ARGSUSED*/
2156 void GC_remove_protection(h, nblocks, is_ptrfree)
2157 struct hblk *h;
2158 word nblocks;
2159 GC_bool is_ptrfree;
2160 {
2161 }
2162 
2163 # endif /* DEFAULT_VDB */
2164 
2165 
2166 # ifdef MPROTECT_VDB
2167 
2168 /*
2169  * See DEFAULT_VDB for interface descriptions.
2170  */
2171 
2172 /*
2173  * This implementation maintains dirty bits itself by catching write
2174  * faults and keeping track of them.  We assume nobody else catches
2175  * SIGBUS or SIGSEGV.  We assume no write faults occur in system calls.
2176  * This means that clients must ensure that system calls don't write
2177  * to the write-protected heap.  Probably the best way to do this is to
2178  * ensure that system calls write at most to POINTERFREE objects in the
2179  * heap, and do even that only if we are on a platform on which those
2180  * are not protected.  Another alternative is to wrap system calls
2181  * (see example for read below), but the current implementation holds
2182  * a lock across blocking calls, making it problematic for multithreaded
2183  * applications. 
2184  * We assume the page size is a multiple of HBLKSIZE.
2185  * We prefer them to be the same.  We avoid protecting POINTERFREE
2186  * objects only if they are the same.
2187  */
2188 
2189 # if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN)
2190 
2191 #   include <sys/mman.h>
2192 #   include <signal.h>
2193 #   include <sys/syscall.h>
2194 
2195 #   define PROTECT(addr, len) \
2196           if (mprotect((caddr_t)(addr), (size_t)(len), \
2197                        PROT_READ | OPT_PROT_EXEC) < 0) { \
2198             ABORT("mprotect failed"); \
2199           }
2200 #   define UNPROTECT(addr, len) \
2201           if (mprotect((caddr_t)(addr), (size_t)(len), \
2202                        PROT_WRITE | PROT_READ | OPT_PROT_EXEC ) < 0) { \
2203             ABORT("un-mprotect failed"); \
2204           }
2205           
2206 # else
2207 
2208 # ifdef DARWIN
2209     /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to
2210        decrease the likelihood of some of the problems described below. */
2211     #include <mach/vm_map.h>
2212     static mach_port_t GC_task_self;
2213     #define PROTECT(addr,len) \
2214         if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
2215                 FALSE,VM_PROT_READ) != KERN_SUCCESS) { \
2216             ABORT("vm_portect failed"); \
2217         }
2218     #define UNPROTECT(addr,len) \
2219         if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
2220                 FALSE,VM_PROT_READ|VM_PROT_WRITE) != KERN_SUCCESS) { \
2221             ABORT("vm_portect failed"); \
2222         }
2223 # else
2224     
2225 #   ifndef MSWINCE
2226 #     include <signal.h>
2227 #   endif
2228 
2229     static DWORD protect_junk;
2230 #   define PROTECT(addr, len) \
2231           if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READ, \
2232                               &protect_junk)) { \
2233             DWORD last_error = GetLastError(); \
2234             GC_printf1("Last error code: %lx\n", last_error); \
2235             ABORT("VirtualProtect failed"); \
2236           }
2237 #   define UNPROTECT(addr, len) \
2238           if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READWRITE, \
2239                               &protect_junk)) { \
2240             ABORT("un-VirtualProtect failed"); \
2241           }
2242 # endif /* !DARWIN */
2243 # endif /* MSWIN32 || MSWINCE || DARWIN */
2244 
2245 #if defined(SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
2246     typedef void (* SIG_PF)();
2247 #endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
2248 
2249 #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \
2250     || defined(HURD)
2251 # ifdef __STDC__
2252     typedef void (* SIG_PF)(int);
2253 # else
2254     typedef void (* SIG_PF)();
2255 # endif
2256 #endif /* SUNOS5SIGS || OSF1 || LINUX || HURD */
2257 
2258 #if defined(MSWIN32)
2259     typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF;
2260 #   undef SIG_DFL
2261 #   define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1)
2262 #endif
2263 #if defined(MSWINCE)
2264     typedef LONG (WINAPI *SIG_PF)(struct _EXCEPTION_POINTERS *);
2265 #   undef SIG_DFL
2266 #   define SIG_DFL (SIG_PF) (-1)
2267 #endif
2268 
2269 #if defined(IRIX5) || defined(OSF1) || defined(HURD)
2270     typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
2271 #endif /* IRIX5 || OSF1 || HURD */
2272 
2273 #if defined(SUNOS5SIGS)
2274 # if defined(HPUX) || defined(FREEBSD)
2275 #   define SIGINFO_T siginfo_t
2276 # else
2277 #   define SIGINFO_T struct siginfo
2278 # endif
2279 # ifdef __STDC__
2280     typedef void (* REAL_SIG_PF)(int, SIGINFO_T *, void *);
2281 # else
2282     typedef void (* REAL_SIG_PF)();
2283 # endif
2284 #endif /* SUNOS5SIGS */
2285 
2286 #if defined(LINUX)
2287 #   if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
2288       typedef struct sigcontext s_c;
2289 #   else  /* glibc < 2.2 */
2290 #     include <linux/version.h>
2291 #     if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(ARM32)
2292         typedef struct sigcontext s_c;
2293 #     else
2294         typedef struct sigcontext_struct s_c;
2295 #     endif
2296 #   endif  /* glibc < 2.2 */
2297 #   if defined(ALPHA) || defined(M68K)
2298       typedef void (* REAL_SIG_PF)(int, int, s_c *);
2299 #   else
2300 #     if defined(IA64) || defined(HP_PA) || defined(X86_64)
2301         typedef void (* REAL_SIG_PF)(int, siginfo_t *, s_c *);
2302         /* FIXME:                                                 */
2303         /* According to SUSV3, the last argument should have type */
2304         /* void * or ucontext_t *                                 */
2305 #     else
2306         typedef void (* REAL_SIG_PF)(int, s_c);
2307 #     endif
2308 #   endif
2309 #   ifdef ALPHA
2310     /* Retrieve fault address from sigcontext structure by decoding     */
2311     /* instruction.                                                     */
2312     char * get_fault_addr(s_c *sc) {
2313         unsigned instr;
2314         word faultaddr;
2315 
2316         instr = *((unsigned *)(sc->sc_pc));
2317         faultaddr = sc->sc_regs[(instr >> 16) & 0x1f];
2318         faultaddr += (word) (((int)instr << 16) >> 16);
2319         return (char *)faultaddr;
2320     }
2321 #   endif /* !ALPHA */
2322 # endif /* LINUX */
2323 
2324 #ifndef DARWIN
2325 SIG_PF GC_old_bus_handler;
2326 SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
2327 #endif /* !DARWIN */
2328 
2329 #if defined(THREADS)
2330 /* We need to lock around the bitmap update in the write fault handler  */
2331 /* in order to avoid the risk of losing a bit.  We do this with a       */
2332 /* test-and-set spin lock if we know how to do that.  Otherwise we      */
2333 /* check whether we are already in the handler and use the dumb but     */
2334 /* safe fallback algorithm of setting all bits in the word.             */
2335 /* Contention should be very rare, so we do the minimum to handle it    */
2336 /* correctly.                                                           */
2337 #ifdef GC_TEST_AND_SET_DEFINED
2338   static VOLATILE unsigned int fault_handler_lock = 0;
2339   void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
2340     while (GC_test_and_set(&fault_handler_lock)) {}
2341     /* Could also revert to set_pht_entry_from_index_safe if initial    */
2342     /* GC_test_and_set fails.                                           */
2343     set_pht_entry_from_index(db, index);
2344     GC_clear(&fault_handler_lock);
2345   }
2346 #else /* !GC_TEST_AND_SET_DEFINED */
2347   /* THIS IS INCORRECT! The dirty bit vector may be temporarily wrong,  */
2348   /* just before we notice the conflict and correct it. We may end up   */
2349   /* looking at it while it's wrong.  But this requires contention      */
2350   /* exactly when a GC is triggered, which seems far less likely to     */
2351   /* fail than the old code, which had no reported failures.  Thus we   */
2352   /* leave it this way while we think of something better, or support   */
2353   /* GC_test_and_set on the remaining platforms.                        */
2354   static VOLATILE word currently_updating = 0;
2355   void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
2356     unsigned int update_dummy;
2357     currently_updating = (word)(&update_dummy);
2358     set_pht_entry_from_index(db, index);
2359     /* If we get contention in the 10 or so instruction window here,    */
2360     /* and we get stopped by a GC between the two updates, we lose!     */
2361     if (currently_updating != (word)(&update_dummy)) {
2362         set_pht_entry_from_index_safe(db, index);
2363         /* We claim that if two threads concurrently try to update the  */
2364         /* dirty bit vector, the first one to execute UPDATE_START      */
2365         /* will see it changed when UPDATE_END is executed.  (Note that */
2366         /* &update_dummy must differ in two distinct threads.)  It      */
2367         /* will then execute set_pht_entry_from_index_safe, thus        */
2368         /* returning us to a safe state, though not soon enough.        */
2369     }
2370   }
2371 #endif /* !GC_TEST_AND_SET_DEFINED */
2372 #else /* !THREADS */
2373 # define async_set_pht_entry_from_index(db, index) \
2374         set_pht_entry_from_index(db, index)
2375 #endif /* !THREADS */
2376 
2377 /*ARGSUSED*/
2378 #if !defined(DARWIN)
2379 # if defined (SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
2380     void GC_write_fault_handler(sig, code, scp, addr)
2381     int sig, code;
2382     struct sigcontext *scp;
2383     char * addr;
2384 #   ifdef SUNOS4
2385 #     define SIG_OK (sig == SIGSEGV || sig == SIGBUS)
2386 #     define CODE_OK (FC_CODE(code) == FC_PROT \
2387                     || (FC_CODE(code) == FC_OBJERR \
2388                        && FC_ERRNO(code) == FC_PROT))
2389 #   endif
2390 #   ifdef FREEBSD
2391 #     define SIG_OK (sig == SIGBUS)
2392 #     define CODE_OK TRUE
2393 #   endif
2394 # endif /* SUNOS4 || (FREEBSD && !SUNOS5SIGS) */
2395 
2396 # if defined(IRIX5) || defined(OSF1) || defined(HURD)
2397 #   include <errno.h>
2398     void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
2399 #   ifdef OSF1
2400 #     define SIG_OK (sig == SIGSEGV)
2401 #     define CODE_OK (code == 2 /* experimentally determined */)
2402 #   endif
2403 #   ifdef IRIX5
2404 #     define SIG_OK (sig == SIGSEGV)
2405 #     define CODE_OK (code == EACCES)
2406 #   endif
2407 #   ifdef HURD
2408 #     define SIG_OK (sig == SIGBUS || sig == SIGSEGV)   
2409 #     define CODE_OK  TRUE
2410 #   endif
2411 # endif /* IRIX5 || OSF1 || HURD */
2412 
2413 # if defined(LINUX)
2414 #   if defined(ALPHA) || defined(M68K)
2415       void GC_write_fault_handler(int sig, int code, s_c * sc)
2416 #   else
2417 #     if defined(IA64) || defined(HP_PA) || defined(X86_64)
2418         void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp)
2419 #     else
2420 #       if defined(ARM32)
2421           void GC_write_fault_handler(int sig, int a2, int a3, int a4, s_c sc)
2422 #       else
2423           void GC_write_fault_handler(int sig, s_c sc)
2424 #       endif
2425 #     endif
2426 #   endif
2427 #   define SIG_OK (sig == SIGSEGV)
2428 #   define CODE_OK TRUE
2429         /* Empirically c.trapno == 14, on IA32, but is that useful?     */
2430         /* Should probably consider alignment issues on other           */
2431         /* architectures.                                               */
2432 # endif /* LINUX */
2433 
2434 # if defined(SUNOS5SIGS)
2435 #  ifdef __STDC__
2436     void GC_write_fault_handler(int sig, SIGINFO_T *scp, void * context)
2437 #  else
2438     void GC_write_fault_handler(sig, scp, context)
2439     int sig;
2440     SIGINFO_T *scp;
2441     void * context;
2442 #  endif
2443 #   ifdef HPUX
2444 #     define SIG_OK (sig == SIGSEGV || sig == SIGBUS)
2445 #     define CODE_OK (scp -> si_code == SEGV_ACCERR) \
2446                      || (scp -> si_code == BUS_ADRERR) \
2447                      || (scp -> si_code == BUS_UNKNOWN) \
2448                      || (scp -> si_code == SEGV_UNKNOWN) \
2449                      || (scp -> si_code == BUS_OBJERR)
2450 #   else
2451 #     ifdef FREEBSD
2452 #       define SIG_OK (sig == SIGBUS)
2453 #       define CODE_OK (scp -> si_code == BUS_PAGE_FAULT)
2454 #     else
2455 #       define SIG_OK (sig == SIGSEGV)
2456 #       define CODE_OK (scp -> si_code == SEGV_ACCERR)
2457 #     endif
2458 #   endif    
2459 # endif /* SUNOS5SIGS */
2460 
2461 # if defined(MSWIN32) || defined(MSWINCE)
2462     LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info)
2463 #   define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode == \
2464                         STATUS_ACCESS_VIOLATION)
2465 #   define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1)
2466                         /* Write fault */
2467 # endif /* MSWIN32 || MSWINCE */
2468 {
2469     register unsigned i;
2470 #   if defined(HURD) 
2471         char *addr = (char *) code;
2472 #   endif
2473 #   ifdef IRIX5
2474         char * addr = (char *) (size_t) (scp -> sc_badvaddr);
2475 #   endif
2476 #   if defined(OSF1) && defined(ALPHA)
2477         char * addr = (char *) (scp -> sc_traparg_a0);
2478 #   endif
2479 #   ifdef SUNOS5SIGS
2480         char * addr = (char *) (scp -> si_addr);
2481 #   endif
2482 #   ifdef LINUX
2483 #     if defined(I386)
2484         char * addr = (char *) (sc.cr2);
2485 #     else
2486 #       if defined(M68K)
2487           char * addr = NULL;
2488 
2489           struct sigcontext *scp = (struct sigcontext *)(sc);
2490 
2491           int format = (scp->sc_formatvec >> 12) & 0xf;
2492           unsigned long *framedata = (unsigned long *)(scp + 1); 
2493           unsigned long ea;
2494 
2495           if (format == 0xa || format == 0xb) {
2496                 /* 68020/030 */
2497                 ea = framedata[2];
2498           } else if (format == 7) {
2499                 /* 68040 */
2500                 ea = framedata[3];
2501                 if (framedata[1] & 0x08000000) {
2502                         /* correct addr on misaligned access */
2503                         ea = (ea+4095)&(~4095);
2504                 }
2505           } else if (format == 4) {
2506                 /* 68060 */
2507                 ea = framedata[0];
2508                 if (framedata[1] & 0x08000000) {
2509                         /* correct addr on misaligned access */
2510                         ea = (ea+4095)&(~4095);
2511                 }
2512           }     
2513           addr = (char *)ea;
2514 #       else
2515 #         ifdef ALPHA
2516             char * addr = get_fault_addr(sc);
2517 #         else
2518 #           if defined(IA64) || defined(HP_PA) || defined(X86_64)
2519               char * addr = si -> si_addr;
2520               /* I believe this is claimed to work on all platforms for */
2521               /* Linux 2.3.47 and later.  Hopefully we don't have to    */
2522               /* worry about earlier kernels on IA64.                   */
2523 #           else
2524 #             if defined(POWERPC)
2525                 char * addr = (char *) (sc.regs->dar);
2526 #             else
2527 #               if defined(ARM32)
2528                   char * addr = (char *)sc.fault_address;
2529 #               else
2530 #                 if defined(CRIS)
2531                     char * addr = (char *)sc.regs.csraddr;
2532 #                 else
2533                     --> architecture not supported
2534 #                 endif
2535 #               endif
2536 #             endif
2537 #           endif
2538 #         endif
2539 #       endif
2540 #     endif
2541 #   endif
2542 #   if defined(MSWIN32) || defined(MSWINCE)
2543         char * addr = (char *) (exc_info -> ExceptionRecord
2544                                 -> ExceptionInformation[1]);
2545 #       define sig SIGSEGV
2546 #   endif
2547     
2548     if (SIG_OK && CODE_OK) {
2549         register struct hblk * h =
2550                         (struct hblk *)((word)addr & ~(GC_page_size-1));
2551         GC_bool in_allocd_block;
2552         
2553 #       ifdef SUNOS5SIGS
2554             /* Address is only within the correct physical page.        */
2555             in_allocd_block = FALSE;
2556             for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
2557               if (HDR(h+i) != 0) {
2558                 in_allocd_block = TRUE;
2559               }
2560             }
2561 #       else
2562             in_allocd_block = (HDR(addr) != 0);
2563 #       endif
2564         if (!in_allocd_block) {
2565             /* FIXME - We should make sure that we invoke the   */
2566             /* old handler with the appropriate calling         */
2567             /* sequence, which often depends on SA_SIGINFO.     */
2568 
2569             /* Heap blocks now begin and end on page boundaries */
2570             SIG_PF old_handler;
2571             
2572             if (sig == SIGSEGV) {
2573                 old_handler = GC_old_segv_handler;
2574             } else {
2575                 old_handler = GC_old_bus_handler;
2576             }
2577             if (old_handler == SIG_DFL) {
2578 #               if !defined(MSWIN32) && !defined(MSWINCE)
2579                     GC_err_printf1("Segfault at 0x%lx\n", addr);
2580                     ABORT("Unexpected bus error or segmentation fault");
2581 #               else
2582                     return(EXCEPTION_CONTINUE_SEARCH);
2583 #               endif
2584             } else {
2585 #               if defined (SUNOS4) \
2586                     || (defined(FREEBSD) && !defined(SUNOS5SIGS))
2587                     (*old_handler) (sig, code, scp, addr);
2588                     return;
2589 #               endif
2590 #               if defined (SUNOS5SIGS)
2591                     /*
2592                      * FIXME: For FreeBSD, this code should check if the 
2593                      * old signal handler used the traditional BSD style and
2594                      * if so call it using that style.
2595                      */
2596                     (*(REAL_SIG_PF)old_handler) (sig, scp, context);
2597                     return;
2598 #               endif
2599 #               if defined (LINUX)
2600 #                   if defined(ALPHA) || defined(M68K)
2601                         (*(REAL_SIG_PF)old_handler) (sig, code, sc);
2602 #                   else 
2603 #                     if defined(IA64) || defined(HP_PA) || defined(X86_64)
2604                         (*(REAL_SIG_PF)old_handler) (sig, si, scp);
2605 #                     else
2606                         (*(REAL_SIG_PF)old_handler) (sig, sc);
2607 #                     endif
2608 #                   endif
2609                     return;
2610 #               endif
2611 #               if defined (IRIX5) || defined(OSF1) || defined(HURD)
2612                     (*(REAL_SIG_PF)old_handler) (sig, code, scp);
2613                     return;
2614 #               endif
2615 #               ifdef MSWIN32
2616                     return((*old_handler)(exc_info));
2617 #               endif
2618             }
2619         }
2620         UNPROTECT(h, GC_page_size);
2621         /* We need to make sure that no collection occurs between       */
2622         /* the UNPROTECT and the setting of the dirty bit.  Otherwise   */
2623         /* a write by a third thread might go unnoticed.  Reversing     */
2624         /* the order is just as bad, since we would end up unprotecting */
2625         /* a page in a GC cycle during which it's not marked.           */
2626         /* Currently we do this by disabling the thread stopping        */
2627         /* signals while this handler is running.  An alternative might */
2628         /* be to record the fact that we're about to unprotect, or      */
2629         /* have just unprotected a page in the GC's thread structure,   */
2630         /* and then to have the thread stopping code set the dirty      */
2631         /* flag, if necessary.                                          */
2632         for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
2633             register int index = PHT_HASH(h+i);
2634             
2635             async_set_pht_entry_from_index(GC_dirty_pages, index);
2636         }
2637 #       if defined(OSF1)
2638             /* These reset the signal handler each time by default. */
2639             signal(SIGSEGV, (SIG_PF) GC_write_fault_handler);
2640 #       endif
2641         /* The write may not take place before dirty bits are read.     */
2642         /* But then we'll fault again ...                               */
2643 #       if defined(MSWIN32) || defined(MSWINCE)
2644             return(EXCEPTION_CONTINUE_EXECUTION);
2645 #       else
2646             return;
2647 #       endif
2648     }
2649 #if defined(MSWIN32) || defined(MSWINCE)
2650     return EXCEPTION_CONTINUE_SEARCH;
2651 #else
2652     GC_err_printf1("Segfault at 0x%lx\n", addr);
2653     ABORT("Unexpected bus error or segmentation fault");
2654 #endif
2655 }
2656 #endif /* !DARWIN */
2657 
2658 /*
2659  * We hold the allocation lock.  We expect block h to be written
2660  * shortly.  Ensure that all pages containing any part of the n hblks
2661  * starting at h are no longer protected.  If is_ptrfree is false,
2662  * also ensure that they will subsequently appear to be dirty.
2663  */
2664 void GC_remove_protection(h, nblocks, is_ptrfree)
2665 struct hblk *h;
2666 word nblocks;
2667 GC_bool is_ptrfree;
2668 {
2669     struct hblk * h_trunc;  /* Truncated to page boundary */
2670     struct hblk * h_end;    /* Page boundary following block end */
2671     struct hblk * current;
2672     GC_bool found_clean;
2673     
2674     if (!GC_dirty_maintained) return;
2675     h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1));
2676     h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size-1)
2677                             & ~(GC_page_size-1));
2678     found_clean = FALSE;
2679     for (current = h_trunc; current < h_end; ++current) {
2680         int index = PHT_HASH(current);
2681             
2682         if (!is_ptrfree || current < h || current >= h + nblocks) {
2683             async_set_pht_entry_from_index(GC_dirty_pages, index);
2684         }
2685     }
2686     UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc);
2687 }
2688 
2689 #if !defined(DARWIN)
2690 void GC_dirty_init()
2691 {
2692 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \
2693        defined(OSF1) || defined(HURD)
2694       struct sigaction  act, oldact;
2695       /* We should probably specify SA_SIGINFO for Linux, and handle    */
2696       /* the different architectures more uniformly.                    */
2697 #     if defined(IRIX5) || defined(LINUX) && !defined(X86_64) \
2698          || defined(OSF1) || defined(HURD)
2699         act.sa_flags    = SA_RESTART;
2700         act.sa_handler  = (SIG_PF)GC_write_fault_handler;
2701 #     else
2702         act.sa_flags    = SA_RESTART | SA_SIGINFO;
2703         act.sa_sigaction = GC_write_fault_handler;
2704 #     endif
2705       (void)sigemptyset(&act.sa_mask);
2706 #     ifdef SIG_SUSPEND
2707         /* Arrange to postpone SIG_SUSPEND while we're in a write fault */
2708         /* handler.  This effectively makes the handler atomic w.r.t.   */
2709         /* stopping the world for GC.                                   */
2710         (void)sigaddset(&act.sa_mask, SIG_SUSPEND);
2711 #     endif /* SIG_SUSPEND */
2712 #    endif
2713 #   ifdef PRINTSTATS
2714         GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n");
2715 #   endif
2716     GC_dirty_maintained = TRUE;
2717     if (GC_page_size % HBLKSIZE != 0) {
2718         GC_err_printf0("Page size not multiple of HBLKSIZE\n");
2719         ABORT("Page size not multiple of HBLKSIZE");
2720     }
2721 #   if defined(SUNOS4) || (defined(FREEBSD) && !defined(SUNOS5SIGS))
2722       GC_old_bus_handler = signal(SIGBUS, GC_write_fault_handler);
2723       if (GC_old_bus_handler == SIG_IGN) {
2724         GC_err_printf0("Previously ignored bus error!?");
2725         GC_old_bus_handler = SIG_DFL;
2726       }
2727       if (GC_old_bus_handler != SIG_DFL) {
2728 #       ifdef PRINTSTATS
2729           GC_err_printf0("Replaced other SIGBUS handler\n");
2730 #       endif
2731       }
2732 #   endif
2733 #   if defined(SUNOS4)
2734       GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler);
2735       if (GC_old_segv_handler == SIG_IGN) {
2736         GC_err_printf0("Previously ignored segmentation violation!?");
2737         GC_old_segv_handler = SIG_DFL;
2738       }
2739       if (GC_old_segv_handler != SIG_DFL) {
2740 #       ifdef PRINTSTATS
2741           GC_err_printf0("Replaced other SIGSEGV handler\n");
2742 #       endif
2743       }
2744 #   endif
2745 #   if (defined(SUNOS5SIGS) && !defined(FREEBSD)) || defined(IRIX5) \
2746        || defined(LINUX) || defined(OSF1) || defined(HURD)
2747       /* SUNOS5SIGS includes HPUX */
2748 #     if defined(GC_IRIX_THREADS)
2749         sigaction(SIGSEGV, 0, &oldact);
2750         sigaction(SIGSEGV, &act, 0);
2751 #     else 
2752         {
2753           int res = sigaction(SIGSEGV, &act, &oldact);
2754           if (res != 0) ABORT("Sigaction failed");
2755         }
2756 #     endif
2757 #     if defined(_sigargs) || defined(HURD) || !defined(SA_SIGINFO)
2758         /* This is Irix 5.x, not 6.x.  Irix 5.x does not have   */
2759         /* sa_sigaction.                                        */
2760         GC_old_segv_handler = oldact.sa_handler;
2761 #     else /* Irix 6.x or SUNOS5SIGS or LINUX */
2762         if (oldact.sa_flags & SA_SIGINFO) {
2763           GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction);
2764         } else {
2765           GC_old_segv_handler = oldact.sa_handler;
2766         }
2767 #     endif
2768       if (GC_old_segv_handler == SIG_IGN) {
2769              GC_err_printf0("Previously ignored segmentation violation!?");
2770              GC_old_segv_handler = SIG_DFL;
2771       }
2772       if (GC_old_segv_handler != SIG_DFL) {
2773 #       ifdef PRINTSTATS
2774           GC_err_printf0("Replaced other SIGSEGV handler\n");
2775 #       endif
2776       }
2777 #   endif /* (SUNOS5SIGS && !FREEBSD) || IRIX5 || LINUX || OSF1 || HURD */
2778 #   if defined(HPUX) || defined(LINUX) || defined(HURD) \
2779       || (defined(FREEBSD) && defined(SUNOS5SIGS))
2780       sigaction(SIGBUS, &act, &oldact);
2781       GC_old_bus_handler = oldact.sa_handler;
2782       if (GC_old_bus_handler == SIG_IGN) {
2783              GC_err_printf0("Previously ignored bus error!?");
2784              GC_old_bus_handler = SIG_DFL;
2785       }
2786       if (GC_old_bus_handler != SIG_DFL) {
2787 #       ifdef PRINTSTATS
2788           GC_err_printf0("Replaced other SIGBUS handler\n");
2789 #       endif
2790       }
2791 #   endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */
2792 #   if defined(MSWIN32)
2793       GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
2794       if (GC_old_segv_handler != NULL) {
2795 #       ifdef PRINTSTATS
2796           GC_err_printf0("Replaced other UnhandledExceptionFilter\n");
2797 #       endif
2798       } else {
2799           GC_old_segv_handler = SIG_DFL;
2800       }
2801 #   endif
2802 }
2803 #endif /* !DARWIN */
2804 
2805 int GC_incremental_protection_needs()
2806 {
2807     if (GC_page_size == HBLKSIZE) {
2808         return GC_PROTECTS_POINTER_HEAP;
2809     } else {
2810         return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP;
2811     }
2812 }
2813 
2814 #define HAVE_INCREMENTAL_PROTECTION_NEEDS
2815 
2816 #define IS_PTRFREE(hhdr) ((hhdr)->hb_descr == 0)
2817 
2818 #define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size - 1))
2819 void GC_protect_heap()
2820 {
2821     ptr_t start;
2822     word len;
2823     struct hblk * current;
2824     struct hblk * current_start;  /* Start of block to be protected. */
2825     struct hblk * limit;
2826     unsigned i;
2827     GC_bool protect_all = 
2828           (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP));
2829     for (i = 0; i < GC_n_heap_sects; i++) {
2830         start = GC_heap_sects[i].hs_start;
2831         len = GC_heap_sects[i].hs_bytes;
2832         if (protect_all) {
2833           PROTECT(start, len);
2834         } else {
2835           GC_ASSERT(PAGE_ALIGNED(len))
2836           GC_ASSERT(PAGE_ALIGNED(start))
2837           current_start = current = (struct hblk *)start;
2838           limit = (struct hblk *)(start + len);
2839           while (current < limit) {
2840             hdr * hhdr;
2841             word nhblks;
2842             GC_bool is_ptrfree;
2843 
2844             GC_ASSERT(PAGE_ALIGNED(current));
2845             GET_HDR(current, hhdr);
2846             if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
2847               /* This can happen only if we're at the beginning of a    */
2848               /* heap segment, and a block spans heap segments.         */
2849               /* We will handle that block as part of the preceding     */
2850               /* segment.                                               */
2851               GC_ASSERT(current_start == current);
2852               current_start = ++current;
2853               continue;
2854             }
2855             if (HBLK_IS_FREE(hhdr)) {
2856               GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz));
2857               nhblks = divHBLKSZ(hhdr -> hb_sz);
2858               is_ptrfree = TRUE;        /* dirty on alloc */
2859             } else {
2860               nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz);
2861               is_ptrfree = IS_PTRFREE(hhdr);
2862             }
2863             if (is_ptrfree) {
2864               if (current_start < current) {
2865                 PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
2866               }
2867               current_start = (current += nhblks);
2868             } else {
2869               current += nhblks;
2870             }
2871           } 
2872           if (current_start < current) {
2873             PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
2874           }
2875         }
2876     }
2877 }
2878 
2879 /* We assume that either the world is stopped or its OK to lose dirty   */
2880 /* bits while this is happenning (as in GC_enable_incremental).         */
2881 void GC_read_dirty()
2882 {
2883     BCOPY((word *)GC_dirty_pages, GC_grungy_pages,
2884           (sizeof GC_dirty_pages));
2885     BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages));
2886     GC_protect_heap();
2887 }
2888 
2889 GC_bool GC_page_was_dirty(h)
2890 struct hblk * h;
2891 {
2892     register word index = PHT_HASH(h);
2893     
2894     return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index));
2895 }
2896 
2897 /*
2898  * Acquiring the allocation lock here is dangerous, since this
2899  * can be called from within GC_call_with_alloc_lock, and the cord
2900  * package does so.  On systems that allow nested lock acquisition, this
2901  * happens to work.
2902  * On other systems, SET_LOCK_HOLDER and friends must be suitably defined.
2903  */
2904 
2905 static GC_bool syscall_acquired_lock = FALSE;   /* Protected by GC lock. */
2906  
2907 void GC_begin_syscall()
2908 {
2909     if (!I_HOLD_LOCK()) {
2910         LOCK();
2911         syscall_acquired_lock = TRUE;
2912     }
2913 }
2914 
2915 void GC_end_syscall()
2916 {
2917     if (syscall_acquired_lock) {
2918         syscall_acquired_lock = FALSE;
2919         UNLOCK();
2920     }
2921 }
2922 
2923 void GC_unprotect_range(addr, len)
2924 ptr_t addr;
2925 word len;
2926 {
2927     struct hblk * start_block;
2928     struct hblk * end_block;
2929     register struct hblk *h;
2930     ptr_t obj_start;
2931     
2932     if (!GC_dirty_maintained) return;
2933     obj_start = GC_base(addr);
2934     if (obj_start == 0) return;
2935     if (GC_base(addr + len - 1) != obj_start) {
2936         ABORT("GC_unprotect_range(range bigger than object)");
2937     }
2938     start_block = (struct hblk *)((word)addr & ~(GC_page_size - 1));
2939     end_block = (struct hblk *)((word)(addr + len - 1) & ~(GC_page_size - 1));
2940     end_block += GC_page_size/HBLKSIZE - 1;
2941     for (h = start_block; h <= end_block; h++) {
2942         register word index = PHT_HASH(h);
2943         
2944         async_set_pht_entry_from_index(GC_dirty_pages, index);
2945     }
2946     UNPROTECT(start_block,
2947               ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
2948 }
2949 
2950 #if 0
2951 
2952 /* We no longer wrap read by default, since that was causing too many   */
2953 /* problems.  It is preferred that the client instead avoids writing    */
2954 /* to the write-protected heap with a system call.                      */
2955 /* This still serves as sample code if you do want to wrap system calls.*/
2956 
2957 #if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_USE_LD_WRAP)
2958 /* Replacement for UNIX system call.                                      */
2959 /* Other calls that write to the heap should be handled similarly.        */
2960 /* Note that this doesn't work well for blocking reads:  It will hold     */
2961 /* the allocation lock for the entire duration of the call. Multithreaded */
2962 /* clients should really ensure that it won't block, either by setting    */
2963 /* the descriptor nonblocking, or by calling select or poll first, to     */
2964 /* make sure that input is available.                                     */
2965 /* Another, preferred alternative is to ensure that system calls never    */
2966 /* write to the protected heap (see above).                               */
2967 # if defined(__STDC__) && !defined(SUNOS4)
2968 #   include <unistd.h>
2969 #   include <sys/uio.h>
2970     ssize_t read(int fd, void *buf, size_t nbyte)
2971 # else
2972 #   ifndef LINT
2973       int read(fd, buf, nbyte)
2974 #   else
2975       int GC_read(fd, buf, nbyte)
2976 #   endif
2977     int fd;
2978     char *buf;
2979     int nbyte;
2980 # endif
2981 {
2982     int result;
2983     
2984     GC_begin_syscall();
2985     GC_unprotect_range(buf, (word)nbyte);
2986 #   if defined(IRIX5) || defined(GC_LINUX_THREADS)
2987         /* Indirect system call may not always be easily available.     */
2988         /* We could call _read, but that would interfere with the       */
2989         /* libpthread interception of read.                             */
2990         /* On Linux, we have to be careful with the linuxthreads        */
2991         /* read interception.                                           */
2992         {
2993             struct iovec iov;
2994 
2995             iov.iov_base = buf;
2996             iov.iov_len = nbyte;
2997             result = readv(fd, &iov, 1);
2998         }
2999 #   else
3000 #     if defined(HURD)  
3001         result = __read(fd, buf, nbyte);
3002 #     else
3003         /* The two zero args at the end of this list are because one
3004            IA-64 syscall() implementation actually requires six args
3005            to be passed, even though they aren't always used. */
3006         result = syscall(SYS_read, fd, buf, nbyte, 0, 0);
3007 #     endif /* !HURD */
3008 #   endif
3009     GC_end_syscall();
3010     return(result);
3011 }
3012 #endif /* !MSWIN32 && !MSWINCE && !GC_LINUX_THREADS */
3013 
3014 #if defined(GC_USE_LD_WRAP) && !defined(THREADS)
3015     /* We use the GNU ld call wrapping facility.                        */
3016     /* This requires that the linker be invoked with "--wrap read".     */
3017     /* This can be done by passing -Wl,"--wrap read" to gcc.            */
3018     /* I'm not sure that this actually wraps whatever version of read   */
3019     /* is called by stdio.  That code also mentions __read.             */
3020 #   include <unistd.h>
3021     ssize_t __wrap_read(int fd, void *buf, size_t nbyte)
3022     {
3023         int result;
3024 
3025         GC_begin_syscall();
3026         GC_unprotect_range(buf, (word)nbyte);
3027         result = __real_read(fd, buf, nbyte);
3028         GC_end_syscall();
3029         return(result);
3030     }
3031 
3032     /* We should probably also do this for __read, or whatever stdio    */
3033     /* actually calls.                                                  */
3034 #endif
3035 
3036 #endif /* 0 */
3037 
3038 /*ARGSUSED*/
3039 GC_bool GC_page_was_ever_dirty(h)
3040 struct hblk *h;
3041 {
3042     return(TRUE);
3043 }
3044 
3045 /* Reset the n pages starting at h to "was never dirty" status. */
3046 /*ARGSUSED*/
3047 void GC_is_fresh(h, n)
3048 struct hblk *h;
3049 word n;
3050 {
3051 }
3052 
3053 # endif /* MPROTECT_VDB */
3054 
3055 # ifdef PROC_VDB
3056 
3057 /*
3058  * See DEFAULT_VDB for interface descriptions.
3059  */
3060  
3061 /*
3062  * This implementaion assumes a Solaris 2.X like /proc pseudo-file-system
3063  * from which we can read page modified bits.  This facility is far from
3064  * optimal (e.g. we would like to get the info for only some of the
3065  * address space), but it avoids intercepting system calls.
3066  */
3067 
3068 #include <errno.h>
3069 #include <sys/types.h>
3070 #include <sys/signal.h>
3071 #include <sys/fault.h>
3072 #include <sys/syscall.h>
3073 #include <sys/procfs.h>
3074 #include <sys/stat.h>
3075 
3076 #define INITIAL_BUF_SZ 16384
3077 word GC_proc_buf_size = INITIAL_BUF_SZ;
3078 char *GC_proc_buf;
3079 
3080 #ifdef GC_SOLARIS_THREADS
3081 /* We don't have exact sp values for threads.  So we count on   */
3082 /* occasionally declaring stack pages to be fresh.  Thus we     */
3083 /* need a real implementation of GC_is_fresh.  We can't clear   */
3084 /* entries in GC_written_pages, since that would declare all    */
3085 /* pages with the given hash address to be fresh.               */
3086 #   define MAX_FRESH_PAGES 8*1024       /* Must be power of 2 */
3087     struct hblk ** GC_fresh_pages;      /* A direct mapped cache.       */
3088                                         /* Collisions are dropped.      */
3089 
3090 #   define FRESH_PAGE_SLOT(h) (divHBLKSZ((word)(h)) & (MAX_FRESH_PAGES-1))
3091 #   define ADD_FRESH_PAGE(h) \
3092         GC_fresh_pages[FRESH_PAGE_SLOT(h)] = (h)
3093 #   define PAGE_IS_FRESH(h) \
3094         (GC_fresh_pages[FRESH_PAGE_SLOT(h)] == (h) && (h) != 0)
3095 #endif
3096 
3097 /* Add all pages in pht2 to pht1 */
3098 void GC_or_pages(pht1, pht2)
3099 page_hash_table pht1, pht2;
3100 {
3101     register int i;
3102     
3103     for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i];
3104 }
3105 
3106 int GC_proc_fd;
3107 
3108 void GC_dirty_init()
3109 {
3110     int fd;
3111     char buf[30];
3112 
3113     GC_dirty_maintained = TRUE;
3114     if (GC_words_allocd != 0 || GC_words_allocd_before_gc != 0) {
3115         register int i;
3116     
3117         for (i = 0; i < PHT_SIZE; i++) GC_written_pages[i] = (word)(-1);
3118 #       ifdef PRINTSTATS
3119             GC_printf1("Allocated words:%lu:all pages may have been written\n",
3120                        (unsigned long)
3121                                 (GC_words_allocd + GC_words_allocd_before_gc));
3122 #       endif       
3123     }
3124     sprintf(buf, "/proc/%d", getpid());
3125     fd = open(buf, O_RDONLY);
3126     if (fd < 0) {
3127         ABORT("/proc open failed");
3128     }
3129     GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0);
3130     close(fd);
3131     syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC);
3132     if (GC_proc_fd < 0) {
3133         ABORT("/proc ioctl failed");
3134     }
3135     GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size);
3136 #   ifdef GC_SOLARIS_THREADS
3137         GC_fresh_pages = (struct hblk **)
3138           GC_scratch_alloc(MAX_FRESH_PAGES * sizeof (struct hblk *));
3139         if (GC_fresh_pages == 0) {
3140             GC_err_printf0("No space for fresh pages\n");
3141             EXIT();
3142         }
3143         BZERO(GC_fresh_pages, MAX_FRESH_PAGES * sizeof (struct hblk *));
3144 #   endif
3145 }
3146 
3147 /* Ignore write hints. They don't help us here. */
3148 /*ARGSUSED*/
3149 void GC_remove_protection(h, nblocks, is_ptrfree)
3150 struct hblk *h;
3151 word nblocks;
3152 GC_bool is_ptrfree;
3153 {
3154 }
3155 
3156 #ifdef GC_SOLARIS_THREADS
3157 #   define READ(fd,buf,nbytes) syscall(SYS_read, fd, buf, nbytes)
3158 #else
3159 #   define READ(fd,buf,nbytes) read(fd, buf, nbytes)
3160 #endif
3161 
3162 void GC_read_dirty()
3163 {
3164     unsigned long ps, np;
3165     int nmaps;
3166     ptr_t vaddr;
3167     struct prasmap * map;
3168     char * bufp;
3169     ptr_t current_addr, limit;
3170     int i;
3171 int dummy;
3172 
3173     BZERO(GC_grungy_pages, (sizeof GC_grungy_pages));
3174     
3175     bufp = GC_proc_buf;
3176     if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
3177 #       ifdef PRINTSTATS
3178             GC_printf1("/proc read failed: GC_proc_buf_size = %lu\n",
3179                        GC_proc_buf_size);
3180 #       endif       
3181         {
3182             /* Retry with larger buffer. */
3183             word new_size = 2 * GC_proc_buf_size;
3184             char * new_buf = GC_scratch_alloc(new_size);
3185             
3186             if (new_buf != 0) {
3187                 GC_proc_buf = bufp = new_buf;
3188                 GC_proc_buf_size = new_size;
3189             }
3190             if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
3191                 WARN("Insufficient space for /proc read\n", 0);
3192                 /* Punt:        */
3193                 memset(GC_grungy_pages, 0xff, sizeof (page_hash_table));
3194                 memset(GC_written_pages, 0xff, sizeof(page_hash_table));
3195 #               ifdef GC_SOLARIS_THREADS
3196                     BZERO(GC_fresh_pages,
3197                           MAX_FRESH_PAGES * sizeof (struct hblk *)); 
3198 #               endif
3199                 return;
3200             }
3201         }
3202     }
3203     /* Copy dirty bits into GC_grungy_pages */
3204         nmaps = ((struct prpageheader *)bufp) -> pr_nmap;
3205         /* printf( "nmaps = %d, PG_REFERENCED = %d, PG_MODIFIED = %d\n",
3206                      nmaps, PG_REFERENCED, PG_MODIFIED); */
3207         bufp = bufp + sizeof(struct prpageheader);
3208         for (i = 0; i < nmaps; i++) {
3209             map = (struct prasmap *)bufp;
3210             vaddr = (ptr_t)(map -> pr_vaddr);
3211             ps = map -> pr_pagesize;
3212             np = map -> pr_npage;
3213             /* printf("vaddr = 0x%X, ps = 0x%X, np = 0x%X\n", vaddr, ps, np); */
3214             limit = vaddr + ps * np;
3215             bufp += sizeof (struct prasmap);
3216             for (current_addr = vaddr;
3217                  current_addr < limit; current_addr += ps){
3218                 if ((*bufp++) & PG_MODIFIED) {
3219                     register struct hblk * h = (struct hblk *) current_addr;
3220                     
3221                     while ((ptr_t)h < current_addr + ps) {
3222                         register word index = PHT_HASH(h);
3223                         
3224                         set_pht_entry_from_index(GC_grungy_pages, index);
3225 #                       ifdef GC_SOLARIS_THREADS
3226                           {
3227                             register int slot = FRESH_PAGE_SLOT(h);
3228                             
3229                             if (GC_fresh_pages[slot] == h) {
3230                                 GC_fresh_pages[slot] = 0;
3231                             }
3232                           }
3233 #                       endif
3234                         h++;
3235                     }
3236                 }
3237             }
3238             bufp += sizeof(long) - 1;
3239             bufp = (char *)((unsigned long)bufp & ~(sizeof(long)-1));
3240         }
3241     /* Update GC_written_pages. */
3242         GC_or_pages(GC_written_pages, GC_grungy_pages);
3243 #   ifdef GC_SOLARIS_THREADS
3244       /* Make sure that old stacks are considered completely clean      */
3245       /* unless written again.                                          */
3246         GC_old_stacks_are_fresh();
3247 #   endif
3248 }
3249 
3250 #undef READ
3251 
3252 GC_bool GC_page_was_dirty(h)
3253 struct hblk *h;
3254 {
3255     register word index = PHT_HASH(h);
3256     register GC_bool result;
3257     
3258     result = get_pht_entry_from_index(GC_grungy_pages, index);
3259 #   ifdef GC_SOLARIS_THREADS
3260         if (result && PAGE_IS_FRESH(h)) result = FALSE;
3261         /* This happens only if page was declared fresh since   */
3262         /* the read_dirty call, e.g. because it's in an unused  */
3263         /* thread stack.  It's OK to treat it as clean, in      */
3264         /* that case.  And it's consistent with                 */
3265         /* GC_page_was_ever_dirty.                              */
3266 #   endif
3267     return(result);
3268 }
3269 
3270 GC_bool GC_page_was_ever_dirty(h)
3271 struct hblk *h;
3272 {
3273     register word index = PHT_HASH(h);
3274     register GC_bool result;
3275     
3276     result = get_pht_entry_from_index(GC_written_pages, index);
3277 #   ifdef GC_SOLARIS_THREADS
3278         if (result && PAGE_IS_FRESH(h)) result = FALSE;
3279 #   endif
3280     return(result);
3281 }
3282 
3283 /* Caller holds allocation lock.        */
3284 void GC_is_fresh(h, n)
3285 struct hblk *h;
3286 word n;
3287 {
3288 
3289     register word index;
3290     
3291 #   ifdef GC_SOLARIS_THREADS
3292       register word i;
3293       
3294       if (GC_fresh_pages != 0) {
3295         for (i = 0; i < n; i++) {
3296           ADD_FRESH_PAGE(h + i);
3297         }
3298       }
3299 #   endif
3300 }
3301 
3302 # endif /* PROC_VDB */
3303 
3304 
3305 # ifdef PCR_VDB
3306 
3307 # include "vd/PCR_VD.h"
3308 
3309 # define NPAGES (32*1024)       /* 128 MB */
3310 
3311 PCR_VD_DB  GC_grungy_bits[NPAGES];
3312 
3313 ptr_t GC_vd_base;       /* Address corresponding to GC_grungy_bits[0]   */
3314                         /* HBLKSIZE aligned.                            */
3315 
3316 void GC_dirty_init()
3317 {
3318     GC_dirty_maintained = TRUE;
3319     /* For the time being, we assume the heap generally grows up */
3320     GC_vd_base = GC_heap_sects[0].hs_start;
3321     if (GC_vd_base == 0) {
3322         ABORT("Bad initial heap segment");
3323     }
3324     if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE)
3325         != PCR_ERes_okay) {
3326         ABORT("dirty bit initialization failed");
3327     }
3328 }
3329 
3330 void GC_read_dirty()
3331 {
3332     /* lazily enable dirty bits on newly added heap sects */
3333     {
3334         static int onhs = 0;
3335         int nhs = GC_n_heap_sects;
3336         for( ; onhs < nhs; onhs++ ) {
3337             PCR_VD_WriteProtectEnable(
3338                     GC_heap_sects[onhs].hs_start,
3339                     GC_heap_sects[onhs].hs_bytes );
3340         }
3341     }
3342 
3343 
3344     if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits)
3345         != PCR_ERes_okay) {
3346         ABORT("dirty bit read failed");
3347     }
3348 }
3349 
3350 GC_bool GC_page_was_dirty(h)
3351 struct hblk *h;
3352 {
3353     if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) {
3354         return(TRUE);
3355     }
3356     return(GC_grungy_bits[h - (struct hblk *)GC_vd_base] & PCR_VD_DB_dirtyBit);
3357 }
3358 
3359 /*ARGSUSED*/
3360 void GC_remove_protection(h, nblocks, is_ptrfree)
3361 struct hblk *h;
3362 word nblocks;
3363 GC_bool is_ptrfree;
3364 {
3365     PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE);
3366     PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE);
3367 }
3368 
3369 # endif /* PCR_VDB */
3370 
3371 #if defined(MPROTECT_VDB) && defined(DARWIN)
3372 /* The following sources were used as a *reference* for this exception handling
3373    code:
3374       1. Apple's mach/xnu documentation
3375       2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
3376          omnigroup's macosx-dev list. 
3377          www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html
3378       3. macosx-nat.c from Apple's GDB source code.
3379 */
3380    
3381 /* The bug that caused all this trouble should now be fixed. This should
3382    eventually be removed if all goes well. */
3383 /* define BROKEN_EXCEPTION_HANDLING */
3384     
3385 #include <mach/mach.h>
3386 #include <mach/mach_error.h>
3387 #include <mach/thread_status.h>
3388 #include <mach/exception.h>
3389 #include <mach/task.h>
3390 #include <pthread.h>
3391 
3392 /* These are not defined in any header, although they are documented */
3393 extern boolean_t exc_server(mach_msg_header_t *,mach_msg_header_t *);
3394 extern kern_return_t exception_raise(
3395     mach_port_t,mach_port_t,mach_port_t,
3396     exception_type_t,exception_data_t,mach_msg_type_number_t);
3397 extern kern_return_t exception_raise_state(
3398     mach_port_t,mach_port_t,mach_port_t,
3399     exception_type_t,exception_data_t,mach_msg_type_number_t,
3400     thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
3401     thread_state_t,mach_msg_type_number_t*);
3402 extern kern_return_t exception_raise_state_identity(
3403     mach_port_t,mach_port_t,mach_port_t,
3404     exception_type_t,exception_data_t,mach_msg_type_number_t,
3405     thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
3406     thread_state_t,mach_msg_type_number_t*);
3407 
3408 
3409 #define MAX_EXCEPTION_PORTS 16
3410 
3411 static struct {
3412     mach_msg_type_number_t count;
3413     exception_mask_t      masks[MAX_EXCEPTION_PORTS];
3414     exception_handler_t   ports[MAX_EXCEPTION_PORTS];
3415     exception_behavior_t  behaviors[MAX_EXCEPTION_PORTS];
3416     thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS];
3417 } GC_old_exc_ports;
3418 
3419 static struct {
3420     mach_port_t exception;
3421 #if defined(THREADS)
3422     mach_port_t reply;
3423 #endif
3424 } GC_ports;
3425 
3426 typedef struct {
3427     mach_msg_header_t head;
3428 } GC_msg_t;
3429 
3430 typedef enum {
3431     GC_MP_NORMAL, GC_MP_DISCARDING, GC_MP_STOPPED
3432 } GC_mprotect_state_t;
3433 
3434 /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field,
3435    but it isn't  documented. Use the source and see if they
3436    should be ok. */
3437 #define ID_STOP 1
3438 #define ID_RESUME 2
3439 
3440 /* These values are only used on the reply port */
3441 #define ID_ACK 3
3442 
3443 #if defined(THREADS)
3444 
3445 GC_mprotect_state_t GC_mprotect_state;
3446 
3447 /* The following should ONLY be called when the world is stopped  */
3448 static void GC_mprotect_thread_notify(mach_msg_id_t id) {
3449     struct {
3450         GC_msg_t msg;
3451         mach_msg_trailer_t trailer;
3452     } buf;
3453     mach_msg_return_t r;
3454     /* remote, local */
3455     buf.msg.head.msgh_bits = 
3456         MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0);
3457     buf.msg.head.msgh_size = sizeof(buf.msg);
3458     buf.msg.head.msgh_remote_port = GC_ports.exception;
3459     buf.msg.head.msgh_local_port = MACH_PORT_NULL;
3460     buf.msg.head.msgh_id = id;
3461             
3462     r = mach_msg(
3463         &buf.msg.head,
3464         MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_LARGE,
3465         sizeof(buf.msg),
3466         sizeof(buf),
3467         GC_ports.reply,
3468         MACH_MSG_TIMEOUT_NONE,
3469         MACH_PORT_NULL);
3470     if(r != MACH_MSG_SUCCESS)
3471         ABORT("mach_msg failed in GC_mprotect_thread_notify");
3472     if(buf.msg.head.msgh_id != ID_ACK)
3473         ABORT("invalid ack in GC_mprotect_thread_notify");
3474 }
3475 
3476 /* Should only be called by the mprotect thread */
3477 static void GC_mprotect_thread_reply() {
3478     GC_msg_t msg;
3479     mach_msg_return_t r;
3480     /* remote, local */
3481     msg.head.msgh_bits = 
3482         MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0);
3483     msg.head.msgh_size = sizeof(msg);
3484     msg.head.msgh_remote_port = GC_ports.reply;
3485     msg.head.msgh_local_port = MACH_PORT_NULL;
3486     msg.head.msgh_id = ID_ACK;
3487             
3488     r = mach_msg(
3489         &msg.head,
3490         MACH_SEND_MSG,
3491         sizeof(msg),
3492         0,
3493         MACH_PORT_NULL,
3494         MACH_MSG_TIMEOUT_NONE,
3495         MACH_PORT_NULL);
3496     if(r != MACH_MSG_SUCCESS)
3497         ABORT("mach_msg failed in GC_mprotect_thread_reply");
3498 }
3499 
3500 void GC_mprotect_stop() {
3501     GC_mprotect_thread_notify(ID_STOP);
3502 }
3503 void GC_mprotect_resume() {
3504     GC_mprotect_thread_notify(ID_RESUME);
3505 }
3506 
3507 #else /* !THREADS */
3508 /* The compiler should optimize away any GC_mprotect_state computations */
3509 #define GC_mprotect_state GC_MP_NORMAL
3510 #endif
3511 
3512 static void *GC_mprotect_thread(void *arg) {
3513     mach_msg_return_t r;
3514     /* These two structures contain some private kernel data. We don't need to
3515        access any of it so we don't bother defining a proper struct. The
3516        correct definitions are in the xnu source code. */
3517     struct {
3518         mach_msg_header_t head;
3519         char data[256];
3520     } reply;
3521     struct {
3522         mach_msg_header_t head;
3523         mach_msg_body_t msgh_body;
3524         char data[1024];
3525     } msg;
3526 
3527     mach_msg_id_t id;
3528 
3529     GC_darwin_register_mach_handler_thread(mach_thread_self());
3530     
3531     for(;;) {
3532         r = mach_msg(
3533             &msg.head,
3534             MACH_RCV_MSG|MACH_RCV_LARGE|
3535                 (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0),
3536             0,
3537             sizeof(msg),
3538             GC_ports.exception,
3539             GC_mprotect_state == GC_MP_DISCARDING ? 0 : MACH_MSG_TIMEOUT_NONE,
3540             MACH_PORT_NULL);
3541         
3542         id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1;
3543         
3544 #if defined(THREADS)
3545         if(GC_mprotect_state == GC_MP_DISCARDING) {
3546             if(r == MACH_RCV_TIMED_OUT) {
3547                 GC_mprotect_state = GC_MP_STOPPED;
3548                 GC_mprotect_thread_reply();
3549                 continue;
3550             }
3551             if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME))
3552                 ABORT("out of order mprotect thread request");
3553         }
3554 #endif
3555         
3556         if(r != MACH_MSG_SUCCESS) {
3557             GC_err_printf2("mach_msg failed with %d %s\n", 
3558                 (int)r,mach_error_string(r));
3559             ABORT("mach_msg failed");
3560         }
3561         
3562         switch(id) {
3563 #if defined(THREADS)
3564             case ID_STOP:
3565                 if(GC_mprotect_state != GC_MP_NORMAL)
3566                     ABORT("Called mprotect_stop when state wasn't normal");
3567                 GC_mprotect_state = GC_MP_DISCARDING;
3568                 break;
3569             case ID_RESUME:
3570                 if(GC_mprotect_state != GC_MP_STOPPED)
3571                     ABORT("Called mprotect_resume when state wasn't stopped");
3572                 GC_mprotect_state = GC_MP_NORMAL;
3573                 GC_mprotect_thread_reply();
3574                 break;
3575 #endif /* THREADS */
3576             default:
3577                     /* Handle the message (calls catch_exception_raise) */
3578                 if(!exc_server(&msg.head,&reply.head))
3579                     ABORT("exc_server failed");
3580                 /* Send the reply */
3581                 r = mach_msg(
3582                     &reply.head,
3583                     MACH_SEND_MSG,
3584                     reply.head.msgh_size,
3585                     0,
3586                     MACH_PORT_NULL,
3587                     MACH_MSG_TIMEOUT_NONE,
3588                     MACH_PORT_NULL);
3589                 if(r != MACH_MSG_SUCCESS) {
3590                         /* This will fail if the thread dies, but the thread shouldn't
3591                            die... */
3592                         #ifdef BROKEN_EXCEPTION_HANDLING
3593                         GC_err_printf2(
3594                         "mach_msg failed with %d %s while sending exc reply\n",
3595                         (int)r,mach_error_string(r));
3596                 #else
3597                         ABORT("mach_msg failed while sending exception reply");
3598                 #endif
3599                 }
3600         } /* switch */
3601     } /* for(;;) */
3602     /* NOT REACHED */
3603     return NULL;
3604 }
3605 
3606 /* All this SIGBUS code shouldn't be necessary. All protection faults should
3607    be going throught the mach exception handler. However, it seems a SIGBUS is
3608    occasionally sent for some unknown reason. Even more odd, it seems to be
3609    meaningless and safe to ignore. */
3610 #ifdef BROKEN_EXCEPTION_HANDLING
3611 
3612 typedef void (* SIG_PF)();
3613 static SIG_PF GC_old_bus_handler;
3614 
3615 /* Updates to this aren't atomic, but the SIGBUSs seem pretty rare.
3616    Even if this doesn't get updated property, it isn't really a problem */
3617 static int GC_sigbus_count;
3618 
3619 static void GC_darwin_sigbus(int num,siginfo_t *sip,void *context) {
3620     if(num != SIGBUS) ABORT("Got a non-sigbus signal in the sigbus handler");
3621     
3622     /* Ugh... some seem safe to ignore, but too many in a row probably means
3623        trouble. GC_sigbus_count is reset for each mach exception that is
3624        handled */
3625     if(GC_sigbus_count >= 8) {
3626         ABORT("Got more than 8 SIGBUSs in a row!");
3627     } else {
3628         GC_sigbus_count++;
3629         GC_err_printf0("GC: WARNING: Ignoring SIGBUS.\n");
3630     }
3631 }
3632 #endif /* BROKEN_EXCEPTION_HANDLING */
3633 
3634 void GC_dirty_init() {
3635     kern_return_t r;
3636     mach_port_t me;
3637     pthread_t thread;
3638     pthread_attr_t attr;
3639     exception_mask_t mask;
3640     
3641 #   ifdef PRINTSTATS
3642         GC_printf0("Inititalizing mach/darwin mprotect virtual dirty bit "
3643             "implementation\n");
3644 #   endif  
3645 #       ifdef BROKEN_EXCEPTION_HANDLING
3646         GC_err_printf0("GC: WARNING: Enabling workarounds for various darwin "
3647             "exception handling bugs.\n");
3648 #       endif
3649     GC_dirty_maintained = TRUE;
3650     if (GC_page_size % HBLKSIZE != 0) {
3651         GC_err_printf0("Page size not multiple of HBLKSIZE\n");
3652         ABORT("Page size not multiple of HBLKSIZE");
3653     }
3654     
3655     GC_task_self = me = mach_task_self();
3656     
3657     r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.exception);
3658     if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (exception port)");
3659     
3660     r = mach_port_insert_right(me,GC_ports.exception,GC_ports.exception,
3661         MACH_MSG_TYPE_MAKE_SEND);
3662     if(r != KERN_SUCCESS)
3663         ABORT("mach_port_insert_right failed (exception port)");
3664 
3665     #if defined(THREADS)
3666         r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.reply);
3667         if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (reply port)");
3668     #endif
3669 
3670     /* The exceptions we want to catch */  
3671     mask = EXC_MASK_BAD_ACCESS;
3672 
3673     r = task_get_exception_ports(
3674         me,
3675         mask,
3676         GC_old_exc_ports.masks,
3677         &GC_old_exc_ports.count,
3678         GC_old_exc_ports.ports,
3679         GC_old_exc_ports.behaviors,
3680         GC_old_exc_ports.flavors
3681     );
3682     if(r != KERN_SUCCESS) ABORT("task_get_exception_ports failed");
3683         
3684     r = task_set_exception_ports(
3685         me,
3686         mask,
3687         GC_ports.exception,
3688         EXCEPTION_DEFAULT,
3689         MACHINE_THREAD_STATE
3690     );
3691     if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed");
3692 
3693     if(pthread_attr_init(&attr) != 0) ABORT("pthread_attr_init failed");
3694     if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) != 0) 
3695         ABORT("pthread_attr_setdetachedstate failed");
3696 
3697 #       undef pthread_create
3698     /* This will call the real pthread function, not our wrapper */
3699     if(pthread_create(&thread,&attr,GC_mprotect_thread,NULL) != 0)
3700         ABORT("pthread_create failed");
3701     pthread_attr_destroy(&attr);
3702     
3703     /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */
3704     #ifdef BROKEN_EXCEPTION_HANDLING 
3705     {
3706         struct sigaction sa, oldsa;
3707         sa.sa_handler = (SIG_PF)GC_darwin_sigbus;
3708         sigemptyset(&sa.sa_mask);
3709         sa.sa_flags = SA_RESTART|SA_SIGINFO;
3710         if(sigaction(SIGBUS,&sa,&oldsa) < 0) ABORT("sigaction");
3711         GC_old_bus_handler = (SIG_PF)oldsa.sa_handler;
3712         if (GC_old_bus_handler != SIG_DFL) {
3713 #               ifdef PRINTSTATS
3714                 GC_err_printf0("Replaced other SIGBUS handler\n");
3715 #               endif
3716         }
3717     }
3718     #endif /* BROKEN_EXCEPTION_HANDLING  */
3719 }
3720  
3721 /* The source code for Apple's GDB was used as a reference for the exception
3722    forwarding code. This code is similar to be GDB code only because there is 
3723    only one way to do it. */
3724 static kern_return_t GC_forward_exception(
3725         mach_port_t thread,
3726         mach_port_t task,
3727         exception_type_t exception,
3728         exception_data_t data,
3729         mach_msg_type_number_t data_count
3730 ) {
3731     int i;
3732     kern_return_t r;
3733     mach_port_t port;
3734     exception_behavior_t behavior;
3735     thread_state_flavor_t flavor;
3736     
3737     thread_state_t thread_state;
3738     mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
3739         
3740     for(i=0;i<GC_old_exc_ports.count;i++)
3741         if(GC_old_exc_ports.masks[i] & (1 << exception))
3742             break;
3743     if(i==GC_old_exc_ports.count) ABORT("No handler for exception!");
3744     
3745     port = GC_old_exc_ports.ports[i];
3746     behavior = GC_old_exc_ports.behaviors[i];
3747     flavor = GC_old_exc_ports.flavors[i];
3748 
3749     if(behavior != EXCEPTION_DEFAULT) {
3750         r = thread_get_state(thread,flavor,thread_state,&thread_state_count);
3751         if(r != KERN_SUCCESS)
3752             ABORT("thread_get_state failed in forward_exception");
3753     }
3754     
3755     switch(behavior) {
3756         case EXCEPTION_DEFAULT:
3757             r = exception_raise(port,thread,task,exception,data,data_count);
3758             break;
3759         case EXCEPTION_STATE:
3760             r = exception_raise_state(port,thread,task,exception,data,
3761                 data_count,&flavor,thread_state,thread_state_count,
3762                 thread_state,&thread_state_count);
3763             break;
3764         case EXCEPTION_STATE_IDENTITY:
3765             r = exception_raise_state_identity(port,thread,task,exception,data,
3766                 data_count,&flavor,thread_state,thread_state_count,
3767                 thread_state,&thread_state_count);
3768             break;
3769         default:
3770             r = KERN_FAILURE; /* make gcc happy */
3771             ABORT("forward_exception: unknown behavior");
3772             break;
3773     }
3774     
3775     if(behavior != EXCEPTION_DEFAULT) {
3776         r = thread_set_state(thread,flavor,thread_state,thread_state_count);
3777         if(r != KERN_SUCCESS)
3778             ABORT("thread_set_state failed in forward_exception");
3779     }
3780     
3781     return r;
3782 }
3783 
3784 #define FWD() GC_forward_exception(thread,task,exception,code,code_count)
3785 
3786 /* This violates the namespace rules but there isn't anything that can be done
3787    about it. The exception handling stuff is hard coded to call this */
3788 kern_return_t
3789 catch_exception_raise(
3790    mach_port_t exception_port,mach_port_t thread,mach_port_t task,
3791    exception_type_t exception,exception_data_t code,
3792    mach_msg_type_number_t code_count
3793 ) {
3794     kern_return_t r;
3795     char *addr;
3796     struct hblk *h;
3797     int i;
3798 #   if defined(POWERPC)
3799 #     if CPP_WORDSZ == 32
3800         thread_state_flavor_t flavor = PPC_EXCEPTION_STATE;
3801         mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE_COUNT;
3802         ppc_exception_state_t exc_state;
3803 #     else
3804         thread_state_flavor_t flavor = PPC_EXCEPTION_STATE64;
3805         mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE64_COUNT;
3806         ppc_exception_state64_t exc_state;
3807 #     endif
3808 #   else
3809 #       error FIXME for non-ppc darwin
3810 #   endif
3811 
3812     
3813     if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) {
3814         #ifdef DEBUG_EXCEPTION_HANDLING
3815         /* We aren't interested, pass it on to the old handler */
3816         GC_printf3("Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
3817             exception,
3818             code_count > 0 ? code[0] : -1,
3819             code_count > 1 ? code[1] : -1); 
3820         #endif
3821         return FWD();
3822     }
3823 
3824     r = thread_get_state(thread,flavor,
3825         (natural_t*)&exc_state,&exc_state_count);
3826     if(r != KERN_SUCCESS) {
3827         /* The thread is supposed to be suspended while the exception handler
3828            is called. This shouldn't fail. */
3829         #ifdef BROKEN_EXCEPTION_HANDLING
3830             GC_err_printf0("thread_get_state failed in "
3831                 "catch_exception_raise\n");
3832             return KERN_SUCCESS;
3833         #else
3834             ABORT("thread_get_state failed in catch_exception_raise");
3835         #endif
3836     }
3837     
3838     /* This is the address that caused the fault */
3839     addr = (char*) exc_state.dar;
3840         
3841     if((HDR(addr)) == 0) {
3842         /* Ugh... just like the SIGBUS problem above, it seems we get a bogus 
3843            KERN_PROTECTION_FAILURE every once and a while. We wait till we get
3844            a bunch in a row before doing anything about it. If a "real" fault 
3845            ever occurres it'll just keep faulting over and over and we'll hit
3846            the limit pretty quickly. */
3847         #ifdef BROKEN_EXCEPTION_HANDLING
3848             static char *last_fault;
3849             static int last_fault_count;
3850             
3851             if(addr != last_fault) {
3852                 last_fault = addr;
3853                 last_fault_count = 0;
3854             }
3855             if(++last_fault_count < 32) {
3856                 if(last_fault_count == 1)
3857                     GC_err_printf1(
3858                         "GC: WARNING: Ignoring KERN_PROTECTION_FAILURE at %p\n",
3859                         addr);
3860                 return KERN_SUCCESS;
3861             }
3862             
3863             GC_err_printf1("Unexpected KERN_PROTECTION_FAILURE at %p\n",addr);
3864             /* Can't pass it along to the signal handler because that is
3865                ignoring SIGBUS signals. We also shouldn't call ABORT here as
3866                signals don't always work too well from the exception handler. */
3867             GC_err_printf0("Aborting\n");
3868             exit(EXIT_FAILURE);
3869         #else /* BROKEN_EXCEPTION_HANDLING */
3870             /* Pass it along to the next exception handler 
3871                (which should call SIGBUS/SIGSEGV) */
3872             return FWD();
3873         #endif /* !BROKEN_EXCEPTION_HANDLING */
3874     }
3875 
3876     #ifdef BROKEN_EXCEPTION_HANDLING
3877         /* Reset the number of consecutive SIGBUSs */
3878         GC_sigbus_count = 0;
3879     #endif
3880     
3881     if(GC_mprotect_state == GC_MP_NORMAL) { /* common case */
3882         h = (struct hblk*)((word)addr & ~(GC_page_size-1));
3883         UNPROTECT(h, GC_page_size);     
3884         for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
3885             register int index = PHT_HASH(h+i);
3886             async_set_pht_entry_from_index(GC_dirty_pages, index);
3887         }
3888     } else if(GC_mprotect_state == GC_MP_DISCARDING) {
3889         /* Lie to the thread for now. No sense UNPROTECT()ing the memory
3890            when we're just going to PROTECT() it again later. The thread
3891            will just fault again once it resumes */
3892     } else {
3893         /* Shouldn't happen, i don't think */
3894         GC_printf0("KERN_PROTECTION_FAILURE while world is stopped\n");
3895         return FWD();
3896     }
3897     return KERN_SUCCESS;
3898 }
3899 #undef FWD
3900 
3901 /* These should never be called, but just in case...  */
3902 kern_return_t catch_exception_raise_state(mach_port_name_t exception_port,
3903     int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
3904     int flavor, thread_state_t old_state, int old_stateCnt,
3905     thread_state_t new_state, int new_stateCnt)
3906 {
3907     ABORT("catch_exception_raise_state");
3908     return(KERN_INVALID_ARGUMENT);
3909 }
3910 kern_return_t catch_exception_raise_state_identity(
3911     mach_port_name_t exception_port, mach_port_t thread, mach_port_t task,
3912     int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
3913     int flavor, thread_state_t old_state, int old_stateCnt, 
3914     thread_state_t new_state, int new_stateCnt)
3915 {
3916     ABORT("catch_exception_raise_state_identity");
3917     return(KERN_INVALID_ARGUMENT);
3918 }
3919 
3920 
3921 #endif /* DARWIN && MPROTECT_VDB */
3922 
3923 # ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS
3924   int GC_incremental_protection_needs()
3925   {
3926     return GC_PROTECTS_NONE;
3927   }
3928 # endif /* !HAVE_INCREMENTAL_PROTECTION_NEEDS */
3929 
3930 /*
3931  * Call stack save code for debugging.
3932  * Should probably be in mach_dep.c, but that requires reorganization.
3933  */
3934 
3935 /* I suspect the following works for most X86 *nix variants, so         */
3936 /* long as the frame pointer is explicitly stored.  In the case of gcc, */
3937 /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is.  */
3938 #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)
3939 #   include <features.h>
3940 
3941     struct frame {
3942         struct frame *fr_savfp;
3943         long    fr_savpc;
3944         long    fr_arg[NARGS];  /* All the arguments go here.   */
3945     };
3946 #endif
3947 
3948 #if defined(SPARC)
3949 #  if defined(LINUX)
3950 #    include <features.h>
3951 
3952      struct frame {
3953         long    fr_local[8];
3954         long    fr_arg[6];
3955         struct frame *fr_savfp;
3956         long    fr_savpc;
3957 #       ifndef __arch64__
3958           char  *fr_stret;
3959 #       endif
3960         long    fr_argd[6];
3961         long    fr_argx[0];
3962      };
3963 #  else
3964 #    if defined(SUNOS4)
3965 #      include <machine/frame.h>
3966 #    else
3967 #      if defined (DRSNX)
3968 #        include <sys/sparc/frame.h>
3969 #      else
3970 #        if defined(OPENBSD)
3971 #          include <frame.h>
3972 #        else
3973 #          if defined(FREEBSD) || defined(NETBSD)
3974 #            include <machine/frame.h>
3975 #          else
3976 #            include <sys/frame.h>
3977 #          endif
3978 #        endif
3979 #      endif
3980 #    endif
3981 #  endif
3982 #  if NARGS > 6
3983         --> We only know how to to get the first 6 arguments
3984 #  endif
3985 #endif /* SPARC */
3986 
3987 #ifdef  NEED_CALLINFO
3988 /* Fill in the pc and argument information for up to NFRAMES of my      */
3989 /* callers.  Ignore my frame and my callers frame.                      */
3990 
3991 #ifdef LINUX
3992 #   include <unistd.h>
3993 #endif
3994 
3995 #endif /* NEED_CALLINFO */
3996 
3997 #if defined(GC_HAVE_BUILTIN_BACKTRACE)
3998 # include <execinfo.h>
3999 #endif
4000 
4001 #ifdef SAVE_CALL_CHAIN
4002 
4003 #if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \
4004     && defined(GC_HAVE_BUILTIN_BACKTRACE)
4005 
4006 #ifdef REDIRECT_MALLOC
4007   /* Deal with possible malloc calls in backtrace by omitting   */
4008   /* the infinitely recursing backtrace.                        */
4009 # ifdef THREADS
4010     __thread    /* If your compiler doesn't understand this */
4011                 /* you could use something like pthread_getspecific.    */
4012 # endif
4013   GC_in_save_callers = FALSE;
4014 #endif
4015 
4016 void GC_save_callers (info) 
4017 struct callinfo info[NFRAMES];
4018 {
4019   void * tmp_info[NFRAMES + 1];
4020   int npcs, i;
4021 # define IGNORE_FRAMES 1
4022   
4023   /* We retrieve NFRAMES+1 pc values, but discard the first, since it   */
4024   /* points to our own frame.                                           */
4025 # ifdef REDIRECT_MALLOC
4026     if (GC_in_save_callers) {
4027       info[0].ci_pc = (word)(&GC_save_callers);
4028       for (i = 1; i < NFRAMES; ++i) info[i].ci_pc = 0;
4029       return;
4030     }
4031     GC_in_save_callers = TRUE;
4032 # endif
4033   GC_ASSERT(sizeof(struct callinfo) == sizeof(void *));
4034   npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES);
4035   BCOPY(tmp_info+IGNORE_FRAMES, info, (npcs - IGNORE_FRAMES) * sizeof(void *));
4036   for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0;
4037 # ifdef REDIRECT_MALLOC
4038     GC_in_save_callers = FALSE;
4039 # endif
4040 }
4041 
4042 #else /* No builtin backtrace; do it ourselves */
4043 
4044 #if (defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD)) && defined(SPARC)
4045 #  define FR_SAVFP fr_fp
4046 #  define FR_SAVPC fr_pc
4047 #else
4048 #  define FR_SAVFP fr_savfp
4049 #  define FR_SAVPC fr_savpc
4050 #endif
4051 
4052 #if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9))
4053 #   define BIAS 2047
4054 #else
4055 #   define BIAS 0
4056 #endif
4057 
4058 void GC_save_callers (info) 
4059 struct callinfo info[NFRAMES];
4060 {
4061   struct frame *frame;
4062   struct frame *fp;
4063   int nframes = 0;
4064 # ifdef I386
4065     /* We assume this is turned on only with gcc as the compiler. */
4066     asm("movl %%ebp,%0" : "=r"(frame));
4067     fp = frame;
4068 # else
4069     frame = (struct frame *) GC_save_regs_in_stack ();
4070     fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);
4071 #endif
4072   
4073    for (; (!(fp HOTTER_THAN frame) && !(GC_stackbottom HOTTER_THAN (ptr_t)fp)
4074            && (nframes < NFRAMES));
4075        fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), nframes++) {
4076       register int i;
4077       
4078       info[nframes].ci_pc = fp->FR_SAVPC;
4079 #     if NARGS > 0
4080         for (i = 0; i < NARGS; i++) {
4081           info[nframes].ci_arg[i] = ~(fp->fr_arg[i]);
4082         }
4083 #     endif /* NARGS > 0 */
4084   }
4085   if (nframes < NFRAMES) info[nframes].ci_pc = 0;
4086 }
4087 
4088 #endif /* No builtin backtrace */
4089 
4090 #endif /* SAVE_CALL_CHAIN */
4091 
4092 #ifdef NEED_CALLINFO
4093 
4094 /* Print info to stderr.  We do NOT hold the allocation lock */
4095 void GC_print_callers (info)
4096 struct callinfo info[NFRAMES];
4097 {
4098     register int i;
4099     static int reentry_count = 0;
4100     GC_bool stop = FALSE;
4101 
4102     /* FIXME: This should probably use a different lock, so that we     */
4103     /* become callable with or without the allocation lock.             */
4104     LOCK();
4105       ++reentry_count;
4106     UNLOCK();
4107     
4108 #   if NFRAMES == 1
4109       GC_err_printf0("\tCaller at allocation:\n");
4110 #   else
4111       GC_err_printf0("\tCall chain at allocation:\n");
4112 #   endif
4113     for (i = 0; i < NFRAMES && !stop ; i++) {
4114         if (info[i].ci_pc == 0) break;
4115 #       if NARGS > 0
4116         {
4117           int j;
4118 
4119           GC_err_printf0("\t\targs: ");
4120           for (j = 0; j < NARGS; j++) {
4121             if (j != 0) GC_err_printf0(", ");
4122             GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]),
4123                                         ~(info[i].ci_arg[j]));
4124           }
4125           GC_err_printf0("\n");
4126         }
4127 #       endif
4128         if (reentry_count > 1) {
4129             /* We were called during an allocation during       */
4130             /* a previous GC_print_callers call; punt.          */
4131             GC_err_printf1("\t\t##PC##= 0x%lx\n", info[i].ci_pc);
4132             continue;
4133         }
4134         {
4135 #         ifdef LINUX
4136             FILE *pipe;
4137 #         endif
4138 #         if defined(GC_HAVE_BUILTIN_BACKTRACE) \
4139              && !defined(GC_BACKTRACE_SYMBOLS_BROKEN)
4140             char **sym_name =
4141               backtrace_symbols((void **)(&(info[i].ci_pc)), 1);
4142             char *name = sym_name[0];
4143 #         else
4144             char buf[40];
4145             char *name = buf;
4146             sprintf(buf, "##PC##= 0x%lx", info[i].ci_pc);
4147 #         endif
4148 #         if defined(LINUX) && !defined(SMALL_CONFIG)
4149             /* Try for a line number. */
4150             {
4151 #               define EXE_SZ 100
4152                 static char exe_name[EXE_SZ];
4153 #               define CMD_SZ 200
4154                 char cmd_buf[CMD_SZ];
4155 #               define RESULT_SZ 200
4156                 static char result_buf[RESULT_SZ];
4157                 size_t result_len;
4158                 char *old_preload;
4159 #               define PRELOAD_SZ 200
4160                 char preload_buf[PRELOAD_SZ];
4161                 static GC_bool found_exe_name = FALSE;
4162                 static GC_bool will_fail = FALSE;
4163                 int ret_code;
4164                 /* Try to get it via a hairy and expensive scheme.      */
4165                 /* First we get the name of the executable:             */
4166                 if (will_fail) goto out;
4167                 if (!found_exe_name) { 
4168                   ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ);
4169                   if (ret_code < 0 || ret_code >= EXE_SZ
4170                       || exe_name[0] != '/') {
4171                     will_fail = TRUE;   /* Dont try again. */
4172                     goto out;
4173                   }
4174                   exe_name[ret_code] = '\0';
4175                   found_exe_name = TRUE;
4176                 }
4177                 /* Then we use popen to start addr2line -e <exe> <addr> */
4178                 /* There are faster ways to do this, but hopefully this */
4179                 /* isn't time critical.                                 */
4180                 sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name,
4181                                  (unsigned long)info[i].ci_pc);
4182                 old_preload = getenv ("LD_PRELOAD");
4183                 if (0 != old_preload) {
4184                   if (strlen (old_preload) >= PRELOAD_SZ) {
4185                     will_fail = TRUE;
4186                     goto out;
4187                   }
4188                   strcpy (preload_buf, old_preload);
4189                   unsetenv ("LD_PRELOAD");
4190                 }
4191                 pipe = popen(cmd_buf, "r");
4192                 if (0 != old_preload
4193                     && 0 != setenv ("LD_PRELOAD", preload_buf, 0)) {
4194                   WARN("Failed to reset LD_PRELOAD\n", 0);
4195                 }
4196                 if (pipe == NULL
4197                     || (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe))
4198                        == 0) {
4199                   if (pipe != NULL) pclose(pipe);
4200                   will_fail = TRUE;
4201                   goto out;
4202                 }
4203                 if (result_buf[result_len - 1] == '\n') --result_len;
4204                 result_buf[result_len] = 0;
4205                 if (result_buf[0] == '?'
4206                     || result_buf[result_len-2] == ':' 
4207                        && result_buf[result_len-1] == '0') {
4208                     pclose(pipe);
4209                     goto out;
4210                 }
4211                 /* Get rid of embedded newline, if any.  Test for "main" */
4212                 {
4213                    char * nl = strchr(result_buf, '\n');
4214                    if (nl != NULL && nl < result_buf + result_len) {
4215                      *nl = ':';
4216                    }
4217                    if (strncmp(result_buf, "main", nl - result_buf) == 0) {
4218                      stop = TRUE;
4219                    }
4220                 }
4221                 if (result_len < RESULT_SZ - 25) {
4222                   /* Add in hex address */
4223                     sprintf(result_buf + result_len, " [0x%lx]",
4224                           (unsigned long)info[i].ci_pc);
4225                 }
4226                 name = result_buf;
4227                 pclose(pipe);
4228                 out:;
4229             }
4230 #         endif /* LINUX */
4231           GC_err_printf1("\t\t%s\n", name);
4232 #         if defined(GC_HAVE_BUILTIN_BACKTRACE) \
4233              && !defined(GC_BACKTRACE_SYMBOLS_BROKEN)
4234             free(sym_name);  /* May call GC_free; that's OK */
4235 #         endif
4236         }
4237     }
4238     LOCK();
4239       --reentry_count;
4240     UNLOCK();
4241 }
4242 
4243 #endif /* NEED_CALLINFO */
4244 
4245 
4246 
4247 #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
4248 
4249 /* Dump /proc/self/maps to GC_stderr, to enable looking up names for
4250    addresses in FIND_LEAK output. */
4251 
4252 static word dump_maps(char *maps)
4253 {
4254     GC_err_write(maps, strlen(maps));
4255     return 1;
4256 }
4257 
4258 void GC_print_address_map()
4259 {
4260     GC_err_printf0("---------- Begin address map ----------\n");
4261     GC_apply_to_maps(dump_maps);
4262     GC_err_printf0("---------- End address map ----------\n");
4263 }
4264 
4265 #endif
4266 
4267 

/* [<][>][^][v][top][bottom][index][help] */