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