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