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