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