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