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 */
33 #if !defined(MACOS) && !defined(_WIN32_WCE)
34 # include <sys/types.h>
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(). */
47 # define GC_must_restore_redefined_dlopen
49 # undef GC_must_restore_redefined_dlopen
52 #if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \
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) && \
62 --> We only know how to find data segments of dynamic libraries for the
63 --> above. Additional SVR4 variants might not be too
77 /* struct link_map field overrides */
78 # define l_next lm_next
79 # define l_addr lm_addr
80 # define l_name lm_name
84 # include <machine/elf_machdep.h>
85 # define ELFSIZE ARCH_ELFSIZE
88 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
89 (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
90 (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
96 /* Newer versions of GNU/Linux define this macro. We
97 * define it similarly for any ELF systems that don't. */
100 # if __ELF_WORD_SIZE == 32
101 # define ElfW(type) Elf32_##type
103 # define ElfW(type) Elf64_##type
108 # define ElfW(type) Elf32_##type
110 # define ElfW(type) Elf64_##type
113 # if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
114 # define ElfW(type) Elf32_##type
116 # define ElfW(type) Elf64_##type
122 #if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES)
128 static struct link_map *
129 GC_FirstDLOpenedLinkMap()
131 extern ElfW(Dyn) _DYNAMIC;
134 static struct link_map * cachedResult = 0;
135 static ElfW(Dyn) *dynStructureAddr = 0;
136 /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */
138 # ifdef SUNOS53_SHARED_LIB
139 /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */
140 /* up properly in dynamically linked .so's. This means we have */
141 /* to use its value in the set of original object files loaded */
142 /* at program startup. */
143 if( dynStructureAddr == 0 ) {
144 void* startupSyms = dlopen(0, RTLD_LAZY);
145 dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC");
148 dynStructureAddr = &_DYNAMIC;
151 if( dynStructureAddr == 0) {
154 if( cachedResult == 0 ) {
156 for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
157 if( tag == DT_DEBUG ) {
159 = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
160 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
168 #endif /* SUNOS5DL ... */
170 /* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
171 # if defined(GC_must_restore_redefined_dlopen)
172 # define dlopen GC_dlopen
175 #if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES)
178 struct link_dynamic _DYNAMIC;
181 static struct link_map *
182 GC_FirstDLOpenedLinkMap()
184 extern struct link_dynamic _DYNAMIC;
186 if( &_DYNAMIC == 0) {
189 return(_DYNAMIC.ld_un.ld_1->ld_loaded);
192 /* Return the address of the ld.so allocated common symbol */
193 /* with the least address, or 0 if none. */
194 static ptr_t GC_first_common()
197 extern struct link_dynamic _DYNAMIC;
198 struct rtc_symb * curr_symbol;
200 if( &_DYNAMIC == 0) {
203 curr_symbol = _DYNAMIC.ldd -> ldd_cp;
204 for (; curr_symbol != 0; curr_symbol = curr_symbol -> rtc_next) {
206 || (ptr_t)(curr_symbol -> rtc_sp -> n_value) < result) {
207 result = (ptr_t)(curr_symbol -> rtc_sp -> n_value);
213 #endif /* SUNOS4 ... */
215 # if defined(SUNOS4) || defined(SUNOS5DL)
216 /* Add dynamic library data sections to the root set. */
217 # if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS)
219 --> fix mutual exclusion with dlopen
220 # endif /* We assume M3 programs don't call dlopen for now */
223 # ifndef USE_PROC_FOR_LIBRARIES
224 void GC_register_dynamic_libraries()
226 struct link_map *lm = GC_FirstDLOpenedLinkMap();
229 for (lm = GC_FirstDLOpenedLinkMap();
230 lm != (struct link_map *) 0; lm = lm->l_next)
235 e = (struct exec *) lm->lm_addr;
237 ((char *) (N_DATOFF(*e) + lm->lm_addr)),
238 ((char *) (N_BSSADDR(*e) + e->a_bss + lm->lm_addr)),
244 unsigned long offset;
248 e = (ElfW(Ehdr) *) lm->l_addr;
249 p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
250 offset = ((unsigned long)(lm->l_addr));
251 for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
252 switch( p->p_type ) {
255 if( !(p->p_flags & PF_W) ) break;
256 start = ((char *)(p->p_vaddr)) + offset;
272 static ptr_t common_start = 0;
274 extern ptr_t GC_find_limit();
276 if (common_start == 0) common_start = GC_first_common();
277 if (common_start != 0) {
278 common_end = GC_find_limit(common_start, TRUE);
279 GC_add_roots_inner((char *)common_start, (char *)common_end, TRUE);
285 # endif /* !USE_PROC ... */
288 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
289 (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
290 (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
293 #ifdef USE_PROC_FOR_LIBRARIES
297 #include <sys/stat.h>
301 #define MAPS_BUF_SIZE (32*1024)
303 extern ssize_t GC_repeat_read(int fd, char *buf, size_t count);
304 /* Repeatedly read until buffer is filled, or EOF is encountered */
305 /* Defined in os_dep.c. */
307 char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
308 char *prot_buf, unsigned int *maj_dev);
309 word GC_apply_to_maps(word (*fn)(char *));
312 word GC_register_map_entries(char *maps)
315 char *buf_ptr = maps;
318 unsigned int maj_dev;
319 word least_ha, greatest_ha;
321 word datastart = (word)(DATASTART);
323 /* Compute heap bounds. FIXME: Should be done by add_to_heap? */
324 least_ha = (word)(-1);
326 for (i = 0; i < GC_n_heap_sects; ++i) {
327 word sect_start = (word)GC_heap_sects[i].hs_start;
328 word sect_end = sect_start + GC_heap_sects[i].hs_bytes;
329 if (sect_start < least_ha) least_ha = sect_start;
330 if (sect_end > greatest_ha) greatest_ha = sect_end;
332 if (greatest_ha < (word)GC_scratch_last_end_ptr)
333 greatest_ha = (word)GC_scratch_last_end_ptr;
336 buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
337 if (buf_ptr == NULL) return 1;
338 if (prot_buf[1] == 'w') {
339 /* This is a writable mapping. Add it to */
340 /* the root set unless it is already otherwise */
342 if (start <= (word)GC_stackbottom && end >= (word)GC_stackbottom) {
343 /* Stack mapping; discard */
347 if (GC_segment_is_thread_stack(start, end)) continue;
349 /* We no longer exclude the main data segment. */
350 if (start < least_ha && end > least_ha) {
353 if (start < greatest_ha && end > greatest_ha) {
356 if (start >= least_ha && end <= greatest_ha) continue;
357 GC_add_roots_inner((char *)start, (char *)end, TRUE);
363 void GC_register_dynamic_libraries()
365 if (!GC_apply_to_maps(GC_register_map_entries))
366 ABORT("Failed to read /proc for library registration.");
369 /* We now take care of the main data segment ourselves: */
370 GC_bool GC_register_main_static_data()
375 # define HAVE_REGISTER_MAIN_STATIC_DATA
377 #endif /* USE_PROC_FOR_LIBRARIES */
379 #if !defined(USE_PROC_FOR_LIBRARIES)
380 /* The following is the preferred way to walk dynamic libraries */
381 /* For glibc 2.2.4+. Unfortunately, it doesn't work for older */
382 /* versions. Thanks to Jakub Jelinek for most of the code. */
384 # if defined(LINUX) /* Are others OK here, too? */ \
385 && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
386 || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
388 /* We have the header files for a glibc that includes dl_iterate_phdr. */
389 /* It may still not be available in the library on the target system. */
390 /* Thus we also treat it as a weak symbol. */
391 #define HAVE_DL_ITERATE_PHDR
393 static int GC_register_dynlib_callback(info, size, ptr)
394 struct dl_phdr_info * info;
398 const ElfW(Phdr) * p;
402 /* Make sure struct dl_phdr_info is at least as big as we need. */
403 if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
404 + sizeof (info->dlpi_phnum))
408 for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
409 switch( p->p_type ) {
412 if( !(p->p_flags & PF_W) ) break;
413 start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
414 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
422 * (int *)ptr = 1; /* Signal that we were called */
426 /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
428 #pragma weak dl_iterate_phdr
430 GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
432 if (dl_iterate_phdr) {
433 int did_something = 0;
434 dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
435 if (!did_something) {
436 /* dl_iterate_phdr may forget the static data segment in */
437 /* statically linked executables. */
438 GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE);
439 # if defined(DATASTART2)
440 GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
450 /* Do we need to separately register the main static data segment? */
451 GC_bool GC_register_main_static_data()
453 return (dl_iterate_phdr == 0);
456 #define HAVE_REGISTER_MAIN_STATIC_DATA
458 # else /* !LINUX || version(glibc) < 2.2.4 */
460 /* Dynamic loading code for Linux running ELF. Somewhat tested on
461 * Linux/x86, untested but hopefully should work on Linux/Alpha.
462 * This code was derived from the Solaris/ELF support. Thanks to
463 * whatever kind soul wrote that. - Patrick Bridges */
465 /* This doesn't necessarily work in all cases, e.g. with preloaded
466 * dynamic libraries. */
469 # include <sys/exec_elf.h>
470 /* for compatibility with 1.4.x */
488 # pragma weak _DYNAMIC
490 extern ElfW(Dyn) _DYNAMIC[];
492 static struct link_map *
493 GC_FirstDLOpenedLinkMap()
497 static struct link_map *cachedResult = 0;
502 if( cachedResult == 0 ) {
504 for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
505 if( tag == DT_DEBUG ) {
507 = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
508 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
517 void GC_register_dynamic_libraries()
522 # ifdef HAVE_DL_ITERATE_PHDR
523 if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
527 lm = GC_FirstDLOpenedLinkMap();
528 for (lm = GC_FirstDLOpenedLinkMap();
529 lm != (struct link_map *) 0; lm = lm->l_next)
533 unsigned long offset;
537 e = (ElfW(Ehdr) *) lm->l_addr;
538 p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
539 offset = ((unsigned long)(lm->l_addr));
540 for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
541 switch( p->p_type ) {
544 if( !(p->p_flags & PF_W) ) break;
545 start = ((char *)(p->p_vaddr)) + offset;
546 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
556 #endif /* !USE_PROC_FOR_LIBRARIES */
560 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
562 #include <sys/procfs.h>
563 #include <sys/stat.h>
567 #include <signal.h> /* Only for the following test. */
572 extern void * GC_roots_present();
573 /* The type is a lie, since the real type doesn't make sense here, */
574 /* and we only test for NULL. */
577 /* We use /proc to track down all parts of the address space that are */
578 /* mapped by the process, and throw out regions we know we shouldn't */
579 /* worry about. This may also work under other SVR4 variants. */
580 void GC_register_dynamic_libraries()
584 static prmap_t * addr_map = 0;
585 static int current_sz = 0; /* Number of records currently in addr_map */
586 static int needed_sz; /* Required size of addr_map */
589 register ptr_t start;
590 register ptr_t limit;
591 ptr_t heap_start = (ptr_t)HEAP_START;
592 ptr_t heap_end = heap_start;
596 # endif /* SUNOS5DL */
599 sprintf(buf, "/proc/%d", getpid());
600 /* The above generates a lint complaint, since pid_t varies. */
601 /* It's unclear how to improve this. */
602 fd = open(buf, O_RDONLY);
604 ABORT("/proc open failed");
607 if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
608 GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
609 ABORT("/proc PIOCNMAP ioctl failed");
611 if (needed_sz >= current_sz) {
612 current_sz = needed_sz * 2 + 1;
613 /* Expansion, plus room for 0 record */
614 addr_map = (prmap_t *)GC_scratch_alloc((word)
615 (current_sz * sizeof(prmap_t)));
617 if (ioctl(fd, PIOCMAP, addr_map) < 0) {
618 GC_err_printf4("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
619 fd, errno, needed_sz, addr_map);
620 ABORT("/proc PIOCMAP ioctl failed");
622 if (GC_n_heap_sects > 0) {
623 heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
624 + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
625 if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr;
627 for (i = 0; i < needed_sz; i++) {
628 flags = addr_map[i].pr_mflags;
629 if ((flags & (MA_BREAK | MA_STACK | MA_PHYS)) != 0) goto irrelevant;
630 if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
632 /* The latter test is empirically useless in very old Irix */
633 /* versions. Other than the */
634 /* main data and stack segments, everything appears to be */
635 /* mapped readable, writable, executable, and shared(!!). */
636 /* This makes no sense to me. - HB */
637 start = (ptr_t)(addr_map[i].pr_vaddr);
638 if (GC_roots_present(start)) goto irrelevant;
639 if (start < heap_end && start >= heap_start)
642 if (GC_is_thread_stack(start)) goto irrelevant;
643 # endif /* MMAP_STACKS */
645 limit = start + addr_map[i].pr_size;
646 /* The following seemed to be necessary for very old versions */
647 /* of Irix, but it has been reported to discard relevant */
648 /* segments under Irix 6.5. */
650 if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
651 /* Discard text segments, i.e. 0-offset mappings against */
652 /* executable files which appear to have ELF headers. */
655 # define MAP_IRR_SZ 10
656 static ptr_t map_irr[MAP_IRR_SZ];
657 /* Known irrelevant map entries */
658 static int n_irr = 0;
662 for (i = 0; i < n_irr; i++) {
663 if (map_irr[i] == start) goto irrelevant;
665 arg = (caddr_t)start;
666 obj = ioctl(fd, PIOCOPENM, &arg);
670 if ((buf.st_mode & 0111) != 0) {
671 if (n_irr < MAP_IRR_SZ) {
672 map_irr[n_irr++] = start;
679 GC_add_roots_inner(start, limit, TRUE);
682 /* Dont keep cached descriptor, for now. Some kernels don't like us */
683 /* to keep a /proc file descriptor around during kill -9. */
684 if (close(fd) < 0) ABORT("Couldnt close /proc file");
688 # endif /* USE_PROC || IRIX5 */
690 # if defined(MSWIN32) || defined(MSWINCE)
692 # define WIN32_LEAN_AND_MEAN
694 # include <windows.h>
697 /* We traverse the entire address space and register all segments */
698 /* that could possibly have been written to. */
700 extern GC_bool GC_is_heap_base (ptr_t p);
702 # ifdef GC_WIN32_THREADS
703 extern void GC_get_next_stack(char *start, char **lo, char **hi);
704 void GC_cond_add_roots(char *base, char * limit)
706 char * curr_base = base;
707 char * next_stack_lo;
708 char * next_stack_hi;
710 if (base == limit) return;
712 GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
713 if (next_stack_lo >= limit) break;
714 GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
715 curr_base = next_stack_hi;
717 if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
720 void GC_cond_add_roots(char *base, char * limit)
724 = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
725 if (base == limit) return;
726 if (limit > stack_top && base < GC_stackbottom) {
727 /* Part of the stack; ignore it. */
730 GC_add_roots_inner(base, limit, TRUE);
735 /* Do we need to separately register the main static data segment? */
736 GC_bool GC_register_main_static_data()
741 extern GC_bool GC_no_win32_dlls;
743 GC_bool GC_register_main_static_data()
745 return GC_no_win32_dlls;
749 # define HAVE_REGISTER_MAIN_STATIC_DATA
751 GC_bool GC_warn_fb = TRUE; /* Warn about traced likely */
752 /* graphics memory. */
753 GC_bool GC_disallow_ignore_fb = FALSE;
754 int GC_ignore_fb_mb; /* Ignore mappings bigger than the */
755 /* specified number of MB. */
756 GC_bool GC_ignore_fb = FALSE; /* Enable frame buffer */
759 /* Issue warning if tracing apparent framebuffer. */
760 /* This limits us to one warning, and it's a back door to */
763 /* Should [start, start+len) be treated as a frame buffer */
765 /* Unfortunately, we currently have no real way to tell */
766 /* automatically, and rely largely on user input. */
767 /* FIXME: If we had more data on this phenomenon (e.g. */
768 /* is start aligned to a MB multiple?) we should be able to */
770 /* Based on a very limited sample, it appears that: */
771 /* - Frame buffer mappings appear as mappings of length */
772 /* 2**n MB - 192K. (We guess the 192K can vary a bit.) */
773 /* - Have a stating address at best 64K aligned. */
774 /* I'd love more information about the mapping, since I */
775 /* can't reproduce the problem. */
776 static GC_bool is_frame_buffer(ptr_t start, size_t len)
778 static GC_bool initialized = FALSE;
779 # define MB (1024*1024)
780 # define DEFAULT_FB_MB 15
783 if (GC_disallow_ignore_fb) return FALSE;
785 char * ignore_fb_string = GETENV("GC_IGNORE_FB");
787 if (0 != ignore_fb_string) {
788 while (*ignore_fb_string == ' ' || *ignore_fb_string == '\t')
790 if (*ignore_fb_string == '\0') {
791 GC_ignore_fb_mb = DEFAULT_FB_MB;
793 GC_ignore_fb_mb = atoi(ignore_fb_string);
794 if (GC_ignore_fb_mb < MIN_FB_MB) {
795 WARN("Bad GC_IGNORE_FB value. Using %ld\n", DEFAULT_FB_MB);
796 GC_ignore_fb_mb = DEFAULT_FB_MB;
801 GC_ignore_fb_mb = DEFAULT_FB_MB; /* For warning */
805 if (len >= ((size_t)GC_ignore_fb_mb << 20)) {
810 WARN("Possible frame buffer mapping at 0x%lx: \n"
811 "\tConsider setting GC_IGNORE_FB to improve performance.\n",
822 # ifdef DEBUG_VIRTUALQUERY
823 void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
825 GC_printf4("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
826 buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
828 GC_printf4("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
830 buf -> AllocationProtect, buf -> State, buf -> Protect,
833 # endif /* DEBUG_VIRTUALQUERY */
835 void GC_register_dynamic_libraries()
837 MEMORY_BASIC_INFORMATION buf;
842 char * limit, * new_limit;
845 if (GC_no_win32_dlls) return;
847 base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
848 # if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
849 /* Only the first 32 MB of address space belongs to the current process */
850 while (p < (LPVOID)0x02000000) {
851 result = VirtualQuery(p, &buf, sizeof(buf));
853 /* Page is free; advance to the next possible allocation base */
855 (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
856 & ~(GC_sysinfo.dwAllocationGranularity-1));
859 while (p < GC_sysinfo.lpMaximumApplicationAddress) {
860 result = VirtualQuery(p, &buf, sizeof(buf));
863 if (result != sizeof(buf)) {
864 ABORT("Weird VirtualQuery result");
866 new_limit = (char *)p + buf.RegionSize;
867 protect = buf.Protect;
868 if (buf.State == MEM_COMMIT
869 && (protect == PAGE_EXECUTE_READWRITE
870 || protect == PAGE_READWRITE)
871 && !GC_is_heap_base(buf.AllocationBase)
872 && !is_frame_buffer(p, buf.RegionSize)) {
873 # ifdef DEBUG_VIRTUALQUERY
874 GC_dump_meminfo(&buf);
876 if ((char *)p != limit) {
877 GC_cond_add_roots(base, limit);
883 if (p > (LPVOID)new_limit /* overflow */) break;
884 p = (LPVOID)new_limit;
886 GC_cond_add_roots(base, limit);
889 #endif /* MSWIN32 || MSWINCE */
891 #if defined(ALPHA) && defined(OSF1)
895 void GC_register_dynamic_libraries()
901 ldr_module_t moduleid = LDR_NULL_MODULE;
902 ldr_module_info_t moduleinfo;
903 size_t moduleinfosize = sizeof(moduleinfo);
904 size_t modulereturnsize;
908 ldr_region_info_t regioninfo;
909 size_t regioninfosize = sizeof(regioninfo);
910 size_t regionreturnsize;
912 /* Obtain id of this process */
913 mypid = ldr_my_process();
915 /* For each module */
918 /* Get the next (first) module */
919 status = ldr_next_module(mypid, &moduleid);
921 /* Any more modules? */
922 if (moduleid == LDR_NULL_MODULE)
923 break; /* No more modules */
925 /* Check status AFTER checking moduleid because */
926 /* of a bug in the non-shared ldr_next_module stub */
928 GC_printf1("dynamic_load: status = %ld\n", (long)status);
930 extern char *sys_errlist[];
933 if (errno <= sys_nerr) {
934 GC_printf1("dynamic_load: %s\n", (long)sys_errlist[errno]);
936 GC_printf1("dynamic_load: %d\n", (long)errno);
939 ABORT("ldr_next_module failed");
942 /* Get the module information */
943 status = ldr_inq_module(mypid, moduleid, &moduleinfo,
944 moduleinfosize, &modulereturnsize);
946 ABORT("ldr_inq_module failed");
948 /* is module for the main program (i.e. nonshared portion)? */
949 if (moduleinfo.lmi_flags & LDR_MAIN)
950 continue; /* skip the main module */
953 GC_printf("---Module---\n");
954 GC_printf("Module ID = %16ld\n", moduleinfo.lmi_modid);
955 GC_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion);
956 GC_printf("flags for module = %16lx\n", moduleinfo.lmi_flags);
957 GC_printf("pathname of module = \"%s\"\n", moduleinfo.lmi_name);
960 /* For each region in this module */
961 for (region = 0; region < moduleinfo.lmi_nregion; region++) {
963 /* Get the region information */
964 status = ldr_inq_region(mypid, moduleid, region, ®ioninfo,
965 regioninfosize, ®ionreturnsize);
967 ABORT("ldr_inq_region failed");
969 /* only process writable (data) regions */
970 if (! (regioninfo.lri_prot & LDR_W))
974 GC_printf("--- Region ---\n");
975 GC_printf("Region number = %16ld\n",
976 regioninfo.lri_region_no);
977 GC_printf("Protection flags = %016x\n", regioninfo.lri_prot);
978 GC_printf("Virtual address = %16p\n", regioninfo.lri_vaddr);
979 GC_printf("Mapped address = %16p\n", regioninfo.lri_mapaddr);
980 GC_printf("Region size = %16ld\n", regioninfo.lri_size);
981 GC_printf("Region name = \"%s\"\n", regioninfo.lri_name);
984 /* register region as a garbage collection root */
986 (char *)regioninfo.lri_mapaddr,
987 (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
1001 extern char *sys_errlist[];
1002 extern int sys_nerr;
1004 void GC_register_dynamic_libraries()
1007 int index = 1; /* Ordinal position in shared library search list */
1008 struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
1010 /* For each dynamic library loaded */
1013 /* Get info about next shared library */
1014 status = shl_get(index, &shl_desc);
1016 /* Check if this is the end of the list or if some error occured */
1018 # ifdef GC_HPUX_THREADS
1019 /* I've seen errno values of 0. The man page is not clear */
1020 /* as to whether errno should get set on a -1 return. */
1023 if (errno == EINVAL) {
1024 break; /* Moved past end of shared library list --> finished */
1026 if (errno <= sys_nerr) {
1027 GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]);
1029 GC_printf1("dynamic_load: %d\n", (long) errno);
1031 ABORT("shl_get failed");
1037 GC_printf0("---Shared library---\n");
1038 GC_printf1("\tfilename = \"%s\"\n", shl_desc->filename);
1039 GC_printf1("\tindex = %d\n", index);
1040 GC_printf1("\thandle = %08x\n",
1041 (unsigned long) shl_desc->handle);
1042 GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart);
1043 GC_printf1("\ttext seg. end = %08x\n", shl_desc->tend);
1044 GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart);
1045 GC_printf1("\tdata seg. end = %08x\n", shl_desc->dend);
1046 GC_printf1("\tref. count = %lu\n", shl_desc->ref_count);
1049 /* register shared library's data segment as a garbage collection root */
1050 GC_add_roots_inner((char *) shl_desc->dstart,
1051 (char *) shl_desc->dend, TRUE);
1060 #include <sys/ldr.h>
1061 #include <sys/errno.h>
1062 void GC_register_dynamic_libraries()
1067 struct ld_info *ldi;
1069 ldibuf = alloca(ldibuflen = 8192);
1071 while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
1072 if (errno != ENOMEM) {
1073 ABORT("loadquery failed");
1075 ldibuf = alloca(ldibuflen *= 2);
1078 ldi = (struct ld_info *)ldibuf;
1080 len = ldi->ldinfo_next;
1082 ldi->ldinfo_dataorg,
1083 (ptr_t)(unsigned long)ldi->ldinfo_dataorg
1084 + ldi->ldinfo_datasize,
1086 ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
1093 /* __private_extern__ hack required for pre-3.4 gcc versions. */
1094 #ifndef __private_extern__
1095 # define __private_extern__ extern
1096 # include <mach-o/dyld.h>
1097 # undef __private_extern__
1099 # include <mach-o/dyld.h>
1101 #include <mach-o/getsect.h>
1103 /*#define DARWIN_DEBUG*/
1105 const static struct {
1108 } GC_dyld_sections[] = {
1109 { SEG_DATA, SECT_DATA },
1110 { SEG_DATA, SECT_BSS },
1111 { SEG_DATA, SECT_COMMON }
1115 static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
1117 c = _dyld_image_count();
1118 for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
1119 return _dyld_get_image_name(i);
1124 /* This should never be called by a thread holding the lock */
1125 static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
1126 unsigned long start,end,i;
1127 const struct section *sec;
1128 for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1129 sec = getsectbynamefromheader(
1130 hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1131 if(sec == NULL || sec->size == 0) continue;
1132 start = slide + sec->addr;
1133 end = start + sec->size;
1134 # ifdef DARWIN_DEBUG
1135 GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
1136 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1138 GC_add_roots((char*)start,(char*)end);
1140 # ifdef DARWIN_DEBUG
1141 GC_print_static_roots();
1145 /* This should never be called by a thread holding the lock */
1146 static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
1147 unsigned long start,end,i;
1148 const struct section *sec;
1149 for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1150 sec = getsectbynamefromheader(
1151 hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1152 if(sec == NULL || sec->size == 0) continue;
1153 start = slide + sec->addr;
1154 end = start + sec->size;
1155 # ifdef DARWIN_DEBUG
1156 GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
1157 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1159 GC_remove_roots((char*)start,(char*)end);
1161 # ifdef DARWIN_DEBUG
1162 GC_print_static_roots();
1166 void GC_register_dynamic_libraries() {
1167 /* Currently does nothing. The callbacks are setup by GC_init_dyld()
1168 The dyld library takes it from there. */
1171 /* The _dyld_* functions have an internal lock so no _dyld functions
1172 can be called while the world is stopped without the risk of a deadlock.
1173 Because of this we MUST setup callbacks BEFORE we ever stop the world.
1174 This should be called BEFORE any thread in created and WITHOUT the
1175 allocation lock held. */
1177 void GC_init_dyld() {
1178 static GC_bool initialized = FALSE;
1179 char *bind_fully_env = NULL;
1181 if(initialized) return;
1183 # ifdef DARWIN_DEBUG
1184 GC_printf0("Registering dyld callbacks...\n");
1187 /* Apple's Documentation:
1188 When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1189 calls the specified callback (func) once for each of the images that is
1190 currently loaded into the program. When a new image is added to the program,
1191 your callback is called again with the mach_header for the new image, and the
1192 virtual memory slide amount of the new image.
1194 This WILL properly register already linked libraries and libraries
1195 linked in the future
1198 _dyld_register_func_for_add_image(GC_dyld_image_add);
1199 _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1201 /* Set this early to avoid reentrancy issues. */
1204 bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
1206 if (bind_fully_env == NULL) {
1207 # ifdef DARWIN_DEBUG
1208 GC_printf0("Forcing full bind of GC code...\n");
1211 if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1212 GC_abort("_dyld_bind_fully_image_containing_address failed");
1217 #define HAVE_REGISTER_MAIN_STATIC_DATA
1218 GC_bool GC_register_main_static_data()
1220 /* Already done through dyld callbacks */
1226 #else /* !DYNAMIC_LOADING */
1230 # include "il/PCR_IL.h"
1231 # include "th/PCR_ThCtl.h"
1232 # include "mm/PCR_MM.h"
1234 void GC_register_dynamic_libraries()
1236 /* Add new static data areas of dynamically loaded modules. */
1238 PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1239 PCR_IL_LoadedSegment * q;
1241 /* Skip uncommited files */
1242 while (p != NIL && !(p -> lf_commitPoint)) {
1243 /* The loading of this file has not yet been committed */
1244 /* Hence its description could be inconsistent. */
1245 /* Furthermore, it hasn't yet been run. Hence its data */
1246 /* segments can't possibly reference heap allocated */
1250 for (; p != NIL; p = p -> lf_prev) {
1251 for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1252 if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1253 == PCR_IL_SegFlags_Traced_on) {
1255 ((char *)(q -> ls_addr),
1256 (char *)(q -> ls_addr) + q -> ls_bytes,
1267 void GC_register_dynamic_libraries(){}
1269 int GC_no_dynamic_loading;
1273 #endif /* !DYNAMIC_LOADING */
1275 #ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1277 /* Do we need to separately register the main static data segment? */
1278 GC_bool GC_register_main_static_data()
1282 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */