OSDN Git Service

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