OSDN Git Service

* local-alloc.c (combine_regs): Handle SUBREG_REG being a MEM.
[pf3gnuchains/gcc-fork.git] / boehm-gc / os_dep.c
1 /*
2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3  * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
4  * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
5  * Copyright (c) 1999 by Hewlett-Packard Company.  All rights reserved.
6  *
7  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
9  *
10  * Permission is hereby granted to use or copy this program
11  * for any purpose,  provided the above notices are retained on all copies.
12  * Permission to modify the code and to distribute modified code is granted,
13  * provided the above notices are retained, and a notice that the code was
14  * modified is included with the above copyright notice.
15  */
16
17 # include "private/gc_priv.h"
18
19 # if defined(LINUX) && !defined(POWERPC)
20 #   include <linux/version.h>
21 #   if (LINUX_VERSION_CODE <= 0x10400)
22       /* Ugly hack to get struct sigcontext_struct definition.  Required      */
23       /* for some early 1.3.X releases.  Will hopefully go away soon. */
24       /* in some later Linux releases, asm/sigcontext.h may have to   */
25       /* be included instead.                                         */
26 #     define __KERNEL__
27 #     include <asm/signal.h>
28 #     undef __KERNEL__
29 #   else
30       /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */
31       /* struct sigcontext.  libc6 (glibc2) uses "struct sigcontext" in     */
32       /* prototypes, so we have to include the top-level sigcontext.h to    */
33       /* make sure the former gets defined to be the latter if appropriate. */
34 #     include <features.h>
35 #     if 2 <= __GLIBC__
36 #       if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__
37           /* glibc 2.1 no longer has sigcontext.h.  But signal.h        */
38           /* has the right declaration for glibc 2.1.                   */
39 #         include <sigcontext.h>
40 #       endif /* 0 == __GLIBC_MINOR__ */
41 #     else /* not 2 <= __GLIBC__ */
42         /* libc5 doesn't have <sigcontext.h>: go directly with the kernel   */
43         /* one.  Check LINUX_VERSION_CODE to see which we should reference. */
44 #       include <asm/sigcontext.h>
45 #     endif /* 2 <= __GLIBC__ */
46 #   endif
47 # endif
48 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
49     && !defined(MSWINCE)
50 #   include <sys/types.h>
51 #   if !defined(MSWIN32) && !defined(SUNOS4)
52 #       include <unistd.h>
53 #   endif
54 # endif
55
56 # include <stdio.h>
57 # if defined(MSWINCE)
58 #   define SIGSEGV 0 /* value is irrelevant */
59 # else
60 #   include <signal.h>
61 # endif
62
63 /* Blatantly OS dependent routines, except for those that are related   */
64 /* to dynamic loading.                                                  */
65
66 # if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START)
67 #   define NEED_FIND_LIMIT
68 # endif
69
70 # if !defined(STACKBOTTOM) && defined(HEURISTIC2)
71 #   define NEED_FIND_LIMIT
72 # endif
73
74 # if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
75 #   define NEED_FIND_LIMIT
76 # endif
77
78 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
79       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
80 #   define NEED_FIND_LIMIT
81 # endif
82
83 #ifdef NEED_FIND_LIMIT
84 #   include <setjmp.h>
85 #endif
86
87 #ifdef FREEBSD
88 #  include <machine/trap.h>
89 #endif
90
91 #ifdef AMIGA
92 # define GC_AMIGA_DEF
93 # include "AmigaOS.c"
94 # undef GC_AMIGA_DEF
95 #endif
96
97 #if defined(MSWIN32) || defined(MSWINCE)
98 # define WIN32_LEAN_AND_MEAN
99 # define NOSERVICE
100 # include <windows.h>
101 #endif
102
103 #ifdef MACOS
104 # include <Processes.h>
105 #endif
106
107 #ifdef IRIX5
108 # include <sys/uio.h>
109 # include <malloc.h>   /* for locking */
110 #endif
111 #ifdef USE_MMAP
112 # include <sys/types.h>
113 # include <sys/mman.h>
114 # include <sys/stat.h>
115 #endif
116
117 #ifdef UNIX_LIKE
118 # include <fcntl.h>
119 #endif
120
121 #if defined(SUNOS5SIGS) || defined (HURD) || defined(LINUX)
122 # ifdef SUNOS5SIGS
123 #  include <sys/siginfo.h>
124 # endif
125 # undef setjmp
126 # undef longjmp
127 # define setjmp(env) sigsetjmp(env, 1)
128 # define longjmp(env, val) siglongjmp(env, val)
129 # define jmp_buf sigjmp_buf
130 #endif
131
132 #ifdef DJGPP
133   /* Apparently necessary for djgpp 2.01.  May cause problems with      */
134   /* other versions.                                                    */
135   typedef long unsigned int caddr_t;
136 #endif
137
138 #ifdef PCR
139 # include "il/PCR_IL.h"
140 # include "th/PCR_ThCtl.h"
141 # include "mm/PCR_MM.h"
142 #endif
143
144 #if !defined(NO_EXECUTE_PERMISSION)
145 # define OPT_PROT_EXEC PROT_EXEC
146 #else
147 # define OPT_PROT_EXEC 0
148 #endif
149
150 #if defined(SEARCH_FOR_DATA_START)
151   /* The I386 case can be handled without a search.  The Alpha case     */
152   /* used to be handled differently as well, but the rules changed      */
153   /* for recent Linux versions.  This seems to be the easiest way to    */
154   /* cover all versions.                                                */
155
156 # ifdef LINUX
157 #   pragma weak __data_start
158     extern int __data_start;
159 #   pragma weak data_start
160     extern int data_start;
161 # endif /* LINUX */
162   extern int _end;
163
164   ptr_t GC_data_start;
165
166   void GC_init_linux_data_start()
167   {
168     extern ptr_t GC_find_limit();
169
170 #   ifdef LINUX
171       /* Try the easy approaches first: */
172       if (&__data_start != 0) {
173           GC_data_start = (ptr_t)(&__data_start);
174           return;
175       }
176       if (&data_start != 0) {
177           GC_data_start = (ptr_t)(&data_start);
178           return;
179       }
180 #   endif /* LINUX */
181     GC_data_start = GC_find_limit((ptr_t)(&_end), FALSE);
182   }
183 #endif
184
185 # ifdef ECOS
186
187 # ifndef ECOS_GC_MEMORY_SIZE
188 # define ECOS_GC_MEMORY_SIZE (448 * 1024)
189 # endif /* ECOS_GC_MEMORY_SIZE */
190
191 // setjmp() function, as described in ANSI para 7.6.1.1
192 #define setjmp( __env__ )  hal_setjmp( __env__ )
193
194 // FIXME: This is a simple way of allocating memory which is
195 // compatible with ECOS early releases.  Later releases use a more
196 // sophisticated means of allocating memory than this simple static
197 // allocator, but this method is at least bound to work.
198 static char memory[ECOS_GC_MEMORY_SIZE];
199 static char *brk = memory;
200
201 static void *tiny_sbrk(ptrdiff_t increment)
202 {
203   void *p = brk;
204
205   brk += increment;
206
207   if (brk >  memory + sizeof memory)
208     {
209       brk -= increment;
210       return NULL;
211     }
212
213   return p;
214 }
215 #define sbrk tiny_sbrk
216 # endif /* ECOS */
217
218 #if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
219   ptr_t GC_data_start;
220
221   void GC_init_netbsd_elf()
222   {
223     extern ptr_t GC_find_limit();
224     extern char **environ;
225         /* This may need to be environ, without the underscore, for     */
226         /* some versions.                                               */
227     GC_data_start = GC_find_limit((ptr_t)&environ, FALSE);
228   }
229 #endif
230
231 # ifdef OS2
232
233 # include <stddef.h>
234
235 # if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */
236
237 struct exe_hdr {
238     unsigned short      magic_number;
239     unsigned short      padding[29];
240     long                new_exe_offset;
241 };
242
243 #define E_MAGIC(x)      (x).magic_number
244 #define EMAGIC          0x5A4D  
245 #define E_LFANEW(x)     (x).new_exe_offset
246
247 struct e32_exe {
248     unsigned char       magic_number[2]; 
249     unsigned char       byte_order; 
250     unsigned char       word_order; 
251     unsigned long       exe_format_level;
252     unsigned short      cpu;       
253     unsigned short      os;
254     unsigned long       padding1[13];
255     unsigned long       object_table_offset;
256     unsigned long       object_count;    
257     unsigned long       padding2[31];
258 };
259
260 #define E32_MAGIC1(x)   (x).magic_number[0]
261 #define E32MAGIC1       'L'
262 #define E32_MAGIC2(x)   (x).magic_number[1]
263 #define E32MAGIC2       'X'
264 #define E32_BORDER(x)   (x).byte_order
265 #define E32LEBO         0
266 #define E32_WORDER(x)   (x).word_order
267 #define E32LEWO         0
268 #define E32_CPU(x)      (x).cpu
269 #define E32CPU286       1
270 #define E32_OBJTAB(x)   (x).object_table_offset
271 #define E32_OBJCNT(x)   (x).object_count
272
273 struct o32_obj {
274     unsigned long       size;  
275     unsigned long       base;
276     unsigned long       flags;  
277     unsigned long       pagemap;
278     unsigned long       mapsize; 
279     unsigned long       reserved;
280 };
281
282 #define O32_FLAGS(x)    (x).flags
283 #define OBJREAD         0x0001L
284 #define OBJWRITE        0x0002L
285 #define OBJINVALID      0x0080L
286 #define O32_SIZE(x)     (x).size
287 #define O32_BASE(x)     (x).base
288
289 # else  /* IBM's compiler */
290
291 /* A kludge to get around what appears to be a header file bug */
292 # ifndef WORD
293 #   define WORD unsigned short
294 # endif
295 # ifndef DWORD
296 #   define DWORD unsigned long
297 # endif
298
299 # define EXE386 1
300 # include <newexe.h>
301 # include <exe386.h>
302
303 # endif  /* __IBMC__ */
304
305 # define INCL_DOSEXCEPTIONS
306 # define INCL_DOSPROCESS
307 # define INCL_DOSERRORS
308 # define INCL_DOSMODULEMGR
309 # define INCL_DOSMEMMGR
310 # include <os2.h>
311
312
313 /* Disable and enable signals during nontrivial allocations     */
314
315 void GC_disable_signals(void)
316 {
317     ULONG nest;
318     
319     DosEnterMustComplete(&nest);
320     if (nest != 1) ABORT("nested GC_disable_signals");
321 }
322
323 void GC_enable_signals(void)
324 {
325     ULONG nest;
326     
327     DosExitMustComplete(&nest);
328     if (nest != 0) ABORT("GC_enable_signals");
329 }
330
331
332 # else
333
334 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
335       && !defined(MSWINCE) \
336       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW)
337
338 #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
339         /* Use the traditional BSD interface */
340 #       define SIGSET_T int
341 #       define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
342 #       define SIG_FILL(set)  (set) = 0x7fffffff
343           /* Setting the leading bit appears to provoke a bug in some   */
344           /* longjmp implementations.  Most systems appear not to have  */
345           /* a signal 32.                                               */
346 #       define SIGSETMASK(old, new) (old) = sigsetmask(new)
347 #   else
348         /* Use POSIX/SYSV interface     */
349 #       define SIGSET_T sigset_t
350 #       define SIG_DEL(set, signal) sigdelset(&(set), (signal))
351 #       define SIG_FILL(set) sigfillset(&set)
352 #       define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old))
353 #   endif
354
355 static GC_bool mask_initialized = FALSE;
356
357 static SIGSET_T new_mask;
358
359 static SIGSET_T old_mask;
360
361 static SIGSET_T dummy;
362
363 #if defined(PRINTSTATS) && !defined(THREADS)
364 # define CHECK_SIGNALS
365   int GC_sig_disabled = 0;
366 #endif
367
368 void GC_disable_signals()
369 {
370     if (!mask_initialized) {
371         SIG_FILL(new_mask);
372
373         SIG_DEL(new_mask, SIGSEGV);
374         SIG_DEL(new_mask, SIGILL);
375         SIG_DEL(new_mask, SIGQUIT);
376 #       ifdef SIGBUS
377             SIG_DEL(new_mask, SIGBUS);
378 #       endif
379 #       ifdef SIGIOT
380             SIG_DEL(new_mask, SIGIOT);
381 #       endif
382 #       ifdef SIGEMT
383             SIG_DEL(new_mask, SIGEMT);
384 #       endif
385 #       ifdef SIGTRAP
386             SIG_DEL(new_mask, SIGTRAP);
387 #       endif 
388         mask_initialized = TRUE;
389     }
390 #   ifdef CHECK_SIGNALS
391         if (GC_sig_disabled != 0) ABORT("Nested disables");
392         GC_sig_disabled++;
393 #   endif
394     SIGSETMASK(old_mask,new_mask);
395 }
396
397 void GC_enable_signals()
398 {
399 #   ifdef CHECK_SIGNALS
400         if (GC_sig_disabled != 1) ABORT("Unmatched enable");
401         GC_sig_disabled--;
402 #   endif
403     SIGSETMASK(dummy,old_mask);
404 }
405
406 #  endif  /* !PCR */
407
408 # endif /*!OS/2 */
409
410 /* Ivan Demakov: simplest way (to me) */
411 #if defined (DOS4GW)
412   void GC_disable_signals() { }
413   void GC_enable_signals() { }
414 #endif
415
416 /* Find the page size */
417 word GC_page_size;
418
419 # if defined(MSWIN32) || defined(MSWINCE)
420   void GC_setpagesize()
421   {
422     GetSystemInfo(&GC_sysinfo);
423     GC_page_size = GC_sysinfo.dwPageSize;
424   }
425
426 # else
427 #   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) \
428        || defined(USE_MUNMAP)
429         void GC_setpagesize()
430         {
431             GC_page_size = GETPAGESIZE();
432         }
433 #   else
434         /* It's acceptable to fake it. */
435         void GC_setpagesize()
436         {
437             GC_page_size = HBLKSIZE;
438         }
439 #   endif
440 # endif
441
442 /* 
443  * Find the base of the stack. 
444  * Used only in single-threaded environment.
445  * With threads, GC_mark_roots needs to know how to do this.
446  * Called with allocator lock held.
447  */
448 # if defined(MSWIN32) || defined(MSWINCE)
449 # define is_writable(prot) ((prot) == PAGE_READWRITE \
450                             || (prot) == PAGE_WRITECOPY \
451                             || (prot) == PAGE_EXECUTE_READWRITE \
452                             || (prot) == PAGE_EXECUTE_WRITECOPY)
453 /* Return the number of bytes that are writable starting at p.  */
454 /* The pointer p is assumed to be page aligned.                 */
455 /* If base is not 0, *base becomes the beginning of the         */
456 /* allocation region containing p.                              */
457 word GC_get_writable_length(ptr_t p, ptr_t *base)
458 {
459     MEMORY_BASIC_INFORMATION buf;
460     word result;
461     word protect;
462     
463     result = VirtualQuery(p, &buf, sizeof(buf));
464     if (result != sizeof(buf)) ABORT("Weird VirtualQuery result");
465     if (base != 0) *base = (ptr_t)(buf.AllocationBase);
466     protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE));
467     if (!is_writable(protect)) {
468         return(0);
469     }
470     if (buf.State != MEM_COMMIT) return(0);
471     return(buf.RegionSize);
472 }
473
474 ptr_t GC_get_stack_base()
475 {
476     int dummy;
477     ptr_t sp = (ptr_t)(&dummy);
478     ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
479     word size = GC_get_writable_length(trunc_sp, 0);
480    
481     return(trunc_sp + size);
482 }
483
484
485 # endif /* MS Windows */
486
487 # ifdef BEOS
488 # include <kernel/OS.h>
489 ptr_t GC_get_stack_base(){
490         thread_info th;
491         get_thread_info(find_thread(NULL),&th);
492         return th.stack_end;
493 }
494 # endif /* BEOS */
495
496
497 # ifdef OS2
498
499 ptr_t GC_get_stack_base()
500 {
501     PTIB ptib;
502     PPIB ppib;
503     
504     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
505         GC_err_printf0("DosGetInfoBlocks failed\n");
506         ABORT("DosGetInfoBlocks failed\n");
507     }
508     return((ptr_t)(ptib -> tib_pstacklimit));
509 }
510
511 # endif /* OS2 */
512
513 # ifdef AMIGA
514 #   define GC_AMIGA_SB
515 #   include "AmigaOS.c"
516 #   undef GC_AMIGA_SB
517 # endif /* AMIGA */
518
519 # if defined(NEED_FIND_LIMIT) || (defined(UNIX_LIKE) && !defined(ECOS))
520
521 #   ifdef __STDC__
522         typedef void (*handler)(int);
523 #   else
524         typedef void (*handler)();
525 #   endif
526
527 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) || defined(HURD)
528         static struct sigaction old_segv_act;
529 #       if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) || defined(HURD)
530             static struct sigaction old_bus_act;
531 #       endif
532 #   else
533         static handler old_segv_handler, old_bus_handler;
534 #   endif
535     
536 #   ifdef __STDC__
537       void GC_set_and_save_fault_handler(handler h)
538 #   else
539       void GC_set_and_save_fault_handler(h)
540       handler h;
541 #   endif
542     {
543 # ifndef ECOS
544 #       if defined(SUNOS5SIGS) || defined(IRIX5)  \
545         || defined(OSF1) || defined(HURD)
546           struct sigaction      act;
547
548           act.sa_handler        = h;
549 #         ifdef SUNOS5SIGS
550             act.sa_flags          = SA_RESTART | SA_NODEFER;
551 #         else
552             act.sa_flags          = SA_RESTART;
553 #         endif
554           /* The presence of SA_NODEFER represents yet another gross    */
555           /* hack.  Under Solaris 2.3, siglongjmp doesn't appear to     */
556           /* interact correctly with -lthread.  We hide the confusion   */
557           /* by making sure that signal handling doesn't affect the     */
558           /* signal mask.                                               */
559
560           (void) sigemptyset(&act.sa_mask);
561 #         ifdef GC_IRIX_THREADS
562                 /* Older versions have a bug related to retrieving and  */
563                 /* and setting a handler at the same time.              */
564                 (void) sigaction(SIGSEGV, 0, &old_segv_act);
565                 (void) sigaction(SIGSEGV, &act, 0);
566 #         else
567                 (void) sigaction(SIGSEGV, &act, &old_segv_act);
568 #               if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
569                    || defined(HPUX) || defined(HURD)
570                     /* Under Irix 5.x or HP/UX, we may get SIGBUS.      */
571                     /* Pthreads doesn't exist under Irix 5.x, so we     */
572                     /* don't have to worry in the threads case.         */
573                     (void) sigaction(SIGBUS, &act, &old_bus_act);
574 #               endif
575 #         endif /* GC_IRIX_THREADS */
576 #       else
577           old_segv_handler = signal(SIGSEGV, h);
578 #         ifdef SIGBUS
579             old_bus_handler = signal(SIGBUS, h);
580 #         endif
581 #       endif
582 # endif /* ECOS */
583     }
584 # endif /* NEED_FIND_LIMIT || UNIX_LIKE */
585
586 # ifdef NEED_FIND_LIMIT
587   /* Some tools to implement HEURISTIC2 */
588 #   define MIN_PAGE_SIZE 256    /* Smallest conceivable page size, bytes */
589     /* static */ jmp_buf GC_jmp_buf;
590     
591     /*ARGSUSED*/
592     void GC_fault_handler(sig)
593     int sig;
594     {
595         longjmp(GC_jmp_buf, 1);
596     }
597
598     void GC_setup_temporary_fault_handler()
599     {
600         GC_set_and_save_fault_handler(GC_fault_handler);
601     }
602     
603     void GC_reset_fault_handler()
604     {
605 # ifndef ECOS
606 #       if defined(SUNOS5SIGS) || defined(IRIX5) \
607            || defined(OSF1) || defined(HURD)
608           (void) sigaction(SIGSEGV, &old_segv_act, 0);
609 #         if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
610              || defined(HPUX) || defined(HURD)
611               (void) sigaction(SIGBUS, &old_bus_act, 0);
612 #         endif
613 #       else
614           (void) signal(SIGSEGV, old_segv_handler);
615 #         ifdef SIGBUS
616             (void) signal(SIGBUS, old_bus_handler);
617 #         endif
618 #       endif
619 # endif /* ECOS */
620     }
621
622     /* Return the first nonaddressible location > p (up) or     */
623     /* the smallest location q s.t. [q,p] is addressible (!up). */
624     ptr_t GC_find_limit(p, up)
625     ptr_t p;
626     GC_bool up;
627     {
628 # ifndef ECOS
629         static VOLATILE ptr_t result;
630                 /* Needs to be static, since otherwise it may not be    */
631                 /* preserved across the longjmp.  Can safely be         */
632                 /* static since it's only called once, with the         */
633                 /* allocation lock held.                                */
634
635
636         GC_setup_temporary_fault_handler();
637         if (setjmp(GC_jmp_buf) == 0) {
638             result = (ptr_t)(((word)(p))
639                               & ~(MIN_PAGE_SIZE-1));
640             for (;;) {
641                 if (up) {
642                     result += MIN_PAGE_SIZE;
643                 } else {
644                     result -= MIN_PAGE_SIZE;
645                 }
646                 GC_noop1((word)(*result));
647             }
648         }
649         GC_reset_fault_handler();
650         if (!up) {
651             result += MIN_PAGE_SIZE;
652         }
653         return(result);
654 # else /* ECOS */
655         abort();
656 # endif /* ECOS */
657     }
658 # endif
659
660 # ifndef ECOS
661
662 #ifdef LINUX_STACKBOTTOM
663
664 #include <sys/types.h>
665 #include <sys/stat.h>
666
667 # define STAT_SKIP 27   /* Number of fields preceding startstack        */
668                         /* field in /proc/self/stat                     */
669
670 # pragma weak __libc_stack_end
671   extern ptr_t __libc_stack_end;
672
673 # ifdef IA64
674 #   pragma weak __libc_ia64_register_backing_store_base
675     extern ptr_t __libc_ia64_register_backing_store_base;
676
677     ptr_t GC_get_register_stack_base(void)
678     {
679       if (0 != &__libc_ia64_register_backing_store_base
680           && 0 != __libc_ia64_register_backing_store_base) {
681         /* Glibc 2.2.4 has a bug such that for dynamically linked       */
682         /* executables __libc_ia64_register_backing_store_base is       */
683         /* defined but ininitialized during constructor calls.          */
684         /* Hence we check for both nonzero address and value.           */
685         return __libc_ia64_register_backing_store_base;
686       } else {
687         word result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
688         result += BACKING_STORE_ALIGNMENT - 1;
689         result &= ~(BACKING_STORE_ALIGNMENT - 1);
690         return (ptr_t)result;
691       }
692     }
693 # endif
694
695   ptr_t GC_linux_stack_base(void)
696   {
697     /* We read the stack base value from /proc/self/stat.  We do this   */
698     /* using direct I/O system calls in order to avoid calling malloc   */
699     /* in case REDIRECT_MALLOC is defined.                              */ 
700 #   define STAT_BUF_SIZE 4096
701 #   if defined(GC_USE_LD_WRAP)
702 #       define STAT_READ __real_read
703 #   else
704 #       define STAT_READ read
705 #   endif    
706     char stat_buf[STAT_BUF_SIZE];
707     int f;
708     char c;
709     word result = 0;
710     size_t i, buf_offset = 0;
711
712     /* First try the easy way.  This should work for glibc 2.2  */
713       if (0 != &__libc_stack_end) {
714         return __libc_stack_end;
715       }
716     f = open("/proc/self/stat", O_RDONLY);
717     if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
718         ABORT("Couldn't read /proc/self/stat");
719     }
720     c = stat_buf[buf_offset++];
721     /* Skip the required number of fields.  This number is hopefully    */
722     /* constant across all Linux implementations.                       */
723       for (i = 0; i < STAT_SKIP; ++i) {
724         while (isspace(c)) c = stat_buf[buf_offset++];
725         while (!isspace(c)) c = stat_buf[buf_offset++];
726       }
727     while (isspace(c)) c = stat_buf[buf_offset++];
728     while (isdigit(c)) {
729       result *= 10;
730       result += c - '0';
731       c = stat_buf[buf_offset++];
732     }
733     close(f);
734     if (result < 0x10000000) ABORT("Absurd stack bottom value");
735     return (ptr_t)result;
736   }
737
738 #endif /* LINUX_STACKBOTTOM */
739
740 #ifdef FREEBSD_STACKBOTTOM
741
742 /* This uses an undocumented sysctl call, but at least one expert       */
743 /* believes it will stay.                                               */
744
745 #include <unistd.h>
746 #include <sys/types.h>
747 #include <sys/sysctl.h>
748
749   ptr_t GC_freebsd_stack_base(void)
750   {
751     int nm[2] = { CTL_KERN, KERN_USRSTACK}, base, len, r;
752     
753     len = sizeof(int);
754     r = sysctl(nm, 2, &base, &len, NULL, 0);
755     
756     if (r) ABORT("Error getting stack base");
757
758     return (ptr_t)base;
759   }
760
761 #endif /* FREEBSD_STACKBOTTOM */
762
763 #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
764     && !defined(MSWINCE) && !defined(OS2) && !defined(ECOS)
765
766 ptr_t GC_get_stack_base()
767 {
768     word dummy;
769     ptr_t result;
770
771 #   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
772
773 #   ifdef STACKBOTTOM
774         return(STACKBOTTOM);
775 #   else
776 #       ifdef HEURISTIC1
777 #          ifdef STACK_GROWS_DOWN
778              result = (ptr_t)((((word)(&dummy))
779                                + STACKBOTTOM_ALIGNMENT_M1)
780                               & ~STACKBOTTOM_ALIGNMENT_M1);
781 #          else
782              result = (ptr_t)(((word)(&dummy))
783                               & ~STACKBOTTOM_ALIGNMENT_M1);
784 #          endif
785 #       endif /* HEURISTIC1 */
786 #       ifdef LINUX_STACKBOTTOM
787            result = GC_linux_stack_base();
788 #       endif
789 #       ifdef FREEBSD_STACKBOTTOM
790            result = GC_freebsd_stack_base();
791 #       endif
792 #       ifdef HEURISTIC2
793 #           ifdef STACK_GROWS_DOWN
794                 result = GC_find_limit((ptr_t)(&dummy), TRUE);
795 #               ifdef HEURISTIC2_LIMIT
796                     if (result > HEURISTIC2_LIMIT
797                         && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) {
798                             result = HEURISTIC2_LIMIT;
799                     }
800 #               endif
801 #           else
802                 result = GC_find_limit((ptr_t)(&dummy), FALSE);
803 #               ifdef HEURISTIC2_LIMIT
804                     if (result < HEURISTIC2_LIMIT
805                         && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) {
806                             result = HEURISTIC2_LIMIT;
807                     }
808 #               endif
809 #           endif
810
811 #       endif /* HEURISTIC2 */
812 #       ifdef STACK_GROWS_DOWN
813             if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t));
814 #       endif
815         return(result);
816 #   endif /* STACKBOTTOM */
817 }
818 # endif /* ECOS */
819
820 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */
821
822 /*
823  * Register static data segment(s) as roots.
824  * If more data segments are added later then they need to be registered
825  * add that point (as we do with SunOS dynamic loading),
826  * or GC_mark_roots needs to check for them (as we do with PCR).
827  * Called with allocator lock held.
828  */
829
830 # ifdef OS2
831
832 void GC_register_data_segments()
833 {
834     PTIB ptib;
835     PPIB ppib;
836     HMODULE module_handle;
837 #   define PBUFSIZ 512
838     UCHAR path[PBUFSIZ];
839     FILE * myexefile;
840     struct exe_hdr hdrdos;      /* MSDOS header.        */
841     struct e32_exe hdr386;      /* Real header for my executable */
842     struct o32_obj seg; /* Currrent segment */
843     int nsegs;
844     
845     
846     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
847         GC_err_printf0("DosGetInfoBlocks failed\n");
848         ABORT("DosGetInfoBlocks failed\n");
849     }
850     module_handle = ppib -> pib_hmte;
851     if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
852         GC_err_printf0("DosQueryModuleName failed\n");
853         ABORT("DosGetInfoBlocks failed\n");
854     }
855     myexefile = fopen(path, "rb");
856     if (myexefile == 0) {
857         GC_err_puts("Couldn't open executable ");
858         GC_err_puts(path); GC_err_puts("\n");
859         ABORT("Failed to open executable\n");
860     }
861     if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) {
862         GC_err_puts("Couldn't read MSDOS header from ");
863         GC_err_puts(path); GC_err_puts("\n");
864         ABORT("Couldn't read MSDOS header");
865     }
866     if (E_MAGIC(hdrdos) != EMAGIC) {
867         GC_err_puts("Executable has wrong DOS magic number: ");
868         GC_err_puts(path); GC_err_puts("\n");
869         ABORT("Bad DOS magic number");
870     }
871     if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
872         GC_err_puts("Seek to new header failed in ");
873         GC_err_puts(path); GC_err_puts("\n");
874         ABORT("Bad DOS magic number");
875     }
876     if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) {
877         GC_err_puts("Couldn't read MSDOS header from ");
878         GC_err_puts(path); GC_err_puts("\n");
879         ABORT("Couldn't read OS/2 header");
880     }
881     if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
882         GC_err_puts("Executable has wrong OS/2 magic number:");
883         GC_err_puts(path); GC_err_puts("\n");
884         ABORT("Bad OS/2 magic number");
885     }
886     if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
887         GC_err_puts("Executable %s has wrong byte order: ");
888         GC_err_puts(path); GC_err_puts("\n");
889         ABORT("Bad byte order");
890     }
891     if ( E32_CPU(hdr386) == E32CPU286) {
892         GC_err_puts("GC can't handle 80286 executables: ");
893         GC_err_puts(path); GC_err_puts("\n");
894         EXIT();
895     }
896     if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
897               SEEK_SET) != 0) {
898         GC_err_puts("Seek to object table failed: ");
899         GC_err_puts(path); GC_err_puts("\n");
900         ABORT("Seek to object table failed");
901     }
902     for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
903       int flags;
904       if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) {
905         GC_err_puts("Couldn't read obj table entry from ");
906         GC_err_puts(path); GC_err_puts("\n");
907         ABORT("Couldn't read obj table entry");
908       }
909       flags = O32_FLAGS(seg);
910       if (!(flags & OBJWRITE)) continue;
911       if (!(flags & OBJREAD)) continue;
912       if (flags & OBJINVALID) {
913           GC_err_printf0("Object with invalid pages?\n");
914           continue;
915       } 
916       GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE);
917     }
918 }
919
920 # else /* !OS2 */
921
922 # if defined(MSWIN32) || defined(MSWINCE)
923
924 # ifdef MSWIN32
925   /* Unfortunately, we have to handle win32s very differently from NT,  */
926   /* Since VirtualQuery has very different semantics.  In particular,   */
927   /* under win32s a VirtualQuery call on an unmapped page returns an    */
928   /* invalid result.  Under GC_register_data_segments is a noop and     */
929   /* all real work is done by GC_register_dynamic_libraries.  Under     */
930   /* win32s, we cannot find the data segments associated with dll's.    */
931   /* We rgister the main data segment here.                             */
932   GC_bool GC_win32s = FALSE;    /* We're running under win32s.  */
933   
934   GC_bool GC_is_win32s()
935   {
936       DWORD v = GetVersion();
937       
938       /* Check that this is not NT, and Windows major version <= 3      */
939       return ((v & 0x80000000) && (v & 0xff) <= 3);
940   }
941   
942   void GC_init_win32()
943   {
944       GC_win32s = GC_is_win32s();
945   }
946
947   /* Return the smallest address a such that VirtualQuery               */
948   /* returns correct results for all addresses between a and start.     */
949   /* Assumes VirtualQuery returns correct information for start.        */
950   ptr_t GC_least_described_address(ptr_t start)
951   {  
952     MEMORY_BASIC_INFORMATION buf;
953     DWORD result;
954     LPVOID limit;
955     ptr_t p;
956     LPVOID q;
957     
958     limit = GC_sysinfo.lpMinimumApplicationAddress;
959     p = (ptr_t)((word)start & ~(GC_page_size - 1));
960     for (;;) {
961         q = (LPVOID)(p - GC_page_size);
962         if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break;
963         result = VirtualQuery(q, &buf, sizeof(buf));
964         if (result != sizeof(buf) || buf.AllocationBase == 0) break;
965         p = (ptr_t)(buf.AllocationBase);
966     }
967     return(p);
968   }
969 # endif
970   
971   /* Is p the start of either the malloc heap, or of one of our */
972   /* heap sections?                                             */
973   GC_bool GC_is_heap_base (ptr_t p)
974   {
975      
976      register unsigned i;
977      
978 #    ifndef REDIRECT_MALLOC
979        static ptr_t malloc_heap_pointer = 0;
980      
981        if (0 == malloc_heap_pointer) {
982          MEMORY_BASIC_INFORMATION buf;
983          void *pTemp = malloc( 1 );
984          register DWORD result = VirtualQuery(pTemp, &buf, sizeof(buf));
985            
986          free( pTemp );
987
988          
989          if (result != sizeof(buf)) {
990              ABORT("Weird VirtualQuery result");
991          }
992          malloc_heap_pointer = (ptr_t)(buf.AllocationBase);
993        }
994        if (p == malloc_heap_pointer) return(TRUE);
995 #    endif
996      for (i = 0; i < GC_n_heap_bases; i++) {
997          if (GC_heap_bases[i] == p) return(TRUE);
998      }
999      return(FALSE);
1000   }
1001
1002 # ifdef MSWIN32
1003   void GC_register_root_section(ptr_t static_root)
1004   {
1005       MEMORY_BASIC_INFORMATION buf;
1006       DWORD result;
1007       DWORD protect;
1008       LPVOID p;
1009       char * base;
1010       char * limit, * new_limit;
1011     
1012       if (!GC_win32s) return;
1013       p = base = limit = GC_least_described_address(static_root);
1014       while (p < GC_sysinfo.lpMaximumApplicationAddress) {
1015         result = VirtualQuery(p, &buf, sizeof(buf));
1016         if (result != sizeof(buf) || buf.AllocationBase == 0
1017             || GC_is_heap_base(buf.AllocationBase)) break;
1018         new_limit = (char *)p + buf.RegionSize;
1019         protect = buf.Protect;
1020         if (buf.State == MEM_COMMIT
1021             && is_writable(protect)) {
1022             if ((char *)p == limit) {
1023                 limit = new_limit;
1024             } else {
1025                 if (base != limit) GC_add_roots_inner(base, limit, FALSE);
1026                 base = p;
1027                 limit = new_limit;
1028             }
1029         }
1030         if (p > (LPVOID)new_limit /* overflow */) break;
1031         p = (LPVOID)new_limit;
1032       }
1033       if (base != limit) GC_add_roots_inner(base, limit, FALSE);
1034   }
1035 #endif
1036   
1037   void GC_register_data_segments()
1038   {
1039 #     ifdef MSWIN32
1040       static char dummy;
1041       GC_register_root_section((ptr_t)(&dummy));
1042 #     endif
1043   }
1044
1045 # else /* !OS2 && !Windows */
1046
1047 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
1048       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
1049 char * GC_SysVGetDataStart(max_page_size, etext_addr)
1050 int max_page_size;
1051 int * etext_addr;
1052 {
1053     word text_end = ((word)(etext_addr) + sizeof(word) - 1)
1054                     & ~(sizeof(word) - 1);
1055         /* etext rounded to word boundary       */
1056     word next_page = ((text_end + (word)max_page_size - 1)
1057                       & ~((word)max_page_size - 1));
1058     word page_offset = (text_end & ((word)max_page_size - 1));
1059     VOLATILE char * result = (char *)(next_page + page_offset);
1060     /* Note that this isnt equivalent to just adding            */
1061     /* max_page_size to &etext if &etext is at a page boundary  */
1062     
1063     GC_setup_temporary_fault_handler();
1064     if (setjmp(GC_jmp_buf) == 0) {
1065         /* Try writing to the address.  */
1066         *result = *result;
1067         GC_reset_fault_handler();
1068     } else {
1069         GC_reset_fault_handler();
1070         /* We got here via a longjmp.  The address is not readable.     */
1071         /* This is known to happen under Solaris 2.4 + gcc, which place */
1072         /* string constants in the text segment, but after etext.       */
1073         /* Use plan B.  Note that we now know there is a gap between    */
1074         /* text and data segments, so plan A bought us something.       */
1075         result = (char *)GC_find_limit((ptr_t)(DATAEND) - MIN_PAGE_SIZE, FALSE);
1076     }
1077     return((char *)result);
1078 }
1079 # endif
1080
1081
1082 #ifdef AMIGA
1083
1084 #  define GC_AMIGA_DS
1085 #  include "AmigaOS.c"
1086 #  undef GC_AMIGA_DS
1087
1088 #else /* !OS2 && !Windows && !AMIGA */
1089
1090 void GC_register_data_segments()
1091 {
1092 #   if !defined(PCR) && !defined(SRC_M3) && !defined(NEXT) && !defined(MACOS) \
1093        && !defined(MACOSX)
1094 #     if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS)
1095         /* As of Solaris 2.3, the Solaris threads implementation        */
1096         /* allocates the data structure for the initial thread with     */
1097         /* sbrk at process startup.  It needs to be scanned, so that    */
1098         /* we don't lose some malloc allocated data structures          */
1099         /* hanging from it.  We're on thin ice here ...                 */
1100         extern caddr_t sbrk();
1101
1102         GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
1103 #     else
1104         GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
1105 #     endif
1106 #   endif
1107 #   if !defined(PCR) && (defined(NEXT) || defined(MACOSX))
1108       GC_add_roots_inner(DATASTART, (char *) get_end(), FALSE);
1109 #   endif
1110 #   if defined(MACOS)
1111     {
1112 #   if defined(THINK_C)
1113         extern void* GC_MacGetDataStart(void);
1114         /* globals begin above stack and end at a5. */
1115         GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
1116                            (ptr_t)LMGetCurrentA5(), FALSE);
1117 #   else
1118 #     if defined(__MWERKS__)
1119 #       if !__POWERPC__
1120           extern void* GC_MacGetDataStart(void);
1121           /* MATTHEW: Function to handle Far Globals (CW Pro 3) */
1122 #         if __option(far_data)
1123           extern void* GC_MacGetDataEnd(void);
1124 #         endif
1125           /* globals begin above stack and end at a5. */
1126           GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
1127                              (ptr_t)LMGetCurrentA5(), FALSE);
1128           /* MATTHEW: Handle Far Globals */                          
1129 #         if __option(far_data)
1130       /* Far globals follow he QD globals: */
1131           GC_add_roots_inner((ptr_t)LMGetCurrentA5(),
1132                              (ptr_t)GC_MacGetDataEnd(), FALSE);
1133 #         endif
1134 #       else
1135           extern char __data_start__[], __data_end__[];
1136           GC_add_roots_inner((ptr_t)&__data_start__,
1137                              (ptr_t)&__data_end__, FALSE);
1138 #       endif /* __POWERPC__ */
1139 #     endif /* __MWERKS__ */
1140 #   endif /* !THINK_C */
1141     }
1142 #   endif /* MACOS */
1143
1144     /* Dynamic libraries are added at every collection, since they may  */
1145     /* change.                                                          */
1146 }
1147
1148 # endif  /* ! AMIGA */
1149 # endif  /* ! MSWIN32 && ! MSWINCE*/
1150 # endif  /* ! OS2 */
1151
1152 /*
1153  * Auxiliary routines for obtaining memory from OS.
1154  */
1155
1156 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
1157         && !defined(MSWIN32) && !defined(MSWINCE) \
1158         && !defined(MACOS) && !defined(DOS4GW)
1159
1160 # ifdef SUNOS4
1161     extern caddr_t sbrk();
1162 # endif
1163 # ifdef __STDC__
1164 #   define SBRK_ARG_T ptrdiff_t
1165 # else
1166 #   define SBRK_ARG_T int
1167 # endif
1168
1169
1170 # ifdef RS6000
1171 /* The compiler seems to generate speculative reads one past the end of */
1172 /* an allocated object.  Hence we need to make sure that the page       */
1173 /* following the last heap page is also mapped.                         */
1174 ptr_t GC_unix_get_mem(bytes)
1175 word bytes;
1176 {
1177     caddr_t cur_brk = (caddr_t)sbrk(0);
1178     caddr_t result;
1179     SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
1180     static caddr_t my_brk_val = 0;
1181     
1182     if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
1183     if (lsbs != 0) {
1184         if((caddr_t)(sbrk(GC_page_size - lsbs)) == (caddr_t)(-1)) return(0);
1185     }
1186     if (cur_brk == my_brk_val) {
1187         /* Use the extra block we allocated last time. */
1188         result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
1189         if (result == (caddr_t)(-1)) return(0);
1190         result -= GC_page_size;
1191     } else {
1192         result = (ptr_t)sbrk(GC_page_size + (SBRK_ARG_T)bytes);
1193         if (result == (caddr_t)(-1)) return(0);
1194     }
1195     my_brk_val = result + bytes + GC_page_size; /* Always page aligned */
1196     return((ptr_t)result);
1197 }
1198
1199 #else  /* Not RS6000 */
1200
1201 #if defined(USE_MMAP)
1202 /* Tested only under Linux, IRIX5 and Solaris 2 */
1203
1204 #ifdef USE_MMAP_FIXED
1205 #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE
1206         /* Seems to yield better performance on Solaris 2, but can      */
1207         /* be unreliable if something is already mapped at the address. */
1208 #else
1209 #   define GC_MMAP_FLAGS MAP_PRIVATE
1210 #endif
1211
1212 #ifndef HEAP_START
1213 #   define HEAP_START 0
1214 #endif
1215
1216 ptr_t GC_unix_get_mem(bytes)
1217 word bytes;
1218 {
1219     static GC_bool initialized = FALSE;
1220     static int fd;
1221     void *result;
1222     static ptr_t last_addr = HEAP_START;
1223
1224     if (!initialized) {
1225         fd = open("/dev/zero", O_RDONLY);
1226         initialized = TRUE;
1227     }
1228     if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
1229     result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
1230                   GC_MMAP_FLAGS, fd, 0/* offset */);
1231     if (result == MAP_FAILED) return(0);
1232     last_addr = (ptr_t)result + bytes + GC_page_size - 1;
1233     last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
1234 #   if !defined(LINUX)
1235       if (last_addr == 0) {
1236         /* Oops.  We got the end of the address space.  This isn't      */
1237         /* usable by arbitrary C code, since one-past-end pointers      */
1238         /* don't work, so we discard it and try again.                  */
1239         munmap(result, (size_t)(-GC_page_size) - (size_t)result);
1240                         /* Leave last page mapped, so we can't repeat. */
1241         return GC_unix_get_mem(bytes);
1242       }
1243 #   else
1244       GC_ASSERT(last_addr != 0);
1245 #   endif
1246     return((ptr_t)result);
1247 }
1248
1249 #else /* Not RS6000, not USE_MMAP */
1250 ptr_t GC_unix_get_mem(bytes)
1251 word bytes;
1252 {
1253   ptr_t result;
1254 # ifdef IRIX5
1255     /* Bare sbrk isn't thread safe.  Play by malloc rules.      */
1256     /* The equivalent may be needed on other systems as well.   */
1257     __LOCK_MALLOC();
1258 # endif
1259   {
1260     ptr_t cur_brk = (ptr_t)sbrk(0);
1261     SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
1262     
1263     if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
1264     if (lsbs != 0) {
1265         if((ptr_t)sbrk(GC_page_size - lsbs) == (ptr_t)(-1)) return(0);
1266     }
1267     result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
1268     if (result == (ptr_t)(-1)) result = 0;
1269   }
1270 # ifdef IRIX5
1271     __UNLOCK_MALLOC();
1272 # endif
1273   return(result);
1274 }
1275
1276 #endif /* Not USE_MMAP */
1277 #endif /* Not RS6000 */
1278
1279 # endif /* UN*X */
1280
1281 # ifdef OS2
1282
1283 void * os2_alloc(size_t bytes)
1284 {
1285     void * result;
1286
1287     if (DosAllocMem(&result, bytes, PAG_EXECUTE | PAG_READ |
1288                                     PAG_WRITE | PAG_COMMIT)
1289                     != NO_ERROR) {
1290         return(0);
1291     }
1292     if (result == 0) return(os2_alloc(bytes));
1293     return(result);
1294 }
1295
1296 # endif /* OS2 */
1297
1298
1299 # if defined(MSWIN32) || defined(MSWINCE)
1300 SYSTEM_INFO GC_sysinfo;
1301 # endif
1302
1303
1304 # ifdef MSWIN32
1305 word GC_n_heap_bases = 0;
1306
1307 ptr_t GC_win32_get_mem(bytes)
1308 word bytes;
1309 {
1310     ptr_t result;
1311
1312     if (GC_win32s) {
1313         /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE.    */
1314         /* There are also unconfirmed rumors of other           */
1315         /* problems, so we dodge the issue.                     */
1316         result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
1317         result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));
1318     } else {
1319         result = (ptr_t) VirtualAlloc(NULL, bytes,
1320                                       MEM_COMMIT | MEM_RESERVE,
1321                                       PAGE_EXECUTE_READWRITE);
1322     }
1323     if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1324         /* If I read the documentation correctly, this can      */
1325         /* only happen if HBLKSIZE > 64k or not a power of 2.   */
1326     if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
1327     GC_heap_bases[GC_n_heap_bases++] = result;
1328     return(result);                       
1329 }
1330
1331 void GC_win32_free_heap ()
1332 {
1333     if (GC_win32s) {
1334         while (GC_n_heap_bases > 0) {
1335             GlobalFree (GC_heap_bases[--GC_n_heap_bases]);
1336             GC_heap_bases[GC_n_heap_bases] = 0;
1337         }
1338     }
1339 }
1340 # endif
1341
1342 #ifdef AMIGA
1343 # define GC_AMIGA_AM
1344 # include "AmigaOS.c"
1345 # undef GC_AMIGA_AM
1346 #endif
1347
1348
1349 # ifdef MSWINCE
1350 word GC_n_heap_bases = 0;
1351
1352 ptr_t GC_wince_get_mem(bytes)
1353 word bytes;
1354 {
1355     ptr_t result;
1356     word i;
1357
1358     /* Round up allocation size to multiple of page size */
1359     bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1);
1360
1361     /* Try to find reserved, uncommitted pages */
1362     for (i = 0; i < GC_n_heap_bases; i++) {
1363         if (((word)(-(signed_word)GC_heap_lengths[i])
1364              & (GC_sysinfo.dwAllocationGranularity-1))
1365             >= bytes) {
1366             result = GC_heap_bases[i] + GC_heap_lengths[i];
1367             break;
1368         }
1369     }
1370
1371     if (i == GC_n_heap_bases) {
1372         /* Reserve more pages */
1373         word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
1374                          & ~(GC_sysinfo.dwAllocationGranularity-1);
1375         result = (ptr_t) VirtualAlloc(NULL, res_bytes,
1376                                       MEM_RESERVE | MEM_TOP_DOWN,
1377                                       PAGE_EXECUTE_READWRITE);
1378         if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1379             /* If I read the documentation correctly, this can  */
1380             /* only happen if HBLKSIZE > 64k or not a power of 2.       */
1381         if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
1382         GC_heap_bases[GC_n_heap_bases] = result;
1383         GC_heap_lengths[GC_n_heap_bases] = 0;
1384         GC_n_heap_bases++;
1385     }
1386
1387     /* Commit pages */
1388     result = (ptr_t) VirtualAlloc(result, bytes,
1389                                   MEM_COMMIT,
1390                                   PAGE_EXECUTE_READWRITE);
1391     if (result != NULL) {
1392         if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1393         GC_heap_lengths[i] += bytes;
1394     }
1395
1396     return(result);                       
1397 }
1398 # endif
1399
1400 #ifdef USE_MUNMAP
1401
1402 /* For now, this only works on Win32/WinCE and some Unix-like   */
1403 /* systems.  If you have something else, don't define           */
1404 /* USE_MUNMAP.                                                  */
1405 /* We assume ANSI C to support this feature.                    */
1406
1407 #if !defined(MSWIN32) && !defined(MSWINCE)
1408
1409 #include <unistd.h>
1410 #include <sys/mman.h>
1411 #include <sys/stat.h>
1412 #include <sys/types.h>
1413
1414 #endif
1415
1416 /* Compute a page aligned starting address for the unmap        */
1417 /* operation on a block of size bytes starting at start.        */
1418 /* Return 0 if the block is too small to make this feasible.    */
1419 ptr_t GC_unmap_start(ptr_t start, word bytes)
1420 {
1421     ptr_t result = start;
1422     /* Round start to next page boundary.       */
1423         result += GC_page_size - 1;
1424         result = (ptr_t)((word)result & ~(GC_page_size - 1));
1425     if (result + GC_page_size > start + bytes) return 0;
1426     return result;
1427 }
1428
1429 /* Compute end address for an unmap operation on the indicated  */
1430 /* block.                                                       */
1431 ptr_t GC_unmap_end(ptr_t start, word bytes)
1432 {
1433     ptr_t end_addr = start + bytes;
1434     end_addr = (ptr_t)((word)end_addr & ~(GC_page_size - 1));
1435     return end_addr;
1436 }
1437
1438 /* Under Win32/WinCE we commit (map) and decommit (unmap)       */
1439 /* memory using VirtualAlloc and VirtualFree.  These functions  */
1440 /* work on individual allocations of virtual memory, made       */
1441 /* previously using VirtualAlloc with the MEM_RESERVE flag.     */
1442 /* The ranges we need to (de)commit may span several of these   */
1443 /* allocations; therefore we use VirtualQuery to check          */
1444 /* allocation lengths, and split up the range as necessary.     */
1445
1446 /* We assume that GC_remap is called on exactly the same range  */
1447 /* as a previous call to GC_unmap.  It is safe to consistently  */
1448 /* round the endpoints in both places.                          */
1449 void GC_unmap(ptr_t start, word bytes)
1450 {
1451     ptr_t start_addr = GC_unmap_start(start, bytes);
1452     ptr_t end_addr = GC_unmap_end(start, bytes);
1453     word len = end_addr - start_addr;
1454     if (0 == start_addr) return;
1455 #   if defined(MSWIN32) || defined(MSWINCE)
1456       while (len != 0) {
1457           MEMORY_BASIC_INFORMATION mem_info;
1458           GC_word free_len;
1459           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1460               != sizeof(mem_info))
1461               ABORT("Weird VirtualQuery result");
1462           free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1463           if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
1464               ABORT("VirtualFree failed");
1465           GC_unmapped_bytes += free_len;
1466           start_addr += free_len;
1467           len -= free_len;
1468       }
1469 #   else
1470       if (munmap(start_addr, len) != 0) ABORT("munmap failed");
1471       GC_unmapped_bytes += len;
1472 #   endif
1473 }
1474
1475
1476 void GC_remap(ptr_t start, word bytes)
1477 {
1478     static int zero_descr = -1;
1479     ptr_t start_addr = GC_unmap_start(start, bytes);
1480     ptr_t end_addr = GC_unmap_end(start, bytes);
1481     word len = end_addr - start_addr;
1482     ptr_t result;
1483
1484 #   if defined(MSWIN32) || defined(MSWINCE)
1485       if (0 == start_addr) return;
1486       while (len != 0) {
1487           MEMORY_BASIC_INFORMATION mem_info;
1488           GC_word alloc_len;
1489           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1490               != sizeof(mem_info))
1491               ABORT("Weird VirtualQuery result");
1492           alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1493           result = VirtualAlloc(start_addr, alloc_len,
1494                                 MEM_COMMIT,
1495                                 PAGE_EXECUTE_READWRITE);
1496           if (result != start_addr) {
1497               ABORT("VirtualAlloc remapping failed");
1498           }
1499           GC_unmapped_bytes -= alloc_len;
1500           start_addr += alloc_len;
1501           len -= alloc_len;
1502       }
1503 #   else
1504       if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);
1505       if (0 == start_addr) return;
1506       result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
1507                     MAP_FIXED | MAP_PRIVATE, zero_descr, 0);
1508       if (result != start_addr) {
1509           ABORT("mmap remapping failed");
1510       }
1511       GC_unmapped_bytes -= len;
1512 #   endif
1513 }
1514
1515 /* Two adjacent blocks have already been unmapped and are about to      */
1516 /* be merged.  Unmap the whole block.  This typically requires          */
1517 /* that we unmap a small section in the middle that was not previously  */
1518 /* unmapped due to alignment constraints.                               */
1519 void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
1520 {
1521     ptr_t start1_addr = GC_unmap_start(start1, bytes1);
1522     ptr_t end1_addr = GC_unmap_end(start1, bytes1);
1523     ptr_t start2_addr = GC_unmap_start(start2, bytes2);
1524     ptr_t end2_addr = GC_unmap_end(start2, bytes2);
1525     ptr_t start_addr = end1_addr;
1526     ptr_t end_addr = start2_addr;
1527     word len;
1528     GC_ASSERT(start1 + bytes1 == start2);
1529     if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2);
1530     if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
1531     if (0 == start_addr) return;
1532     len = end_addr - start_addr;
1533 #   if defined(MSWIN32) || defined(MSWINCE)
1534       while (len != 0) {
1535           MEMORY_BASIC_INFORMATION mem_info;
1536           GC_word free_len;
1537           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1538               != sizeof(mem_info))
1539               ABORT("Weird VirtualQuery result");
1540           free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1541           if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
1542               ABORT("VirtualFree failed");
1543           GC_unmapped_bytes += free_len;
1544           start_addr += free_len;
1545           len -= free_len;
1546       }
1547 #   else
1548       if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
1549       GC_unmapped_bytes += len;
1550 #   endif
1551 }
1552
1553 #endif /* USE_MUNMAP */
1554
1555 /* Routine for pushing any additional roots.  In THREADS        */
1556 /* environment, this is also responsible for marking from       */
1557 /* thread stacks.                                               */
1558 #ifndef THREADS
1559 void (*GC_push_other_roots)() = 0;
1560 #else /* THREADS */
1561
1562 # ifdef PCR
1563 PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy)
1564 {
1565     struct PCR_ThCtl_TInfoRep info;
1566     PCR_ERes result;
1567     
1568     info.ti_stkLow = info.ti_stkHi = 0;
1569     result = PCR_ThCtl_GetInfo(t, &info);
1570     GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi));
1571     return(result);
1572 }
1573
1574 /* Push the contents of an old object. We treat this as stack   */
1575 /* data only becasue that makes it robust against mark stack    */
1576 /* overflow.                                                    */
1577 PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data)
1578 {
1579     GC_push_all_stack((ptr_t)p, (ptr_t)p + size);
1580     return(PCR_ERes_okay);
1581 }
1582
1583
1584 void GC_default_push_other_roots GC_PROTO((void))
1585 {
1586     /* Traverse data allocated by previous memory managers.             */
1587         {
1588           extern struct PCR_MM_ProcsRep * GC_old_allocator;
1589           
1590           if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false,
1591                                                    GC_push_old_obj, 0)
1592               != PCR_ERes_okay) {
1593               ABORT("Old object enumeration failed");
1594           }
1595         }
1596     /* Traverse all thread stacks. */
1597         if (PCR_ERes_IsErr(
1598                 PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0))
1599               || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) {
1600               ABORT("Thread stack marking failed\n");
1601         }
1602 }
1603
1604 # endif /* PCR */
1605
1606 # ifdef SRC_M3
1607
1608 # ifdef ALL_INTERIOR_POINTERS
1609     --> misconfigured
1610 # endif
1611
1612 void GC_push_thread_structures GC_PROTO((void))
1613 {
1614     /* Not our responsibibility. */
1615 }
1616
1617 extern void ThreadF__ProcessStacks();
1618
1619 void GC_push_thread_stack(start, stop)
1620 word start, stop;
1621 {
1622    GC_push_all_stack((ptr_t)start, (ptr_t)stop + sizeof(word));
1623 }
1624
1625 /* Push routine with M3 specific calling convention. */
1626 GC_m3_push_root(dummy1, p, dummy2, dummy3)
1627 word *p;
1628 ptr_t dummy1, dummy2;
1629 int dummy3;
1630 {
1631     word q = *p;
1632     
1633     GC_PUSH_ONE_STACK(q, p);
1634 }
1635
1636 /* M3 set equivalent to RTHeap.TracedRefTypes */
1637 typedef struct { int elts[1]; }  RefTypeSet;
1638 RefTypeSet GC_TracedRefTypes = {{0x1}};
1639
1640 void GC_default_push_other_roots GC_PROTO((void))
1641 {
1642     /* Use the M3 provided routine for finding static roots.     */
1643     /* This is a bit dubious, since it presumes no C roots.      */
1644     /* We handle the collector roots explicitly in GC_push_roots */
1645         RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes);
1646         if (GC_words_allocd > 0) {
1647             ThreadF__ProcessStacks(GC_push_thread_stack);
1648         }
1649         /* Otherwise this isn't absolutely necessary, and we have       */
1650         /* startup ordering problems.                                   */
1651 }
1652
1653 # endif /* SRC_M3 */
1654
1655 # if defined(GC_SOLARIS_THREADS) || defined(GC_PTHREADS) || \
1656      defined(GC_WIN32_THREADS)
1657
1658 extern void GC_push_all_stacks();
1659
1660 void GC_default_push_other_roots GC_PROTO((void))
1661 {
1662     GC_push_all_stacks();
1663 }
1664
1665 # endif /* GC_SOLARIS_THREADS || GC_PTHREADS */
1666
1667 void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
1668
1669 #endif /* THREADS */
1670
1671 /*
1672  * Routines for accessing dirty  bits on virtual pages.
1673  * We plan to eventually implement four strategies for doing so:
1674  * DEFAULT_VDB: A simple dummy implementation that treats every page
1675  *              as possibly dirty.  This makes incremental collection
1676  *              useless, but the implementation is still correct.
1677  * PCR_VDB:     Use PPCRs virtual dirty bit facility.
1678  * PROC_VDB:    Use the /proc facility for reading dirty bits.  Only
1679  *              works under some SVR4 variants.  Even then, it may be
1680  *              too slow to be entirely satisfactory.  Requires reading
1681  *              dirty bits for entire address space.  Implementations tend
1682  *              to assume that the client is a (slow) debugger.
1683  * MPROTECT_VDB:Protect pages and then catch the faults to keep track of
1684  *              dirtied pages.  The implementation (and implementability)
1685  *              is highly system dependent.  This usually fails when system
1686  *              calls write to a protected page.  We prevent the read system
1687  *              call from doing so.  It is the clients responsibility to
1688  *              make sure that other system calls are similarly protected
1689  *              or write only to the stack.
1690  */
1691  
1692 GC_bool GC_dirty_maintained = FALSE;
1693
1694 # ifdef DEFAULT_VDB
1695
1696 /* All of the following assume the allocation lock is held, and */
1697 /* signals are disabled.                                        */
1698
1699 /* The client asserts that unallocated pages in the heap are never      */
1700 /* written.                                                             */
1701
1702 /* Initialize virtual dirty bit implementation.                 */
1703 void GC_dirty_init()
1704 {
1705     GC_dirty_maintained = TRUE;
1706 }
1707
1708 /* Retrieve system dirty bits for heap to a local buffer.       */
1709 /* Restore the systems notion of which pages are dirty.         */
1710 void GC_read_dirty()
1711 {}
1712
1713 /* Is the HBLKSIZE sized page at h marked dirty in the local buffer?    */
1714 /* If the actual page size is different, this returns TRUE if any       */
1715 /* of the pages overlapping h are dirty.  This routine may err on the   */
1716 /* side of labelling pages as dirty (and this implementation does).     */
1717 /*ARGSUSED*/
1718 GC_bool GC_page_was_dirty(h)
1719 struct hblk *h;
1720 {
1721     return(TRUE);
1722 }
1723
1724 /*
1725  * The following two routines are typically less crucial.  They matter
1726  * most with large dynamic libraries, or if we can't accurately identify
1727  * stacks, e.g. under Solaris 2.X.  Otherwise the following default
1728  * versions are adequate.
1729  */
1730  
1731 /* Could any valid GC heap pointer ever have been written to this page? */
1732 /*ARGSUSED*/
1733 GC_bool GC_page_was_ever_dirty(h)
1734 struct hblk *h;
1735 {
1736     return(TRUE);
1737 }
1738
1739 /* Reset the n pages starting at h to "was never dirty" status. */
1740 void GC_is_fresh(h, n)
1741 struct hblk *h;
1742 word n;
1743 {
1744 }
1745
1746 /* A call hints that h is about to be written.  */
1747 /* May speed up some dirty bit implementations. */
1748 /*ARGSUSED*/
1749 void GC_write_hint(h)
1750 struct hblk *h;
1751 {
1752 }
1753
1754 # endif /* DEFAULT_VDB */
1755
1756
1757 # ifdef MPROTECT_VDB
1758
1759 /*
1760  * See DEFAULT_VDB for interface descriptions.
1761  */
1762
1763 /*
1764  * This implementation maintains dirty bits itself by catching write
1765  * faults and keeping track of them.  We assume nobody else catches
1766  * SIGBUS or SIGSEGV.  We assume no write faults occur in system calls
1767  * except as a result of a read system call.  This means clients must
1768  * either ensure that system calls do not touch the heap, or must
1769  * provide their own wrappers analogous to the one for read.
1770  * We assume the page size is a multiple of HBLKSIZE.
1771  * This implementation is currently SunOS 4.X and IRIX 5.X specific, though we
1772  * tried to use portable code where easily possible.  It is known
1773  * not to work under a number of other systems.
1774  */
1775
1776 # if !defined(MSWIN32) && !defined(MSWINCE)
1777
1778 #   include <sys/mman.h>
1779 #   include <signal.h>
1780 #   include <sys/syscall.h>
1781
1782 #   define PROTECT(addr, len) \
1783           if (mprotect((caddr_t)(addr), (size_t)(len), \
1784                        PROT_READ | OPT_PROT_EXEC) < 0) { \
1785             ABORT("mprotect failed"); \
1786           }
1787 #   define UNPROTECT(addr, len) \
1788           if (mprotect((caddr_t)(addr), (size_t)(len), \
1789                        PROT_WRITE | PROT_READ | OPT_PROT_EXEC ) < 0) { \
1790             ABORT("un-mprotect failed"); \
1791           }
1792           
1793 # else
1794
1795 #   ifndef MSWINCE
1796 #     include <signal.h>
1797 #   endif
1798
1799     static DWORD protect_junk;
1800 #   define PROTECT(addr, len) \
1801           if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READ, \
1802                               &protect_junk)) { \
1803             DWORD last_error = GetLastError(); \
1804             GC_printf1("Last error code: %lx\n", last_error); \
1805             ABORT("VirtualProtect failed"); \
1806           }
1807 #   define UNPROTECT(addr, len) \
1808           if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READWRITE, \
1809                               &protect_junk)) { \
1810             ABORT("un-VirtualProtect failed"); \
1811           }
1812           
1813 # endif
1814
1815 #if defined(SUNOS4) || defined(FREEBSD)
1816     typedef void (* SIG_PF)();
1817 #endif
1818 #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \
1819     || defined(MACOSX) || defined(HURD)
1820 # ifdef __STDC__
1821     typedef void (* SIG_PF)(int);
1822 # else
1823     typedef void (* SIG_PF)();
1824 # endif
1825 #endif
1826 #if defined(MSWIN32)
1827     typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF;
1828 #   undef SIG_DFL
1829 #   define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1)
1830 #endif
1831 #if defined(MSWINCE)
1832     typedef LONG (WINAPI *SIG_PF)(struct _EXCEPTION_POINTERS *);
1833 #   undef SIG_DFL
1834 #   define SIG_DFL (SIG_PF) (-1)
1835 #endif
1836
1837 #if defined(IRIX5) || defined(OSF1) || defined(HURD)
1838     typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
1839 #endif
1840 #if defined(SUNOS5SIGS)
1841 # ifdef HPUX
1842 #   define SIGINFO __siginfo
1843 # else
1844 #   define SIGINFO siginfo
1845 # endif
1846 # ifdef __STDC__
1847     typedef void (* REAL_SIG_PF)(int, struct SIGINFO *, void *);
1848 # else
1849     typedef void (* REAL_SIG_PF)();
1850 # endif
1851 #endif
1852 #if defined(LINUX)
1853 #   include <linux/version.h>
1854 #   if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(IA64)
1855       typedef struct sigcontext s_c;
1856 #   else
1857       typedef struct sigcontext_struct s_c;
1858 #   endif
1859 #   if defined(ALPHA) || defined(M68K)
1860       typedef void (* REAL_SIG_PF)(int, int, s_c *);
1861 #   else
1862 #     if defined(IA64) || defined(HP_PA)
1863         typedef void (* REAL_SIG_PF)(int, siginfo_t *, s_c *);
1864 #     else
1865         typedef void (* REAL_SIG_PF)(int, s_c);
1866 #     endif
1867 #   endif
1868 #   ifdef ALPHA
1869     /* Retrieve fault address from sigcontext structure by decoding     */
1870     /* instruction.                                                     */
1871     char * get_fault_addr(s_c *sc) {
1872         unsigned instr;
1873         word faultaddr;
1874
1875         instr = *((unsigned *)(sc->sc_pc));
1876         faultaddr = sc->sc_regs[(instr >> 16) & 0x1f];
1877         faultaddr += (word) (((int)instr << 16) >> 16);
1878         return (char *)faultaddr;
1879     }
1880 #   endif /* !ALPHA */
1881 # endif
1882
1883 # if defined(MACOSX) /* Should also test for PowerPC? */
1884     typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
1885
1886 /* Decodes the machine instruction which was responsible for the sending of the
1887    SIGBUS signal. Sadly this is the only way to find the faulting address because
1888    the signal handler doesn't get it directly from the kernel (although it is
1889    available on the Mach level, but droppped by the BSD personality before it
1890    calls our signal handler...)
1891    This code should be able to deal correctly with all PPCs starting from the
1892    601 up to and including the G4s (including Velocity Engine). */
1893 #define EXTRACT_OP1(iw)     (((iw) & 0xFC000000) >> 26)
1894 #define EXTRACT_OP2(iw)     (((iw) & 0x000007FE) >> 1)
1895 #define EXTRACT_REGA(iw)    (((iw) & 0x001F0000) >> 16)
1896 #define EXTRACT_REGB(iw)    (((iw) & 0x03E00000) >> 21)
1897 #define EXTRACT_REGC(iw)    (((iw) & 0x0000F800) >> 11)
1898 #define EXTRACT_DISP(iw)    ((short *) &(iw))[1]
1899
1900 static char *get_fault_addr(struct sigcontext *scp)
1901 {
1902    unsigned int   instr = *((unsigned int *) scp->sc_ir);
1903    unsigned int * regs = &((unsigned int *) scp->sc_regs)[2];
1904    int            disp = 0, tmp;
1905    unsigned int   baseA = 0, baseB = 0;
1906    unsigned int   addr, alignmask = 0xFFFFFFFF;
1907
1908 #ifdef GC_DEBUG_DECODER
1909    GC_err_printf1("Instruction: 0x%lx\n", instr);
1910    GC_err_printf1("Opcode 1: d\n", (int)EXTRACT_OP1(instr));
1911 #endif
1912    switch(EXTRACT_OP1(instr)) {
1913       case 38:   /* stb */
1914       case 39:   /* stbu */
1915       case 54:   /* stfd */
1916       case 55:   /* stfdu */
1917       case 52:   /* stfs */
1918       case 53:   /* stfsu */
1919       case 44:   /* sth */
1920       case 45:   /* sthu */
1921       case 47:   /* stmw */
1922       case 36:   /* stw */
1923       case 37:   /* stwu */
1924             tmp = EXTRACT_REGA(instr);
1925             if(tmp > 0)
1926                baseA = regs[tmp];
1927             disp = EXTRACT_DISP(instr);
1928             break;
1929       case 31:
1930 #ifdef GC_DEBUG_DECODER
1931             GC_err_printf1("Opcode 2: %d\n", (int)EXTRACT_OP2(instr));
1932 #endif
1933             switch(EXTRACT_OP2(instr)) {
1934                case 86:    /* dcbf */
1935                case 54:    /* dcbst */
1936                case 1014:  /* dcbz */
1937                case 247:   /* stbux */
1938                case 215:   /* stbx */
1939                case 759:   /* stfdux */
1940                case 727:   /* stfdx */
1941                case 983:   /* stfiwx */
1942                case 695:   /* stfsux */
1943                case 663:   /* stfsx */
1944                case 918:   /* sthbrx */
1945                case 439:   /* sthux */
1946                case 407:   /* sthx */
1947                case 661:   /* stswx */
1948                case 662:   /* stwbrx */
1949                case 150:   /* stwcx. */
1950                case 183:   /* stwux */
1951                case 151:   /* stwx */
1952                case 135:   /* stvebx */
1953                case 167:   /* stvehx */
1954                case 199:   /* stvewx */
1955                case 231:   /* stvx */
1956                case 487:   /* stvxl */
1957                      tmp = EXTRACT_REGA(instr);
1958                      if(tmp > 0)
1959                         baseA = regs[tmp];
1960                         baseB = regs[EXTRACT_REGC(instr)];
1961                         /* determine Altivec alignment mask */
1962                         switch(EXTRACT_OP2(instr)) {
1963                            case 167:   /* stvehx */
1964                                  alignmask = 0xFFFFFFFE;
1965                                  break;
1966                            case 199:   /* stvewx */
1967                                  alignmask = 0xFFFFFFFC;
1968                                  break;
1969                            case 231:   /* stvx */
1970                                  alignmask = 0xFFFFFFF0;
1971                                  break;
1972                            case 487:  /* stvxl */
1973                                  alignmask = 0xFFFFFFF0;
1974                                  break;
1975                         }
1976                         break;
1977                case 725:   /* stswi */
1978                      tmp = EXTRACT_REGA(instr);
1979                      if(tmp > 0)
1980                         baseA = regs[tmp];
1981                         break;
1982                default:   /* ignore instruction */
1983 #ifdef GC_DEBUG_DECODER
1984                      GC_err_printf("Ignored by inner handler\n");
1985 #endif
1986                      return NULL;
1987                     break;
1988             }
1989             break;
1990       default:   /* ignore instruction */
1991 #ifdef GC_DEBUG_DECODER
1992             GC_err_printf("Ignored by main handler\n");
1993 #endif
1994             return NULL;
1995             break;
1996    }
1997         
1998    addr = (baseA + baseB) + disp;
1999   addr &= alignmask;
2000 #ifdef GC_DEBUG_DECODER
2001    GC_err_printf1("BaseA: %d\n", baseA);
2002    GC_err_printf1("BaseB: %d\n", baseB);
2003    GC_err_printf1("Disp:  %d\n", disp);
2004    GC_err_printf1("Address: %d\n", addr);
2005 #endif
2006    return (char *)addr;
2007 }
2008 #endif /* MACOSX */
2009
2010 SIG_PF GC_old_bus_handler;
2011 SIG_PF GC_old_segv_handler;     /* Also old MSWIN32 ACCESS_VIOLATION filter */
2012
2013 #ifdef THREADS
2014 /* We need to lock around the bitmap update in the write fault handler  */
2015 /* in order to avoid the risk of losing a bit.  We do this with a       */
2016 /* test-and-set spin lock if we know how to do that.  Otherwise we      */
2017 /* check whether we are already in the handler and use the dumb but     */
2018 /* safe fallback algorithm of setting all bits in the word.             */
2019 /* Contention should be very rare, so we do the minimum to handle it    */
2020 /* correctly.                                                           */
2021 #ifdef GC_TEST_AND_SET_DEFINED
2022   static VOLATILE unsigned int fault_handler_lock = 0;
2023   void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
2024     while (GC_test_and_set(&fault_handler_lock)) {}
2025     /* Could also revert to set_pht_entry_from_index_safe if initial    */
2026     /* GC_test_and_set fails.                                           */
2027     set_pht_entry_from_index(db, index);
2028     GC_clear(&fault_handler_lock);
2029   }
2030 #else /* !GC_TEST_AND_SET_DEFINED */
2031   /* THIS IS INCORRECT! The dirty bit vector may be temporarily wrong,  */
2032   /* just before we notice the conflict and correct it. We may end up   */
2033   /* looking at it while it's wrong.  But this requires contention      */
2034   /* exactly when a GC is triggered, which seems far less likely to     */
2035   /* fail than the old code, which had no reported failures.  Thus we   */
2036   /* leave it this way while we think of something better, or support   */
2037   /* GC_test_and_set on the remaining platforms.                        */
2038   static VOLATILE word currently_updating = 0;
2039   void async_set_pht_entry_from_index(VOLATILE page_hash_table db, int index) {
2040     unsigned int update_dummy;
2041     currently_updating = (word)(&update_dummy);
2042     set_pht_entry_from_index(db, index);
2043     /* If we get contention in the 10 or so instruction window here,    */
2044     /* and we get stopped by a GC between the two updates, we lose!     */
2045     if (currently_updating != (word)(&update_dummy)) {
2046         set_pht_entry_from_index_safe(db, index);
2047         /* We claim that if two threads concurrently try to update the  */
2048         /* dirty bit vector, the first one to execute UPDATE_START      */
2049         /* will see it changed when UPDATE_END is executed.  (Note that */
2050         /* &update_dummy must differ in two distinct threads.)  It      */
2051         /* will then execute set_pht_entry_from_index_safe, thus        */
2052         /* returning us to a safe state, though not soon enough.        */
2053     }
2054   }
2055 #endif /* !GC_TEST_AND_SET_DEFINED */
2056 #else /* !THREADS */
2057 # define async_set_pht_entry_from_index(db, index) \
2058         set_pht_entry_from_index(db, index)
2059 #endif /* !THREADS */
2060
2061 /*ARGSUSED*/
2062 # if defined (SUNOS4) || defined(FREEBSD)
2063     void GC_write_fault_handler(sig, code, scp, addr)
2064     int sig, code;
2065     struct sigcontext *scp;
2066     char * addr;
2067 #   ifdef SUNOS4
2068 #     define SIG_OK (sig == SIGSEGV || sig == SIGBUS)
2069 #     define CODE_OK (FC_CODE(code) == FC_PROT \
2070                     || (FC_CODE(code) == FC_OBJERR \
2071                        && FC_ERRNO(code) == FC_PROT))
2072 #   endif
2073 #   ifdef FREEBSD
2074 #     define SIG_OK (sig == SIGBUS)
2075 #     define CODE_OK (code == BUS_PAGE_FAULT)
2076 #   endif
2077 # endif
2078 # if defined(IRIX5) || defined(OSF1) || defined(HURD)
2079 #   include <errno.h>
2080     void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
2081 #   ifdef OSF1
2082 #     define SIG_OK (sig == SIGSEGV)
2083 #     define CODE_OK (code == 2 /* experimentally determined */)
2084 #   endif
2085 #   ifdef IRIX5
2086 #     define SIG_OK (sig == SIGSEGV)
2087 #     define CODE_OK (code == EACCES)
2088 #   endif
2089 #   ifdef HURD
2090 #     define SIG_OK (sig == SIGBUS || sig == SIGSEGV)   
2091 #     define CODE_OK  TRUE
2092 #   endif
2093 # endif
2094 # if defined(LINUX)
2095 #   if defined(ALPHA) || defined(M68K)
2096       void GC_write_fault_handler(int sig, int code, s_c * sc)
2097 #   else
2098 #     if defined(IA64) || defined(HP_PA)
2099         void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp)
2100 #     else
2101         void GC_write_fault_handler(int sig, s_c sc)
2102 #     endif
2103 #   endif
2104 #   define SIG_OK (sig == SIGSEGV)
2105 #   define CODE_OK TRUE
2106         /* Empirically c.trapno == 14, on IA32, but is that useful?     */
2107         /* Should probably consider alignment issues on other           */
2108         /* architectures.                                               */
2109 # endif
2110 # if defined(SUNOS5SIGS)
2111 #  ifdef __STDC__
2112     void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context)
2113 #  else
2114     void GC_write_fault_handler(sig, scp, context)
2115     int sig;
2116     struct SIGINFO *scp;
2117     void * context;
2118 #  endif
2119 #   ifdef HPUX
2120 #     define SIG_OK (sig == SIGSEGV || sig == SIGBUS)
2121 #     define CODE_OK (scp -> si_code == SEGV_ACCERR) \
2122                      || (scp -> si_code == BUS_ADRERR) \
2123                      || (scp -> si_code == BUS_UNKNOWN) \
2124                      || (scp -> si_code == SEGV_UNKNOWN) \
2125                      || (scp -> si_code == BUS_OBJERR)
2126 #   else
2127 #     define SIG_OK (sig == SIGSEGV)
2128 #     define CODE_OK (scp -> si_code == SEGV_ACCERR)
2129 #   endif
2130 # endif
2131
2132 # if defined(MACOSX)
2133     void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
2134 #   define SIG_OK (sig == SIGBUS)
2135 #   define CODE_OK (code == 0 /* experimentally determined */)
2136 # endif
2137
2138 # if defined(MSWIN32) || defined(MSWINCE)
2139     LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info)
2140 #   define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode == \
2141                         STATUS_ACCESS_VIOLATION)
2142 #   define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1)
2143                         /* Write fault */
2144 # endif
2145 {
2146     register unsigned i;
2147 #   if defined(HURD) 
2148         char *addr = (char *) code;
2149 #   endif
2150 #   ifdef IRIX5
2151         char * addr = (char *) (size_t) (scp -> sc_badvaddr);
2152 #   endif
2153 #   if defined(OSF1) && defined(ALPHA)
2154         char * addr = (char *) (scp -> sc_traparg_a0);
2155 #   endif
2156 #   ifdef SUNOS5SIGS
2157         char * addr = (char *) (scp -> si_addr);
2158 #   endif
2159 #   ifdef LINUX
2160 #     ifdef I386
2161         char * addr = (char *) (sc.cr2);
2162 #     else
2163 #       if defined(M68K)
2164           char * addr = NULL;
2165
2166           struct sigcontext *scp = (struct sigcontext *)(sc);
2167
2168           int format = (scp->sc_formatvec >> 12) & 0xf;
2169           unsigned long *framedata = (unsigned long *)(scp + 1); 
2170           unsigned long ea;
2171
2172           if (format == 0xa || format == 0xb) {
2173                 /* 68020/030 */
2174                 ea = framedata[2];
2175           } else if (format == 7) {
2176                 /* 68040 */
2177                 ea = framedata[3];
2178                 if (framedata[1] & 0x08000000) {
2179                         /* correct addr on misaligned access */
2180                         ea = (ea+4095)&(~4095);
2181                 }
2182           } else if (format == 4) {
2183                 /* 68060 */
2184                 ea = framedata[0];
2185                 if (framedata[1] & 0x08000000) {
2186                         /* correct addr on misaligned access */
2187                         ea = (ea+4095)&(~4095);
2188                 }
2189           }     
2190           addr = (char *)ea;
2191 #       else
2192 #         ifdef ALPHA
2193             char * addr = get_fault_addr(sc);
2194 #         else
2195 #           if defined(IA64) || defined(HP_PA)
2196               char * addr = si -> si_addr;
2197               /* I believe this is claimed to work on all platforms for */
2198               /* Linux 2.3.47 and later.  Hopefully we don't have to    */
2199               /* worry about earlier kernels on IA64.                   */
2200 #           else
2201 #             if defined(POWERPC)
2202                 char * addr = (char *) (sc.regs->dar);
2203 #             else
2204                 --> architecture not supported
2205 #             endif
2206 #           endif
2207 #         endif
2208 #       endif
2209 #     endif
2210 #   endif
2211 #   if defined(MACOSX)
2212         char * addr = get_fault_addr(scp);
2213 #   endif
2214 #   if defined(MSWIN32) || defined(MSWINCE)
2215         char * addr = (char *) (exc_info -> ExceptionRecord
2216                                 -> ExceptionInformation[1]);
2217 #       define sig SIGSEGV
2218 #   endif
2219     
2220     if (SIG_OK && CODE_OK) {
2221         register struct hblk * h =
2222                         (struct hblk *)((word)addr & ~(GC_page_size-1));
2223         GC_bool in_allocd_block;
2224         
2225 #       ifdef SUNOS5SIGS
2226             /* Address is only within the correct physical page.        */
2227             in_allocd_block = FALSE;
2228             for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
2229               if (HDR(h+i) != 0) {
2230                 in_allocd_block = TRUE;
2231               }
2232             }
2233 #       else
2234             in_allocd_block = (HDR(addr) != 0);
2235 #       endif
2236         if (!in_allocd_block) {
2237             /* Heap blocks now begin and end on page boundaries */
2238             SIG_PF old_handler;
2239             
2240             if (sig == SIGSEGV) {
2241                 old_handler = GC_old_segv_handler;
2242             } else {
2243                 old_handler = GC_old_bus_handler;
2244             }
2245             if (old_handler == SIG_DFL) {
2246 #               if !defined(MSWIN32) && !defined(MSWINCE)
2247                     GC_err_printf1("Segfault at 0x%lx\n", addr);
2248                     ABORT("Unexpected bus error or segmentation fault");
2249 #               else
2250                     return(EXCEPTION_CONTINUE_SEARCH);
2251 #               endif
2252             } else {
2253 #               if defined (SUNOS4) || defined(FREEBSD)
2254                     (*old_handler) (sig, code, scp, addr);
2255                     return;
2256 #               endif
2257 #               if defined (SUNOS5SIGS)
2258                     (*(REAL_SIG_PF)old_handler) (sig, scp, context);
2259                     return;
2260 #               endif
2261 #               if defined (LINUX)
2262 #                   if defined(ALPHA) || defined(M68K)
2263                         (*(REAL_SIG_PF)old_handler) (sig, code, sc);
2264 #                   else 
2265 #                     if defined(IA64) || defined(HP_PA)
2266                         (*(REAL_SIG_PF)old_handler) (sig, si, scp);
2267 #                     else
2268                         (*(REAL_SIG_PF)old_handler) (sig, sc);
2269 #                     endif
2270 #                   endif
2271                     return;
2272 #               endif
2273 #               if defined (IRIX5) || defined(OSF1) || defined(HURD)
2274                     (*(REAL_SIG_PF)old_handler) (sig, code, scp);
2275                     return;
2276 #               endif
2277 #               ifdef MACOSX
2278                     (*(REAL_SIG_PF)old_handler) (sig, code, scp);
2279 #               endif
2280 #               ifdef MSWIN32
2281                     return((*old_handler)(exc_info));
2282 #               endif
2283             }
2284         }
2285         UNPROTECT(h, GC_page_size);
2286         /* We need to make sure that no collection occurs between       */
2287         /* the UNPROTECT and the setting of the dirty bit.  Otherwise   */
2288         /* a write by a third thread might go unnoticed.  Reversing     */
2289         /* the order is just as bad, since we would end up unprotecting */
2290         /* a page in a GC cycle during which it's not marked.           */
2291         /* Currently we do this by disabling the thread stopping        */
2292         /* signals while this handler is running.  An alternative might */
2293         /* be to record the fact that we're about to unprotect, or      */
2294         /* have just unprotected a page in the GC's thread structure,   */
2295         /* and then to have the thread stopping code set the dirty      */
2296         /* flag, if necessary.                                          */
2297         for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
2298             register int index = PHT_HASH(h+i);
2299             
2300             async_set_pht_entry_from_index(GC_dirty_pages, index);
2301         }
2302 #       if defined(OSF1)
2303             /* These reset the signal handler each time by default. */
2304             signal(SIGSEGV, (SIG_PF) GC_write_fault_handler);
2305 #       endif
2306         /* The write may not take place before dirty bits are read.     */
2307         /* But then we'll fault again ...                               */
2308 #       if defined(MSWIN32) || defined(MSWINCE)
2309             return(EXCEPTION_CONTINUE_EXECUTION);
2310 #       else
2311             return;
2312 #       endif
2313     }
2314 #if defined(MSWIN32) || defined(MSWINCE)
2315     return EXCEPTION_CONTINUE_SEARCH;
2316 #else
2317     GC_err_printf1("Segfault at 0x%lx\n", addr);
2318     ABORT("Unexpected bus error or segmentation fault");
2319 #endif
2320 }
2321
2322 /*
2323  * We hold the allocation lock.  We expect block h to be written
2324  * shortly.
2325  */
2326 void GC_write_hint(h)
2327 struct hblk *h;
2328 {
2329     register struct hblk * h_trunc;
2330     register unsigned i;
2331     register GC_bool found_clean;
2332     
2333     if (!GC_dirty_maintained) return;
2334     h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1));
2335     found_clean = FALSE;
2336     for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
2337         register int index = PHT_HASH(h_trunc+i);
2338             
2339         if (!get_pht_entry_from_index(GC_dirty_pages, index)) {
2340             found_clean = TRUE;
2341             async_set_pht_entry_from_index(GC_dirty_pages, index);
2342         }
2343     }
2344     if (found_clean) {
2345         UNPROTECT(h_trunc, GC_page_size);
2346     }
2347 }
2348
2349 void GC_dirty_init()
2350 {
2351 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \
2352        defined(OSF1) || defined(HURD)
2353       struct sigaction  act, oldact;
2354       /* We should probably specify SA_SIGINFO for Linux, and handle    */
2355       /* the different architectures more uniformly.                    */
2356 #     if defined(IRIX5) || defined(LINUX) || defined(OSF1) || defined(HURD)
2357         act.sa_flags    = SA_RESTART;
2358         act.sa_handler  = (SIG_PF)GC_write_fault_handler;
2359 #     else
2360         act.sa_flags    = SA_RESTART | SA_SIGINFO;
2361         act.sa_sigaction = GC_write_fault_handler;
2362 #     endif
2363       (void)sigemptyset(&act.sa_mask);
2364 #     ifdef SIG_SUSPEND
2365         /* Arrange to postpone SIG_SUSPEND while we're in a write fault */
2366         /* handler.  This effectively makes the handler atomic w.r.t.   */
2367         /* stopping the world for GC.                                   */
2368         (void)sigaddset(&act.sa_mask, SIG_SUSPEND);
2369 #     endif /* SIG_SUSPEND */
2370 #    endif
2371 #   if defined(MACOSX)
2372       struct sigaction act, oldact;
2373
2374       act.sa_flags = SA_RESTART;
2375       act.sa_handler = GC_write_fault_handler;
2376       sigemptyset(&act.sa_mask);
2377 #   endif
2378 #   ifdef PRINTSTATS
2379         GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n");
2380 #   endif
2381     GC_dirty_maintained = TRUE;
2382     if (GC_page_size % HBLKSIZE != 0) {
2383         GC_err_printf0("Page size not multiple of HBLKSIZE\n");
2384         ABORT("Page size not multiple of HBLKSIZE");
2385     }
2386 #   if defined(SUNOS4) || defined(FREEBSD)
2387       GC_old_bus_handler = signal(SIGBUS, GC_write_fault_handler);
2388       if (GC_old_bus_handler == SIG_IGN) {
2389         GC_err_printf0("Previously ignored bus error!?");
2390         GC_old_bus_handler = SIG_DFL;
2391       }
2392       if (GC_old_bus_handler != SIG_DFL) {
2393 #       ifdef PRINTSTATS
2394           GC_err_printf0("Replaced other SIGBUS handler\n");
2395 #       endif
2396       }
2397 #   endif
2398 #   if defined(SUNOS4)
2399       GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler);
2400       if (GC_old_segv_handler == SIG_IGN) {
2401         GC_err_printf0("Previously ignored segmentation violation!?");
2402         GC_old_segv_handler = SIG_DFL;
2403       }
2404       if (GC_old_segv_handler != SIG_DFL) {
2405 #       ifdef PRINTSTATS
2406           GC_err_printf0("Replaced other SIGSEGV handler\n");
2407 #       endif
2408       }
2409 #   endif
2410 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) \
2411        || defined(OSF1) || defined(HURD)
2412       /* SUNOS5SIGS includes HPUX */
2413 #     if defined(GC_IRIX_THREADS)
2414         sigaction(SIGSEGV, 0, &oldact);
2415         sigaction(SIGSEGV, &act, 0);
2416 #     else
2417         sigaction(SIGSEGV, &act, &oldact);
2418 #     endif
2419 #     if defined(_sigargs) || defined(HURD)
2420         /* This is Irix 5.x, not 6.x.  Irix 5.x does not have   */
2421         /* sa_sigaction.                                        */
2422         GC_old_segv_handler = oldact.sa_handler;
2423 #     else /* Irix 6.x or SUNOS5SIGS or LINUX */
2424         if (oldact.sa_flags & SA_SIGINFO) {
2425           GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction);
2426         } else {
2427           GC_old_segv_handler = oldact.sa_handler;
2428         }
2429 #     endif
2430       if (GC_old_segv_handler == SIG_IGN) {
2431              GC_err_printf0("Previously ignored segmentation violation!?");
2432              GC_old_segv_handler = SIG_DFL;
2433       }
2434       if (GC_old_segv_handler != SIG_DFL) {
2435 #       ifdef PRINTSTATS
2436           GC_err_printf0("Replaced other SIGSEGV handler\n");
2437 #       endif
2438       }
2439 #   endif
2440 #   if defined(MACOSX) || defined(HPUX) || defined(LINUX) || defined(HURD)
2441       sigaction(SIGBUS, &act, &oldact);
2442       GC_old_bus_handler = oldact.sa_handler;
2443       if (GC_old_bus_handler == SIG_IGN) {
2444              GC_err_printf0("Previously ignored bus error!?");
2445              GC_old_bus_handler = SIG_DFL;
2446       }
2447       if (GC_old_bus_handler != SIG_DFL) {
2448 #       ifdef PRINTSTATS
2449           GC_err_printf0("Replaced other SIGBUS handler\n");
2450 #       endif
2451       }
2452 #   endif /* MACOS || HPUX || LINUX */
2453 #   if defined(MSWIN32)
2454       GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
2455       if (GC_old_segv_handler != NULL) {
2456 #       ifdef PRINTSTATS
2457           GC_err_printf0("Replaced other UnhandledExceptionFilter\n");
2458 #       endif
2459       } else {
2460           GC_old_segv_handler = SIG_DFL;
2461       }
2462 #   endif
2463 }
2464
2465
2466
2467 void GC_protect_heap()
2468 {
2469     ptr_t start;
2470     word len;
2471     unsigned i;
2472     
2473     for (i = 0; i < GC_n_heap_sects; i++) {
2474         start = GC_heap_sects[i].hs_start;
2475         len = GC_heap_sects[i].hs_bytes;
2476         PROTECT(start, len);
2477     }
2478 }
2479
2480 /* We assume that either the world is stopped or its OK to lose dirty   */
2481 /* bits while this is happenning (as in GC_enable_incremental).         */
2482 void GC_read_dirty()
2483 {
2484     BCOPY((word *)GC_dirty_pages, GC_grungy_pages,
2485           (sizeof GC_dirty_pages));
2486     BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages));
2487     GC_protect_heap();
2488 }
2489
2490 GC_bool GC_page_was_dirty(h)
2491 struct hblk * h;
2492 {
2493     register word index = PHT_HASH(h);
2494     
2495     return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index));
2496 }
2497
2498 /*
2499  * Acquiring the allocation lock here is dangerous, since this
2500  * can be called from within GC_call_with_alloc_lock, and the cord
2501  * package does so.  On systems that allow nested lock acquisition, this
2502  * happens to work.
2503  * On other systems, SET_LOCK_HOLDER and friends must be suitably defined.
2504  */
2505
2506 static GC_bool syscall_acquired_lock = FALSE;   /* Protected by GC lock. */
2507  
2508 void GC_begin_syscall()
2509 {
2510     if (!I_HOLD_LOCK()) {
2511         LOCK();
2512         syscall_acquired_lock = TRUE;
2513     }
2514 }
2515
2516 void GC_end_syscall()
2517 {
2518     if (syscall_acquired_lock) {
2519         syscall_acquired_lock = FALSE;
2520         UNLOCK();
2521     }
2522 }
2523
2524 void GC_unprotect_range(addr, len)
2525 ptr_t addr;
2526 word len;
2527 {
2528     struct hblk * start_block;
2529     struct hblk * end_block;
2530     register struct hblk *h;
2531     ptr_t obj_start;
2532     
2533     if (!GC_incremental) return;
2534     obj_start = GC_base(addr);
2535     if (obj_start == 0) return;
2536     if (GC_base(addr + len - 1) != obj_start) {
2537         ABORT("GC_unprotect_range(range bigger than object)");
2538     }
2539     start_block = (struct hblk *)((word)addr & ~(GC_page_size - 1));
2540     end_block = (struct hblk *)((word)(addr + len - 1) & ~(GC_page_size - 1));
2541     end_block += GC_page_size/HBLKSIZE - 1;
2542     for (h = start_block; h <= end_block; h++) {
2543         register word index = PHT_HASH(h);
2544         
2545         async_set_pht_entry_from_index(GC_dirty_pages, index);
2546     }
2547     UNPROTECT(start_block,
2548               ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
2549 }
2550
2551 #if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_LINUX_THREADS) \
2552     && !defined(GC_USE_LD_WRAP)
2553 /* Replacement for UNIX system call.     */
2554 /* Other calls that write to the heap    */
2555 /* should be handled similarly.          */
2556 # if defined(__STDC__) && !defined(SUNOS4)
2557 #   include <unistd.h>
2558 #   include <sys/uio.h>
2559     ssize_t read(int fd, void *buf, size_t nbyte)
2560 # else
2561 #   ifndef LINT
2562       int read(fd, buf, nbyte)
2563 #   else
2564       int GC_read(fd, buf, nbyte)
2565 #   endif
2566     int fd;
2567     char *buf;
2568     int nbyte;
2569 # endif
2570 {
2571     int result;
2572     
2573     GC_begin_syscall();
2574     GC_unprotect_range(buf, (word)nbyte);
2575 #   if defined(IRIX5) || defined(GC_LINUX_THREADS)
2576         /* Indirect system call may not always be easily available.     */
2577         /* We could call _read, but that would interfere with the       */
2578         /* libpthread interception of read.                             */
2579         /* On Linux, we have to be careful with the linuxthreads        */
2580         /* read interception.                                           */
2581         {
2582             struct iovec iov;
2583
2584             iov.iov_base = buf;
2585             iov.iov_len = nbyte;
2586             result = readv(fd, &iov, 1);
2587         }
2588 #   else
2589 #     if defined(HURD)  
2590         result = __read(fd, buf, nbyte);
2591 #     else
2592         /* The two zero args at the end of this list are because one
2593            IA-64 syscall() implementation actually requires six args
2594            to be passed, even though they aren't always used. */
2595         result = syscall(SYS_read, fd, buf, nbyte, 0, 0);
2596 #     endif /* !HURD */
2597 #   endif
2598     GC_end_syscall();
2599     return(result);
2600 }
2601 #endif /* !MSWIN32 && !MSWINCE && !GC_LINUX_THREADS */
2602
2603 #ifdef GC_USE_LD_WRAP
2604     /* We use the GNU ld call wrapping facility.                        */
2605     /* This requires that the linker be invoked with "--wrap read".     */
2606     /* This can be done by passing -Wl,"--wrap read" to gcc.            */
2607     /* I'm not sure that this actually wraps whatever version of read   */
2608     /* is called by stdio.  That code also mentions __read.             */
2609 #   include <unistd.h>
2610     ssize_t __wrap_read(int fd, void *buf, size_t nbyte)
2611     {
2612         int result;
2613
2614         GC_begin_syscall();
2615         GC_unprotect_range(buf, (word)nbyte);
2616         result = __real_read(fd, buf, nbyte);
2617         GC_end_syscall();
2618         return(result);
2619     }
2620
2621     /* We should probably also do this for __read, or whatever stdio    */
2622     /* actually calls.                                                  */
2623 #endif
2624
2625 /*ARGSUSED*/
2626 GC_bool GC_page_was_ever_dirty(h)
2627 struct hblk *h;
2628 {
2629     return(TRUE);
2630 }
2631
2632 /* Reset the n pages starting at h to "was never dirty" status. */
2633 /*ARGSUSED*/
2634 void GC_is_fresh(h, n)
2635 struct hblk *h;
2636 word n;
2637 {
2638 }
2639
2640 # else /* !MPROTECT_VDB */
2641
2642 #   ifdef GC_USE_LD_WRAP
2643       ssize_t __wrap_read(int fd, void *buf, size_t nbyte)
2644       { return __real_read(fd, buf, nbyte); }
2645 #   endif
2646
2647 # endif /* MPROTECT_VDB */
2648
2649 # ifdef PROC_VDB
2650
2651 /*
2652  * See DEFAULT_VDB for interface descriptions.
2653  */
2654  
2655 /*
2656  * This implementaion assumes a Solaris 2.X like /proc pseudo-file-system
2657  * from which we can read page modified bits.  This facility is far from
2658  * optimal (e.g. we would like to get the info for only some of the
2659  * address space), but it avoids intercepting system calls.
2660  */
2661
2662 #include <errno.h>
2663 #include <sys/types.h>
2664 #include <sys/signal.h>
2665 #include <sys/fault.h>
2666 #include <sys/syscall.h>
2667 #include <sys/procfs.h>
2668 #include <sys/stat.h>
2669
2670 #define INITIAL_BUF_SZ 4096
2671 word GC_proc_buf_size = INITIAL_BUF_SZ;
2672 char *GC_proc_buf;
2673
2674 #ifdef GC_SOLARIS_THREADS
2675 /* We don't have exact sp values for threads.  So we count on   */
2676 /* occasionally declaring stack pages to be fresh.  Thus we     */
2677 /* need a real implementation of GC_is_fresh.  We can't clear   */
2678 /* entries in GC_written_pages, since that would declare all    */
2679 /* pages with the given hash address to be fresh.               */
2680 #   define MAX_FRESH_PAGES 8*1024       /* Must be power of 2 */
2681     struct hblk ** GC_fresh_pages;      /* A direct mapped cache.       */
2682                                         /* Collisions are dropped.      */
2683
2684 #   define FRESH_PAGE_SLOT(h) (divHBLKSZ((word)(h)) & (MAX_FRESH_PAGES-1))
2685 #   define ADD_FRESH_PAGE(h) \
2686         GC_fresh_pages[FRESH_PAGE_SLOT(h)] = (h)
2687 #   define PAGE_IS_FRESH(h) \
2688         (GC_fresh_pages[FRESH_PAGE_SLOT(h)] == (h) && (h) != 0)
2689 #endif
2690
2691 /* Add all pages in pht2 to pht1 */
2692 void GC_or_pages(pht1, pht2)
2693 page_hash_table pht1, pht2;
2694 {
2695     register int i;
2696     
2697     for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i];
2698 }
2699
2700 int GC_proc_fd;
2701
2702 void GC_dirty_init()
2703 {
2704     int fd;
2705     char buf[30];
2706
2707     GC_dirty_maintained = TRUE;
2708     if (GC_words_allocd != 0 || GC_words_allocd_before_gc != 0) {
2709         register int i;
2710     
2711         for (i = 0; i < PHT_SIZE; i++) GC_written_pages[i] = (word)(-1);
2712 #       ifdef PRINTSTATS
2713             GC_printf1("Allocated words:%lu:all pages may have been written\n",
2714                        (unsigned long)
2715                                 (GC_words_allocd + GC_words_allocd_before_gc));
2716 #       endif       
2717     }
2718     sprintf(buf, "/proc/%d", getpid());
2719     fd = open(buf, O_RDONLY);
2720     if (fd < 0) {
2721         ABORT("/proc open failed");
2722     }
2723     GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0);
2724     close(fd);
2725     if (GC_proc_fd < 0) {
2726         ABORT("/proc ioctl failed");
2727     }
2728     GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size);
2729 #   ifdef GC_SOLARIS_THREADS
2730         GC_fresh_pages = (struct hblk **)
2731           GC_scratch_alloc(MAX_FRESH_PAGES * sizeof (struct hblk *));
2732         if (GC_fresh_pages == 0) {
2733             GC_err_printf0("No space for fresh pages\n");
2734             EXIT();
2735         }
2736         BZERO(GC_fresh_pages, MAX_FRESH_PAGES * sizeof (struct hblk *));
2737 #   endif
2738 }
2739
2740 /* Ignore write hints. They don't help us here. */
2741 /*ARGSUSED*/
2742 void GC_write_hint(h)
2743 struct hblk *h;
2744 {
2745 }
2746
2747 #ifdef GC_SOLARIS_THREADS
2748 #   define READ(fd,buf,nbytes) syscall(SYS_read, fd, buf, nbytes)
2749 #else
2750 #   define READ(fd,buf,nbytes) read(fd, buf, nbytes)
2751 #endif
2752
2753 void GC_read_dirty()
2754 {
2755     unsigned long ps, np;
2756     int nmaps;
2757     ptr_t vaddr;
2758     struct prasmap * map;
2759     char * bufp;
2760     ptr_t current_addr, limit;
2761     int i;
2762 int dummy;
2763
2764     BZERO(GC_grungy_pages, (sizeof GC_grungy_pages));
2765     
2766     bufp = GC_proc_buf;
2767     if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
2768 #       ifdef PRINTSTATS
2769             GC_printf1("/proc read failed: GC_proc_buf_size = %lu\n",
2770                        GC_proc_buf_size);
2771 #       endif       
2772         {
2773             /* Retry with larger buffer. */
2774             word new_size = 2 * GC_proc_buf_size;
2775             char * new_buf = GC_scratch_alloc(new_size);
2776             
2777             if (new_buf != 0) {
2778                 GC_proc_buf = bufp = new_buf;
2779                 GC_proc_buf_size = new_size;
2780             }
2781             if (syscall(SYS_read, GC_proc_fd, bufp, GC_proc_buf_size) <= 0) {
2782                 WARN("Insufficient space for /proc read\n", 0);
2783                 /* Punt:        */
2784                 memset(GC_grungy_pages, 0xff, sizeof (page_hash_table));
2785                 memset(GC_written_pages, 0xff, sizeof(page_hash_table));
2786 #               ifdef GC_SOLARIS_THREADS
2787                     BZERO(GC_fresh_pages,
2788                           MAX_FRESH_PAGES * sizeof (struct hblk *)); 
2789 #               endif
2790                 return;
2791             }
2792         }
2793     }
2794     /* Copy dirty bits into GC_grungy_pages */
2795         nmaps = ((struct prpageheader *)bufp) -> pr_nmap;
2796         /* printf( "nmaps = %d, PG_REFERENCED = %d, PG_MODIFIED = %d\n",
2797                      nmaps, PG_REFERENCED, PG_MODIFIED); */
2798         bufp = bufp + sizeof(struct prpageheader);
2799         for (i = 0; i < nmaps; i++) {
2800             map = (struct prasmap *)bufp;
2801             vaddr = (ptr_t)(map -> pr_vaddr);
2802             ps = map -> pr_pagesize;
2803             np = map -> pr_npage;
2804             /* printf("vaddr = 0x%X, ps = 0x%X, np = 0x%X\n", vaddr, ps, np); */
2805             limit = vaddr + ps * np;
2806             bufp += sizeof (struct prasmap);
2807             for (current_addr = vaddr;
2808                  current_addr < limit; current_addr += ps){
2809                 if ((*bufp++) & PG_MODIFIED) {
2810                     register struct hblk * h = (struct hblk *) current_addr;
2811                     
2812                     while ((ptr_t)h < current_addr + ps) {
2813                         register word index = PHT_HASH(h);
2814                         
2815                         set_pht_entry_from_index(GC_grungy_pages, index);
2816 #                       ifdef GC_SOLARIS_THREADS
2817                           {
2818                             register int slot = FRESH_PAGE_SLOT(h);
2819                             
2820                             if (GC_fresh_pages[slot] == h) {
2821                                 GC_fresh_pages[slot] = 0;
2822                             }
2823                           }
2824 #                       endif
2825                         h++;
2826                     }
2827                 }
2828             }
2829             bufp += sizeof(long) - 1;
2830             bufp = (char *)((unsigned long)bufp & ~(sizeof(long)-1));
2831         }
2832     /* Update GC_written_pages. */
2833         GC_or_pages(GC_written_pages, GC_grungy_pages);
2834 #   ifdef GC_SOLARIS_THREADS
2835       /* Make sure that old stacks are considered completely clean      */
2836       /* unless written again.                                          */
2837         GC_old_stacks_are_fresh();
2838 #   endif
2839 }
2840
2841 #undef READ
2842
2843 GC_bool GC_page_was_dirty(h)
2844 struct hblk *h;
2845 {
2846     register word index = PHT_HASH(h);
2847     register GC_bool result;
2848     
2849     result = get_pht_entry_from_index(GC_grungy_pages, index);
2850 #   ifdef GC_SOLARIS_THREADS
2851         if (result && PAGE_IS_FRESH(h)) result = FALSE;
2852         /* This happens only if page was declared fresh since   */
2853         /* the read_dirty call, e.g. because it's in an unused  */
2854         /* thread stack.  It's OK to treat it as clean, in      */
2855         /* that case.  And it's consistent with                 */
2856         /* GC_page_was_ever_dirty.                              */
2857 #   endif
2858     return(result);
2859 }
2860
2861 GC_bool GC_page_was_ever_dirty(h)
2862 struct hblk *h;
2863 {
2864     register word index = PHT_HASH(h);
2865     register GC_bool result;
2866     
2867     result = get_pht_entry_from_index(GC_written_pages, index);
2868 #   ifdef GC_SOLARIS_THREADS
2869         if (result && PAGE_IS_FRESH(h)) result = FALSE;
2870 #   endif
2871     return(result);
2872 }
2873
2874 /* Caller holds allocation lock.        */
2875 void GC_is_fresh(h, n)
2876 struct hblk *h;
2877 word n;
2878 {
2879
2880     register word index;
2881     
2882 #   ifdef GC_SOLARIS_THREADS
2883       register word i;
2884       
2885       if (GC_fresh_pages != 0) {
2886         for (i = 0; i < n; i++) {
2887           ADD_FRESH_PAGE(h + i);
2888         }
2889       }
2890 #   endif
2891 }
2892
2893 # endif /* PROC_VDB */
2894
2895
2896 # ifdef PCR_VDB
2897
2898 # include "vd/PCR_VD.h"
2899
2900 # define NPAGES (32*1024)       /* 128 MB */
2901
2902 PCR_VD_DB  GC_grungy_bits[NPAGES];
2903
2904 ptr_t GC_vd_base;       /* Address corresponding to GC_grungy_bits[0]   */
2905                         /* HBLKSIZE aligned.                            */
2906
2907 void GC_dirty_init()
2908 {
2909     GC_dirty_maintained = TRUE;
2910     /* For the time being, we assume the heap generally grows up */
2911     GC_vd_base = GC_heap_sects[0].hs_start;
2912     if (GC_vd_base == 0) {
2913         ABORT("Bad initial heap segment");
2914     }
2915     if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE)
2916         != PCR_ERes_okay) {
2917         ABORT("dirty bit initialization failed");
2918     }
2919 }
2920
2921 void GC_read_dirty()
2922 {
2923     /* lazily enable dirty bits on newly added heap sects */
2924     {
2925         static int onhs = 0;
2926         int nhs = GC_n_heap_sects;
2927         for( ; onhs < nhs; onhs++ ) {
2928             PCR_VD_WriteProtectEnable(
2929                     GC_heap_sects[onhs].hs_start,
2930                     GC_heap_sects[onhs].hs_bytes );
2931         }
2932     }
2933
2934
2935     if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits)
2936         != PCR_ERes_okay) {
2937         ABORT("dirty bit read failed");
2938     }
2939 }
2940
2941 GC_bool GC_page_was_dirty(h)
2942 struct hblk *h;
2943 {
2944     if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) {
2945         return(TRUE);
2946     }
2947     return(GC_grungy_bits[h - (struct hblk *)GC_vd_base] & PCR_VD_DB_dirtyBit);
2948 }
2949
2950 /*ARGSUSED*/
2951 void GC_write_hint(h)
2952 struct hblk *h;
2953 {
2954     PCR_VD_WriteProtectDisable(h, HBLKSIZE);
2955     PCR_VD_WriteProtectEnable(h, HBLKSIZE);
2956 }
2957
2958 # endif /* PCR_VDB */
2959
2960 /*
2961  * Call stack save code for debugging.
2962  * Should probably be in mach_dep.c, but that requires reorganization.
2963  */
2964
2965 /* I suspect the following works for most X86 *nix variants, so         */
2966 /* long as the frame pointer is explicitly stored.  In the case of gcc, */
2967 /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is.  */
2968 #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)
2969     struct frame {
2970         struct frame *fr_savfp;
2971         long    fr_savpc;
2972         long    fr_arg[NARGS];  /* All the arguments go here.   */
2973     };
2974 #endif
2975
2976 #if defined(SPARC)
2977 #  if defined(LINUX)
2978      struct frame {
2979         long    fr_local[8];
2980         long    fr_arg[6];
2981         struct frame *fr_savfp;
2982         long    fr_savpc;
2983 #       ifndef __arch64__
2984           char  *fr_stret;
2985 #       endif
2986         long    fr_argd[6];
2987         long    fr_argx[0];
2988      };
2989 #  else
2990 #    if defined(SUNOS4)
2991 #      include <machine/frame.h>
2992 #    else
2993 #      if defined (DRSNX)
2994 #        include <sys/sparc/frame.h>
2995 #      else
2996 #        if defined(OPENBSD) || defined(NETBSD)
2997 #          include <frame.h>
2998 #        else
2999 #          include <sys/frame.h>
3000 #        endif
3001 #      endif
3002 #    endif
3003 #  endif
3004 #  if NARGS > 6
3005         --> We only know how to to get the first 6 arguments
3006 #  endif
3007 #endif /* SPARC */
3008
3009 #ifdef SAVE_CALL_CHAIN
3010 /* Fill in the pc and argument information for up to NFRAMES of my      */
3011 /* callers.  Ignore my frame and my callers frame.                      */
3012
3013 #if (defined(OPENBSD) || defined(NETBSD)) && defined(SPARC)
3014 #  define FR_SAVFP fr_fp
3015 #  define FR_SAVPC fr_pc
3016 #else
3017 #  define FR_SAVFP fr_savfp
3018 #  define FR_SAVPC fr_savpc
3019 #endif
3020
3021 #if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9))
3022 #   define BIAS 2047
3023 #else
3024 #   define BIAS 0
3025 #endif
3026
3027 void GC_save_callers (info) 
3028 struct callinfo info[NFRAMES];
3029 {
3030   struct frame *frame;
3031   struct frame *fp;
3032   int nframes = 0;
3033 # ifdef I386
3034     /* We assume this is turned on only with gcc as the compiler. */
3035     asm("movl %%ebp,%0" : "=r"(frame));
3036     fp = frame;
3037 # else
3038     word GC_save_regs_in_stack();
3039
3040     frame = (struct frame *) GC_save_regs_in_stack ();
3041     fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);
3042 #endif
3043   
3044    for (; (!(fp HOTTER_THAN frame) && !(GC_stackbottom HOTTER_THAN (ptr_t)fp)
3045            && (nframes < NFRAMES));
3046        fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), nframes++) {
3047       register int i;
3048       
3049       info[nframes].ci_pc = fp->FR_SAVPC;
3050 #     if NARGS > 0
3051         for (i = 0; i < NARGS; i++) {
3052           info[nframes].ci_arg[i] = ~(fp->fr_arg[i]);
3053         }
3054 #     endif /* NARGS > 0 */
3055   }
3056   if (nframes < NFRAMES) info[nframes].ci_pc = 0;
3057 }
3058
3059 #endif /* SAVE_CALL_CHAIN */
3060
3061 #if defined(LINUX) && defined(__ELF__) && \
3062     (!defined(SMALL_CONFIG) || defined(USE_PROC_FOR_LIBRARIES))
3063 #ifdef GC_USE_LD_WRAP
3064 #   define READ __real_read
3065 #else
3066 #   define READ read
3067 #endif
3068
3069
3070 /* Repeatedly perform a read call until the buffer is filled or */
3071 /* we encounter EOF.                                            */
3072 ssize_t GC_repeat_read(int fd, char *buf, size_t count)
3073 {
3074     ssize_t num_read = 0;
3075     ssize_t result;
3076     
3077     while (num_read < count) {
3078         result = READ(fd, buf + num_read, count - num_read);
3079         if (result < 0) return result;
3080         if (result == 0) break;
3081         num_read += result;
3082     }
3083     return num_read;
3084 }
3085 #endif /* LINUX && ... */
3086
3087
3088 #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
3089
3090 /* Dump /proc/self/maps to GC_stderr, to enable looking up names for
3091    addresses in FIND_LEAK output. */
3092
3093 void GC_print_address_map()
3094 {
3095     int f;
3096     int result;
3097     char maps_temp[32768];
3098     GC_err_printf0("---------- Begin address map ----------\n");
3099         f = open("/proc/self/maps", O_RDONLY);
3100         if (-1 == f) ABORT("Couldn't open /proc/self/maps");
3101         do {
3102             result = GC_repeat_read(f, maps_temp, sizeof(maps_temp));
3103             if (result <= 0) ABORT("Couldn't read /proc/self/maps");
3104             GC_err_write(maps_temp, result);
3105         } while (result == sizeof(maps_temp));
3106      
3107     GC_err_printf0("---------- End address map ----------\n");
3108 }
3109
3110 #endif
3111
3112