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