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