1 /*
2  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
3  * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
4  *
5  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
6  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
7  *
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.
13  *
14  * Original author: Bill Janssen
15  * Heavily modified by Hans Boehm and others
16  */
17 
18 /*
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 ...
25  *
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.
28  */
29 #if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \
30      && !defined(_GNU_SOURCE)
31     /* Can't test LINUX, since this must be defined before other includes */
32 #   define _GNU_SOURCE
33 #endif
34 #if !defined(MACOS) && !defined(_WIN32_WCE)
35 #  include <sys/types.h>
36 #endif
37 #include "private/gc_priv.h"
38 
39 /* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
40 # if (defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS)) \
41       && defined(dlopen) && !defined(GC_USE_LD_WRAP)
42     /* To support threads in Solaris, gc.h interposes on dlopen by       */
43     /* defining "dlopen" to be "GC_dlopen", which is implemented below.  */
44     /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the   */
45     /* real system dlopen() in their implementation. We first remove     */
46     /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */
47 #   undef dlopen
48 #   define GC_must_restore_redefined_dlopen
49 # else
50 #   undef GC_must_restore_redefined_dlopen
51 # endif
52 
53 /* A user-supplied routine that is called to determine if a DSO must
54    be scanned by the gc.  */
55 static int (*GC_has_static_roots)(const char *, void *, size_t);
56 
57 
58 #if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \
59     && !defined(PCR)
60 #if !defined(SOLARISDL) && !defined(IRIX5) && \
61     !defined(MSWIN32) && !defined(MSWINCE) && \
62     !(defined(ALPHA) && defined(OSF1)) && \
63     !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \
64     !defined(AIX) && !defined(SCO_ELF) && !defined(DGUX) && \
65     !(defined(FREEBSD) && defined(__ELF__)) && \
66     !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
67     !defined(DARWIN) && !defined(CYGWIN32) && !defined(HAIKU)
68  --> We only know how to find data segments of dynamic libraries for the
69  --> above.  Additional SVR4 variants might not be too
70  --> hard to add.
71 #endif
72 
73 #include <stdio.h>
74 #ifdef SOLARISDL
75 #   include <sys/elf.h>
76 #   include <dlfcn.h>
77 #   include <link.h>
78 #endif
79 
80 #if defined(NETBSD)
81 #   include <machine/elf_machdep.h>
82 #   define ELFSIZE ARCH_ELFSIZE
83 #endif
84 
85 #if defined(HAIKU)
86 //  purposefully empty
87 #endif
88 
89 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
90     (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
91     (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
92 #   include <stddef.h>
93 #   include <elf.h>
94 #   include <link.h>
95 #endif
96 
97 /* Newer versions of GNU/Linux define this macro.  We
98  * define it similarly for any ELF systems that don't.  */
99 #  ifndef ElfW
100 #    if defined(FREEBSD)
101 #      if __ELF_WORD_SIZE == 32
102 #        define ElfW(type) Elf32_##type
103 #      else
104 #        define ElfW(type) Elf64_##type
105 #      endif
106 #    elif defined(NETBSD)
107 #      if ELFSIZE == 32
108 #        define ElfW(type) Elf32_##type
109 #      else
110 #        define ElfW(type) Elf64_##type
111 #      endif
112 #    else
113 #      if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
114 #        define ElfW(type) Elf32_##type
115 #      else
116 #        define ElfW(type) Elf64_##type
117 #      endif
118 #    endif
119 #  endif
120 
121 #if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES)
122 
123 #ifdef LINT
124     Elf32_Dyn _DYNAMIC;
125 #endif
126 
127 static struct link_map *
GC_FirstDLOpenedLinkMap()128 GC_FirstDLOpenedLinkMap()
129 {
130     extern ElfW(Dyn) _DYNAMIC;
131     ElfW(Dyn) *dp;
132     struct r_debug *r;
133     static struct link_map * cachedResult = 0;
134     static ElfW(Dyn) *dynStructureAddr = 0;
135     			/* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */
136 
137 #   ifdef SUNOS53_SHARED_LIB
138 	/* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set	*/
139 	/* up properly in dynamically linked .so's. This means we have	*/
140 	/* to use its value in the set of original object files loaded	*/
141 	/* at program startup.						*/
142 	if( dynStructureAddr == 0 ) {
143 	  void* startupSyms = dlopen(0, RTLD_LAZY);
144 	  dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC");
145 		}
146 #   else
147 	dynStructureAddr = &_DYNAMIC;
148 #   endif
149 
150     if( dynStructureAddr == 0) {
151         return(0);
152     }
153     if( cachedResult == 0 ) {
154         int tag;
155         for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
156             if( tag == DT_DEBUG ) {
157                 struct link_map *lm
158                         = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
159                 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
160                 break;
161             }
162         }
163     }
164     return cachedResult;
165 }
166 
167 #endif /* SOLARISDL ... */
168 
169 /* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
170 # if defined(GC_must_restore_redefined_dlopen)
171 #   define dlopen GC_dlopen
172 # endif
173 
174 # if defined(SOLARISDL)
175 /* Add dynamic library data sections to the root set.		*/
176 # if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS)
177 	--> fix mutual exclusion with dlopen
178 # endif
179 
180 # ifndef USE_PROC_FOR_LIBRARIES
181 void GC_register_dynamic_libraries()
182 {
183   struct link_map *lm = GC_FirstDLOpenedLinkMap();
184 
185 
186   for (lm = GC_FirstDLOpenedLinkMap();
187        lm != (struct link_map *) 0;  lm = lm->l_next)
188     {
189 	ElfW(Ehdr) * e;
190         ElfW(Phdr) * p;
191         unsigned long offset;
192         char * start;
193         register int i;
194 
195 	e = (ElfW(Ehdr) *) lm->l_addr;
196         p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
197         offset = ((unsigned long)(lm->l_addr));
198         for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
199           switch( p->p_type ) {
200             case PT_LOAD:
201               {
202                 if( !(p->p_flags & PF_W) ) break;
203                 start = ((char *)(p->p_vaddr)) + offset;
204                 GC_add_roots_inner(
205                   start,
206                   start + p->p_memsz,
207                   TRUE
208                 );
209               }
210               break;
211             default:
212               break;
213           }
214 	}
215     }
216 }
217 
218 # endif /* !USE_PROC ... */
219 # endif /* SOLARISDL */
220 
221 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
222     (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
223     (defined(NETBSD) && defined(__ELF__)) || defined(HURD) \
224     defined(HAIKU)
225 
226 
227 #ifdef USE_PROC_FOR_LIBRARIES
228 
229 #include <string.h>
230 
231 #include <sys/stat.h>
232 #include <fcntl.h>
233 #include <unistd.h>
234 
235 #define MAPS_BUF_SIZE (32*1024)
236 
237 extern ssize_t GC_repeat_read(int fd, char *buf, size_t count);
238 	/* Repeatedly read until buffer is filled, or EOF is encountered */
239 	/* Defined in os_dep.c.  					 */
240 
241 char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end,
242                          char **prot, unsigned int *maj_dev,
243 			 char **mapping_name);
244 char *GC_get_maps(void);
245 	/* From os_dep.c	*/
246 
GC_register_map_entries(char * maps)247 word GC_register_map_entries(char *maps)
248 {
249     char *prot;
250     char *buf_ptr = maps;
251     int count;
252     ptr_t start, end;
253     unsigned int maj_dev;
254     ptr_t least_ha, greatest_ha;
255     unsigned i;
256     ptr_t datastart = (ptr_t)(DATASTART);
257 
258     /* Compute heap bounds. FIXME: Should work if heap and roots are 	*/
259     /* interleaved?							*/
260 	least_ha = (ptr_t)(word)(-1);
261 	greatest_ha = 0;
262 	for (i = 0; i < GC_n_heap_sects; ++i) {
263 	    ptr_t sect_start = GC_heap_sects[i].hs_start;
264 	    ptr_t sect_end = sect_start + GC_heap_sects[i].hs_bytes;
265 	    if (sect_start < least_ha) least_ha = sect_start;
266 	    if (sect_end > greatest_ha) greatest_ha = sect_end;
267         }
268     	if (greatest_ha < (ptr_t)GC_scratch_last_end_ptr)
269 	    greatest_ha = (ptr_t)GC_scratch_last_end_ptr;
270 
271     for (;;) {
272         buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot, &maj_dev, 0);
273 	if (buf_ptr == NULL) return 1;
274 	if (prot[1] == 'w') {
275 	    /* This is a writable mapping.  Add it to		*/
276 	    /* the root set unless it is already otherwise	*/
277 	    /* accounted for.					*/
278 	    if (start <= GC_stackbottom && end >= GC_stackbottom) {
279 		/* Stack mapping; discard	*/
280 		continue;
281 	    }
282 #	    ifdef THREADS
283 	      /* This may fail, since a thread may already be 		*/
284 	      /* unregistered, but its thread stack may still be there.	*/
285 	      /* That can fail because the stack may disappear while	*/
286 	      /* we're marking.  Thus the marker is, and has to be	*/
287 	      /* prepared to recover from segmentation faults.		*/
288 	      if (GC_segment_is_thread_stack(start, end)) continue;
289 	      /* FIXME: REDIRECT_MALLOC actually works with threads on	*/
290 	      /* LINUX/IA64 if we omit this check.  The problem is that	*/
291 	      /* thread stacks contain pointers to dynamic thread	*/
292 	      /* vectors, which may be reused due to thread caching.	*/
293 	      /* Currently they may not be marked if the thread is 	*/
294 	      /* still live.						*/
295 	      /* For dead threads, we trace the whole stack, which is	*/
296 	      /* very suboptimal for performance reasons.		*/
297 #	    endif
298 	    /* We no longer exclude the main data segment.		*/
299 	    if (start < least_ha && end > least_ha) {
300 		end = least_ha;
301 	    }
302 	    if (start < greatest_ha && end > greatest_ha) {
303 		start = greatest_ha;
304 	    }
305 	    if (start >= least_ha && end <= greatest_ha) continue;
306 	    GC_add_roots_inner((char *)start, (char *)end, TRUE);
307 	}
308     }
309     return 1;
310 }
311 
GC_register_dynamic_libraries()312 void GC_register_dynamic_libraries()
313 {
314     if (!GC_register_map_entries(GC_get_maps()))
315         ABORT("Failed to read /proc for library registration.");
316 }
317 
318 /* We now take care of the main data segment ourselves: */
GC_register_main_static_data()319 GC_bool GC_register_main_static_data()
320 {
321     return FALSE;
322 }
323 
324 # define HAVE_REGISTER_MAIN_STATIC_DATA
325 
326 #endif /* USE_PROC_FOR_LIBRARIES */
327 
328 #if !defined(USE_PROC_FOR_LIBRARIES)
329 /* The following is the preferred way to walk dynamic libraries	*/
330 /* For glibc 2.2.4+.  Unfortunately, it doesn't work for older	*/
331 /* versions.  Thanks to Jakub Jelinek for most of the code.	*/
332 
333 # if (defined(LINUX) || defined (__GLIBC__)) /* Are others OK here, too? */ \
334      && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
335          || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
336 
337 /* We have the header files for a glibc that includes dl_iterate_phdr.	*/
338 /* It may still not be available in the library on the target system.   */
339 /* Thus we also treat it as a weak symbol.				*/
340 #define HAVE_DL_ITERATE_PHDR
341 
GC_register_dynlib_callback(info,size,ptr)342 static int GC_register_dynlib_callback(info, size, ptr)
343      struct dl_phdr_info * info;
344      size_t size;
345      void * ptr;
346 {
347   const ElfW(Phdr) * p;
348   char * start;
349   register int i;
350 
351   /* Make sure struct dl_phdr_info is at least as big as we need.  */
352   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
353       + sizeof (info->dlpi_phnum))
354     return -1;
355 
356   p = info->dlpi_phdr;
357   for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
358     switch( p->p_type ) {
359       case PT_LOAD:
360 	{
361 	  if( !(p->p_flags & PF_W) ) break;
362 	  start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
363 
364 	  if (GC_has_static_roots
365 	      && !GC_has_static_roots(info->dlpi_name, start, p->p_memsz))
366 	    break;
367 
368 	  GC_add_roots_inner(start, start + p->p_memsz, TRUE);
369 	}
370       break;
371       default:
372 	break;
373     }
374   }
375 
376   * (int *)ptr = 1;	/* Signal that we were called */
377   return 0;
378 }
379 
380 /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
381 
382 #pragma weak dl_iterate_phdr
383 
GC_register_dynamic_libraries_dl_iterate_phdr()384 GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
385 {
386   if (dl_iterate_phdr) {
387     int did_something = 0;
388     dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
389     if (!did_something) {
390 	/* dl_iterate_phdr may forget the static data segment in	*/
391 	/* statically linked executables.				*/
392 	GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE);
393 #       if defined(DATASTART2)
394           GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
395 #       endif
396     }
397 
398     return TRUE;
399   } else {
400     return FALSE;
401   }
402 }
403 
404 /* Do we need to separately register the main static data segment? */
GC_register_main_static_data()405 GC_bool GC_register_main_static_data()
406 {
407   return (dl_iterate_phdr == 0);
408 }
409 
410 #define HAVE_REGISTER_MAIN_STATIC_DATA
411 
412 # else /* !LINUX || version(glibc) < 2.2.4 */
413 
414 /* Dynamic loading code for Linux running ELF. Somewhat tested on
415  * Linux/x86, untested but hopefully should work on Linux/Alpha.
416  * This code was derived from the Solaris/ELF support. Thanks to
417  * whatever kind soul wrote that.  - Patrick Bridges */
418 
419 /* This doesn't necessarily work in all cases, e.g. with preloaded
420  * dynamic libraries.						*/
421 
422 #if defined(NETBSD)
423 #  include <sys/exec_elf.h>
424 /* for compatibility with 1.4.x */
425 #  ifndef DT_DEBUG
426 #  define DT_DEBUG     21
427 #  endif
428 #  ifndef PT_LOAD
429 #  define PT_LOAD      1
430 #  endif
431 #  ifndef PF_W
432 #  define PF_W         2
433 #  endif
434 #else
435 #  include <elf.h>
436 #endif
437 #include <link.h>
438 
439 # endif
440 
441 #ifdef __GNUC__
442 # pragma weak _DYNAMIC
443 #endif
444 extern ElfW(Dyn) _DYNAMIC[];
445 
446 static struct link_map *
GC_FirstDLOpenedLinkMap()447 GC_FirstDLOpenedLinkMap()
448 {
449     ElfW(Dyn) *dp;
450     static struct link_map *cachedResult = 0;
451 
452     if( _DYNAMIC == 0) {
453         return(0);
454     }
455     if( cachedResult == 0 ) {
456         int tag;
457         for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
458             if( tag == DT_DEBUG ) {
459                 struct link_map *lm
460                         = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
461                 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
462                 break;
463             }
464         }
465     }
466     return cachedResult;
467 }
468 
469 
GC_register_dynamic_libraries()470 void GC_register_dynamic_libraries()
471 {
472   struct link_map *lm;
473 
474 
475 # ifdef HAVE_DL_ITERATE_PHDR
476     if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
477 	return;
478     }
479 # endif
480   lm = GC_FirstDLOpenedLinkMap();
481   for (lm = GC_FirstDLOpenedLinkMap();
482        lm != (struct link_map *) 0;  lm = lm->l_next)
483     {
484 	ElfW(Ehdr) * e;
485         ElfW(Phdr) * p;
486         unsigned long offset;
487         char * start;
488         register int i;
489 
490 	e = (ElfW(Ehdr) *) lm->l_addr;
491         p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
492         offset = ((unsigned long)(lm->l_addr));
493         for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
494           switch( p->p_type ) {
495             case PT_LOAD:
496               {
497                 if( !(p->p_flags & PF_W) ) break;
498                 start = ((char *)(p->p_vaddr)) + offset;
499                 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
500               }
501               break;
502             default:
503               break;
504           }
505 	}
506     }
507 }
508 
509 #endif /* !USE_PROC_FOR_LIBRARIES */
510 
511 #endif /* LINUX */
512 
513 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
514 
515 #include <sys/procfs.h>
516 #include <sys/stat.h>
517 #include <fcntl.h>
518 #include <elf.h>
519 #include <errno.h>
520 #include <signal.h>  /* Only for the following test. */
521 #ifndef _sigargs
522 # define IRIX6
523 #endif
524 
525 extern void * GC_roots_present();
526 	/* The type is a lie, since the real type doesn't make sense here, */
527 	/* and we only test for NULL.					   */
528 
529 
530 /* We use /proc to track down all parts of the address space that are	*/
531 /* mapped by the process, and throw out regions we know we shouldn't	*/
532 /* worry about.  This may also work under other SVR4 variants.		*/
GC_register_dynamic_libraries()533 void GC_register_dynamic_libraries()
534 {
535     static int fd = -1;
536     char buf[30];
537     static prmap_t * addr_map = 0;
538     static int current_sz = 0;	/* Number of records currently in addr_map */
539     static int needed_sz;	/* Required size of addr_map		*/
540     int i;
541     long flags;
542     ptr_t start;
543     ptr_t limit;
544     ptr_t heap_start = (ptr_t)HEAP_START;
545     ptr_t heap_end = heap_start;
546 
547 #   ifdef SOLARISDL
548 #     define MA_PHYS 0
549 #   endif /* SOLARISDL */
550 
551     if (fd < 0) {
552       sprintf(buf, "/proc/%d", getpid());
553 	/* The above generates a lint complaint, since pid_t varies.	*/
554 	/* It's unclear how to improve this.				*/
555       fd = open(buf, O_RDONLY);
556       if (fd < 0) {
557     	ABORT("/proc open failed");
558       }
559     }
560     if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
561 	GC_err_printf("fd = %d, errno = %d\n", fd, errno);
562     	ABORT("/proc PIOCNMAP ioctl failed");
563     }
564     if (needed_sz >= current_sz) {
565         current_sz = needed_sz * 2 + 1;
566         		/* Expansion, plus room for 0 record */
567         addr_map = (prmap_t *)GC_scratch_alloc((word)
568 						(current_sz * sizeof(prmap_t)));
569     }
570     if (ioctl(fd, PIOCMAP, addr_map) < 0) {
571         GC_err_printf("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
572                         fd, errno, needed_sz, addr_map);
573     	ABORT("/proc PIOCMAP ioctl failed");
574     };
575     if (GC_n_heap_sects > 0) {
576     	heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
577     			+ GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
578     	if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr;
579     }
580     for (i = 0; i < needed_sz; i++) {
581         flags = addr_map[i].pr_mflags;
582 	if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
583 		      | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
584         if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
585             goto irrelevant;
586           /* The latter test is empirically useless in very old Irix	*/
587 	  /* versions.  Other than the					*/
588           /* main data and stack segments, everything appears to be	*/
589           /* mapped readable, writable, executable, and shared(!!).	*/
590           /* This makes no sense to me.	- HB				*/
591         start = (ptr_t)(addr_map[i].pr_vaddr);
592         if (GC_roots_present(start)) goto irrelevant;
593         if (start < heap_end && start >= heap_start)
594         	goto irrelevant;
595 #	ifdef MMAP_STACKS
596 	  if (GC_is_thread_stack(start)) goto irrelevant;
597 #	endif /* MMAP_STACKS */
598 
599         limit = start + addr_map[i].pr_size;
600 	/* The following seemed to be necessary for very old versions 	*/
601 	/* of Irix, but it has been reported to discard relevant	*/
602 	/* segments under Irix 6.5.  					*/
603 #	ifndef IRIX6
604 	  if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
605 	    /* Discard text segments, i.e. 0-offset mappings against	*/
606 	    /* executable files which appear to have ELF headers.	*/
607 	    caddr_t arg;
608 	    int obj;
609 #	    define MAP_IRR_SZ 10
610 	    static ptr_t map_irr[MAP_IRR_SZ];
611 	    				/* Known irrelevant map entries	*/
612 	    static int n_irr = 0;
613 	    struct stat buf;
614 	    register int i;
615 
616 	    for (i = 0; i < n_irr; i++) {
617 	        if (map_irr[i] == start) goto irrelevant;
618 	    }
619 	    arg = (caddr_t)start;
620 	    obj = ioctl(fd, PIOCOPENM, &arg);
621 	    if (obj >= 0) {
622 	        fstat(obj, &buf);
623 	        close(obj);
624 	        if ((buf.st_mode & 0111) != 0) {
625 	            if (n_irr < MAP_IRR_SZ) {
626 	                map_irr[n_irr++] = start;
627 	            }
628 	            goto irrelevant;
629 	        }
630 	    }
631 	  }
632 #	endif /* !IRIX6 */
633         GC_add_roots_inner(start, limit, TRUE);
634       irrelevant: ;
635     }
636     /* Dont keep cached descriptor, for now.  Some kernels don't like us */
637     /* to keep a /proc file descriptor around during kill -9.		 */
638     	if (close(fd) < 0) ABORT("Couldnt close /proc file");
639 	fd = -1;
640 }
641 
642 # endif /* USE_PROC || IRIX5 */
643 
644 # if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
645 
646 # define WIN32_LEAN_AND_MEAN
647 # define NOSERVICE
648 # include <windows.h>
649 # include <stdlib.h>
650 
651   /* We traverse the entire address space and register all segments 	*/
652   /* that could possibly have been written to.				*/
653 
654   extern GC_bool GC_is_heap_base (ptr_t p);
655 
656 # ifdef GC_WIN32_THREADS
657     extern void GC_get_next_stack(char *start, char **lo, char **hi);
GC_cond_add_roots(char * base,char * limit)658     void GC_cond_add_roots(char *base, char * limit)
659     {
660       char * curr_base = base;
661       char * next_stack_lo;
662       char * next_stack_hi;
663 
664       if (base == limit) return;
665       for(;;) {
666 	  GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
667 	  if (next_stack_lo >= limit) break;
668 	  GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
669 	  curr_base = next_stack_hi;
670       }
671       if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
672     }
673 # else
GC_cond_add_roots(char * base,char * limit)674     void GC_cond_add_roots(char *base, char * limit)
675     {
676       char dummy;
677       char * stack_top
678 	 = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
679       if (base == limit) return;
680       if (limit > stack_top && base < GC_stackbottom) {
681     	  /* Part of the stack; ignore it. */
682     	  return;
683       }
684       GC_add_roots_inner(base, limit, TRUE);
685     }
686 # endif
687 
688 # ifdef MSWINCE
689   /* Do we need to separately register the main static data segment? */
GC_register_main_static_data()690   GC_bool GC_register_main_static_data()
691   {
692     return FALSE;
693   }
694 # else /* win32 */
695   extern GC_bool GC_no_win32_dlls;
696 
GC_register_main_static_data()697   GC_bool GC_register_main_static_data()
698   {
699     return GC_no_win32_dlls;
700   }
701 # endif /* win32 */
702 
703 # define HAVE_REGISTER_MAIN_STATIC_DATA
704 
705 # ifdef DEBUG_VIRTUALQUERY
GC_dump_meminfo(MEMORY_BASIC_INFORMATION * buf)706   void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
707   {
708     GC_printf("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
709 	       buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
710 	       buf -> RegionSize);
711     GC_printf("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
712 	       "Type = %lx\n",
713 	       buf -> AllocationProtect, buf -> State, buf -> Protect,
714 	       buf -> Type);
715   }
716 # endif /* DEBUG_VIRTUALQUERY */
717 
718   extern GC_bool GC_wnt;  /* Is Windows NT derivative.		*/
719   			  /* Defined and set in os_dep.c.	*/
720 
GC_register_dynamic_libraries()721   void GC_register_dynamic_libraries()
722   {
723     MEMORY_BASIC_INFORMATION buf;
724     size_t result;
725     DWORD protect;
726     LPVOID p;
727     char * base;
728     char * limit, * new_limit;
729 
730 #   ifdef MSWIN32
731       if (GC_no_win32_dlls) return;
732 #   endif
733     base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
734 #   if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
735       /* Only the first 32 MB of address space belongs to the current process */
736       while (p < (LPVOID)0x02000000) {
737         result = VirtualQuery(p, &buf, sizeof(buf));
738 	if (result == 0) {
739 	    /* Page is free; advance to the next possible allocation base */
740 	    new_limit = (char *)
741 		(((DWORD) p + GC_sysinfo.dwAllocationGranularity)
742 		 & ~(GC_sysinfo.dwAllocationGranularity-1));
743 	} else
744 #   else
745       while (p < GC_sysinfo.lpMaximumApplicationAddress) {
746         result = VirtualQuery(p, &buf, sizeof(buf));
747 #   endif
748 	{
749 	    if (result != sizeof(buf)) {
750 		ABORT("Weird VirtualQuery result");
751 	    }
752 	    new_limit = (char *)p + buf.RegionSize;
753 	    protect = buf.Protect;
754 	    if (buf.State == MEM_COMMIT
755 		&& (protect == PAGE_EXECUTE_READWRITE
756 		    || protect == PAGE_READWRITE)
757 		&& !GC_is_heap_base(buf.AllocationBase)
758  		/* There is some evidence that we cannot always
759  		 * ignore MEM_PRIVATE sections under Windows ME
760  		 * and predecessors.  Hence we now also check for
761  		 * that case.	*/
762  		&& (buf.Type == MEM_IMAGE ||
763  		    !GC_wnt && buf.Type == MEM_PRIVATE)) {
764 #	        ifdef DEBUG_VIRTUALQUERY
765 	          GC_dump_meminfo(&buf);
766 #	        endif
767 		if ((char *)p != limit) {
768 		    GC_cond_add_roots(base, limit);
769 		    base = p;
770 		}
771 		limit = new_limit;
772 	    }
773 	}
774         if (p > (LPVOID)new_limit /* overflow */) break;
775         p = (LPVOID)new_limit;
776     }
777     GC_cond_add_roots(base, limit);
778   }
779 
780 #endif /* MSWIN32 || MSWINCE || CYGWIN32 */
781 
782 #if defined(ALPHA) && defined(OSF1)
783 
784 #include <loader.h>
785 
786 void GC_register_dynamic_libraries()
787 {
788   int status;
789   ldr_process_t mypid;
790 
791   /* module */
792     ldr_module_t moduleid = LDR_NULL_MODULE;
793     ldr_module_info_t moduleinfo;
794     size_t moduleinfosize = sizeof(moduleinfo);
795     size_t modulereturnsize;
796 
797   /* region */
798     ldr_region_t region;
799     ldr_region_info_t regioninfo;
800     size_t regioninfosize = sizeof(regioninfo);
801     size_t regionreturnsize;
802 
803   /* Obtain id of this process */
804     mypid = ldr_my_process();
805 
806   /* For each module */
807     while (TRUE) {
808 
809       /* Get the next (first) module */
810         status = ldr_next_module(mypid, &moduleid);
811 
812       /* Any more modules? */
813         if (moduleid == LDR_NULL_MODULE)
814             break;    /* No more modules */
815 
816       /* Check status AFTER checking moduleid because */
817       /* of a bug in the non-shared ldr_next_module stub */
818         if (status != 0 ) {
819             GC_printf("dynamic_load: status = %d\n", status);
820             {
821                 extern char *sys_errlist[];
822                 extern int sys_nerr;
823                 extern int errno;
824                 if (errno <= sys_nerr) {
825                     GC_printf("dynamic_load: %s\n", sys_errlist[errno]);
826                } else {
827                     GC_printf("dynamic_load: %d\n", errno);
828                 }
829         }
830             ABORT("ldr_next_module failed");
831          }
832 
833       /* Get the module information */
834         status = ldr_inq_module(mypid, moduleid, &moduleinfo,
835                                 moduleinfosize, &modulereturnsize);
836         if (status != 0 )
837             ABORT("ldr_inq_module failed");
838 
839       /* is module for the main program (i.e. nonshared portion)? */
840           if (moduleinfo.lmi_flags & LDR_MAIN)
841               continue;    /* skip the main module */
842 
843 #     ifdef DL_VERBOSE
844           GC_printf("---Module---\n");
845           GC_printf("Module ID            = %16ld\n", moduleinfo.lmi_modid);
846           GC_printf("Count of regions     = %16d\n", moduleinfo.lmi_nregion);
847           GC_printf("flags for module     = %16lx\n", moduleinfo.lmi_flags);
848           GC_printf("pathname of module   = \"%s\"\n", moduleinfo.lmi_name);
849 #     endif
850 
851       /* For each region in this module */
852         for (region = 0; region < moduleinfo.lmi_nregion; region++) {
853 
854           /* Get the region information */
855             status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
856                                     regioninfosize, &regionreturnsize);
857             if (status != 0 )
858                 ABORT("ldr_inq_region failed");
859 
860           /* only process writable (data) regions */
861             if (! (regioninfo.lri_prot & LDR_W))
862                 continue;
863 
864 #         ifdef DL_VERBOSE
865               GC_printf("--- Region ---\n");
866               GC_printf("Region number    = %16ld\n",
867               	        regioninfo.lri_region_no);
868               GC_printf("Protection flags = %016x\n",  regioninfo.lri_prot);
869               GC_printf("Virtual address  = %16p\n",   regioninfo.lri_vaddr);
870               GC_printf("Mapped address   = %16p\n",   regioninfo.lri_mapaddr);
871               GC_printf("Region size      = %16ld\n",  regioninfo.lri_size);
872               GC_printf("Region name      = \"%s\"\n", regioninfo.lri_name);
873 #         endif
874 
875           /* register region as a garbage collection root */
876             GC_add_roots_inner (
877                 (char *)regioninfo.lri_mapaddr,
878                 (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
879                 TRUE);
880 
881         }
882     }
883 }
884 #endif
885 
886 #if defined(HPUX)
887 
888 #include <errno.h>
889 #include <dl.h>
890 
891 extern char *sys_errlist[];
892 extern int sys_nerr;
893 
894 void GC_register_dynamic_libraries()
895 {
896   int status;
897   int index = 1; /* Ordinal position in shared library search list */
898   struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
899 
900   /* For each dynamic library loaded */
901     while (TRUE) {
902 
903       /* Get info about next shared library */
904         status = shl_get(index, &shl_desc);
905 
906       /* Check if this is the end of the list or if some error occured */
907         if (status != 0) {
908 #	 ifdef GC_HPUX_THREADS
909 	   /* I've seen errno values of 0.  The man page is not clear	*/
910 	   /* as to whether errno should get set on a -1 return.	*/
911 	   break;
912 #	 else
913           if (errno == EINVAL) {
914               break; /* Moved past end of shared library list --> finished */
915           } else {
916               if (errno <= sys_nerr) {
917                     GC_printf("dynamic_load: %s\n", sys_errlist[errno]);
918               } else {
919                     GC_printf("dynamic_load: %d\n", errno);
920 	      }
921               ABORT("shl_get failed");
922           }
923 #	 endif
924         }
925 
926 #     ifdef DL_VERBOSE
927           GC_printf("---Shared library---\n");
928           GC_printf("\tfilename        = \"%s\"\n", shl_desc->filename);
929           GC_printf("\tindex           = %d\n", index);
930           GC_printf("\thandle          = %08x\n",
931 					(unsigned long) shl_desc->handle);
932           GC_printf("\ttext seg. start = %08x\n", shl_desc->tstart);
933           GC_printf("\ttext seg. end   = %08x\n", shl_desc->tend);
934           GC_printf("\tdata seg. start = %08x\n", shl_desc->dstart);
935           GC_printf("\tdata seg. end   = %08x\n", shl_desc->dend);
936           GC_printf("\tref. count      = %lu\n", shl_desc->ref_count);
937 #     endif
938 
939       /* register shared library's data segment as a garbage collection root */
940         GC_add_roots_inner((char *) shl_desc->dstart,
941 			   (char *) shl_desc->dend, TRUE);
942 
943         index++;
944     }
945 }
946 #endif /* HPUX */
947 
948 #ifdef AIX
949 #pragma alloca
950 #include <sys/ldr.h>
951 #include <sys/errno.h>
952 void GC_register_dynamic_libraries()
953 {
954 	int len;
955 	char *ldibuf;
956 	int ldibuflen;
957 	struct ld_info *ldi;
958 
959 	ldibuf = alloca(ldibuflen = 8192);
960 
961 	while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
962 		if (errno != ENOMEM) {
963 			ABORT("loadquery failed");
964 		}
965 		ldibuf = alloca(ldibuflen *= 2);
966 	}
967 
968 	ldi = (struct ld_info *)ldibuf;
969 	while (ldi) {
970 		len = ldi->ldinfo_next;
971 		GC_add_roots_inner(
972 				ldi->ldinfo_dataorg,
973 				(ptr_t)(unsigned long)ldi->ldinfo_dataorg
974 			        + ldi->ldinfo_datasize,
975 				TRUE);
976 		ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
977 	}
978 }
979 #endif /* AIX */
980 
981 #ifdef DARWIN
982 
983 /* __private_extern__ hack required for pre-3.4 gcc versions.	*/
984 #ifndef __private_extern__
985 # define __private_extern__ extern
986 # include <mach-o/dyld.h>
987 # undef __private_extern__
988 #else
989 # include <mach-o/dyld.h>
990 #endif
991 #include <mach-o/getsect.h>
992 
993 /*#define DARWIN_DEBUG*/
994 
995 const static struct {
996         const char *seg;
997         const char *sect;
998 } GC_dyld_sections[] = {
999         { SEG_DATA, SECT_DATA },
1000         { SEG_DATA, SECT_BSS },
1001         { SEG_DATA, SECT_COMMON }
1002 };
1003 
1004 #ifdef DARWIN_DEBUG
1005 static const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) {
1006     unsigned long i,c;
1007     c = _dyld_image_count();
1008     for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
1009         return _dyld_get_image_name(i);
1010     return NULL;
1011 }
1012 #endif
1013 
1014 /* This should never be called by a thread holding the lock */
1015 static void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, intptr_t slide)
1016 {
1017     unsigned long start,end,i;
1018     const struct GC_MACH_SECTION *sec;
1019     if (GC_no_dls) return;
1020     for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1021 #   if defined (__LP64__)
1022       sec = getsectbynamefromheader_64(
1023 #   else
1024       sec = getsectbynamefromheader(
1025 #   endif
1026 				    hdr, GC_dyld_sections[i].seg,
1027 				    GC_dyld_sections[i].sect);
1028       if(sec == NULL || sec->size == 0) continue;
1029       start = slide + sec->addr;
1030       end = start + sec->size;
1031 #   ifdef DARWIN_DEBUG
1032       GC_printf("Adding section at %p-%p (%lu bytes) from image %s\n",
1033 		start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1034 #   endif
1035       GC_add_roots((char*)start,(char*)end);
1036     }
1037 #   ifdef DARWIN_DEBUG
1038        GC_print_static_roots();
1039 #   endif
1040 }
1041 
1042 /* This should never be called by a thread holding the lock */
1043 static void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr,
1044 				 intptr_t slide)
1045 {
1046     unsigned long start,end,i;
1047     const struct GC_MACH_SECTION *sec;
1048     for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1049 #   if defined (__LP64__)
1050       sec = getsectbynamefromheader_64(
1051 #   else
1052       sec = getsectbynamefromheader(
1053 #   endif
1054 				    hdr, GC_dyld_sections[i].seg,
1055 				    GC_dyld_sections[i].sect);
1056       if(sec == NULL || sec->size == 0) continue;
1057       start = slide + sec->addr;
1058       end = start + sec->size;
1059 #   ifdef DARWIN_DEBUG
1060       GC_printf("Removing section at %p-%p (%lu bytes) from image %s\n",
1061 		start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1062 #   endif
1063       GC_remove_roots((char*)start,(char*)end);
1064     }
1065 #   ifdef DARWIN_DEBUG
1066 	GC_print_static_roots();
1067 #   endif
1068 }
1069 
1070 void GC_register_dynamic_libraries() {
1071     /* Currently does nothing. The callbacks are setup by GC_init_dyld()
1072     The dyld library takes it from there. */
1073 }
1074 
1075 /* The _dyld_* functions have an internal lock so no _dyld functions
1076    can be called while the world is stopped without the risk of a deadlock.
1077    Because of this we MUST setup callbacks BEFORE we ever stop the world.
1078    This should be called BEFORE any thread in created and WITHOUT the
1079    allocation lock held. */
1080 
1081 void GC_init_dyld() {
1082   static GC_bool initialized = FALSE;
1083   char *bind_fully_env = NULL;
1084 
1085   if(initialized) return;
1086 
1087 #   ifdef DARWIN_DEBUG
1088       GC_printf("Registering dyld callbacks...\n");
1089 #   endif
1090 
1091   /* Apple's Documentation:
1092      When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1093      calls the specified callback (func) once for each of the images that is
1094      currently loaded into the program. When a new image is added to the program,
1095      your callback is called again with the mach_header for the new image, and the
1096      virtual memory slide amount of the new image.
1097 
1098      This WILL properly register already linked libraries and libraries
1099      linked in the future
1100   */
1101 
1102     _dyld_register_func_for_add_image(GC_dyld_image_add);
1103     _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1104 
1105     /* Set this early to avoid reentrancy issues. */
1106     initialized = TRUE;
1107 
1108     bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
1109 
1110     if (bind_fully_env == NULL) {
1111 #   ifdef DARWIN_DEBUG
1112       GC_printf("Forcing full bind of GC code...\n");
1113 #   endif
1114 
1115       if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1116         GC_abort("_dyld_bind_fully_image_containing_address failed");
1117     }
1118 
1119 }
1120 
1121 #define HAVE_REGISTER_MAIN_STATIC_DATA
1122 GC_bool GC_register_main_static_data()
1123 {
1124   /* Already done through dyld callbacks */
1125   return FALSE;
1126 }
1127 
1128 #endif /* DARWIN */
1129 
1130 #else /* !DYNAMIC_LOADING */
1131 
1132 #ifdef PCR
1133 
1134 #   include "il/PCR_IL.h"
1135 #   include "th/PCR_ThCtl.h"
1136 #   include "mm/PCR_MM.h"
1137 
1138 void GC_register_dynamic_libraries()
1139 {
1140     /* Add new static data areas of dynamically loaded modules.	*/
1141         {
1142           PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1143           PCR_IL_LoadedSegment * q;
1144 
1145           /* Skip uncommited files */
1146           while (p != NIL && !(p -> lf_commitPoint)) {
1147               /* The loading of this file has not yet been committed	*/
1148               /* Hence its description could be inconsistent.  		*/
1149               /* Furthermore, it hasn't yet been run.  Hence its data	*/
1150               /* segments can't possibly reference heap allocated	*/
1151               /* objects.						*/
1152               p = p -> lf_prev;
1153           }
1154           for (; p != NIL; p = p -> lf_prev) {
1155             for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1156               if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1157                   == PCR_IL_SegFlags_Traced_on) {
1158                 GC_add_roots_inner
1159                 	((char *)(q -> ls_addr),
1160                 	 (char *)(q -> ls_addr) + q -> ls_bytes,
1161                 	 TRUE);
1162               }
1163             }
1164           }
1165         }
1166 }
1167 
1168 
1169 #else /* !PCR */
1170 
1171 void GC_register_dynamic_libraries(){}
1172 
1173 int GC_no_dynamic_loading;
1174 
1175 #endif /* !PCR */
1176 
1177 #endif /* !DYNAMIC_LOADING */
1178 
1179 #ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1180 
1181 /* Do we need to separately register the main static data segment? */
1182 GC_bool GC_register_main_static_data()
1183 {
1184   return TRUE;
1185 }
1186 
1187 /* Register a routine to filter dynamic library registration.  */
1188 void
1189 GC_register_has_static_roots_callback
1190   (int (*callback)(const char *, void *, size_t)) {
1191   GC_has_static_roots = callback;
1192 }
1193 
1194 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
1195 
1196