OSDN Git Service

* tree-pass.h (pass_cfg): Remove.
[pf3gnuchains/gcc-fork.git] / boehm-gc / dyn_load.c
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(_GNU_SOURCE)
30     /* Can't test LINUX, since this must be define before other includes */
31 #   define _GNU_SOURCE
32 #endif
33 #if !defined(MACOS) && !defined(_WIN32_WCE)
34 #  include <sys/types.h>
35 #endif
36 #include "private/gc_priv.h"
37
38 /* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
39 # if (defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS)) \
40       && defined(dlopen) && !defined(GC_USE_LD_WRAP)
41     /* To support threads in Solaris, gc.h interposes on dlopen by       */
42     /* defining "dlopen" to be "GC_dlopen", which is implemented below.  */
43     /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the   */
44     /* real system dlopen() in their implementation. We first remove     */
45     /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */
46 #   undef dlopen
47 #   define GC_must_restore_redefined_dlopen
48 # else
49 #   undef GC_must_restore_redefined_dlopen
50 # endif
51
52 #if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \
53     && !defined(PCR)
54 #if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \
55     !defined(MSWIN32) && !defined(MSWINCE) && \
56     !(defined(ALPHA) && defined(OSF1)) && \
57     !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \
58     !defined(RS6000) && !defined(SCO_ELF) && !defined(DGUX) && \
59     !(defined(FREEBSD) && defined(__ELF__)) && \
60     !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
61     !defined(DARWIN)
62  --> We only know how to find data segments of dynamic libraries for the
63  --> above.  Additional SVR4 variants might not be too
64  --> hard to add.
65 #endif
66
67 #include <stdio.h>
68 #ifdef SUNOS5DL
69 #   include <sys/elf.h>
70 #   include <dlfcn.h>
71 #   include <link.h>
72 #endif
73 #ifdef SUNOS4
74 #   include <dlfcn.h>
75 #   include <link.h>
76 #   include <a.out.h>
77   /* struct link_map field overrides */
78 #   define l_next       lm_next
79 #   define l_addr       lm_addr
80 #   define l_name       lm_name
81 #endif
82
83 #if defined(NETBSD)
84 #   include <machine/elf_machdep.h>
85 #   define ELFSIZE ARCH_ELFSIZE
86 #endif
87
88 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
89     (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
90     (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
91 #   include <stddef.h>
92 #   include <elf.h>
93 #   include <link.h>
94 #endif
95
96 /* Newer versions of GNU/Linux define this macro.  We
97  * define it similarly for any ELF systems that don't.  */
98 #  ifndef ElfW
99 #    if defined(FREEBSD)
100 #      if __ELF_WORD_SIZE == 32
101 #        define ElfW(type) Elf32_##type
102 #      else
103 #        define ElfW(type) Elf64_##type
104 #      endif
105 #    else
106 #      ifdef 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 #  endif
121
122 #if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES)
123
124 #ifdef LINT
125     Elf32_Dyn _DYNAMIC;
126 #endif
127
128 static struct link_map *
129 GC_FirstDLOpenedLinkMap()
130 {
131     extern ElfW(Dyn) _DYNAMIC;
132     ElfW(Dyn) *dp;
133     struct r_debug *r;
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 */
137
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");
146                 }
147 #   else
148         dynStructureAddr = &_DYNAMIC;
149 #   endif
150
151     if( dynStructureAddr == 0) {
152         return(0);
153     }
154     if( cachedResult == 0 ) {
155         int tag;
156         for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
157             if( tag == DT_DEBUG ) {
158                 struct link_map *lm
159                         = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
160                 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
161                 break;
162             }
163         }
164     }
165     return cachedResult;
166 }
167
168 #endif /* SUNOS5DL ... */
169
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
173 # endif
174
175 #if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES)
176
177 #ifdef LINT
178     struct link_dynamic _DYNAMIC;
179 #endif
180
181 static struct link_map *
182 GC_FirstDLOpenedLinkMap()
183 {
184     extern struct link_dynamic _DYNAMIC;
185
186     if( &_DYNAMIC == 0) {
187         return(0);
188     }
189     return(_DYNAMIC.ld_un.ld_1->ld_loaded);
190 }
191
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()
195 {
196     ptr_t result = 0;
197     extern struct link_dynamic _DYNAMIC;
198     struct rtc_symb * curr_symbol;
199     
200     if( &_DYNAMIC == 0) {
201         return(0);
202     }
203     curr_symbol = _DYNAMIC.ldd -> ldd_cp;
204     for (; curr_symbol != 0; curr_symbol = curr_symbol -> rtc_next) {
205         if (result == 0
206             || (ptr_t)(curr_symbol -> rtc_sp -> n_value) < result) {
207             result = (ptr_t)(curr_symbol -> rtc_sp -> n_value);
208         }
209     }
210     return(result);
211 }
212
213 #endif  /* SUNOS4 ... */
214
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)
218 #   ifndef SRC_M3
219         --> fix mutual exclusion with dlopen
220 #   endif  /* We assume M3 programs don't call dlopen for now */
221 # endif
222
223 # ifndef USE_PROC_FOR_LIBRARIES
224 void GC_register_dynamic_libraries()
225 {
226   struct link_map *lm = GC_FirstDLOpenedLinkMap();
227   
228
229   for (lm = GC_FirstDLOpenedLinkMap();
230        lm != (struct link_map *) 0;  lm = lm->l_next)
231     {
232 #     ifdef SUNOS4
233         struct exec *e;
234          
235         e = (struct exec *) lm->lm_addr;
236         GC_add_roots_inner(
237                     ((char *) (N_DATOFF(*e) + lm->lm_addr)),
238                     ((char *) (N_BSSADDR(*e) + e->a_bss + lm->lm_addr)),
239                     TRUE);
240 #     endif
241 #     ifdef SUNOS5DL
242         ElfW(Ehdr) * e;
243         ElfW(Phdr) * p;
244         unsigned long offset;
245         char * start;
246         register int i;
247         
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 ) {
253             case PT_LOAD:
254               {
255                 if( !(p->p_flags & PF_W) ) break;
256                 start = ((char *)(p->p_vaddr)) + offset;
257                 GC_add_roots_inner(
258                   start,
259                   start + p->p_memsz,
260                   TRUE
261                 );
262               }
263               break;
264             default:
265               break;
266           }
267         }
268 #     endif
269     }
270 #   ifdef SUNOS4
271       {
272         static ptr_t common_start = 0;
273         ptr_t common_end;
274         extern ptr_t GC_find_limit();
275         
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);
280         }
281       }
282 #   endif
283 }
284
285 # endif /* !USE_PROC ... */
286 # endif /* SUNOS */
287
288 #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
289     (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
290     (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
291
292
293 #ifdef USE_PROC_FOR_LIBRARIES
294
295 #include <string.h>
296
297 #include <sys/stat.h>
298 #include <fcntl.h>
299 #include <unistd.h>
300
301 #define MAPS_BUF_SIZE (32*1024)
302
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.                                          */
306
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 *));
310         /* From os_dep.c        */
311
312 word GC_register_map_entries(char *maps)
313 {
314     char prot_buf[5];
315     char *buf_ptr = maps;
316     int count;
317     word start, end;
318     unsigned int maj_dev;
319     word least_ha, greatest_ha;
320     unsigned i;
321     word datastart = (word)(DATASTART);
322
323     /* Compute heap bounds. FIXME: Should be done by add_to_heap?       */
324         least_ha = (word)(-1);
325         greatest_ha = 0;
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;
331         }
332         if (greatest_ha < (word)GC_scratch_last_end_ptr)
333             greatest_ha = (word)GC_scratch_last_end_ptr; 
334
335     for (;;) {
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      */
341             /* accounted for.                                   */
342             if (start <= (word)GC_stackbottom && end >= (word)GC_stackbottom) {
343                 /* Stack mapping; discard       */
344                 continue;
345             }
346 #           ifdef THREADS
347               if (GC_segment_is_thread_stack(start, end)) continue;
348 #           endif
349             /* We no longer exclude the main data segment.              */
350             if (start < least_ha && end > least_ha) {
351                 end = least_ha;
352             }
353             if (start < greatest_ha && end > greatest_ha) {
354                 start = greatest_ha;
355             }
356             if (start >= least_ha && end <= greatest_ha) continue;
357             GC_add_roots_inner((char *)start, (char *)end, TRUE);
358         }
359     }
360     return 1;
361 }
362
363 void GC_register_dynamic_libraries()
364 {
365    if (!GC_apply_to_maps(GC_register_map_entries))
366        ABORT("Failed to read /proc for library registration.");
367 }
368
369 /* We now take care of the main data segment ourselves: */
370 GC_bool GC_register_main_static_data()
371 {
372   return FALSE;
373 }
374   
375 # define HAVE_REGISTER_MAIN_STATIC_DATA
376
377 #endif /* USE_PROC_FOR_LIBRARIES */
378
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.     */
383
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))) 
387
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
392
393 static int GC_register_dynlib_callback(info, size, ptr)
394      struct dl_phdr_info * info;
395      size_t size;
396      void * ptr;
397 {
398   const ElfW(Phdr) * p;
399   char * start;
400   register int i;
401
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))
405     return -1;
406
407   p = info->dlpi_phdr;
408   for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
409     switch( p->p_type ) {
410       case PT_LOAD:
411         {
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);
415         }
416       break;
417       default:
418         break;
419     }
420   }
421
422   * (int *)ptr = 1;     /* Signal that we were called */
423   return 0;
424 }     
425
426 /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
427
428 #pragma weak dl_iterate_phdr
429
430 GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
431 {
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);
441 #       endif
442     }
443
444     return TRUE;
445   } else {
446     return FALSE;
447   }
448 }
449
450 /* Do we need to separately register the main static data segment? */
451 GC_bool GC_register_main_static_data()
452 {
453   return (dl_iterate_phdr == 0);
454 }
455
456 #define HAVE_REGISTER_MAIN_STATIC_DATA
457
458 # else /* !LINUX || version(glibc) < 2.2.4 */
459
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 */
464
465 /* This doesn't necessarily work in all cases, e.g. with preloaded
466  * dynamic libraries.                                           */
467
468 #if defined(NETBSD)
469 #  include <sys/exec_elf.h>
470 /* for compatibility with 1.4.x */
471 #  ifndef DT_DEBUG
472 #  define DT_DEBUG     21
473 #  endif
474 #  ifndef PT_LOAD
475 #  define PT_LOAD      1
476 #  endif
477 #  ifndef PF_W
478 #  define PF_W         2
479 #  endif
480 #else
481 #  include <elf.h>
482 #endif
483 #include <link.h>
484
485 # endif
486
487 #ifdef __GNUC__
488 # pragma weak _DYNAMIC
489 #endif
490 extern ElfW(Dyn) _DYNAMIC[];
491
492 static struct link_map *
493 GC_FirstDLOpenedLinkMap()
494 {
495     ElfW(Dyn) *dp;
496     static struct link_map *cachedResult = 0;
497
498     if( _DYNAMIC == 0) {
499         return(0);
500     }
501     if( cachedResult == 0 ) {
502         int tag;
503         for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
504             /* FIXME: The DT_DEBUG header is not mandated by the        */
505             /* ELF spec.  This code appears to be dependent on          */
506             /* idiosynchracies of older GNU tool chains.  If this code  */
507             /* fails for you, the real problem is probably that it is   */
508             /* being used at all.  You should be getting the            */
509             /* dl_iterate_phdr version.                                 */
510             if( tag == DT_DEBUG ) {
511                 struct link_map *lm
512                         = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
513                 if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
514                 break;
515             }
516         }
517     }
518     return cachedResult;
519 }
520
521
522 void GC_register_dynamic_libraries()
523 {
524   struct link_map *lm;
525   
526
527 # ifdef HAVE_DL_ITERATE_PHDR
528     if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
529         return;
530     }
531 # endif
532   lm = GC_FirstDLOpenedLinkMap();
533   for (lm = GC_FirstDLOpenedLinkMap();
534        lm != (struct link_map *) 0;  lm = lm->l_next)
535     {
536         ElfW(Ehdr) * e;
537         ElfW(Phdr) * p;
538         unsigned long offset;
539         char * start;
540         register int i;
541         
542         e = (ElfW(Ehdr) *) lm->l_addr;
543         p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
544         offset = ((unsigned long)(lm->l_addr));
545         for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
546           switch( p->p_type ) {
547             case PT_LOAD:
548               {
549                 if( !(p->p_flags & PF_W) ) break;
550                 start = ((char *)(p->p_vaddr)) + offset;
551                 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
552               }
553               break;
554             default:
555               break;
556           }
557         }
558     }
559 }
560
561 #endif /* !USE_PROC_FOR_LIBRARIES */
562
563 #endif /* LINUX */
564
565 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
566
567 #include <sys/procfs.h>
568 #include <sys/stat.h>
569 #include <fcntl.h>
570 #include <elf.h>
571 #include <errno.h>
572 #include <signal.h>  /* Only for the following test. */
573 #ifndef _sigargs
574 # define IRIX6
575 #endif
576
577 extern void * GC_roots_present();
578         /* The type is a lie, since the real type doesn't make sense here, */
579         /* and we only test for NULL.                                      */
580
581
582 /* We use /proc to track down all parts of the address space that are   */
583 /* mapped by the process, and throw out regions we know we shouldn't    */
584 /* worry about.  This may also work under other SVR4 variants.          */
585 void GC_register_dynamic_libraries()
586 {
587     static int fd = -1;
588     char buf[30];
589     static prmap_t * addr_map = 0;
590     static int current_sz = 0;  /* Number of records currently in addr_map */
591     static int needed_sz;       /* Required size of addr_map            */
592     register int i;
593     register long flags;
594     register ptr_t start;
595     register ptr_t limit;
596     ptr_t heap_start = (ptr_t)HEAP_START;
597     ptr_t heap_end = heap_start;
598
599 #   ifdef SUNOS5DL
600 #     define MA_PHYS 0
601 #   endif /* SUNOS5DL */
602
603     if (fd < 0) {
604       sprintf(buf, "/proc/%d", getpid());
605         /* The above generates a lint complaint, since pid_t varies.    */
606         /* It's unclear how to improve this.                            */
607       fd = open(buf, O_RDONLY);
608       if (fd < 0) {
609         ABORT("/proc open failed");
610       }
611     }
612     if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
613         GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
614         ABORT("/proc PIOCNMAP ioctl failed");
615     }
616     if (needed_sz >= current_sz) {
617         current_sz = needed_sz * 2 + 1;
618                         /* Expansion, plus room for 0 record */
619         addr_map = (prmap_t *)GC_scratch_alloc((word)
620                                                 (current_sz * sizeof(prmap_t)));
621     }
622     if (ioctl(fd, PIOCMAP, addr_map) < 0) {
623         GC_err_printf4("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
624                         fd, errno, needed_sz, addr_map);
625         ABORT("/proc PIOCMAP ioctl failed");
626     };
627     if (GC_n_heap_sects > 0) {
628         heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
629                         + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
630         if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr; 
631     }
632     for (i = 0; i < needed_sz; i++) {
633         flags = addr_map[i].pr_mflags;
634         if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
635                       | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
636         if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
637             goto irrelevant;
638           /* The latter test is empirically useless in very old Irix    */
639           /* versions.  Other than the                                  */
640           /* main data and stack segments, everything appears to be     */
641           /* mapped readable, writable, executable, and shared(!!).     */
642           /* This makes no sense to me. - HB                            */
643         start = (ptr_t)(addr_map[i].pr_vaddr);
644         if (GC_roots_present(start)) goto irrelevant;
645         if (start < heap_end && start >= heap_start)
646                 goto irrelevant;
647 #       ifdef MMAP_STACKS
648           if (GC_is_thread_stack(start)) goto irrelevant;
649 #       endif /* MMAP_STACKS */
650
651         limit = start + addr_map[i].pr_size;
652         /* The following seemed to be necessary for very old versions   */
653         /* of Irix, but it has been reported to discard relevant        */
654         /* segments under Irix 6.5.                                     */
655 #       ifndef IRIX6
656           if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
657             /* Discard text segments, i.e. 0-offset mappings against    */
658             /* executable files which appear to have ELF headers.       */
659             caddr_t arg;
660             int obj;
661 #           define MAP_IRR_SZ 10
662             static ptr_t map_irr[MAP_IRR_SZ];
663                                         /* Known irrelevant map entries */
664             static int n_irr = 0;
665             struct stat buf;
666             register int i;
667             
668             for (i = 0; i < n_irr; i++) {
669                 if (map_irr[i] == start) goto irrelevant;
670             }
671             arg = (caddr_t)start;
672             obj = ioctl(fd, PIOCOPENM, &arg);
673             if (obj >= 0) {
674                 fstat(obj, &buf);
675                 close(obj);
676                 if ((buf.st_mode & 0111) != 0) {
677                     if (n_irr < MAP_IRR_SZ) {
678                         map_irr[n_irr++] = start;
679                     }
680                     goto irrelevant;
681                 }
682             }
683           }
684 #       endif /* !IRIX6 */
685         GC_add_roots_inner(start, limit, TRUE);
686       irrelevant: ;
687     }
688     /* Dont keep cached descriptor, for now.  Some kernels don't like us */
689     /* to keep a /proc file descriptor around during kill -9.            */
690         if (close(fd) < 0) ABORT("Couldnt close /proc file");
691         fd = -1;
692 }
693
694 # endif /* USE_PROC || IRIX5 */
695
696 # if defined(MSWIN32) || defined(MSWINCE)
697
698 # define WIN32_LEAN_AND_MEAN
699 # define NOSERVICE
700 # include <windows.h>
701 # include <stdlib.h>
702
703   /* We traverse the entire address space and register all segments     */
704   /* that could possibly have been written to.                          */
705   
706   extern GC_bool GC_is_heap_base (ptr_t p);
707
708 # ifdef GC_WIN32_THREADS
709     extern void GC_get_next_stack(char *start, char **lo, char **hi);
710     void GC_cond_add_roots(char *base, char * limit)
711     {
712       char * curr_base = base;
713       char * next_stack_lo;
714       char * next_stack_hi;
715    
716       if (base == limit) return;
717       for(;;) {
718           GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
719           if (next_stack_lo >= limit) break;
720           GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
721           curr_base = next_stack_hi;
722       }
723       if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
724     }
725 # else
726     void GC_cond_add_roots(char *base, char * limit)
727     {
728       char dummy;
729       char * stack_top
730          = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
731       if (base == limit) return;
732       if (limit > stack_top && base < GC_stackbottom) {
733           /* Part of the stack; ignore it. */
734           return;
735       }
736       GC_add_roots_inner(base, limit, TRUE);
737     }
738 # endif
739
740 # ifdef MSWINCE
741   /* Do we need to separately register the main static data segment? */
742   GC_bool GC_register_main_static_data()
743   {
744     return FALSE;
745   }
746 # else /* win32 */
747   extern GC_bool GC_no_win32_dlls;
748
749   GC_bool GC_register_main_static_data()
750   {
751     return GC_no_win32_dlls;
752   }
753 # endif /* win32 */
754   
755 # define HAVE_REGISTER_MAIN_STATIC_DATA
756
757   /* The frame buffer testing code is dead in this version.     */
758   /* We leave it here temporarily in case the switch to just    */
759   /* testing for MEM_IMAGE sections causes un expected          */
760   /* problems.                                                  */
761   GC_bool GC_warn_fb = TRUE;    /* Warn about traced likely     */
762                                 /* graphics memory.             */
763   GC_bool GC_disallow_ignore_fb = FALSE;
764   int GC_ignore_fb_mb;  /* Ignore mappings bigger than the      */
765                         /* specified number of MB.              */
766   GC_bool GC_ignore_fb = FALSE; /* Enable frame buffer  */
767                                 /* checking.            */
768   
769   /* Issue warning if tracing apparent framebuffer.             */
770   /* This limits us to one warning, and it's a back door to     */
771   /* disable that.                                              */
772  
773   /* Should [start, start+len) be treated as a frame buffer     */
774   /* and ignored?                                               */
775   /* Unfortunately, we currently are not quite sure how to tell */
776   /* this automatically, and rely largely on user input.        */
777   /* We expect that any mapping with type MEM_MAPPED (which     */
778   /* apparently excludes library data sections) can be safely   */
779   /* ignored.  But we're too chicken to do that in this         */
780   /* version.                                                   */
781   /* Based on a very limited sample, it appears that:           */
782   /*    - Frame buffer mappings appear as mappings of large     */
783   /*      length, usually a bit less than a power of two.       */
784   /*    - The definition of "a bit less" in the above cannot    */
785   /*      be made more precise.                                 */
786   /*    - Have a starting address at best 64K aligned.          */
787   /*    - Have type == MEM_MAPPED.                              */
788   static GC_bool is_frame_buffer(ptr_t start, size_t len, DWORD tp)
789   {
790     static GC_bool initialized = FALSE;
791 #   define MB (1024*1024)
792 #   define DEFAULT_FB_MB 15
793 #   define MIN_FB_MB 3
794
795     if (GC_disallow_ignore_fb || tp != MEM_MAPPED) return FALSE;
796     if (!initialized) {
797       char * ignore_fb_string =  GETENV("GC_IGNORE_FB");
798
799       if (0 != ignore_fb_string) {
800         while (*ignore_fb_string == ' ' || *ignore_fb_string == '\t')
801           ++ignore_fb_string;
802         if (*ignore_fb_string == '\0') {
803           GC_ignore_fb_mb = DEFAULT_FB_MB;
804         } else {
805           GC_ignore_fb_mb = atoi(ignore_fb_string);
806           if (GC_ignore_fb_mb < MIN_FB_MB) {
807             WARN("Bad GC_IGNORE_FB value.  Using %ld\n", DEFAULT_FB_MB);
808             GC_ignore_fb_mb = DEFAULT_FB_MB;
809           }
810         }
811         GC_ignore_fb = TRUE;
812       } else {
813         GC_ignore_fb_mb = DEFAULT_FB_MB;  /* For warning */
814       }
815       initialized = TRUE;
816     }
817     if (len >= ((size_t)GC_ignore_fb_mb << 20)) {
818       if (GC_ignore_fb) {
819         return TRUE;
820       } else {
821         if (GC_warn_fb) {
822           WARN("Possible frame buffer mapping at 0x%lx: \n"
823                "\tConsider setting GC_IGNORE_FB to improve performance.\n",
824                start);
825           GC_warn_fb = FALSE;
826         }
827         return FALSE;
828       }
829     } else {
830       return FALSE;
831     }
832   }
833
834 # ifdef DEBUG_VIRTUALQUERY
835   void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
836   {
837     GC_printf4("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
838                buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
839                buf -> RegionSize);
840     GC_printf4("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
841                "Type = %lx\n",
842                buf -> AllocationProtect, buf -> State, buf -> Protect,
843                buf -> Type);
844   }
845 # endif /* DEBUG_VIRTUALQUERY */
846
847   void GC_register_dynamic_libraries()
848   {
849     MEMORY_BASIC_INFORMATION buf;
850     DWORD result;
851     DWORD protect;
852     LPVOID p;
853     char * base;
854     char * limit, * new_limit;
855
856 #   ifdef MSWIN32
857       if (GC_no_win32_dlls) return;
858 #   endif
859     base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
860 #   if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
861     /* Only the first 32 MB of address space belongs to the current process */
862     while (p < (LPVOID)0x02000000) {
863         result = VirtualQuery(p, &buf, sizeof(buf));
864         if (result == 0) {
865             /* Page is free; advance to the next possible allocation base */
866             new_limit = (char *)
867                 (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
868                  & ~(GC_sysinfo.dwAllocationGranularity-1));
869         } else
870 #   else
871     while (p < GC_sysinfo.lpMaximumApplicationAddress) {
872         result = VirtualQuery(p, &buf, sizeof(buf));
873 #   endif
874         {
875             if (result != sizeof(buf)) {
876                 ABORT("Weird VirtualQuery result");
877             }
878             new_limit = (char *)p + buf.RegionSize;
879             protect = buf.Protect;
880             if (buf.State == MEM_COMMIT
881                 && (protect == PAGE_EXECUTE_READWRITE
882                     || protect == PAGE_READWRITE)
883                 && !GC_is_heap_base(buf.AllocationBase)
884                 /* This used to check for
885                  * !is_frame_buffer(p, buf.RegionSize, buf.Type)
886                  * instead of just checking for MEM_IMAGE.
887                  * If something breaks, change it back. */
888                 && buf.Type == MEM_IMAGE) {  
889 #               ifdef DEBUG_VIRTUALQUERY
890                   GC_dump_meminfo(&buf);
891 #               endif
892                 if ((char *)p != limit) {
893                     GC_cond_add_roots(base, limit);
894                     base = p;
895                 }
896                 limit = new_limit;
897             }
898         }
899         if (p > (LPVOID)new_limit /* overflow */) break;
900         p = (LPVOID)new_limit;
901     }
902     GC_cond_add_roots(base, limit);
903   }
904
905 #endif /* MSWIN32 || MSWINCE */
906   
907 #if defined(ALPHA) && defined(OSF1)
908
909 #include <loader.h>
910
911 void GC_register_dynamic_libraries()
912 {
913   int status;
914   ldr_process_t mypid;
915
916   /* module */
917     ldr_module_t moduleid = LDR_NULL_MODULE;
918     ldr_module_info_t moduleinfo;
919     size_t moduleinfosize = sizeof(moduleinfo);
920     size_t modulereturnsize;    
921
922   /* region */
923     ldr_region_t region; 
924     ldr_region_info_t regioninfo;
925     size_t regioninfosize = sizeof(regioninfo);
926     size_t regionreturnsize;
927
928   /* Obtain id of this process */
929     mypid = ldr_my_process();
930   
931   /* For each module */
932     while (TRUE) {
933
934       /* Get the next (first) module */
935         status = ldr_next_module(mypid, &moduleid);
936
937       /* Any more modules? */
938         if (moduleid == LDR_NULL_MODULE)
939             break;    /* No more modules */
940
941       /* Check status AFTER checking moduleid because */
942       /* of a bug in the non-shared ldr_next_module stub */
943         if (status != 0 ) {
944             GC_printf1("dynamic_load: status = %ld\n", (long)status);
945             {
946                 extern char *sys_errlist[];
947                 extern int sys_nerr;
948                 extern int errno;
949                 if (errno <= sys_nerr) {
950                     GC_printf1("dynamic_load: %s\n", (long)sys_errlist[errno]);
951                } else {
952                     GC_printf1("dynamic_load: %d\n", (long)errno);
953                 }
954         }
955             ABORT("ldr_next_module failed");
956          }
957
958       /* Get the module information */
959         status = ldr_inq_module(mypid, moduleid, &moduleinfo,
960                                 moduleinfosize, &modulereturnsize); 
961         if (status != 0 )
962             ABORT("ldr_inq_module failed");
963
964       /* is module for the main program (i.e. nonshared portion)? */
965           if (moduleinfo.lmi_flags & LDR_MAIN)
966               continue;    /* skip the main module */
967
968 #     ifdef VERBOSE
969           GC_printf("---Module---\n");
970           GC_printf("Module ID            = %16ld\n", moduleinfo.lmi_modid);
971           GC_printf("Count of regions     = %16d\n", moduleinfo.lmi_nregion);
972           GC_printf("flags for module     = %16lx\n", moduleinfo.lmi_flags); 
973           GC_printf("pathname of module   = \"%s\"\n", moduleinfo.lmi_name);
974 #     endif
975
976       /* For each region in this module */
977         for (region = 0; region < moduleinfo.lmi_nregion; region++) {
978
979           /* Get the region information */
980             status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
981                                     regioninfosize, &regionreturnsize);
982             if (status != 0 )
983                 ABORT("ldr_inq_region failed");
984
985           /* only process writable (data) regions */
986             if (! (regioninfo.lri_prot & LDR_W))
987                 continue;
988
989 #         ifdef VERBOSE
990               GC_printf("--- Region ---\n");
991               GC_printf("Region number    = %16ld\n",
992                         regioninfo.lri_region_no);
993               GC_printf("Protection flags = %016x\n",  regioninfo.lri_prot);
994               GC_printf("Virtual address  = %16p\n",   regioninfo.lri_vaddr);
995               GC_printf("Mapped address   = %16p\n",   regioninfo.lri_mapaddr);
996               GC_printf("Region size      = %16ld\n",  regioninfo.lri_size);
997               GC_printf("Region name      = \"%s\"\n", regioninfo.lri_name);
998 #         endif
999
1000           /* register region as a garbage collection root */
1001             GC_add_roots_inner (
1002                 (char *)regioninfo.lri_mapaddr,
1003                 (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
1004                 TRUE);
1005
1006         }
1007     }
1008 }
1009 #endif
1010
1011 #if defined(HPUX)
1012
1013 #include <errno.h>
1014 #include <dl.h>
1015
1016 extern int errno;
1017 extern char *sys_errlist[];
1018 extern int sys_nerr;
1019
1020 void GC_register_dynamic_libraries()
1021 {
1022   int status;
1023   int index = 1; /* Ordinal position in shared library search list */
1024   struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
1025
1026   /* For each dynamic library loaded */
1027     while (TRUE) {
1028
1029       /* Get info about next shared library */
1030         status = shl_get(index, &shl_desc);
1031
1032       /* Check if this is the end of the list or if some error occured */
1033         if (status != 0) {
1034 #        ifdef GC_HPUX_THREADS
1035            /* I've seen errno values of 0.  The man page is not clear   */
1036            /* as to whether errno should get set on a -1 return.        */
1037            break;
1038 #        else
1039           if (errno == EINVAL) {
1040               break; /* Moved past end of shared library list --> finished */
1041           } else {
1042               if (errno <= sys_nerr) {
1043                     GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]);
1044               } else {
1045                     GC_printf1("dynamic_load: %d\n", (long) errno);
1046               }
1047               ABORT("shl_get failed");
1048           }
1049 #        endif
1050         }
1051
1052 #     ifdef VERBOSE
1053           GC_printf0("---Shared library---\n");
1054           GC_printf1("\tfilename        = \"%s\"\n", shl_desc->filename);
1055           GC_printf1("\tindex           = %d\n", index);
1056           GC_printf1("\thandle          = %08x\n",
1057                                         (unsigned long) shl_desc->handle);
1058           GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart);
1059           GC_printf1("\ttext seg. end   = %08x\n", shl_desc->tend);
1060           GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart);
1061           GC_printf1("\tdata seg. end   = %08x\n", shl_desc->dend);
1062           GC_printf1("\tref. count      = %lu\n", shl_desc->ref_count);
1063 #     endif
1064
1065       /* register shared library's data segment as a garbage collection root */
1066         GC_add_roots_inner((char *) shl_desc->dstart,
1067                            (char *) shl_desc->dend, TRUE);
1068
1069         index++;
1070     }
1071 }
1072 #endif /* HPUX */
1073
1074 #ifdef RS6000
1075 #pragma alloca
1076 #include <sys/ldr.h>
1077 #include <sys/errno.h>
1078 void GC_register_dynamic_libraries()
1079 {
1080         int len;
1081         char *ldibuf;
1082         int ldibuflen;
1083         struct ld_info *ldi;
1084
1085         ldibuf = alloca(ldibuflen = 8192);
1086
1087         while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
1088                 if (errno != ENOMEM) {
1089                         ABORT("loadquery failed");
1090                 }
1091                 ldibuf = alloca(ldibuflen *= 2);
1092         }
1093
1094         ldi = (struct ld_info *)ldibuf;
1095         while (ldi) {
1096                 len = ldi->ldinfo_next;
1097                 GC_add_roots_inner(
1098                                 ldi->ldinfo_dataorg,
1099                                 (ptr_t)(unsigned long)ldi->ldinfo_dataorg
1100                                 + ldi->ldinfo_datasize,
1101                                 TRUE);
1102                 ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
1103         }
1104 }
1105 #endif /* RS6000 */
1106
1107 #ifdef DARWIN
1108
1109 /* __private_extern__ hack required for pre-3.4 gcc versions.   */
1110 #ifndef __private_extern__
1111 # define __private_extern__ extern
1112 # include <mach-o/dyld.h>
1113 # undef __private_extern__
1114 #else
1115 # include <mach-o/dyld.h>
1116 #endif
1117 #include <mach-o/getsect.h>
1118
1119 /*#define DARWIN_DEBUG*/
1120
1121 const static struct { 
1122         const char *seg;
1123         const char *sect;
1124 } GC_dyld_sections[] = {
1125         { SEG_DATA, SECT_DATA },
1126         { SEG_DATA, SECT_BSS },
1127         { SEG_DATA, SECT_COMMON }
1128 };
1129     
1130 #ifdef DARWIN_DEBUG
1131 static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
1132     unsigned long i,c;
1133     c = _dyld_image_count();
1134     for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
1135         return _dyld_get_image_name(i);
1136     return NULL;
1137 }
1138 #endif
1139         
1140 /* This should never be called by a thread holding the lock */
1141 static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
1142     unsigned long start,end,i;
1143     const struct section *sec;
1144     if (GC_no_dls) return;
1145     for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1146         sec = getsectbynamefromheader(
1147             hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1148         if(sec == NULL || sec->size == 0) continue;
1149         start = slide + sec->addr;
1150         end = start + sec->size;
1151 #       ifdef DARWIN_DEBUG
1152             GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
1153                 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1154 #       endif
1155         GC_add_roots((char*)start,(char*)end);
1156     }
1157 #   ifdef DARWIN_DEBUG
1158         GC_print_static_roots();
1159 #   endif
1160 }
1161
1162 /* This should never be called by a thread holding the lock */
1163 static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
1164     unsigned long start,end,i;
1165     const struct section *sec;
1166     for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1167         sec = getsectbynamefromheader(
1168             hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1169         if(sec == NULL || sec->size == 0) continue;
1170         start = slide + sec->addr;
1171         end = start + sec->size;
1172 #       ifdef DARWIN_DEBUG
1173             GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
1174                 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1175 #               endif
1176         GC_remove_roots((char*)start,(char*)end);
1177     }
1178 #   ifdef DARWIN_DEBUG
1179         GC_print_static_roots();
1180 #   endif
1181 }
1182
1183 void GC_register_dynamic_libraries() {
1184     /* Currently does nothing. The callbacks are setup by GC_init_dyld() 
1185     The dyld library takes it from there. */
1186 }
1187
1188 /* The _dyld_* functions have an internal lock so no _dyld functions
1189    can be called while the world is stopped without the risk of a deadlock.
1190    Because of this we MUST setup callbacks BEFORE we ever stop the world.
1191    This should be called BEFORE any thread in created and WITHOUT the
1192    allocation lock held. */
1193    
1194 void GC_init_dyld() {
1195   static GC_bool initialized = FALSE;
1196   char *bind_fully_env = NULL;
1197   
1198   if(initialized) return;
1199   
1200 #   ifdef DARWIN_DEBUG
1201   GC_printf0("Registering dyld callbacks...\n");
1202 #   endif
1203   
1204   /* Apple's Documentation:
1205      When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1206      calls the specified callback (func) once for each of the images that is
1207      currently loaded into the program. When a new image is added to the program,
1208      your callback is called again with the mach_header for the new image, and the      
1209      virtual memory slide amount of the new image. 
1210      
1211      This WILL properly register already linked libraries and libraries 
1212      linked in the future
1213   */
1214   
1215     _dyld_register_func_for_add_image(GC_dyld_image_add);
1216     _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1217
1218     /* Set this early to avoid reentrancy issues. */
1219     initialized = TRUE;
1220
1221     bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
1222     
1223     if (bind_fully_env == NULL) {
1224 #   ifdef DARWIN_DEBUG
1225       GC_printf0("Forcing full bind of GC code...\n");
1226 #   endif
1227       
1228       if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1229         GC_abort("_dyld_bind_fully_image_containing_address failed");
1230     }
1231
1232 }
1233
1234 #define HAVE_REGISTER_MAIN_STATIC_DATA
1235 GC_bool GC_register_main_static_data()
1236 {
1237   /* Already done through dyld callbacks */
1238   return FALSE;
1239 }
1240
1241 #endif /* DARWIN */
1242
1243 #else /* !DYNAMIC_LOADING */
1244
1245 #ifdef PCR
1246
1247 #   include "il/PCR_IL.h"
1248 #   include "th/PCR_ThCtl.h"
1249 #   include "mm/PCR_MM.h"
1250
1251 void GC_register_dynamic_libraries()
1252 {
1253     /* Add new static data areas of dynamically loaded modules. */
1254         {
1255           PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1256           PCR_IL_LoadedSegment * q;
1257           
1258           /* Skip uncommited files */
1259           while (p != NIL && !(p -> lf_commitPoint)) {
1260               /* The loading of this file has not yet been committed    */
1261               /* Hence its description could be inconsistent.           */
1262               /* Furthermore, it hasn't yet been run.  Hence its data   */
1263               /* segments can't possibly reference heap allocated       */
1264               /* objects.                                               */
1265               p = p -> lf_prev;
1266           }
1267           for (; p != NIL; p = p -> lf_prev) {
1268             for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1269               if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1270                   == PCR_IL_SegFlags_Traced_on) {
1271                 GC_add_roots_inner
1272                         ((char *)(q -> ls_addr), 
1273                          (char *)(q -> ls_addr) + q -> ls_bytes,
1274                          TRUE);
1275               }
1276             }
1277           }
1278         }
1279 }
1280
1281
1282 #else /* !PCR */
1283
1284 void GC_register_dynamic_libraries(){}
1285
1286 int GC_no_dynamic_loading;
1287
1288 #endif /* !PCR */
1289
1290 #endif /* !DYNAMIC_LOADING */
1291
1292 #ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1293
1294 /* Do we need to separately register the main static data segment? */
1295 GC_bool GC_register_main_static_data()
1296 {
1297   return TRUE;
1298 }
1299 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
1300