2005-02-16 Ben Maurer <bmaurer@ximian.com>
[mono-project.git] / libgc / dyn_load.c
blobf1431223c64984aa3b4d7e3d5f2603efa770af08
1 /*
2 * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
3 * Copyright (c) 1997 by Silicon Graphics. All rights reserved.
5 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
6 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
8 * Permission is hereby granted to use or copy this program
9 * for any purpose, provided the above notices are retained on all copies.
10 * Permission to modify the code and to distribute modified code is granted,
11 * provided the above notices are retained, and a notice that the code was
12 * modified is included with the above copyright notice.
14 * Original author: Bill Janssen
15 * Heavily modified by Hans Boehm and others
19 * This is incredibly OS specific code for tracking down data sections in
20 * dynamic libraries. There appears to be no way of doing this quickly
21 * without groveling through undocumented data structures. We would argue
22 * that this is a bug in the design of the dlopen interface. THIS CODE
23 * MAY BREAK IN FUTURE OS RELEASES. If this matters to you, don't hesitate
24 * to let your vendor know ...
26 * None of this is safe with dlclose and incremental collection.
27 * But then not much of anything is safe in the presence of dlclose.
29 #if defined(__linux__) && !defined(_GNU_SOURCE)
30 /* Can't test LINUX, since this must be define before other includes */
31 # define _GNU_SOURCE
32 #endif
33 #if !defined(MACOS) && !defined(_WIN32_WCE)
34 # include <sys/types.h>
35 #endif
36 #include "private/gc_priv.h"
38 /* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
39 # if (defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS)) \
40 && defined(dlopen) && !defined(GC_USE_LD_WRAP)
41 /* To support threads in Solaris, gc.h interposes on dlopen by */
42 /* defining "dlopen" to be "GC_dlopen", which is implemented below. */
43 /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */
44 /* real system dlopen() in their implementation. We first remove */
45 /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */
46 # undef dlopen
47 # define GC_must_restore_redefined_dlopen
48 # else
49 # undef GC_must_restore_redefined_dlopen
50 # endif
52 #if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \
53 && !defined(PCR)
54 #if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \
55 !defined(MSWIN32) && !defined(MSWINCE) && \
56 !(defined(ALPHA) && defined(OSF1)) && \
57 !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \
58 !defined(RS6000) && !defined(SCO_ELF) && !defined(DGUX) && \
59 !(defined(FREEBSD) && defined(__ELF__)) && \
60 !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
61 !defined(DARWIN)
62 --> We only know how to find data segments of dynamic libraries for the
63 --> above. Additional SVR4 variants might not be too
64 --> hard to add.
65 #endif
67 #include <stdio.h>
68 #ifdef SUNOS5DL
69 # include <sys/elf.h>
70 # include <dlfcn.h>
71 # include <link.h>
72 #endif
73 #ifdef SUNOS4
74 # include <dlfcn.h>
75 # include <link.h>
76 # include <a.out.h>
77 /* struct link_map field overrides */
78 # define l_next lm_next
79 # define l_addr lm_addr
80 # define l_name lm_name
81 #endif
83 #if defined(NETBSD)
84 # include <machine/elf_machdep.h>
85 # define ELFSIZE ARCH_ELFSIZE
86 #endif
88 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
89 (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
90 (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
91 # include <stddef.h>
92 # include <elf.h>
93 # include <link.h>
94 #endif
96 /* Newer versions of GNU/Linux define this macro. We
97 * define it similarly for any ELF systems that don't. */
98 # ifndef ElfW
99 # ifdef NETBSD
100 # if ELFSIZE == 32
101 # define ElfW(type) Elf32_##type
102 # else
103 # define ElfW(type) Elf64_##type
104 # endif
105 # else
106 # if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
107 # define ElfW(type) Elf32_##type
108 # else
109 # define ElfW(type) Elf64_##type
110 # endif
111 # endif
112 # endif
114 #if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES)
116 #ifdef LINT
117 Elf32_Dyn _DYNAMIC;
118 #endif
120 static struct link_map *
121 GC_FirstDLOpenedLinkMap()
123 extern ElfW(Dyn) _DYNAMIC;
124 ElfW(Dyn) *dp;
125 struct r_debug *r;
126 static struct link_map * cachedResult = 0;
127 static ElfW(Dyn) *dynStructureAddr = 0;
128 /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */
130 # ifdef SUNOS53_SHARED_LIB
131 /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */
132 /* up properly in dynamically linked .so's. This means we have */
133 /* to use its value in the set of original object files loaded */
134 /* at program startup. */
135 if( dynStructureAddr == 0 ) {
136 void* startupSyms = dlopen(0, RTLD_LAZY);
137 dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC");
139 # else
140 dynStructureAddr = &_DYNAMIC;
141 # endif
143 if( dynStructureAddr == 0) {
144 return(0);
146 if( cachedResult == 0 ) {
147 int tag;
148 for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
149 if( tag == DT_DEBUG ) {
150 struct link_map *lm
151 = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
152 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
153 break;
157 return cachedResult;
160 #endif /* SUNOS5DL ... */
162 /* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
163 # if defined(GC_must_restore_redefined_dlopen)
164 # define dlopen GC_dlopen
165 # endif
167 #if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES)
169 #ifdef LINT
170 struct link_dynamic _DYNAMIC;
171 #endif
173 static struct link_map *
174 GC_FirstDLOpenedLinkMap()
176 extern struct link_dynamic _DYNAMIC;
178 if( &_DYNAMIC == 0) {
179 return(0);
181 return(_DYNAMIC.ld_un.ld_1->ld_loaded);
184 /* Return the address of the ld.so allocated common symbol */
185 /* with the least address, or 0 if none. */
186 static ptr_t GC_first_common()
188 ptr_t result = 0;
189 extern struct link_dynamic _DYNAMIC;
190 struct rtc_symb * curr_symbol;
192 if( &_DYNAMIC == 0) {
193 return(0);
195 curr_symbol = _DYNAMIC.ldd -> ldd_cp;
196 for (; curr_symbol != 0; curr_symbol = curr_symbol -> rtc_next) {
197 if (result == 0
198 || (ptr_t)(curr_symbol -> rtc_sp -> n_value) < result) {
199 result = (ptr_t)(curr_symbol -> rtc_sp -> n_value);
202 return(result);
205 #endif /* SUNOS4 ... */
207 # if defined(SUNOS4) || defined(SUNOS5DL)
208 /* Add dynamic library data sections to the root set. */
209 # if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS)
210 # ifndef SRC_M3
211 --> fix mutual exclusion with dlopen
212 # endif /* We assume M3 programs don't call dlopen for now */
213 # endif
215 # ifndef USE_PROC_FOR_LIBRARIES
216 void GC_register_dynamic_libraries()
218 struct link_map *lm = GC_FirstDLOpenedLinkMap();
221 for (lm = GC_FirstDLOpenedLinkMap();
222 lm != (struct link_map *) 0; lm = lm->l_next)
224 # ifdef SUNOS4
225 struct exec *e;
227 e = (struct exec *) lm->lm_addr;
228 GC_add_roots_inner(
229 ((char *) (N_DATOFF(*e) + lm->lm_addr)),
230 ((char *) (N_BSSADDR(*e) + e->a_bss + lm->lm_addr)),
231 TRUE);
232 # endif
233 # ifdef SUNOS5DL
234 ElfW(Ehdr) * e;
235 ElfW(Phdr) * p;
236 unsigned long offset;
237 char * start;
238 register int i;
240 e = (ElfW(Ehdr) *) lm->l_addr;
241 p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
242 offset = ((unsigned long)(lm->l_addr));
243 for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
244 switch( p->p_type ) {
245 case PT_LOAD:
247 if( !(p->p_flags & PF_W) ) break;
248 start = ((char *)(p->p_vaddr)) + offset;
249 GC_add_roots_inner(
250 start,
251 start + p->p_memsz,
252 TRUE
255 break;
256 default:
257 break;
260 # endif
262 # ifdef SUNOS4
264 static ptr_t common_start = 0;
265 ptr_t common_end;
266 extern ptr_t GC_find_limit();
268 if (common_start == 0) common_start = GC_first_common();
269 if (common_start != 0) {
270 common_end = GC_find_limit(common_start, TRUE);
271 GC_add_roots_inner((char *)common_start, (char *)common_end, TRUE);
274 # endif
277 # endif /* !USE_PROC ... */
278 # endif /* SUNOS */
280 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
281 (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
282 (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
285 #ifdef USE_PROC_FOR_LIBRARIES
287 #include <string.h>
289 #include <sys/stat.h>
290 #include <fcntl.h>
291 #include <unistd.h>
293 #define MAPS_BUF_SIZE (32*1024)
295 extern ssize_t GC_repeat_read(int fd, char *buf, size_t count);
296 /* Repeatedly read until buffer is filled, or EOF is encountered */
297 /* Defined in os_dep.c. */
299 char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
300 char *prot_buf, unsigned int *maj_dev);
301 word GC_apply_to_maps(word (*fn)(char *));
302 /* From os_dep.c */
304 word GC_register_map_entries(char *maps)
306 char prot_buf[5];
307 char *buf_ptr = maps;
308 int count;
309 word start, end;
310 unsigned int maj_dev;
311 word least_ha, greatest_ha;
312 unsigned i;
313 word datastart = (word)(DATASTART);
315 /* Compute heap bounds. FIXME: Should be done by add_to_heap? */
316 least_ha = (word)(-1);
317 greatest_ha = 0;
318 for (i = 0; i < GC_n_heap_sects; ++i) {
319 word sect_start = (word)GC_heap_sects[i].hs_start;
320 word sect_end = sect_start + GC_heap_sects[i].hs_bytes;
321 if (sect_start < least_ha) least_ha = sect_start;
322 if (sect_end > greatest_ha) greatest_ha = sect_end;
324 if (greatest_ha < (word)GC_scratch_last_end_ptr)
325 greatest_ha = (word)GC_scratch_last_end_ptr;
327 for (;;) {
328 buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
329 if (buf_ptr == NULL) return 1;
330 if (prot_buf[1] == 'w') {
331 /* This is a writable mapping. Add it to */
332 /* the root set unless it is already otherwise */
333 /* accounted for. */
334 if (start <= (word)GC_stackbottom && end >= (word)GC_stackbottom) {
335 /* Stack mapping; discard */
336 continue;
338 # ifdef THREADS
339 if (GC_segment_is_thread_stack(start, end)) continue;
340 # endif
341 /* We no longer exclude the main data segment. */
342 if (start < least_ha && end > least_ha) {
343 end = least_ha;
345 if (start < greatest_ha && end > greatest_ha) {
346 start = greatest_ha;
348 if (start >= least_ha && end <= greatest_ha) continue;
349 GC_add_roots_inner((char *)start, (char *)end, TRUE);
352 return 1;
355 void GC_register_dynamic_libraries()
357 if (!GC_apply_to_maps(GC_register_map_entries))
358 ABORT("Failed to read /proc for library registration.");
361 /* We now take care of the main data segment ourselves: */
362 GC_bool GC_register_main_static_data()
364 return FALSE;
367 # define HAVE_REGISTER_MAIN_STATIC_DATA
369 #endif /* USE_PROC_FOR_LIBRARIES */
371 #if !defined(USE_PROC_FOR_LIBRARIES)
372 /* The following is the preferred way to walk dynamic libraries */
373 /* For glibc 2.2.4+. Unfortunately, it doesn't work for older */
374 /* versions. Thanks to Jakub Jelinek for most of the code. */
376 # if defined(LINUX) /* Are others OK here, too? */ \
377 && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
378 || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
380 /* We have the header files for a glibc that includes dl_iterate_phdr. */
381 /* It may still not be available in the library on the target system. */
382 /* Thus we also treat it as a weak symbol. */
383 #define HAVE_DL_ITERATE_PHDR
385 static int GC_register_dynlib_callback(info, size, ptr)
386 struct dl_phdr_info * info;
387 size_t size;
388 void * ptr;
390 const ElfW(Phdr) * p;
391 char * start;
392 register int i;
394 /* Make sure struct dl_phdr_info is at least as big as we need. */
395 if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
396 + sizeof (info->dlpi_phnum))
397 return -1;
399 p = info->dlpi_phdr;
400 for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
401 switch( p->p_type ) {
402 case PT_LOAD:
404 if( !(p->p_flags & PF_W) ) break;
405 start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
406 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
408 break;
409 default:
410 break;
414 * (int *)ptr = 1; /* Signal that we were called */
415 return 0;
418 /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
420 #pragma weak dl_iterate_phdr
422 GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
424 if (dl_iterate_phdr) {
425 int did_something = 0;
426 dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
427 if (!did_something) {
428 /* dl_iterate_phdr may forget the static data segment in */
429 /* statically linked executables. */
430 GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE);
431 # if defined(DATASTART2)
432 GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
433 # endif
436 return TRUE;
437 } else {
438 return FALSE;
442 /* Do we need to separately register the main static data segment? */
443 GC_bool GC_register_main_static_data()
445 return (dl_iterate_phdr == 0);
448 #define HAVE_REGISTER_MAIN_STATIC_DATA
450 # else /* !LINUX || version(glibc) < 2.2.4 */
452 /* Dynamic loading code for Linux running ELF. Somewhat tested on
453 * Linux/x86, untested but hopefully should work on Linux/Alpha.
454 * This code was derived from the Solaris/ELF support. Thanks to
455 * whatever kind soul wrote that. - Patrick Bridges */
457 /* This doesn't necessarily work in all cases, e.g. with preloaded
458 * dynamic libraries. */
460 #if defined(NETBSD)
461 # include <sys/exec_elf.h>
462 /* for compatibility with 1.4.x */
463 # ifndef DT_DEBUG
464 # define DT_DEBUG 21
465 # endif
466 # ifndef PT_LOAD
467 # define PT_LOAD 1
468 # endif
469 # ifndef PF_W
470 # define PF_W 2
471 # endif
472 #else
473 # include <elf.h>
474 #endif
475 #include <link.h>
477 # endif
479 #ifdef __GNUC__
480 # pragma weak _DYNAMIC
481 #endif
482 extern ElfW(Dyn) _DYNAMIC[];
484 static struct link_map *
485 GC_FirstDLOpenedLinkMap()
487 ElfW(Dyn) *dp;
488 struct r_debug *r;
489 static struct link_map *cachedResult = 0;
491 if( _DYNAMIC == 0) {
492 return(0);
494 if( cachedResult == 0 ) {
495 int tag;
496 for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
497 if( tag == DT_DEBUG ) {
498 struct link_map *lm
499 = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
500 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
501 break;
505 return cachedResult;
509 void GC_register_dynamic_libraries()
511 struct link_map *lm;
514 # ifdef HAVE_DL_ITERATE_PHDR
515 if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
516 return;
518 # endif
519 lm = GC_FirstDLOpenedLinkMap();
520 for (lm = GC_FirstDLOpenedLinkMap();
521 lm != (struct link_map *) 0; lm = lm->l_next)
523 ElfW(Ehdr) * e;
524 ElfW(Phdr) * p;
525 unsigned long offset;
526 char * start;
527 register int i;
529 e = (ElfW(Ehdr) *) lm->l_addr;
530 p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
531 offset = ((unsigned long)(lm->l_addr));
532 for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
533 switch( p->p_type ) {
534 case PT_LOAD:
536 if( !(p->p_flags & PF_W) ) break;
537 start = ((char *)(p->p_vaddr)) + offset;
538 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
540 break;
541 default:
542 break;
548 #endif /* !USE_PROC_FOR_LIBRARIES */
550 #endif /* LINUX */
552 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
554 #include <sys/procfs.h>
555 #include <sys/stat.h>
556 #include <fcntl.h>
557 #include <elf.h>
558 #include <errno.h>
559 #include <signal.h> /* Only for the following test. */
560 #ifndef _sigargs
561 # define IRIX6
562 #endif
564 extern void * GC_roots_present();
565 /* The type is a lie, since the real type doesn't make sense here, */
566 /* and we only test for NULL. */
569 /* We use /proc to track down all parts of the address space that are */
570 /* mapped by the process, and throw out regions we know we shouldn't */
571 /* worry about. This may also work under other SVR4 variants. */
572 void GC_register_dynamic_libraries()
574 static int fd = -1;
575 char buf[30];
576 static prmap_t * addr_map = 0;
577 static int current_sz = 0; /* Number of records currently in addr_map */
578 static int needed_sz; /* Required size of addr_map */
579 register int i;
580 register long flags;
581 register ptr_t start;
582 register ptr_t limit;
583 ptr_t heap_start = (ptr_t)HEAP_START;
584 ptr_t heap_end = heap_start;
586 # ifdef SUNOS5DL
587 # define MA_PHYS 0
588 # endif /* SUNOS5DL */
590 if (fd < 0) {
591 sprintf(buf, "/proc/%d", getpid());
592 /* The above generates a lint complaint, since pid_t varies. */
593 /* It's unclear how to improve this. */
594 fd = open(buf, O_RDONLY);
595 if (fd < 0) {
596 ABORT("/proc open failed");
599 if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
600 GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
601 ABORT("/proc PIOCNMAP ioctl failed");
603 if (needed_sz >= current_sz) {
604 current_sz = needed_sz * 2 + 1;
605 /* Expansion, plus room for 0 record */
606 addr_map = (prmap_t *)GC_scratch_alloc((word)
607 (current_sz * sizeof(prmap_t)));
609 if (ioctl(fd, PIOCMAP, addr_map) < 0) {
610 GC_err_printf4("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
611 fd, errno, needed_sz, addr_map);
612 ABORT("/proc PIOCMAP ioctl failed");
614 if (GC_n_heap_sects > 0) {
615 heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
616 + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
617 if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr;
619 for (i = 0; i < needed_sz; i++) {
620 flags = addr_map[i].pr_mflags;
621 if ((flags & (MA_BREAK | MA_STACK | MA_PHYS)) != 0) goto irrelevant;
622 if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
623 goto irrelevant;
624 /* The latter test is empirically useless in very old Irix */
625 /* versions. Other than the */
626 /* main data and stack segments, everything appears to be */
627 /* mapped readable, writable, executable, and shared(!!). */
628 /* This makes no sense to me. - HB */
629 start = (ptr_t)(addr_map[i].pr_vaddr);
630 if (GC_roots_present(start)) goto irrelevant;
631 if (start < heap_end && start >= heap_start)
632 goto irrelevant;
633 # ifdef MMAP_STACKS
634 if (GC_is_thread_stack(start)) goto irrelevant;
635 # endif /* MMAP_STACKS */
637 limit = start + addr_map[i].pr_size;
638 /* The following seemed to be necessary for very old versions */
639 /* of Irix, but it has been reported to discard relevant */
640 /* segments under Irix 6.5. */
641 # ifndef IRIX6
642 if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
643 /* Discard text segments, i.e. 0-offset mappings against */
644 /* executable files which appear to have ELF headers. */
645 caddr_t arg;
646 int obj;
647 # define MAP_IRR_SZ 10
648 static ptr_t map_irr[MAP_IRR_SZ];
649 /* Known irrelevant map entries */
650 static int n_irr = 0;
651 struct stat buf;
652 register int i;
654 for (i = 0; i < n_irr; i++) {
655 if (map_irr[i] == start) goto irrelevant;
657 arg = (caddr_t)start;
658 obj = ioctl(fd, PIOCOPENM, &arg);
659 if (obj >= 0) {
660 fstat(obj, &buf);
661 close(obj);
662 if ((buf.st_mode & 0111) != 0) {
663 if (n_irr < MAP_IRR_SZ) {
664 map_irr[n_irr++] = start;
666 goto irrelevant;
670 # endif /* !IRIX6 */
671 GC_add_roots_inner(start, limit, TRUE);
672 irrelevant: ;
674 /* Dont keep cached descriptor, for now. Some kernels don't like us */
675 /* to keep a /proc file descriptor around during kill -9. */
676 if (close(fd) < 0) ABORT("Couldnt close /proc file");
677 fd = -1;
680 # endif /* USE_PROC || IRIX5 */
682 # if defined(MSWIN32) || defined(MSWINCE)
684 # define WIN32_LEAN_AND_MEAN
685 # define NOSERVICE
686 # include <windows.h>
687 # include <stdlib.h>
689 /* We traverse the entire address space and register all segments */
690 /* that could possibly have been written to. */
692 extern GC_bool GC_is_heap_base (ptr_t p);
694 # ifdef GC_WIN32_THREADS
695 extern void GC_get_next_stack(char *start, char **lo, char **hi);
696 void GC_cond_add_roots(char *base, char * limit)
698 char * curr_base = base;
699 char * next_stack_lo;
700 char * next_stack_hi;
702 if (base == limit) return;
703 for(;;) {
704 GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
705 if (next_stack_lo >= limit) break;
706 GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
707 curr_base = next_stack_hi;
709 if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
711 # else
712 void GC_cond_add_roots(char *base, char * limit)
714 char dummy;
715 char * stack_top
716 = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
717 if (base == limit) return;
718 if (limit > stack_top && base < GC_stackbottom) {
719 /* Part of the stack; ignore it. */
720 return;
722 GC_add_roots_inner(base, limit, TRUE);
724 # endif
726 # ifdef MSWINCE
727 /* Do we need to separately register the main static data segment? */
728 GC_bool GC_register_main_static_data()
730 return FALSE;
732 # else /* win32 */
733 extern GC_bool GC_no_win32_dlls;
735 GC_bool GC_register_main_static_data()
737 return GC_no_win32_dlls;
739 # endif /* win32 */
741 # define HAVE_REGISTER_MAIN_STATIC_DATA
743 GC_bool GC_warn_fb = TRUE; /* Warn about traced likely */
744 /* graphics memory. */
745 GC_bool GC_disallow_ignore_fb = FALSE;
746 int GC_ignore_fb_mb; /* Ignore mappings bigger than the */
747 /* specified number of MB. */
748 GC_bool GC_ignore_fb = FALSE; /* Enable frame buffer */
749 /* checking. */
751 /* Issue warning if tracing apparent framebuffer. */
752 /* This limits us to one warning, and it's a back door to */
753 /* disable that. */
755 /* Should [start, start+len) be treated as a frame buffer */
756 /* and ignored? */
757 /* Unfortunately, we currently have no real way to tell */
758 /* automatically, and rely largely on user input. */
759 /* FIXME: If we had more data on this phenomenon (e.g. */
760 /* is start aligned to a MB multiple?) we should be able to */
761 /* do better. */
762 /* Based on a very limited sample, it appears that: */
763 /* - Frame buffer mappings appear as mappings of length */
764 /* 2**n MB - 192K. (We guess the 192K can vary a bit.) */
765 /* - Have a stating address at best 64K aligned. */
766 /* I'd love more information about the mapping, since I */
767 /* can't reproduce the problem. */
768 static GC_bool is_frame_buffer(ptr_t start, size_t len)
770 static GC_bool initialized = FALSE;
771 # define MB (1024*1024)
772 # define DEFAULT_FB_MB 15
773 # define MIN_FB_MB 3
775 if (GC_disallow_ignore_fb) return FALSE;
776 if (!initialized) {
777 char * ignore_fb_string = GETENV("GC_IGNORE_FB");
779 if (0 != ignore_fb_string) {
780 while (*ignore_fb_string == ' ' || *ignore_fb_string == '\t')
781 ++ignore_fb_string;
782 if (*ignore_fb_string == '\0') {
783 GC_ignore_fb_mb = DEFAULT_FB_MB;
784 } else {
785 GC_ignore_fb_mb = atoi(ignore_fb_string);
786 if (GC_ignore_fb_mb < MIN_FB_MB) {
787 WARN("Bad GC_IGNORE_FB value. Using %ld\n", DEFAULT_FB_MB);
788 GC_ignore_fb_mb = DEFAULT_FB_MB;
791 GC_ignore_fb = TRUE;
792 } else {
793 GC_ignore_fb_mb = DEFAULT_FB_MB; /* For warning */
795 initialized = TRUE;
797 if (len >= ((size_t)GC_ignore_fb_mb << 20)) {
798 if (GC_ignore_fb) {
799 return TRUE;
800 } else {
801 if (GC_warn_fb) {
802 WARN("Possible frame buffer mapping at 0x%lx: \n"
803 "\tConsider setting GC_IGNORE_FB to improve performance.\n",
804 start);
805 GC_warn_fb = FALSE;
807 return FALSE;
809 } else {
810 return FALSE;
814 # ifdef DEBUG_VIRTUALQUERY
815 void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
817 GC_printf4("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
818 buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
819 buf -> RegionSize);
820 GC_printf4("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
821 "Type = %lx\n",
822 buf -> AllocationProtect, buf -> State, buf -> Protect,
823 buf -> Type);
825 # endif /* DEBUG_VIRTUALQUERY */
827 void GC_register_dynamic_libraries()
829 MEMORY_BASIC_INFORMATION buf;
830 DWORD result;
831 DWORD protect;
832 LPVOID p;
833 char * base;
834 char * limit, * new_limit;
836 # ifdef MSWIN32
837 if (GC_no_win32_dlls) return;
838 # endif
839 base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
840 # if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
841 /* Only the first 32 MB of address space belongs to the current process */
842 while (p < (LPVOID)0x02000000) {
843 result = VirtualQuery(p, &buf, sizeof(buf));
844 if (result == 0) {
845 /* Page is free; advance to the next possible allocation base */
846 new_limit = (char *)
847 (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
848 & ~(GC_sysinfo.dwAllocationGranularity-1));
849 } else
850 # else
851 while (p < GC_sysinfo.lpMaximumApplicationAddress) {
852 result = VirtualQuery(p, &buf, sizeof(buf));
853 # endif
855 if (result != sizeof(buf)) {
856 ABORT("Weird VirtualQuery result");
858 new_limit = (char *)p + buf.RegionSize;
859 protect = buf.Protect;
860 if (buf.State == MEM_COMMIT
861 && (protect == PAGE_EXECUTE_READWRITE
862 || protect == PAGE_READWRITE)
863 && !GC_is_heap_base(buf.AllocationBase)
864 && !is_frame_buffer(p, buf.RegionSize)) {
865 # ifdef DEBUG_VIRTUALQUERY
866 GC_dump_meminfo(&buf);
867 # endif
868 if ((char *)p != limit) {
869 GC_cond_add_roots(base, limit);
870 base = p;
872 limit = new_limit;
875 if (p > (LPVOID)new_limit /* overflow */) break;
876 p = (LPVOID)new_limit;
878 GC_cond_add_roots(base, limit);
881 #endif /* MSWIN32 || MSWINCE */
883 #if defined(ALPHA) && defined(OSF1)
885 #include <loader.h>
887 void GC_register_dynamic_libraries()
889 int status;
890 ldr_process_t mypid;
892 /* module */
893 ldr_module_t moduleid = LDR_NULL_MODULE;
894 ldr_module_info_t moduleinfo;
895 size_t moduleinfosize = sizeof(moduleinfo);
896 size_t modulereturnsize;
898 /* region */
899 ldr_region_t region;
900 ldr_region_info_t regioninfo;
901 size_t regioninfosize = sizeof(regioninfo);
902 size_t regionreturnsize;
904 /* Obtain id of this process */
905 mypid = ldr_my_process();
907 /* For each module */
908 while (TRUE) {
910 /* Get the next (first) module */
911 status = ldr_next_module(mypid, &moduleid);
913 /* Any more modules? */
914 if (moduleid == LDR_NULL_MODULE)
915 break; /* No more modules */
917 /* Check status AFTER checking moduleid because */
918 /* of a bug in the non-shared ldr_next_module stub */
919 if (status != 0 ) {
920 GC_printf1("dynamic_load: status = %ld\n", (long)status);
922 extern char *sys_errlist[];
923 extern int sys_nerr;
924 extern int errno;
925 if (errno <= sys_nerr) {
926 GC_printf1("dynamic_load: %s\n", (long)sys_errlist[errno]);
927 } else {
928 GC_printf1("dynamic_load: %d\n", (long)errno);
931 ABORT("ldr_next_module failed");
934 /* Get the module information */
935 status = ldr_inq_module(mypid, moduleid, &moduleinfo,
936 moduleinfosize, &modulereturnsize);
937 if (status != 0 )
938 ABORT("ldr_inq_module failed");
940 /* is module for the main program (i.e. nonshared portion)? */
941 if (moduleinfo.lmi_flags & LDR_MAIN)
942 continue; /* skip the main module */
944 # ifdef VERBOSE
945 GC_printf("---Module---\n");
946 GC_printf("Module ID = %16ld\n", moduleinfo.lmi_modid);
947 GC_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion);
948 GC_printf("flags for module = %16lx\n", moduleinfo.lmi_flags);
949 GC_printf("pathname of module = \"%s\"\n", moduleinfo.lmi_name);
950 # endif
952 /* For each region in this module */
953 for (region = 0; region < moduleinfo.lmi_nregion; region++) {
955 /* Get the region information */
956 status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
957 regioninfosize, &regionreturnsize);
958 if (status != 0 )
959 ABORT("ldr_inq_region failed");
961 /* only process writable (data) regions */
962 if (! (regioninfo.lri_prot & LDR_W))
963 continue;
965 # ifdef VERBOSE
966 GC_printf("--- Region ---\n");
967 GC_printf("Region number = %16ld\n",
968 regioninfo.lri_region_no);
969 GC_printf("Protection flags = %016x\n", regioninfo.lri_prot);
970 GC_printf("Virtual address = %16p\n", regioninfo.lri_vaddr);
971 GC_printf("Mapped address = %16p\n", regioninfo.lri_mapaddr);
972 GC_printf("Region size = %16ld\n", regioninfo.lri_size);
973 GC_printf("Region name = \"%s\"\n", regioninfo.lri_name);
974 # endif
976 /* register region as a garbage collection root */
977 GC_add_roots_inner (
978 (char *)regioninfo.lri_mapaddr,
979 (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
980 TRUE);
985 #endif
987 #if defined(HPUX)
989 #include <errno.h>
990 #include <dl.h>
992 extern int errno;
993 extern char *sys_errlist[];
994 extern int sys_nerr;
996 void GC_register_dynamic_libraries()
998 int status;
999 int index = 1; /* Ordinal position in shared library search list */
1000 struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
1002 /* For each dynamic library loaded */
1003 while (TRUE) {
1005 /* Get info about next shared library */
1006 status = shl_get(index, &shl_desc);
1008 /* Check if this is the end of the list or if some error occured */
1009 if (status != 0) {
1010 # ifdef GC_HPUX_THREADS
1011 /* I've seen errno values of 0. The man page is not clear */
1012 /* as to whether errno should get set on a -1 return. */
1013 break;
1014 # else
1015 if (errno == EINVAL) {
1016 break; /* Moved past end of shared library list --> finished */
1017 } else {
1018 if (errno <= sys_nerr) {
1019 GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]);
1020 } else {
1021 GC_printf1("dynamic_load: %d\n", (long) errno);
1023 ABORT("shl_get failed");
1025 # endif
1028 # ifdef VERBOSE
1029 GC_printf0("---Shared library---\n");
1030 GC_printf1("\tfilename = \"%s\"\n", shl_desc->filename);
1031 GC_printf1("\tindex = %d\n", index);
1032 GC_printf1("\thandle = %08x\n",
1033 (unsigned long) shl_desc->handle);
1034 GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart);
1035 GC_printf1("\ttext seg. end = %08x\n", shl_desc->tend);
1036 GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart);
1037 GC_printf1("\tdata seg. end = %08x\n", shl_desc->dend);
1038 GC_printf1("\tref. count = %lu\n", shl_desc->ref_count);
1039 # endif
1041 /* register shared library's data segment as a garbage collection root */
1042 GC_add_roots_inner((char *) shl_desc->dstart,
1043 (char *) shl_desc->dend, TRUE);
1045 index++;
1048 #endif /* HPUX */
1050 #ifdef RS6000
1051 #pragma alloca
1052 #include <sys/ldr.h>
1053 #include <sys/errno.h>
1054 void GC_register_dynamic_libraries()
1056 int len;
1057 char *ldibuf;
1058 int ldibuflen;
1059 struct ld_info *ldi;
1061 ldibuf = alloca(ldibuflen = 8192);
1063 while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
1064 if (errno != ENOMEM) {
1065 ABORT("loadquery failed");
1067 ldibuf = alloca(ldibuflen *= 2);
1070 ldi = (struct ld_info *)ldibuf;
1071 while (ldi) {
1072 len = ldi->ldinfo_next;
1073 GC_add_roots_inner(
1074 ldi->ldinfo_dataorg,
1075 (ptr_t)(unsigned long)ldi->ldinfo_dataorg
1076 + ldi->ldinfo_datasize,
1077 TRUE);
1078 ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
1081 #endif /* RS6000 */
1083 #ifdef DARWIN
1085 /* __private_extern__ hack required for pre-3.4 gcc versions. */
1086 #ifndef __private_extern__
1087 # define __private_extern__ extern
1088 # include <mach-o/dyld.h>
1089 # undef __private_extern__
1090 #else
1091 # include <mach-o/dyld.h>
1092 #endif
1093 #include <mach-o/getsect.h>
1095 /*#define DARWIN_DEBUG*/
1097 const static struct {
1098 const char *seg;
1099 const char *sect;
1100 } GC_dyld_sections[] = {
1101 { SEG_DATA, SECT_DATA },
1102 { SEG_DATA, SECT_BSS },
1103 { SEG_DATA, SECT_COMMON }
1106 #ifdef DARWIN_DEBUG
1107 static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
1108 unsigned long i,c;
1109 c = _dyld_image_count();
1110 for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
1111 return _dyld_get_image_name(i);
1112 return NULL;
1114 #endif
1116 /* This should never be called by a thread holding the lock */
1117 static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
1118 unsigned long start,end,i;
1119 const struct section *sec;
1120 for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1121 sec = getsectbynamefromheader(
1122 hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1123 if(sec == NULL || sec->size == 0) continue;
1124 start = slide + sec->addr;
1125 end = start + sec->size;
1126 # ifdef DARWIN_DEBUG
1127 GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
1128 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1129 # endif
1130 GC_add_roots((char*)start,(char*)end);
1132 # ifdef DARWIN_DEBUG
1133 GC_print_static_roots();
1134 # endif
1137 /* This should never be called by a thread holding the lock */
1138 static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
1139 unsigned long start,end,i;
1140 const struct section *sec;
1141 for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1142 sec = getsectbynamefromheader(
1143 hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1144 if(sec == NULL || sec->size == 0) continue;
1145 start = slide + sec->addr;
1146 end = start + sec->size;
1147 # ifdef DARWIN_DEBUG
1148 GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
1149 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1150 # endif
1151 GC_remove_roots((char*)start,(char*)end);
1153 # ifdef DARWIN_DEBUG
1154 GC_print_static_roots();
1155 # endif
1158 void GC_register_dynamic_libraries() {
1159 /* Currently does nothing. The callbacks are setup by GC_init_dyld()
1160 The dyld library takes it from there. */
1163 /* The _dyld_* functions have an internal lock so no _dyld functions
1164 can be called while the world is stopped without the risk of a deadlock.
1165 Because of this we MUST setup callbacks BEFORE we ever stop the world.
1166 This should be called BEFORE any thread in created and WITHOUT the
1167 allocation lock held. */
1169 void GC_init_dyld() {
1170 static GC_bool initialized = FALSE;
1171 char *bind_fully_env = NULL;
1173 if(initialized) return;
1175 # ifdef DARWIN_DEBUG
1176 GC_printf0("Registering dyld callbacks...\n");
1177 # endif
1179 /* Apple's Documentation:
1180 When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1181 calls the specified callback (func) once for each of the images that is
1182 currently loaded into the program. When a new image is added to the program,
1183 your callback is called again with the mach_header for the new image, and the
1184 virtual memory slide amount of the new image.
1186 This WILL properly register already linked libraries and libraries
1187 linked in the future
1190 _dyld_register_func_for_add_image(GC_dyld_image_add);
1191 _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1193 /* Set this early to avoid reentrancy issues. */
1194 initialized = TRUE;
1196 bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
1198 if (bind_fully_env == NULL) {
1199 # ifdef DARWIN_DEBUG
1200 GC_printf0("Forcing full bind of GC code...\n");
1201 # endif
1203 if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1204 GC_abort("_dyld_bind_fully_image_containing_address failed");
1209 #define HAVE_REGISTER_MAIN_STATIC_DATA
1210 GC_bool GC_register_main_static_data()
1212 /* Already done through dyld callbacks */
1213 return FALSE;
1216 #endif /* DARWIN */
1218 #else /* !DYNAMIC_LOADING */
1220 #ifdef PCR
1222 # include "il/PCR_IL.h"
1223 # include "th/PCR_ThCtl.h"
1224 # include "mm/PCR_MM.h"
1226 void GC_register_dynamic_libraries()
1228 /* Add new static data areas of dynamically loaded modules. */
1230 PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1231 PCR_IL_LoadedSegment * q;
1233 /* Skip uncommited files */
1234 while (p != NIL && !(p -> lf_commitPoint)) {
1235 /* The loading of this file has not yet been committed */
1236 /* Hence its description could be inconsistent. */
1237 /* Furthermore, it hasn't yet been run. Hence its data */
1238 /* segments can't possibly reference heap allocated */
1239 /* objects. */
1240 p = p -> lf_prev;
1242 for (; p != NIL; p = p -> lf_prev) {
1243 for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1244 if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1245 == PCR_IL_SegFlags_Traced_on) {
1246 GC_add_roots_inner
1247 ((char *)(q -> ls_addr),
1248 (char *)(q -> ls_addr) + q -> ls_bytes,
1249 TRUE);
1257 #else /* !PCR */
1259 void GC_register_dynamic_libraries(){}
1261 int GC_no_dynamic_loading;
1263 #endif /* !PCR */
1265 #endif /* !DYNAMIC_LOADING */
1267 #ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1269 /* Do we need to separately register the main static data segment? */
1270 GC_bool GC_register_main_static_data()
1272 return TRUE;
1274 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */