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
83 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
84 (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
85 (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
91 /* Newer versions of GNU/Linux define this macro. We
92 * define it similarly for any ELF systems that don't. */
94 # if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
95 # define ElfW(type) Elf32_##type
97 # define ElfW(type) Elf64_##type
101 #if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES)
107 static struct link_map *
108 GC_FirstDLOpenedLinkMap()
110 extern ElfW(Dyn) _DYNAMIC;
113 static struct link_map * cachedResult = 0;
114 static ElfW(Dyn) *dynStructureAddr = 0;
115 /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */
117 # ifdef SUNOS53_SHARED_LIB
118 /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */
119 /* up properly in dynamically linked .so's. This means we have */
120 /* to use its value in the set of original object files loaded */
121 /* at program startup. */
122 if( dynStructureAddr == 0 ) {
123 void* startupSyms = dlopen(0, RTLD_LAZY);
124 dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC");
127 dynStructureAddr = &_DYNAMIC;
130 if( dynStructureAddr == 0) {
133 if( cachedResult == 0 ) {
135 for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
136 if( tag == DT_DEBUG ) {
138 = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
139 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
147 #endif /* SUNOS5DL ... */
149 /* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
150 # if defined(GC_must_restore_redefined_dlopen)
151 # define dlopen GC_dlopen
154 #if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES)
157 struct link_dynamic _DYNAMIC;
160 static struct link_map *
161 GC_FirstDLOpenedLinkMap()
163 extern struct link_dynamic _DYNAMIC;
165 if( &_DYNAMIC == 0) {
168 return(_DYNAMIC.ld_un.ld_1->ld_loaded);
171 /* Return the address of the ld.so allocated common symbol */
172 /* with the least address, or 0 if none. */
173 static ptr_t GC_first_common()
176 extern struct link_dynamic _DYNAMIC;
177 struct rtc_symb * curr_symbol;
179 if( &_DYNAMIC == 0) {
182 curr_symbol = _DYNAMIC.ldd -> ldd_cp;
183 for (; curr_symbol != 0; curr_symbol = curr_symbol -> rtc_next) {
185 || (ptr_t)(curr_symbol -> rtc_sp -> n_value) < result) {
186 result = (ptr_t)(curr_symbol -> rtc_sp -> n_value);
192 #endif /* SUNOS4 ... */
194 # if defined(SUNOS4) || defined(SUNOS5DL)
195 /* Add dynamic library data sections to the root set. */
196 # if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS)
198 --> fix mutual exclusion with dlopen
199 # endif /* We assume M3 programs don't call dlopen for now */
202 # ifndef USE_PROC_FOR_LIBRARIES
203 void GC_register_dynamic_libraries()
205 struct link_map *lm = GC_FirstDLOpenedLinkMap();
208 for (lm = GC_FirstDLOpenedLinkMap();
209 lm != (struct link_map *) 0; lm = lm->l_next)
214 e = (struct exec *) lm->lm_addr;
216 ((char *) (N_DATOFF(*e) + lm->lm_addr)),
217 ((char *) (N_BSSADDR(*e) + e->a_bss + lm->lm_addr)),
223 unsigned long offset;
227 e = (ElfW(Ehdr) *) lm->l_addr;
228 p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
229 offset = ((unsigned long)(lm->l_addr));
230 for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
231 switch( p->p_type ) {
234 if( !(p->p_flags & PF_W) ) break;
235 start = ((char *)(p->p_vaddr)) + offset;
251 static ptr_t common_start = 0;
253 extern ptr_t GC_find_limit();
255 if (common_start == 0) common_start = GC_first_common();
256 if (common_start != 0) {
257 common_end = GC_find_limit(common_start, TRUE);
258 GC_add_roots_inner((char *)common_start, (char *)common_end, TRUE);
264 # endif /* !USE_PROC ... */
267 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
268 (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
269 (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
272 #ifdef USE_PROC_FOR_LIBRARIES
276 #include <sys/stat.h>
280 #define MAPS_BUF_SIZE (32*1024)
282 extern ssize_t GC_repeat_read(int fd, char *buf, size_t count);
283 /* Repeatedly read until buffer is filled, or EOF is encountered */
284 /* Defined in os_dep.c. */
286 char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
287 char *prot_buf, unsigned int *maj_dev);
288 word GC_apply_to_maps(word (*fn)(char *));
291 word GC_register_map_entries(char *maps)
294 char *buf_ptr = maps;
297 unsigned int maj_dev;
298 word least_ha, greatest_ha;
300 word datastart = (word)(DATASTART);
302 /* Compute heap bounds. FIXME: Should be done by add_to_heap? */
303 least_ha = (word)(-1);
305 for (i = 0; i < GC_n_heap_sects; ++i) {
306 word sect_start = (word)GC_heap_sects[i].hs_start;
307 word sect_end = sect_start + GC_heap_sects[i].hs_bytes;
308 if (sect_start < least_ha) least_ha = sect_start;
309 if (sect_end > greatest_ha) greatest_ha = sect_end;
311 if (greatest_ha < (word)GC_scratch_last_end_ptr)
312 greatest_ha = (word)GC_scratch_last_end_ptr;
315 buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
316 if (buf_ptr == NULL) return 1;
317 if (prot_buf[1] == 'w') {
318 /* This is a writable mapping. Add it to */
319 /* the root set unless it is already otherwise */
321 if (start <= (word)GC_stackbottom && end >= (word)GC_stackbottom) {
322 /* Stack mapping; discard */
326 if (GC_segment_is_thread_stack(start, end)) continue;
328 /* We no longer exclude the main data segment. */
329 if (start < least_ha && end > least_ha) {
332 if (start < greatest_ha && end > greatest_ha) {
335 if (start >= least_ha && end <= greatest_ha) continue;
336 GC_add_roots_inner((char *)start, (char *)end, TRUE);
342 void GC_register_dynamic_libraries()
344 if (!GC_apply_to_maps(GC_register_map_entries))
345 ABORT("Failed to read /proc for library registration.");
348 /* We now take care of the main data segment ourselves: */
349 GC_bool GC_register_main_static_data()
354 # define HAVE_REGISTER_MAIN_STATIC_DATA
356 #endif /* USE_PROC_FOR_LIBRARIES */
358 #if !defined(USE_PROC_FOR_LIBRARIES)
359 /* The following is the preferred way to walk dynamic libraries */
360 /* For glibc 2.2.4+. Unfortunately, it doesn't work for older */
361 /* versions. Thanks to Jakub Jelinek for most of the code. */
363 # if defined(LINUX) /* Are others OK here, too? */ \
364 && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
365 || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
367 /* We have the header files for a glibc that includes dl_iterate_phdr. */
368 /* It may still not be available in the library on the target system. */
369 /* Thus we also treat it as a weak symbol. */
370 #define HAVE_DL_ITERATE_PHDR
372 static int GC_register_dynlib_callback(info, size, ptr)
373 struct dl_phdr_info * info;
377 const ElfW(Phdr) * p;
381 /* Make sure struct dl_phdr_info is at least as big as we need. */
382 if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
383 + sizeof (info->dlpi_phnum))
387 for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
388 switch( p->p_type ) {
391 if( !(p->p_flags & PF_W) ) break;
392 start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
393 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
401 * (int *)ptr = 1; /* Signal that we were called */
405 /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
407 #pragma weak dl_iterate_phdr
409 GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
411 if (dl_iterate_phdr) {
412 int did_something = 0;
413 dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
414 if (!did_something) {
415 /* dl_iterate_phdr may forget the static data segment in */
416 /* statically linked executables. */
417 GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE);
418 # if defined(DATASTART2)
419 GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
429 /* Do we need to separately register the main static data segment? */
430 GC_bool GC_register_main_static_data()
432 return (dl_iterate_phdr == 0);
435 #define HAVE_REGISTER_MAIN_STATIC_DATA
437 # else /* !LINUX || version(glibc) < 2.2.4 */
439 /* Dynamic loading code for Linux running ELF. Somewhat tested on
440 * Linux/x86, untested but hopefully should work on Linux/Alpha.
441 * This code was derived from the Solaris/ELF support. Thanks to
442 * whatever kind soul wrote that. - Patrick Bridges */
444 /* This doesn't necessarily work in all cases, e.g. with preloaded
445 * dynamic libraries. */
448 # include <sys/exec_elf.h>
457 # pragma weak _DYNAMIC
459 extern ElfW(Dyn) _DYNAMIC[];
461 static struct link_map *
462 GC_FirstDLOpenedLinkMap()
466 static struct link_map *cachedResult = 0;
471 if( cachedResult == 0 ) {
473 for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
474 if( tag == DT_DEBUG ) {
476 = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
477 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
486 void GC_register_dynamic_libraries()
491 # ifdef HAVE_DL_ITERATE_PHDR
492 if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
496 lm = GC_FirstDLOpenedLinkMap();
497 for (lm = GC_FirstDLOpenedLinkMap();
498 lm != (struct link_map *) 0; lm = lm->l_next)
502 unsigned long offset;
506 e = (ElfW(Ehdr) *) lm->l_addr;
507 p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
508 offset = ((unsigned long)(lm->l_addr));
509 for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
510 switch( p->p_type ) {
513 if( !(p->p_flags & PF_W) ) break;
514 start = ((char *)(p->p_vaddr)) + offset;
515 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
525 #endif /* !USE_PROC_FOR_LIBRARIES */
529 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
531 #include <sys/procfs.h>
532 #include <sys/stat.h>
536 #include <signal.h> /* Only for the following test. */
541 extern void * GC_roots_present();
542 /* The type is a lie, since the real type doesn't make sense here, */
543 /* and we only test for NULL. */
546 /* We use /proc to track down all parts of the address space that are */
547 /* mapped by the process, and throw out regions we know we shouldn't */
548 /* worry about. This may also work under other SVR4 variants. */
549 void GC_register_dynamic_libraries()
553 static prmap_t * addr_map = 0;
554 static int current_sz = 0; /* Number of records currently in addr_map */
555 static int needed_sz; /* Required size of addr_map */
558 register ptr_t start;
559 register ptr_t limit;
560 ptr_t heap_start = (ptr_t)HEAP_START;
561 ptr_t heap_end = heap_start;
565 # endif /* SUNOS5DL */
568 sprintf(buf, "/proc/%d", getpid());
569 /* The above generates a lint complaint, since pid_t varies. */
570 /* It's unclear how to improve this. */
571 fd = open(buf, O_RDONLY);
573 ABORT("/proc open failed");
576 if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
577 GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
578 ABORT("/proc PIOCNMAP ioctl failed");
580 if (needed_sz >= current_sz) {
581 current_sz = needed_sz * 2 + 1;
582 /* Expansion, plus room for 0 record */
583 addr_map = (prmap_t *)GC_scratch_alloc((word)
584 (current_sz * sizeof(prmap_t)));
586 if (ioctl(fd, PIOCMAP, addr_map) < 0) {
587 GC_err_printf4("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
588 fd, errno, needed_sz, addr_map);
589 ABORT("/proc PIOCMAP ioctl failed");
591 if (GC_n_heap_sects > 0) {
592 heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
593 + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
594 if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr;
596 for (i = 0; i < needed_sz; i++) {
597 flags = addr_map[i].pr_mflags;
598 if ((flags & (MA_BREAK | MA_STACK | MA_PHYS)) != 0) goto irrelevant;
599 if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
601 /* The latter test is empirically useless in very old Irix */
602 /* versions. Other than the */
603 /* main data and stack segments, everything appears to be */
604 /* mapped readable, writable, executable, and shared(!!). */
605 /* This makes no sense to me. - HB */
606 start = (ptr_t)(addr_map[i].pr_vaddr);
607 if (GC_roots_present(start)) goto irrelevant;
608 if (start < heap_end && start >= heap_start)
611 if (GC_is_thread_stack(start)) goto irrelevant;
612 # endif /* MMAP_STACKS */
614 limit = start + addr_map[i].pr_size;
615 /* The following seemed to be necessary for very old versions */
616 /* of Irix, but it has been reported to discard relevant */
617 /* segments under Irix 6.5. */
619 if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
620 /* Discard text segments, i.e. 0-offset mappings against */
621 /* executable files which appear to have ELF headers. */
624 # define MAP_IRR_SZ 10
625 static ptr_t map_irr[MAP_IRR_SZ];
626 /* Known irrelevant map entries */
627 static int n_irr = 0;
631 for (i = 0; i < n_irr; i++) {
632 if (map_irr[i] == start) goto irrelevant;
634 arg = (caddr_t)start;
635 obj = ioctl(fd, PIOCOPENM, &arg);
639 if ((buf.st_mode & 0111) != 0) {
640 if (n_irr < MAP_IRR_SZ) {
641 map_irr[n_irr++] = start;
648 GC_add_roots_inner(start, limit, TRUE);
651 /* Dont keep cached descriptor, for now. Some kernels don't like us */
652 /* to keep a /proc file descriptor around during kill -9. */
653 if (close(fd) < 0) ABORT("Couldnt close /proc file");
657 # endif /* USE_PROC || IRIX5 */
659 # if defined(MSWIN32) || defined(MSWINCE)
661 # define WIN32_LEAN_AND_MEAN
663 # include <windows.h>
666 /* We traverse the entire address space and register all segments */
667 /* that could possibly have been written to. */
669 extern GC_bool GC_is_heap_base (ptr_t p);
671 # ifdef GC_WIN32_THREADS
672 extern void GC_get_next_stack(char *start, char **lo, char **hi);
673 void GC_cond_add_roots(char *base, char * limit)
675 char * curr_base = base;
676 char * next_stack_lo;
677 char * next_stack_hi;
679 if (base == limit) return;
681 GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
682 if (next_stack_lo >= limit) break;
683 GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
684 curr_base = next_stack_hi;
686 if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
689 void GC_cond_add_roots(char *base, char * limit)
693 = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
694 if (base == limit) return;
695 if (limit > stack_top && base < GC_stackbottom) {
696 /* Part of the stack; ignore it. */
699 GC_add_roots_inner(base, limit, TRUE);
704 /* Do we need to separately register the main static data segment? */
705 GC_bool GC_register_main_static_data()
710 extern GC_bool GC_no_win32_dlls;
712 GC_bool GC_register_main_static_data()
714 return GC_no_win32_dlls;
718 # define HAVE_REGISTER_MAIN_STATIC_DATA
720 void GC_register_dynamic_libraries()
722 MEMORY_BASIC_INFORMATION buf;
727 char * limit, * new_limit;
730 if (GC_no_win32_dlls) return;
732 base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
733 # if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
734 /* Only the first 32 MB of address space belongs to the current process */
735 while (p < (LPVOID)0x02000000) {
736 result = VirtualQuery(p, &buf, sizeof(buf));
738 /* Page is free; advance to the next possible allocation base */
740 (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
741 & ~(GC_sysinfo.dwAllocationGranularity-1));
744 while (p < GC_sysinfo.lpMaximumApplicationAddress) {
745 result = VirtualQuery(p, &buf, sizeof(buf));
748 if (result != sizeof(buf)) {
749 ABORT("Weird VirtualQuery result");
751 new_limit = (char *)p + buf.RegionSize;
752 protect = buf.Protect;
753 if (buf.State == MEM_COMMIT
754 && (protect == PAGE_EXECUTE_READWRITE
755 || protect == PAGE_READWRITE)
756 && !GC_is_heap_base(buf.AllocationBase)) {
757 if ((char *)p != limit) {
758 GC_cond_add_roots(base, limit);
764 if (p > (LPVOID)new_limit /* overflow */) break;
765 p = (LPVOID)new_limit;
767 GC_cond_add_roots(base, limit);
770 #endif /* MSWIN32 || MSWINCE */
772 #if defined(ALPHA) && defined(OSF1)
776 void GC_register_dynamic_libraries()
782 ldr_module_t moduleid = LDR_NULL_MODULE;
783 ldr_module_info_t moduleinfo;
784 size_t moduleinfosize = sizeof(moduleinfo);
785 size_t modulereturnsize;
789 ldr_region_info_t regioninfo;
790 size_t regioninfosize = sizeof(regioninfo);
791 size_t regionreturnsize;
793 /* Obtain id of this process */
794 mypid = ldr_my_process();
796 /* For each module */
799 /* Get the next (first) module */
800 status = ldr_next_module(mypid, &moduleid);
802 /* Any more modules? */
803 if (moduleid == LDR_NULL_MODULE)
804 break; /* No more modules */
806 /* Check status AFTER checking moduleid because */
807 /* of a bug in the non-shared ldr_next_module stub */
809 GC_printf1("dynamic_load: status = %ld\n", (long)status);
811 extern char *sys_errlist[];
814 if (errno <= sys_nerr) {
815 GC_printf1("dynamic_load: %s\n", (long)sys_errlist[errno]);
817 GC_printf1("dynamic_load: %d\n", (long)errno);
820 ABORT("ldr_next_module failed");
823 /* Get the module information */
824 status = ldr_inq_module(mypid, moduleid, &moduleinfo,
825 moduleinfosize, &modulereturnsize);
827 ABORT("ldr_inq_module failed");
829 /* is module for the main program (i.e. nonshared portion)? */
830 if (moduleinfo.lmi_flags & LDR_MAIN)
831 continue; /* skip the main module */
834 GC_printf("---Module---\n");
835 GC_printf("Module ID = %16ld\n", moduleinfo.lmi_modid);
836 GC_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion);
837 GC_printf("flags for module = %16lx\n", moduleinfo.lmi_flags);
838 GC_printf("pathname of module = \"%s\"\n", moduleinfo.lmi_name);
841 /* For each region in this module */
842 for (region = 0; region < moduleinfo.lmi_nregion; region++) {
844 /* Get the region information */
845 status = ldr_inq_region(mypid, moduleid, region, ®ioninfo,
846 regioninfosize, ®ionreturnsize);
848 ABORT("ldr_inq_region failed");
850 /* only process writable (data) regions */
851 if (! (regioninfo.lri_prot & LDR_W))
855 GC_printf("--- Region ---\n");
856 GC_printf("Region number = %16ld\n",
857 regioninfo.lri_region_no);
858 GC_printf("Protection flags = %016x\n", regioninfo.lri_prot);
859 GC_printf("Virtual address = %16p\n", regioninfo.lri_vaddr);
860 GC_printf("Mapped address = %16p\n", regioninfo.lri_mapaddr);
861 GC_printf("Region size = %16ld\n", regioninfo.lri_size);
862 GC_printf("Region name = \"%s\"\n", regioninfo.lri_name);
865 /* register region as a garbage collection root */
867 (char *)regioninfo.lri_mapaddr,
868 (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
882 extern char *sys_errlist[];
885 void GC_register_dynamic_libraries()
888 int index = 1; /* Ordinal position in shared library search list */
889 struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
891 /* For each dynamic library loaded */
894 /* Get info about next shared library */
895 status = shl_get(index, &shl_desc);
897 /* Check if this is the end of the list or if some error occured */
899 # ifdef GC_HPUX_THREADS
900 /* I've seen errno values of 0. The man page is not clear */
901 /* as to whether errno should get set on a -1 return. */
904 if (errno == EINVAL) {
905 break; /* Moved past end of shared library list --> finished */
907 if (errno <= sys_nerr) {
908 GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]);
910 GC_printf1("dynamic_load: %d\n", (long) errno);
912 ABORT("shl_get failed");
918 GC_printf0("---Shared library---\n");
919 GC_printf1("\tfilename = \"%s\"\n", shl_desc->filename);
920 GC_printf1("\tindex = %d\n", index);
921 GC_printf1("\thandle = %08x\n",
922 (unsigned long) shl_desc->handle);
923 GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart);
924 GC_printf1("\ttext seg. end = %08x\n", shl_desc->tend);
925 GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart);
926 GC_printf1("\tdata seg. end = %08x\n", shl_desc->dend);
927 GC_printf1("\tref. count = %lu\n", shl_desc->ref_count);
930 /* register shared library's data segment as a garbage collection root */
931 GC_add_roots_inner((char *) shl_desc->dstart,
932 (char *) shl_desc->dend, TRUE);
942 #include <sys/errno.h>
943 void GC_register_dynamic_libraries()
950 ldibuf = alloca(ldibuflen = 8192);
952 while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
953 if (errno != ENOMEM) {
954 ABORT("loadquery failed");
956 ldibuf = alloca(ldibuflen *= 2);
959 ldi = (struct ld_info *)ldibuf;
961 len = ldi->ldinfo_next;
964 (ptr_t)(unsigned long)ldi->ldinfo_dataorg
965 + ldi->ldinfo_datasize,
967 ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
974 #include <mach-o/dyld.h>
975 #include <mach-o/getsect.h>
977 /*#define DARWIN_DEBUG*/
979 const static struct {
982 } GC_dyld_sections[] = {
983 { SEG_DATA, SECT_DATA },
984 { SEG_DATA, SECT_BSS },
985 { SEG_DATA, SECT_COMMON }
989 static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
991 c = _dyld_image_count();
992 for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
993 return _dyld_get_image_name(i);
998 /* This should never be called by a thread holding the lock */
999 static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
1000 unsigned long start,end,i;
1001 const struct section *sec;
1002 for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1003 sec = getsectbynamefromheader(
1004 hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1005 if(sec == NULL || sec->size == 0) continue;
1006 start = slide + sec->addr;
1007 end = start + sec->size;
1008 # ifdef DARWIN_DEBUG
1009 GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
1010 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1012 GC_add_roots((char*)start,(char*)end);
1014 # ifdef DARWIN_DEBUG
1015 GC_print_static_roots();
1019 /* This should never be called by a thread holding the lock */
1020 static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
1021 unsigned long start,end,i;
1022 const struct section *sec;
1023 for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1024 sec = getsectbynamefromheader(
1025 hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1026 if(sec == NULL || sec->size == 0) continue;
1027 start = slide + sec->addr;
1028 end = start + sec->size;
1029 # ifdef DARWIN_DEBUG
1030 GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
1031 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1033 GC_remove_roots((char*)start,(char*)end);
1035 # ifdef DARWIN_DEBUG
1036 GC_print_static_roots();
1040 void GC_register_dynamic_libraries() {
1041 /* Currently does nothing. The callbacks are setup by GC_init_dyld()
1042 The dyld library takes it from there. */
1045 /* The _dyld_* functions have an internal lock so no _dyld functions
1046 can be called while the world is stopped without the risk of a deadlock.
1047 Because of this we MUST setup callbacks BEFORE we ever stop the world.
1048 This should be called BEFORE any thread in created and WITHOUT the
1049 allocation lock held. */
1051 void GC_init_dyld() {
1052 static GC_bool initialized = FALSE;
1054 if(initialized) return;
1056 # ifdef DARWIN_DEBUG
1057 GC_printf0("Forcing full bind of GC code...\n");
1059 if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1060 GC_abort("_dyld_bind_fully_image_containing_addres failed");
1062 # ifdef DARWIN_DEBUG
1063 GC_printf0("Registering dyld callbacks...\n");
1066 /* Apple's Documentation:
1067 When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1068 calls the specified callback (func) once for each of the images that is
1069 currently loaded into the program. When a new image is added to the program,
1070 your callback is called again with the mach_header for the new image, and the virtual memory slide amount of the new image.
1072 This WILL properly register existing and all future libraries
1075 _dyld_register_func_for_add_image(GC_dyld_image_add);
1076 _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1080 #define HAVE_REGISTER_MAIN_STATIC_DATA
1081 GC_bool GC_register_main_static_data()
1083 /* Already done through dyld callbacks */
1089 #else /* !DYNAMIC_LOADING */
1093 # include "il/PCR_IL.h"
1094 # include "th/PCR_ThCtl.h"
1095 # include "mm/PCR_MM.h"
1097 void GC_register_dynamic_libraries()
1099 /* Add new static data areas of dynamically loaded modules. */
1101 PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1102 PCR_IL_LoadedSegment * q;
1104 /* Skip uncommited files */
1105 while (p != NIL && !(p -> lf_commitPoint)) {
1106 /* The loading of this file has not yet been committed */
1107 /* Hence its description could be inconsistent. */
1108 /* Furthermore, it hasn't yet been run. Hence its data */
1109 /* segments can't possibly reference heap allocated */
1113 for (; p != NIL; p = p -> lf_prev) {
1114 for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1115 if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1116 == PCR_IL_SegFlags_Traced_on) {
1118 ((char *)(q -> ls_addr),
1119 (char *)(q -> ls_addr) + q -> ls_bytes,
1130 void GC_register_dynamic_libraries(){}
1132 int GC_no_dynamic_loading;
1136 #endif /* !DYNAMIC_LOADING */
1138 #ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1140 /* Do we need to separately register the main static data segment? */
1141 GC_bool GC_register_main_static_data()
1145 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */