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