OSDN Git Service

* java/util/Date.java: Re-merged with Classpath.
[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(IRIX_THREADS) || defined(HPUX_THREADS)
75 #   define NEED_FIND_LIMIT
76 # endif
77
78 # if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
79 #   define NEED_FIND_LIMIT
80 # endif
81
82 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
83       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
84 #   define NEED_FIND_LIMIT
85 # endif
86
87 #ifdef NEED_FIND_LIMIT
88 #   include <setjmp.h>
89 #endif
90
91 #ifdef FREEBSD
92 #  include <machine/trap.h>
93 #endif
94
95 #ifdef AMIGA
96 # define GC_AMIGA_DEF
97 # include "AmigaOS.c"
98 # undef GC_AMIGA_DEF
99 #endif
100
101 #if defined(MSWIN32) || defined(MSWINCE)
102 # define WIN32_LEAN_AND_MEAN
103 # define NOSERVICE
104 # include <windows.h>
105 #endif
106
107 #ifdef MACOS
108 # include <Processes.h>
109 #endif
110
111 #ifdef IRIX5
112 # include <sys/uio.h>
113 # include <malloc.h>   /* for locking */
114 #endif
115 #ifdef USE_MMAP
116 # include <sys/types.h>
117 # include <sys/mman.h>
118 # include <sys/stat.h>
119 #endif
120
121 #ifdef UNIX_LIKE
122 # include <fcntl.h>
123 #endif
124
125 #if defined(SUNOS5SIGS) || defined (HURD) || defined(LINUX)
126 # ifdef SUNOS5SIGS
127 #  include <sys/siginfo.h>
128 # endif
129 # undef setjmp
130 # undef longjmp
131 # define setjmp(env) sigsetjmp(env, 1)
132 # define longjmp(env, val) siglongjmp(env, val)
133 # define jmp_buf sigjmp_buf
134 #endif
135
136 #ifdef DJGPP
137   /* Apparently necessary for djgpp 2.01.  May cause problems with      */
138   /* other versions.                                                    */
139   typedef long unsigned int caddr_t;
140 #endif
141
142 #ifdef PCR
143 # include "il/PCR_IL.h"
144 # include "th/PCR_ThCtl.h"
145 # include "mm/PCR_MM.h"
146 #endif
147
148 #if !defined(NO_EXECUTE_PERMISSION)
149 # define OPT_PROT_EXEC PROT_EXEC
150 #else
151 # define OPT_PROT_EXEC 0
152 #endif
153
154 #if defined(SEARCH_FOR_DATA_START)
155   /* The I386 case can be handled without a search.  The Alpha case     */
156   /* used to be handled differently as well, but the rules changed      */
157   /* for recent Linux versions.  This seems to be the easiest way to    */
158   /* cover all versions.                                                */
159
160 # ifdef LINUX
161 #   pragma weak __data_start
162     extern int __data_start;
163 #   pragma weak data_start
164     extern int data_start;
165 # endif /* LINUX */
166   extern int _end;
167
168   ptr_t GC_data_start;
169
170   void GC_init_linux_data_start()
171   {
172     extern ptr_t GC_find_limit();
173
174 #   ifdef LINUX
175       /* Try the easy approaches first: */
176       if (&__data_start != 0) {
177           GC_data_start = (ptr_t)(&__data_start);
178           return;
179       }
180       if (&data_start != 0) {
181           GC_data_start = (ptr_t)(&data_start);
182           return;
183       }
184 #   endif /* LINUX */
185     GC_data_start = GC_find_limit((ptr_t)(&_end), FALSE);
186   }
187 #endif
188
189 # ifdef ECOS
190
191 # ifndef ECOS_GC_MEMORY_SIZE
192 # define ECOS_GC_MEMORY_SIZE (448 * 1024)
193 # endif /* ECOS_GC_MEMORY_SIZE */
194
195 // setjmp() function, as described in ANSI para 7.6.1.1
196 #define setjmp( __env__ )  hal_setjmp( __env__ )
197
198 // FIXME: This is a simple way of allocating memory which is
199 // compatible with ECOS early releases.  Later releases use a more
200 // sophisticated means of allocating memory than this simple static
201 // allocator, but this method is at least bound to work.
202 static char memory[ECOS_GC_MEMORY_SIZE];
203 static char *brk = memory;
204
205 static void *tiny_sbrk(ptrdiff_t increment)
206 {
207   void *p = brk;
208
209   brk += increment;
210
211   if (brk >  memory + sizeof memory)
212     {
213       brk -= increment;
214       return NULL;
215     }
216
217   return p;
218 }
219 #define sbrk tiny_sbrk
220 # endif /* ECOS */
221
222 #if defined(NETBSD) && defined(__ELF__)
223   ptr_t GC_data_start;
224
225   void GC_init_netbsd_elf()
226   {
227     extern ptr_t GC_find_limit();
228     extern char **environ;
229         /* This may need to be environ, without the underscore, for     */
230         /* some versions.                                               */
231     GC_data_start = GC_find_limit((ptr_t)&environ, FALSE);
232   }
233 #endif
234
235 # ifdef OS2
236
237 # include <stddef.h>
238
239 # if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */
240
241 struct exe_hdr {
242     unsigned short      magic_number;
243     unsigned short      padding[29];
244     long                new_exe_offset;
245 };
246
247 #define E_MAGIC(x)      (x).magic_number
248 #define EMAGIC          0x5A4D  
249 #define E_LFANEW(x)     (x).new_exe_offset
250
251 struct e32_exe {
252     unsigned char       magic_number[2]; 
253     unsigned char       byte_order; 
254     unsigned char       word_order; 
255     unsigned long       exe_format_level;
256     unsigned short      cpu;       
257     unsigned short      os;
258     unsigned long       padding1[13];
259     unsigned long       object_table_offset;
260     unsigned long       object_count;    
261     unsigned long       padding2[31];
262 };
263
264 #define E32_MAGIC1(x)   (x).magic_number[0]
265 #define E32MAGIC1       'L'
266 #define E32_MAGIC2(x)   (x).magic_number[1]
267 #define E32MAGIC2       'X'
268 #define E32_BORDER(x)   (x).byte_order
269 #define E32LEBO         0
270 #define E32_WORDER(x)   (x).word_order
271 #define E32LEWO         0
272 #define E32_CPU(x)      (x).cpu
273 #define E32CPU286       1
274 #define E32_OBJTAB(x)   (x).object_table_offset
275 #define E32_OBJCNT(x)   (x).object_count
276
277 struct o32_obj {
278     unsigned long       size;  
279     unsigned long       base;
280     unsigned long       flags;  
281     unsigned long       pagemap;
282     unsigned long       mapsize; 
283     unsigned long       reserved;
284 };
285
286 #define O32_FLAGS(x)    (x).flags
287 #define OBJREAD         0x0001L
288 #define OBJWRITE        0x0002L
289 #define OBJINVALID      0x0080L
290 #define O32_SIZE(x)     (x).size
291 #define O32_BASE(x)     (x).base
292
293 # else  /* IBM's compiler */
294
295 /* A kludge to get around what appears to be a header file bug */
296 # ifndef WORD
297 #   define WORD unsigned short
298 # endif
299 # ifndef DWORD
300 #   define DWORD unsigned long
301 # endif
302
303 # define EXE386 1
304 # include <newexe.h>
305 # include <exe386.h>
306
307 # endif  /* __IBMC__ */
308
309 # define INCL_DOSEXCEPTIONS
310 # define INCL_DOSPROCESS
311 # define INCL_DOSERRORS
312 # define INCL_DOSMODULEMGR
313 # define INCL_DOSMEMMGR
314 # include <os2.h>
315
316
317 /* Disable and enable signals during nontrivial allocations     */
318
319 void GC_disable_signals(void)
320 {
321     ULONG nest;
322     
323     DosEnterMustComplete(&nest);
324     if (nest != 1) ABORT("nested GC_disable_signals");
325 }
326
327 void GC_enable_signals(void)
328 {
329     ULONG nest;
330     
331     DosExitMustComplete(&nest);
332     if (nest != 0) ABORT("GC_enable_signals");
333 }
334
335
336 # else
337
338 #  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
339       && !defined(MSWINCE) \
340       && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW)
341
342 #   if defined(sigmask) && !defined(UTS4) && !defined(HURD)
343         /* Use the traditional BSD interface */
344 #       define SIGSET_T int
345 #       define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
346 #       define SIG_FILL(set)  (set) = 0x7fffffff
347           /* Setting the leading bit appears to provoke a bug in some   */
348           /* longjmp implementations.  Most systems appear not to have  */
349           /* a signal 32.                                               */
350 #       define SIGSETMASK(old, new) (old) = sigsetmask(new)
351 #   else
352         /* Use POSIX/SYSV interface     */
353 #       define SIGSET_T sigset_t
354 #       define SIG_DEL(set, signal) sigdelset(&(set), (signal))
355 #       define SIG_FILL(set) sigfillset(&set)
356 #       define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old))
357 #   endif
358
359 static GC_bool mask_initialized = FALSE;
360
361 static SIGSET_T new_mask;
362
363 static SIGSET_T old_mask;
364
365 static SIGSET_T dummy;
366
367 #if defined(PRINTSTATS) && !defined(THREADS)
368 # define CHECK_SIGNALS
369   int GC_sig_disabled = 0;
370 #endif
371
372 void GC_disable_signals()
373 {
374     if (!mask_initialized) {
375         SIG_FILL(new_mask);
376
377         SIG_DEL(new_mask, SIGSEGV);
378         SIG_DEL(new_mask, SIGILL);
379         SIG_DEL(new_mask, SIGQUIT);
380 #       ifdef SIGBUS
381             SIG_DEL(new_mask, SIGBUS);
382 #       endif
383 #       ifdef SIGIOT
384             SIG_DEL(new_mask, SIGIOT);
385 #       endif
386 #       ifdef SIGEMT
387             SIG_DEL(new_mask, SIGEMT);
388 #       endif
389 #       ifdef SIGTRAP
390             SIG_DEL(new_mask, SIGTRAP);
391 #       endif 
392         mask_initialized = TRUE;
393     }
394 #   ifdef CHECK_SIGNALS
395         if (GC_sig_disabled != 0) ABORT("Nested disables");
396         GC_sig_disabled++;
397 #   endif
398     SIGSETMASK(old_mask,new_mask);
399 }
400
401 void GC_enable_signals()
402 {
403 #   ifdef CHECK_SIGNALS
404         if (GC_sig_disabled != 1) ABORT("Unmatched enable");
405         GC_sig_disabled--;
406 #   endif
407     SIGSETMASK(dummy,old_mask);
408 }
409
410 #  endif  /* !PCR */
411
412 # endif /*!OS/2 */
413
414 /* Ivan Demakov: simplest way (to me) */
415 #if defined (DOS4GW)
416   void GC_disable_signals() { }
417   void GC_enable_signals() { }
418 #endif
419
420 /* Find the page size */
421 word GC_page_size;
422
423 # if defined(MSWIN32) || defined(MSWINCE)
424   void GC_setpagesize()
425   {
426     GetSystemInfo(&GC_sysinfo);
427     GC_page_size = GC_sysinfo.dwPageSize;
428   }
429
430 # else
431 #   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) \
432        || defined(USE_MUNMAP)
433         void GC_setpagesize()
434         {
435             GC_page_size = GETPAGESIZE();
436         }
437 #   else
438         /* It's acceptable to fake it. */
439         void GC_setpagesize()
440         {
441             GC_page_size = HBLKSIZE;
442         }
443 #   endif
444 # endif
445
446 /* 
447  * Find the base of the stack. 
448  * Used only in single-threaded environment.
449  * With threads, GC_mark_roots needs to know how to do this.
450  * Called with allocator lock held.
451  */
452 # if defined(MSWIN32) || defined(MSWINCE)
453 # define is_writable(prot) ((prot) == PAGE_READWRITE \
454                             || (prot) == PAGE_WRITECOPY \
455                             || (prot) == PAGE_EXECUTE_READWRITE \
456                             || (prot) == PAGE_EXECUTE_WRITECOPY)
457 /* Return the number of bytes that are writable starting at p.  */
458 /* The pointer p is assumed to be page aligned.                 */
459 /* If base is not 0, *base becomes the beginning of the         */
460 /* allocation region containing p.                              */
461 word GC_get_writable_length(ptr_t p, ptr_t *base)
462 {
463     MEMORY_BASIC_INFORMATION buf;
464     word result;
465     word protect;
466     
467     result = VirtualQuery(p, &buf, sizeof(buf));
468     if (result != sizeof(buf)) ABORT("Weird VirtualQuery result");
469     if (base != 0) *base = (ptr_t)(buf.AllocationBase);
470     protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE));
471     if (!is_writable(protect)) {
472         return(0);
473     }
474     if (buf.State != MEM_COMMIT) return(0);
475     return(buf.RegionSize);
476 }
477
478 ptr_t GC_get_stack_base()
479 {
480     int dummy;
481     ptr_t sp = (ptr_t)(&dummy);
482     ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
483     word size = GC_get_writable_length(trunc_sp, 0);
484    
485     return(trunc_sp + size);
486 }
487
488
489 # endif /* MS Windows */
490
491 # ifdef BEOS
492 # include <kernel/OS.h>
493 ptr_t GC_get_stack_base(){
494         thread_info th;
495         get_thread_info(find_thread(NULL),&th);
496         return th.stack_end;
497 }
498 # endif /* BEOS */
499
500
501 # ifdef OS2
502
503 ptr_t GC_get_stack_base()
504 {
505     PTIB ptib;
506     PPIB ppib;
507     
508     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
509         GC_err_printf0("DosGetInfoBlocks failed\n");
510         ABORT("DosGetInfoBlocks failed\n");
511     }
512     return((ptr_t)(ptib -> tib_pstacklimit));
513 }
514
515 # endif /* OS2 */
516
517 # ifdef AMIGA
518 #   define GC_AMIGA_SB
519 #   include "AmigaOS.c"
520 #   undef GC_AMIGA_SB
521 # endif /* AMIGA */
522
523 # if defined(NEED_FIND_LIMIT) || (defined(UNIX_LIKE) && !defined(ECOS))
524
525 #   ifdef __STDC__
526         typedef void (*handler)(int);
527 #   else
528         typedef void (*handler)();
529 #   endif
530
531 #   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) || defined(HURD)
532         static struct sigaction old_segv_act;
533 #       if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) || defined(HURD)
534             static struct sigaction old_bus_act;
535 #       endif
536 #   else
537         static handler old_segv_handler, old_bus_handler;
538 #   endif
539     
540 #   ifdef __STDC__
541       void GC_set_and_save_fault_handler(handler h)
542 #   else
543       void GC_set_and_save_fault_handler(h)
544       handler h;
545 #   endif
546     {
547 # ifndef ECOS
548 #       if defined(SUNOS5SIGS) || defined(IRIX5)  \
549         || defined(OSF1) || defined(HURD)
550           struct sigaction      act;
551
552           act.sa_handler        = h;
553 #         ifdef SUNOS5SIGS
554             act.sa_flags          = SA_RESTART | SA_NODEFER;
555 #         else
556             act.sa_flags          = SA_RESTART;
557 #         endif
558           /* The presence of SA_NODEFER represents yet another gross    */
559           /* hack.  Under Solaris 2.3, siglongjmp doesn't appear to     */
560           /* interact correctly with -lthread.  We hide the confusion   */
561           /* by making sure that signal handling doesn't affect the     */
562           /* signal mask.                                               */
563
564           (void) sigemptyset(&act.sa_mask);
565 #         ifdef IRIX_THREADS
566                 /* Older versions have a bug related to retrieving and  */
567                 /* and setting a handler at the same time.              */
568                 (void) sigaction(SIGSEGV, 0, &old_segv_act);
569                 (void) sigaction(SIGSEGV, &act, 0);
570 #         else
571                 (void) sigaction(SIGSEGV, &act, &old_segv_act);
572 #               if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
573                    || defined(HPUX) || defined(HURD)
574                     /* Under Irix 5.x or HP/UX, we may get SIGBUS.      */
575                     /* Pthreads doesn't exist under Irix 5.x, so we     */
576                     /* don't have to worry in the threads case.         */
577                     (void) sigaction(SIGBUS, &act, &old_bus_act);
578 #               endif
579 #         endif /* IRIX_THREADS */
580 #       else
581           old_segv_handler = signal(SIGSEGV, h);
582 #         ifdef SIGBUS
583             old_bus_handler = signal(SIGBUS, h);
584 #         endif
585 #       endif
586 # endif /* ECOS */
587     }
588 # endif /* NEED_FIND_LIMIT || UNIX_LIKE */
589
590 # ifdef NEED_FIND_LIMIT
591   /* Some tools to implement HEURISTIC2 */
592 #   define MIN_PAGE_SIZE 256    /* Smallest conceivable page size, bytes */
593     /* static */ jmp_buf GC_jmp_buf;
594     
595     /*ARGSUSED*/
596     void GC_fault_handler(sig)
597     int sig;
598     {
599         longjmp(GC_jmp_buf, 1);
600     }
601
602     void GC_setup_temporary_fault_handler()
603     {
604         GC_set_and_save_fault_handler(GC_fault_handler);
605     }
606     
607     void GC_reset_fault_handler()
608     {
609 # ifndef ECOS
610 #       if defined(SUNOS5SIGS) || defined(IRIX5) \
611            || defined(OSF1) || defined(HURD)
612           (void) sigaction(SIGSEGV, &old_segv_act, 0);
613 #         if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
614              || defined(HPUX) || defined(HURD)
615               (void) sigaction(SIGBUS, &old_bus_act, 0);
616 #         endif
617 #       else
618           (void) signal(SIGSEGV, old_segv_handler);
619 #         ifdef SIGBUS
620             (void) signal(SIGBUS, old_bus_handler);
621 #         endif
622 #       endif
623 # endif /* ECOS */
624     }
625
626     /* Return the first nonaddressible location > p (up) or     */
627     /* the smallest location q s.t. [q,p] is addressible (!up). */
628     ptr_t GC_find_limit(p, up)
629     ptr_t p;
630     GC_bool up;
631     {
632 # ifndef ECOS
633         static VOLATILE ptr_t result;
634                 /* Needs to be static, since otherwise it may not be    */
635                 /* preserved across the longjmp.  Can safely be         */
636                 /* static since it's only called once, with the         */
637                 /* allocation lock held.                                */
638
639
640         GC_setup_temporary_fault_handler();
641         if (setjmp(GC_jmp_buf) == 0) {
642             result = (ptr_t)(((word)(p))
643                               & ~(MIN_PAGE_SIZE-1));
644             for (;;) {
645                 if (up) {
646                     result += MIN_PAGE_SIZE;
647                 } else {
648                     result -= MIN_PAGE_SIZE;
649                 }
650                 GC_noop1((word)(*result));
651             }
652         }
653         GC_reset_fault_handler();
654         if (!up) {
655             result += MIN_PAGE_SIZE;
656         }
657         return(result);
658 # else /* ECOS */
659         abort();
660 # endif /* ECOS */
661     }
662 # endif
663
664 # ifndef ECOS
665
666 #ifdef LINUX_STACKBOTTOM
667
668 #include <sys/types.h>
669 #include <sys/stat.h>
670
671 # define STAT_SKIP 27   /* Number of fields preceding startstack        */
672                         /* field in /proc/self/stat                     */
673
674 # pragma weak __libc_stack_end
675   extern ptr_t __libc_stack_end;
676
677 # ifdef IA64
678 #   pragma weak __libc_ia64_register_backing_store_base
679     extern ptr_t __libc_ia64_register_backing_store_base;
680
681     ptr_t GC_get_register_stack_base(void)
682     {
683       if (0 != &__libc_ia64_register_backing_store_base) {
684         return __libc_ia64_register_backing_store_base;
685       } else {
686         word result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
687         result += BACKING_STORE_ALIGNMENT - 1;
688         result &= ~(BACKING_STORE_ALIGNMENT - 1);
689         return (ptr_t)result;
690       }
691     }
692 # endif
693
694   ptr_t GC_linux_stack_base(void)
695   {
696     /* We read the stack base value from /proc/self/stat.  We do this   */
697     /* using direct I/O system calls in order to avoid calling malloc   */
698     /* in case REDIRECT_MALLOC is defined.                              */ 
699 #   define STAT_BUF_SIZE 4096
700 #   if defined(GC_USE_LD_WRAP)
701 #       define STAT_READ __real_read
702 #   else
703 #       define STAT_READ read
704 #   endif    
705     char stat_buf[STAT_BUF_SIZE];
706     int f;
707     char c;
708     word result = 0;
709     size_t i, buf_offset = 0;
710
711     /* First try the easy way.  This should work for glibc 2.2  */
712       if (0 != &__libc_stack_end) {
713         return __libc_stack_end;
714       }
715     f = open("/proc/self/stat", O_RDONLY);
716     if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
717         ABORT("Couldn't read /proc/self/stat");
718     }
719     c = stat_buf[buf_offset++];
720     /* Skip the required number of fields.  This number is hopefully    */
721     /* constant across all Linux implementations.                       */
722       for (i = 0; i < STAT_SKIP; ++i) {
723         while (isspace(c)) c = stat_buf[buf_offset++];
724         while (!isspace(c)) c = stat_buf[buf_offset++];
725       }
726     while (isspace(c)) c = stat_buf[buf_offset++];
727     while (isdigit(c)) {
728       result *= 10;
729       result += c - '0';
730       c = stat_buf[buf_offset++];
731     }
732     close(f);
733     if (result < 0x10000000) ABORT("Absurd stack bottom value");
734     return (ptr_t)result;
735   }
736
737 #endif /* LINUX_STACKBOTTOM */
738
739 #ifdef FREEBSD_STACKBOTTOM
740
741 /* This uses an undocumented sysctl call, but at least one expert       */
742 /* believes it will stay.                                               */
743
744 #include <unistd.h>
745 #include <sys/types.h>
746 #include <sys/sysctl.h>
747
748   ptr_t GC_freebsd_stack_base(void)
749   {
750     int nm[2] = { CTL_KERN, KERN_USRSTACK}, base, len, r;
751     
752     len = sizeof(int);
753     r = sysctl(nm, 2, &base, &len, NULL, 0);
754     
755     if (r) ABORT("Error getting stack base");
756
757     return (ptr_t)base;
758   }
759
760 #endif /* FREEBSD_STACKBOTTOM */
761
762 #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
763     && !defined(MSWINCE) && !defined(OS2) && !defined(ECOS)
764
765 ptr_t GC_get_stack_base()
766 {
767     word dummy;
768     ptr_t result;
769
770 #   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
771
772 #   ifdef STACKBOTTOM
773         return(STACKBOTTOM);
774 #   else
775 #       ifdef HEURISTIC1
776 #          ifdef STACK_GROWS_DOWN
777              result = (ptr_t)((((word)(&dummy))
778                                + STACKBOTTOM_ALIGNMENT_M1)
779                               & ~STACKBOTTOM_ALIGNMENT_M1);
780 #          else
781              result = (ptr_t)(((word)(&dummy))
782                               & ~STACKBOTTOM_ALIGNMENT_M1);
783 #          endif
784 #       endif /* HEURISTIC1 */
785 #       ifdef LINUX_STACKBOTTOM
786            result = GC_linux_stack_base();
787 #       endif
788 #       ifdef FREEBSD_STACKBOTTOM
789            result = GC_freebsd_stack_base();
790 #       endif
791 #       ifdef HEURISTIC2
792 #           ifdef STACK_GROWS_DOWN
793                 result = GC_find_limit((ptr_t)(&dummy), TRUE);
794 #               ifdef HEURISTIC2_LIMIT
795                     if (result > HEURISTIC2_LIMIT
796                         && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) {
797                             result = HEURISTIC2_LIMIT;
798                     }
799 #               endif
800 #           else
801                 result = GC_find_limit((ptr_t)(&dummy), FALSE);
802 #               ifdef HEURISTIC2_LIMIT
803                     if (result < HEURISTIC2_LIMIT
804                         && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) {
805                             result = HEURISTIC2_LIMIT;
806                     }
807 #               endif
808 #           endif
809
810 #       endif /* HEURISTIC2 */
811 #       ifdef STACK_GROWS_DOWN
812             if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t));
813 #       endif
814         return(result);
815 #   endif /* STACKBOTTOM */
816 }
817 # endif /* ECOS */
818
819 # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */
820
821 /*
822  * Register static data segment(s) as roots.
823  * If more data segments are added later then they need to be registered
824  * add that point (as we do with SunOS dynamic loading),
825  * or GC_mark_roots needs to check for them (as we do with PCR).
826  * Called with allocator lock held.
827  */
828
829 # ifdef OS2
830
831 void GC_register_data_segments()
832 {
833     PTIB ptib;
834     PPIB ppib;
835     HMODULE module_handle;
836 #   define PBUFSIZ 512
837     UCHAR path[PBUFSIZ];
838     FILE * myexefile;
839     struct exe_hdr hdrdos;      /* MSDOS header.        */
840     struct e32_exe hdr386;      /* Real header for my executable */
841     struct o32_obj seg; /* Currrent segment */
842     int nsegs;
843     
844     
845     if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
846         GC_err_printf0("DosGetInfoBlocks failed\n");
847         ABORT("DosGetInfoBlocks failed\n");
848     }
849     module_handle = ppib -> pib_hmte;
850     if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
851         GC_err_printf0("DosQueryModuleName failed\n");
852         ABORT("DosGetInfoBlocks failed\n");
853     }
854     myexefile = fopen(path, "rb");
855     if (myexefile == 0) {
856         GC_err_puts("Couldn't open executable ");
857         GC_err_puts(path); GC_err_puts("\n");
858         ABORT("Failed to open executable\n");
859     }
860     if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) {
861         GC_err_puts("Couldn't read MSDOS header from ");
862         GC_err_puts(path); GC_err_puts("\n");
863         ABORT("Couldn't read MSDOS header");
864     }
865     if (E_MAGIC(hdrdos) != EMAGIC) {
866         GC_err_puts("Executable has wrong DOS magic number: ");
867         GC_err_puts(path); GC_err_puts("\n");
868         ABORT("Bad DOS magic number");
869     }
870     if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
871         GC_err_puts("Seek to new header failed in ");
872         GC_err_puts(path); GC_err_puts("\n");
873         ABORT("Bad DOS magic number");
874     }
875     if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) {
876         GC_err_puts("Couldn't read MSDOS header from ");
877         GC_err_puts(path); GC_err_puts("\n");
878         ABORT("Couldn't read OS/2 header");
879     }
880     if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
881         GC_err_puts("Executable has wrong OS/2 magic number:");
882         GC_err_puts(path); GC_err_puts("\n");
883         ABORT("Bad OS/2 magic number");
884     }
885     if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
886         GC_err_puts("Executable %s has wrong byte order: ");
887         GC_err_puts(path); GC_err_puts("\n");
888         ABORT("Bad byte order");
889     }
890     if ( E32_CPU(hdr386) == E32CPU286) {
891         GC_err_puts("GC can't handle 80286 executables: ");
892         GC_err_puts(path); GC_err_puts("\n");
893         EXIT();
894     }
895     if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
896               SEEK_SET) != 0) {
897         GC_err_puts("Seek to object table failed: ");
898         GC_err_puts(path); GC_err_puts("\n");
899         ABORT("Seek to object table failed");
900     }
901     for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
902       int flags;
903       if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) {
904         GC_err_puts("Couldn't read obj table entry from ");
905         GC_err_puts(path); GC_err_puts("\n");
906         ABORT("Couldn't read obj table entry");
907       }
908       flags = O32_FLAGS(seg);
909       if (!(flags & OBJWRITE)) continue;
910       if (!(flags & OBJREAD)) continue;
911       if (flags & OBJINVALID) {
912           GC_err_printf0("Object with invalid pages?\n");
913           continue;
914       } 
915       GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE);
916     }
917 }
918
919 # else /* !OS2 */
920
921 # if defined(MSWIN32) || defined(MSWINCE)
922
923 # ifdef MSWIN32
924   /* Unfortunately, we have to handle win32s very differently from NT,  */
925   /* Since VirtualQuery has very different semantics.  In particular,   */
926   /* under win32s a VirtualQuery call on an unmapped page returns an    */
927   /* invalid result.  Under GC_register_data_segments is a noop and     */
928   /* all real work is done by GC_register_dynamic_libraries.  Under     */
929   /* win32s, we cannot find the data segments associated with dll's.    */
930   /* We rgister the main data segment here.                             */
931   GC_bool GC_win32s = FALSE;    /* We're running under win32s.  */
932   
933   GC_bool GC_is_win32s()
934   {
935       DWORD v = GetVersion();
936       
937       /* Check that this is not NT, and Windows major version <= 3      */
938       return ((v & 0x80000000) && (v & 0xff) <= 3);
939   }
940   
941   void GC_init_win32()
942   {
943       GC_win32s = GC_is_win32s();
944   }
945
946   /* Return the smallest address a such that VirtualQuery               */
947   /* returns correct results for all addresses between a and start.     */
948   /* Assumes VirtualQuery returns correct information for start.        */
949   ptr_t GC_least_described_address(ptr_t start)
950   {  
951     MEMORY_BASIC_INFORMATION buf;
952     DWORD result;
953     LPVOID limit;
954     ptr_t p;
955     LPVOID q;
956     
957     limit = GC_sysinfo.lpMinimumApplicationAddress;
958     p = (ptr_t)((word)start & ~(GC_page_size - 1));
959     for (;;) {
960         q = (LPVOID)(p - GC_page_size);
961         if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break;
962         result = VirtualQuery(q, &buf, sizeof(buf));
963         if (result != sizeof(buf) || buf.AllocationBase == 0) break;
964         p = (ptr_t)(buf.AllocationBase);
965     }
966     return(p);
967   }
968 # endif
969   
970   /* Is p the start of either the malloc heap, or of one of our */
971   /* heap sections?                                             */
972   GC_bool GC_is_heap_base (ptr_t p)
973   {
974      
975      register unsigned i;
976      
977 #    ifndef REDIRECT_MALLOC
978        static ptr_t malloc_heap_pointer = 0;
979      
980        if (0 == malloc_heap_pointer) {
981          MEMORY_BASIC_INFORMATION buf;
982          void *pTemp = malloc( 1 );
983          register DWORD result = VirtualQuery(pTemp, &buf, sizeof(buf));
984            
985          free( pTemp );
986
987          
988          if (result != sizeof(buf)) {
989              ABORT("Weird VirtualQuery result");
990          }
991          malloc_heap_pointer = (ptr_t)(buf.AllocationBase);
992        }
993        if (p == malloc_heap_pointer) return(TRUE);
994 #    endif
995      for (i = 0; i < GC_n_heap_bases; i++) {
996          if (GC_heap_bases[i] == p) return(TRUE);
997      }
998      return(FALSE);
999   }
1000
1001 # ifdef MSWIN32
1002   void GC_register_root_section(ptr_t static_root)
1003   {
1004       MEMORY_BASIC_INFORMATION buf;
1005       DWORD result;
1006       DWORD protect;
1007       LPVOID p;
1008       char * base;
1009       char * limit, * new_limit;
1010     
1011       if (!GC_win32s) return;
1012       p = base = limit = GC_least_described_address(static_root);
1013       while (p < GC_sysinfo.lpMaximumApplicationAddress) {
1014         result = VirtualQuery(p, &buf, sizeof(buf));
1015         if (result != sizeof(buf) || buf.AllocationBase == 0
1016             || GC_is_heap_base(buf.AllocationBase)) break;
1017         new_limit = (char *)p + buf.RegionSize;
1018         protect = buf.Protect;
1019         if (buf.State == MEM_COMMIT
1020             && is_writable(protect)) {
1021             if ((char *)p == limit) {
1022                 limit = new_limit;
1023             } else {
1024                 if (base != limit) GC_add_roots_inner(base, limit, FALSE);
1025                 base = p;
1026                 limit = new_limit;
1027             }
1028         }
1029         if (p > (LPVOID)new_limit /* overflow */) break;
1030         p = (LPVOID)new_limit;
1031       }
1032       if (base != limit) GC_add_roots_inner(base, limit, FALSE);
1033   }
1034 #endif
1035   
1036   void GC_register_data_segments()
1037   {
1038 #     ifdef MSWIN32
1039       static char dummy;
1040       GC_register_root_section((ptr_t)(&dummy));
1041 #     endif
1042   }
1043
1044 # else /* !OS2 && !Windows */
1045
1046 # if (defined(SVR4) || defined(AUX) || defined(DGUX) \
1047       || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
1048 char * GC_SysVGetDataStart(max_page_size, etext_addr)
1049 int max_page_size;
1050 int * etext_addr;
1051 {
1052     word text_end = ((word)(etext_addr) + sizeof(word) - 1)
1053                     & ~(sizeof(word) - 1);
1054         /* etext rounded to word boundary       */
1055     word next_page = ((text_end + (word)max_page_size - 1)
1056                       & ~((word)max_page_size - 1));
1057     word page_offset = (text_end & ((word)max_page_size - 1));
1058     VOLATILE char * result = (char *)(next_page + page_offset);
1059     /* Note that this isnt equivalent to just adding            */
1060     /* max_page_size to &etext if &etext is at a page boundary  */
1061     
1062     GC_setup_temporary_fault_handler();
1063     if (setjmp(GC_jmp_buf) == 0) {
1064         /* Try writing to the address.  */
1065         *result = *result;
1066         GC_reset_fault_handler();
1067     } else {
1068         GC_reset_fault_handler();
1069         /* We got here via a longjmp.  The address is not readable.     */
1070         /* This is known to happen under Solaris 2.4 + gcc, which place */
1071         /* string constants in the text segment, but after etext.       */
1072         /* Use plan B.  Note that we now know there is a gap between    */
1073         /* text and data segments, so plan A bought us something.       */
1074         result = (char *)GC_find_limit((ptr_t)(DATAEND) - MIN_PAGE_SIZE, FALSE);
1075     }
1076     return((char *)result);
1077 }
1078 # endif
1079
1080
1081 #ifdef AMIGA
1082
1083 #  define GC_AMIGA_DS
1084 #  include "AmigaOS.c"
1085 #  undef GC_AMIGA_DS
1086
1087 #else /* !OS2 && !Windows && !AMIGA */
1088
1089 void GC_register_data_segments()
1090 {
1091 #   if !defined(PCR) && !defined(SRC_M3) && !defined(NEXT) && !defined(MACOS) \
1092        && !defined(MACOSX)
1093 #     if defined(REDIRECT_MALLOC) && defined(SOLARIS_THREADS)
1094         /* As of Solaris 2.3, the Solaris threads implementation        */
1095         /* allocates the data structure for the initial thread with     */
1096         /* sbrk at process startup.  It needs to be scanned, so that    */
1097         /* we don't lose some malloc allocated data structures          */
1098         /* hanging from it.  We're on thin ice here ...                 */
1099         extern caddr_t sbrk();
1100
1101         GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE);
1102 #     else
1103         GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE);
1104 #     endif
1105 #   endif
1106 #   if !defined(PCR) && (defined(NEXT) || defined(MACOSX))
1107       GC_add_roots_inner(DATASTART, (char *) get_end(), FALSE);
1108 #   endif
1109 #   if defined(MACOS)
1110     {
1111 #   if defined(THINK_C)
1112         extern void* GC_MacGetDataStart(void);
1113         /* globals begin above stack and end at a5. */
1114         GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
1115                            (ptr_t)LMGetCurrentA5(), FALSE);
1116 #   else
1117 #     if defined(__MWERKS__)
1118 #       if !__POWERPC__
1119           extern void* GC_MacGetDataStart(void);
1120           /* MATTHEW: Function to handle Far Globals (CW Pro 3) */
1121 #         if __option(far_data)
1122           extern void* GC_MacGetDataEnd(void);
1123 #         endif
1124           /* globals begin above stack and end at a5. */
1125           GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
1126                              (ptr_t)LMGetCurrentA5(), FALSE);
1127           /* MATTHEW: Handle Far Globals */                          
1128 #         if __option(far_data)
1129       /* Far globals follow he QD globals: */
1130           GC_add_roots_inner((ptr_t)LMGetCurrentA5(),
1131                              (ptr_t)GC_MacGetDataEnd(), FALSE);
1132 #         endif
1133 #       else
1134           extern char __data_start__[], __data_end__[];
1135           GC_add_roots_inner((ptr_t)&__data_start__,
1136                              (ptr_t)&__data_end__, FALSE);
1137 #       endif /* __POWERPC__ */
1138 #     endif /* __MWERKS__ */
1139 #   endif /* !THINK_C */
1140     }
1141 #   endif /* MACOS */
1142
1143     /* Dynamic libraries are added at every collection, since they may  */
1144     /* change.                                                          */
1145 }
1146
1147 # endif  /* ! AMIGA */
1148 # endif  /* ! MSWIN32 && ! MSWINCE*/
1149 # endif  /* ! OS2 */
1150
1151 /*
1152  * Auxiliary routines for obtaining memory from OS.
1153  */
1154
1155 # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
1156         && !defined(MSWIN32) && !defined(MSWINCE) \
1157         && !defined(MACOS) && !defined(DOS4GW)
1158
1159 # ifdef SUNOS4
1160     extern caddr_t sbrk();
1161 # endif
1162 # ifdef __STDC__
1163 #   define SBRK_ARG_T ptrdiff_t
1164 # else
1165 #   define SBRK_ARG_T int
1166 # endif
1167
1168
1169 # ifdef RS6000
1170 /* The compiler seems to generate speculative reads one past the end of */
1171 /* an allocated object.  Hence we need to make sure that the page       */
1172 /* following the last heap page is also mapped.                         */
1173 ptr_t GC_unix_get_mem(bytes)
1174 word bytes;
1175 {
1176     caddr_t cur_brk = (caddr_t)sbrk(0);
1177     caddr_t result;
1178     SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
1179     static caddr_t my_brk_val = 0;
1180     
1181     if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
1182     if (lsbs != 0) {
1183         if((caddr_t)(sbrk(GC_page_size - lsbs)) == (caddr_t)(-1)) return(0);
1184     }
1185     if (cur_brk == my_brk_val) {
1186         /* Use the extra block we allocated last time. */
1187         result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
1188         if (result == (caddr_t)(-1)) return(0);
1189         result -= GC_page_size;
1190     } else {
1191         result = (ptr_t)sbrk(GC_page_size + (SBRK_ARG_T)bytes);
1192         if (result == (caddr_t)(-1)) return(0);
1193     }
1194     my_brk_val = result + bytes + GC_page_size; /* Always page aligned */
1195     return((ptr_t)result);
1196 }
1197
1198 #else  /* Not RS6000 */
1199
1200 #if defined(USE_MMAP)
1201 /* Tested only under Linux, IRIX5 and Solaris 2 */
1202
1203 #ifdef USE_MMAP_FIXED
1204 #   define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE
1205         /* Seems to yield better performance on Solaris 2, but can      */
1206         /* be unreliable if something is already mapped at the address. */
1207 #else
1208 #   define GC_MMAP_FLAGS MAP_PRIVATE
1209 #endif
1210
1211 #ifndef HEAP_START
1212 #   define HEAP_START 0
1213 #endif
1214
1215 ptr_t GC_unix_get_mem(bytes)
1216 word bytes;
1217 {
1218     static GC_bool initialized = FALSE;
1219     static int fd;
1220     void *result;
1221     static ptr_t last_addr = HEAP_START;
1222
1223     if (!initialized) {
1224         fd = open("/dev/zero", O_RDONLY);
1225         initialized = TRUE;
1226     }
1227     if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
1228     result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
1229                   GC_MMAP_FLAGS, fd, 0/* offset */);
1230     if (result == MAP_FAILED) return(0);
1231     last_addr = (ptr_t)result + bytes + GC_page_size - 1;
1232     last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
1233 #   if !defined(LINUX)
1234       if (last_addr == 0) {
1235         /* Oops.  We got the end of the address space.  This isn't      */
1236         /* usable by arbitrary C code, since one-past-end pointers      */
1237         /* don't work, so we discard it and try again.                  */
1238         munmap(result, (size_t)(-GC_page_size) - (size_t)result);
1239                         /* Leave last page mapped, so we can't repeat. */
1240         return GC_unix_get_mem(bytes);
1241       }
1242 #   else
1243       GC_ASSERT(last_addr != 0);
1244 #   endif
1245     return((ptr_t)result);
1246 }
1247
1248 #else /* Not RS6000, not USE_MMAP */
1249 ptr_t GC_unix_get_mem(bytes)
1250 word bytes;
1251 {
1252   ptr_t result;
1253 # ifdef IRIX5
1254     /* Bare sbrk isn't thread safe.  Play by malloc rules.      */
1255     /* The equivalent may be needed on other systems as well.   */
1256     __LOCK_MALLOC();
1257 # endif
1258   {
1259     ptr_t cur_brk = (ptr_t)sbrk(0);
1260     SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1);
1261     
1262     if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
1263     if (lsbs != 0) {
1264         if((ptr_t)sbrk(GC_page_size - lsbs) == (ptr_t)(-1)) return(0);
1265     }
1266     result = (ptr_t)sbrk((SBRK_ARG_T)bytes);
1267     if (result == (ptr_t)(-1)) result = 0;
1268   }
1269 # ifdef IRIX5
1270     __UNLOCK_MALLOC();
1271 # endif
1272   return(result);
1273 }
1274
1275 #endif /* Not USE_MMAP */
1276 #endif /* Not RS6000 */
1277
1278 # endif /* UN*X */
1279
1280 # ifdef OS2
1281
1282 void * os2_alloc(size_t bytes)
1283 {
1284     void * result;
1285
1286     if (DosAllocMem(&result, bytes, PAG_EXECUTE | PAG_READ |
1287                                     PAG_WRITE | PAG_COMMIT)
1288                     != NO_ERROR) {
1289         return(0);
1290     }
1291     if (result == 0) return(os2_alloc(bytes));
1292     return(result);
1293 }
1294
1295 # endif /* OS2 */
1296
1297
1298 # if defined(MSWIN32) || defined(MSWINCE)
1299 SYSTEM_INFO GC_sysinfo;
1300 # endif
1301
1302
1303 # ifdef MSWIN32
1304 word GC_n_heap_bases = 0;
1305
1306 ptr_t GC_win32_get_mem(bytes)
1307 word bytes;
1308 {
1309     ptr_t result;
1310
1311     if (GC_win32s) {
1312         /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE.    */
1313         /* There are also unconfirmed rumors of other           */
1314         /* problems, so we dodge the issue.                     */
1315         result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
1316         result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));
1317     } else {
1318         result = (ptr_t) VirtualAlloc(NULL, bytes,
1319                                       MEM_COMMIT | MEM_RESERVE,
1320                                       PAGE_EXECUTE_READWRITE);
1321     }
1322     if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1323         /* If I read the documentation correctly, this can      */
1324         /* only happen if HBLKSIZE > 64k or not a power of 2.   */
1325     if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
1326     GC_heap_bases[GC_n_heap_bases++] = result;
1327     return(result);                       
1328 }
1329
1330 void GC_win32_free_heap ()
1331 {
1332     if (GC_win32s) {
1333         while (GC_n_heap_bases > 0) {
1334             GlobalFree (GC_heap_bases[--GC_n_heap_bases]);
1335             GC_heap_bases[GC_n_heap_bases] = 0;
1336         }
1337     }
1338 }
1339 # endif
1340
1341 #ifdef AMIGA
1342 # define GC_AMIGA_AM
1343 # include "AmigaOS.c"
1344 # undef GC_AMIGA_AM
1345 #endif
1346
1347
1348 # ifdef MSWINCE
1349 word GC_n_heap_bases = 0;
1350
1351 ptr_t GC_wince_get_mem(bytes)
1352 word bytes;
1353 {
1354     ptr_t result;
1355     word i;
1356
1357     /* Round up allocation size to multiple of page size */
1358     bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1);
1359
1360     /* Try to find reserved, uncommitted pages */
1361     for (i = 0; i < GC_n_heap_bases; i++) {
1362         if (((word)(-(signed_word)GC_heap_lengths[i])
1363              & (GC_sysinfo.dwAllocationGranularity-1))
1364             >= bytes) {
1365             result = GC_heap_bases[i] + GC_heap_lengths[i];
1366             break;
1367         }
1368     }
1369
1370     if (i == GC_n_heap_bases) {
1371         /* Reserve more pages */
1372         word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
1373                          & ~(GC_sysinfo.dwAllocationGranularity-1);
1374         result = (ptr_t) VirtualAlloc(NULL, res_bytes,
1375                                       MEM_RESERVE | MEM_TOP_DOWN,
1376                                       PAGE_EXECUTE_READWRITE);
1377         if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1378             /* If I read the documentation correctly, this can  */
1379             /* only happen if HBLKSIZE > 64k or not a power of 2.       */
1380         if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
1381         GC_heap_bases[GC_n_heap_bases] = result;
1382         GC_heap_lengths[GC_n_heap_bases] = 0;
1383         GC_n_heap_bases++;
1384     }
1385
1386     /* Commit pages */
1387     result = (ptr_t) VirtualAlloc(result, bytes,
1388                                   MEM_COMMIT,
1389                                   PAGE_EXECUTE_READWRITE);
1390     if (result != NULL) {
1391         if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
1392         GC_heap_lengths[i] += bytes;
1393     }
1394
1395     return(result);                       
1396 }
1397 # endif
1398
1399 #ifdef USE_MUNMAP
1400
1401 /* For now, this only works on Win32/WinCE and some Unix-like   */
1402 /* systems.  If you have something else, don't define           */
1403 /* USE_MUNMAP.                                                  */
1404 /* We assume ANSI C to support this feature.                    */
1405
1406 #if !defined(MSWIN32) && !defined(MSWINCE)
1407
1408 #include <unistd.h>
1409 #include <sys/mman.h>
1410 #include <sys/stat.h>
1411 #include <sys/types.h>
1412
1413 #endif
1414
1415 /* Compute a page aligned starting address for the unmap        */
1416 /* operation on a block of size bytes starting at start.        */
1417 /* Return 0 if the block is too small to make this feasible.    */
1418 ptr_t GC_unmap_start(ptr_t start, word bytes)
1419 {
1420     ptr_t result = start;
1421     /* Round start to next page boundary.       */
1422         result += GC_page_size - 1;
1423         result = (ptr_t)((word)result & ~(GC_page_size - 1));
1424     if (result + GC_page_size > start + bytes) return 0;
1425     return result;
1426 }
1427
1428 /* Compute end address for an unmap operation on the indicated  */
1429 /* block.                                                       */
1430 ptr_t GC_unmap_end(ptr_t start, word bytes)
1431 {
1432     ptr_t end_addr = start + bytes;
1433     end_addr = (ptr_t)((word)end_addr & ~(GC_page_size - 1));
1434     return end_addr;
1435 }
1436
1437 /* Under Win32/WinCE we commit (map) and decommit (unmap)       */
1438 /* memory using VirtualAlloc and VirtualFree.  These functions  */
1439 /* work on individual allocations of virtual memory, made       */
1440 /* previously using VirtualAlloc with the MEM_RESERVE flag.     */
1441 /* The ranges we need to (de)commit may span several of these   */
1442 /* allocations; therefore we use VirtualQuery to check          */
1443 /* allocation lengths, and split up the range as necessary.     */
1444
1445 /* We assume that GC_remap is called on exactly the same range  */
1446 /* as a previous call to GC_unmap.  It is safe to consistently  */
1447 /* round the endpoints in both places.                          */
1448 void GC_unmap(ptr_t start, word bytes)
1449 {
1450     ptr_t start_addr = GC_unmap_start(start, bytes);
1451     ptr_t end_addr = GC_unmap_end(start, bytes);
1452     word len = end_addr - start_addr;
1453     if (0 == start_addr) return;
1454 #   if defined(MSWIN32) || defined(MSWINCE)
1455       while (len != 0) {
1456           MEMORY_BASIC_INFORMATION mem_info;
1457           GC_word free_len;
1458           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1459               != sizeof(mem_info))
1460               ABORT("Weird VirtualQuery result");
1461           free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1462           if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
1463               ABORT("VirtualFree failed");
1464           GC_unmapped_bytes += free_len;
1465           start_addr += free_len;
1466           len -= free_len;
1467       }
1468 #   else
1469       if (munmap(start_addr, len) != 0) ABORT("munmap failed");
1470       GC_unmapped_bytes += len;
1471 #   endif
1472 }
1473
1474
1475 void GC_remap(ptr_t start, word bytes)
1476 {
1477     static int zero_descr = -1;
1478     ptr_t start_addr = GC_unmap_start(start, bytes);
1479     ptr_t end_addr = GC_unmap_end(start, bytes);
1480     word len = end_addr - start_addr;
1481     ptr_t result;
1482
1483 #   if defined(MSWIN32) || defined(MSWINCE)
1484       if (0 == start_addr) return;
1485       while (len != 0) {
1486           MEMORY_BASIC_INFORMATION mem_info;
1487           GC_word alloc_len;
1488           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1489               != sizeof(mem_info))
1490               ABORT("Weird VirtualQuery result");
1491           alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1492           result = VirtualAlloc(start_addr, alloc_len,
1493                                 MEM_COMMIT,
1494                                 PAGE_EXECUTE_READWRITE);
1495           if (result != start_addr) {
1496               ABORT("VirtualAlloc remapping failed");
1497           }
1498           GC_unmapped_bytes -= alloc_len;
1499           start_addr += alloc_len;
1500           len -= alloc_len;
1501       }
1502 #   else
1503       if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);
1504       if (0 == start_addr) return;
1505       result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
1506                     MAP_FIXED | MAP_PRIVATE, zero_descr, 0);
1507       if (result != start_addr) {
1508           ABORT("mmap remapping failed");
1509       }
1510       GC_unmapped_bytes -= len;
1511 #   endif
1512 }
1513
1514 /* Two adjacent blocks have already been unmapped and are about to      */
1515 /* be merged.  Unmap the whole block.  This typically requires          */
1516 /* that we unmap a small section in the middle that was not previously  */
1517 /* unmapped due to alignment constraints.                               */
1518 void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2)
1519 {
1520     ptr_t start1_addr = GC_unmap_start(start1, bytes1);
1521     ptr_t end1_addr = GC_unmap_end(start1, bytes1);
1522     ptr_t start2_addr = GC_unmap_start(start2, bytes2);
1523     ptr_t end2_addr = GC_unmap_end(start2, bytes2);
1524     ptr_t start_addr = end1_addr;
1525     ptr_t end_addr = start2_addr;
1526     word len;
1527     GC_ASSERT(start1 + bytes1 == start2);
1528     if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2);
1529     if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
1530     if (0 == start_addr) return;
1531     len = end_addr - start_addr;
1532 #   if defined(MSWIN32) || defined(MSWINCE)
1533       while (len != 0) {
1534           MEMORY_BASIC_INFORMATION mem_info;
1535           GC_word free_len;
1536           if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
1537               != sizeof(mem_info))
1538               ABORT("Weird VirtualQuery result");
1539           free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize;
1540           if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT))
1541               ABORT("VirtualFree failed");
1542           GC_unmapped_bytes += free_len;
1543           start_addr += free_len;
1544           len -= free_len;
1545       }
1546 #   else
1547       if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed");
1548       GC_unmapped_bytes += len;
1549 #   endif
1550 }
1551
1552 #endif /* USE_MUNMAP */
1553
1554 /* Routine for pushing any additional roots.  In THREADS        */
1555 /* environment, this is also responsible for marking from       */
1556 /* thread stacks.                                               */
1557 #ifndef THREADS
1558 void (*GC_push_other_roots)() = 0;
1559 #else /* THREADS */
1560
1561 # ifdef PCR
1562 PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy)
1563 {
1564     struct PCR_ThCtl_TInfoRep info;
1565     PCR_ERes result;
1566     
1567     info.ti_stkLow = info.ti_stkHi = 0;
1568     result = PCR_ThCtl_GetInfo(t, &info);
1569     GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi));
1570     return(result);
1571 }
1572
1573 /* Push the contents of an old object. We treat this as stack   */
1574 /* data only becasue that makes it robust against mark stack    */
1575 /* overflow.                                                    */
1576 PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data)
1577 {
1578     GC_push_all_stack((ptr_t)p, (ptr_t)p + size);
1579     return(PCR_ERes_okay);
1580 }
1581
1582
1583 void GC_default_push_other_roots GC_PROTO((void))
1584 {
1585     /* Traverse data allocated by previous memory managers.             */
1586         {
1587           extern struct PCR_MM_ProcsRep * GC_old_allocator;
1588           
1589           if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false,
1590                                                    GC_push_old_obj, 0)
1591               != PCR_ERes_okay) {
1592               ABORT("Old object enumeration failed");
1593           }
1594         }
1595     /* Traverse all thread stacks. */
1596         if (PCR_ERes_IsErr(
1597                 PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0))
1598               || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) {
1599               ABORT("Thread stack marking failed\n");
1600         }
1601 }
1602
1603 # endif /* PCR */
1604
1605 # ifdef SRC_M3
1606
1607 # ifdef ALL_INTERIOR_POINTERS
1608     --> misconfigured
1609 # endif
1610
1611 void GC_push_thread_structures GC_PROTO((void))
1612 {
1613     /* Not our responsibibility. */
1614 }
1615
1616 extern void ThreadF__ProcessStacks();
1617
1618 void GC_push_thread_stack(start, stop)
1619 word start, stop;
1620 {
1621    GC_push_all_stack((ptr_t)start, (ptr_t)stop + sizeof(word));
1622 }
1623
1624 /* Push routine with M3 specific calling convention. */
1625 GC_m3_push_root(dummy1, p, dummy2, dummy3)
1626 word *p;
1627 ptr_t dummy1, dummy2;
1628 int dummy3;
1629 {
1630     word q = *p;
1631     
1632     GC_PUSH_ONE_STACK(q, p);
1633 }
1634
1635 /* M3 set equivalent to RTHeap.TracedRefTypes */
1636 typedef struct { int elts[1]; }  RefTypeSet;
1637 RefTypeSet GC_TracedRefTypes = {{0x1}};
1638
1639 void GC_default_push_other_roots GC_PROTO((void))
1640 {
1641     /* Use the M3 provided routine for finding static roots.     */
1642     /* This is a bit dubious, since it presumes no C roots.      */
1643     /* We handle the collector roots explicitly in GC_push_roots */
1644         RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes);
1645         if (GC_words_allocd > 0) {
1646             ThreadF__ProcessStacks(GC_push_thread_stack);
1647         }
1648         /* Otherwise this isn't absolutely necessary, and we have       */
1649         /* startup ordering problems.                                   */
1650 }
1651
1652 # endif /* SRC_M3 */
1653
1654 # if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \
1655      || defined(IRIX_THREADS) || defined(LINUX_THREADS) \
1656      || defined(HPUX_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 /* SOLARIS_THREADS || ... */
1666
1667 void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots;
1668
1669 #endif
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(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(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(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 && !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 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 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 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 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 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 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 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 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 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