2010-05-31 Geoff Norton <gnorton@novell.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         if (e == NULL)
549           continue;
550
551         p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
552         offset = ((unsigned long)(lm->l_addr));
553         for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
554           switch( p->p_type ) {
555             case PT_LOAD:
556               {
557                 if( !(p->p_flags & PF_W) ) break;
558                 start = ((char *)(p->p_vaddr)) + offset;
559                 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
560               }
561               break;
562             default:
563               break;
564           }
565         }
566     }
567 }
568
569 #endif /* !USE_PROC_FOR_LIBRARIES */
570
571 #endif /* LINUX */
572
573 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
574
575 #include <sys/procfs.h>
576 #include <sys/stat.h>
577 #include <fcntl.h>
578 #include <elf.h>
579 #include <errno.h>
580 #include <signal.h>  /* Only for the following test. */
581 #ifndef _sigargs
582 # define IRIX6
583 #endif
584
585 extern void * GC_roots_present();
586         /* The type is a lie, since the real type doesn't make sense here, */
587         /* and we only test for NULL.                                      */
588
589
590 /* We use /proc to track down all parts of the address space that are   */
591 /* mapped by the process, and throw out regions we know we shouldn't    */
592 /* worry about.  This may also work under other SVR4 variants.          */
593 void GC_register_dynamic_libraries()
594 {
595     static int fd = -1;
596     char buf[30];
597     static prmap_t * addr_map = 0;
598     static int current_sz = 0;  /* Number of records currently in addr_map */
599     static int needed_sz;       /* Required size of addr_map            */
600     register int i;
601     register long flags;
602     register ptr_t start;
603     register ptr_t limit;
604     ptr_t heap_start = (ptr_t)HEAP_START;
605     ptr_t heap_end = heap_start;
606
607 #   ifdef SUNOS5DL
608 #     define MA_PHYS 0
609 #   endif /* SUNOS5DL */
610
611     if (fd < 0) {
612       sprintf(buf, "/proc/%d", getpid());
613         /* The above generates a lint complaint, since pid_t varies.    */
614         /* It's unclear how to improve this.                            */
615       fd = open(buf, O_RDONLY);
616       if (fd < 0) {
617         ABORT("/proc open failed");
618       }
619     }
620     if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
621         GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
622         ABORT("/proc PIOCNMAP ioctl failed");
623     }
624     if (needed_sz >= current_sz) {
625         current_sz = needed_sz * 2 + 1;
626                         /* Expansion, plus room for 0 record */
627         addr_map = (prmap_t *)GC_scratch_alloc((word)
628                                                 (current_sz * sizeof(prmap_t)));
629     }
630     if (ioctl(fd, PIOCMAP, addr_map) < 0) {
631         GC_err_printf4("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
632                         fd, errno, needed_sz, addr_map);
633         ABORT("/proc PIOCMAP ioctl failed");
634     };
635     if (GC_n_heap_sects > 0) {
636         heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
637                         + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
638         if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr; 
639     }
640     for (i = 0; i < needed_sz; i++) {
641         flags = addr_map[i].pr_mflags;
642         if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
643                       | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
644         if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
645             goto irrelevant;
646           /* The latter test is empirically useless in very old Irix    */
647           /* versions.  Other than the                                  */
648           /* main data and stack segments, everything appears to be     */
649           /* mapped readable, writable, executable, and shared(!!).     */
650           /* This makes no sense to me. - HB                            */
651         start = (ptr_t)(addr_map[i].pr_vaddr);
652         if (GC_roots_present(start)) goto irrelevant;
653         if (start < heap_end && start >= heap_start)
654                 goto irrelevant;
655 #       ifdef MMAP_STACKS
656           if (GC_is_thread_stack(start)) goto irrelevant;
657 #       endif /* MMAP_STACKS */
658
659         limit = start + addr_map[i].pr_size;
660         /* The following seemed to be necessary for very old versions   */
661         /* of Irix, but it has been reported to discard relevant        */
662         /* segments under Irix 6.5.                                     */
663 #       ifndef IRIX6
664           if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
665             /* Discard text segments, i.e. 0-offset mappings against    */
666             /* executable files which appear to have ELF headers.       */
667             caddr_t arg;
668             int obj;
669 #           define MAP_IRR_SZ 10
670             static ptr_t map_irr[MAP_IRR_SZ];
671                                         /* Known irrelevant map entries */
672             static int n_irr = 0;
673             struct stat buf;
674             register int i;
675             
676             for (i = 0; i < n_irr; i++) {
677                 if (map_irr[i] == start) goto irrelevant;
678             }
679             arg = (caddr_t)start;
680             obj = ioctl(fd, PIOCOPENM, &arg);
681             if (obj >= 0) {
682                 fstat(obj, &buf);
683                 close(obj);
684                 if ((buf.st_mode & 0111) != 0) {
685                     if (n_irr < MAP_IRR_SZ) {
686                         map_irr[n_irr++] = start;
687                     }
688                     goto irrelevant;
689                 }
690             }
691           }
692 #       endif /* !IRIX6 */
693         GC_add_roots_inner(start, limit, TRUE);
694       irrelevant: ;
695     }
696     /* Dont keep cached descriptor, for now.  Some kernels don't like us */
697     /* to keep a /proc file descriptor around during kill -9.            */
698         if (close(fd) < 0) ABORT("Couldnt close /proc file");
699         fd = -1;
700 }
701
702 # endif /* USE_PROC || IRIX5 */
703
704 # if defined(MSWIN32) || defined(MSWINCE)
705
706 # define WIN32_LEAN_AND_MEAN
707 # define NOSERVICE
708 # include <windows.h>
709 # include <stdlib.h>
710
711   /* We traverse the entire address space and register all segments     */
712   /* that could possibly have been written to.                          */
713   
714   extern GC_bool GC_is_heap_base (ptr_t p);
715
716 # ifdef GC_WIN32_THREADS
717     extern void GC_get_next_stack(char *start, char **lo, char **hi);
718     void GC_cond_add_roots(char *base, char * limit)
719     {
720       char * curr_base = base;
721       char * next_stack_lo;
722       char * next_stack_hi;
723    
724       if (base == limit) return;
725       for(;;) {
726           GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
727           if (next_stack_lo >= limit) break;
728           GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
729           curr_base = next_stack_hi;
730       }
731       if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
732     }
733 # else
734     void GC_cond_add_roots(char *base, char * limit)
735     {
736       char dummy;
737       char * stack_top
738          = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
739       if (base == limit) return;
740       if (limit > stack_top && base < GC_stackbottom) {
741           /* Part of the stack; ignore it. */
742           return;
743       }
744       GC_add_roots_inner(base, limit, TRUE);
745     }
746 # endif
747
748 # ifdef MSWINCE
749   /* Do we need to separately register the main static data segment? */
750   GC_bool GC_register_main_static_data()
751   {
752     return FALSE;
753   }
754 # else /* win32 */
755   extern GC_bool GC_no_win32_dlls;
756
757   GC_bool GC_register_main_static_data()
758   {
759     return GC_no_win32_dlls;
760   }
761 # endif /* win32 */
762   
763 # define HAVE_REGISTER_MAIN_STATIC_DATA
764
765   /* The frame buffer testing code is dead in this version.     */
766   /* We leave it here temporarily in case the switch to just    */
767   /* testing for MEM_IMAGE sections causes un expected          */
768   /* problems.                                                  */
769   GC_bool GC_warn_fb = TRUE;    /* Warn about traced likely     */
770                                 /* graphics memory.             */
771   GC_bool GC_disallow_ignore_fb = FALSE;
772   int GC_ignore_fb_mb;  /* Ignore mappings bigger than the      */
773                         /* specified number of MB.              */
774   GC_bool GC_ignore_fb = FALSE; /* Enable frame buffer  */
775                                 /* checking.            */
776   
777   /* Issue warning if tracing apparent framebuffer.             */
778   /* This limits us to one warning, and it's a back door to     */
779   /* disable that.                                              */
780  
781   /* Should [start, start+len) be treated as a frame buffer     */
782   /* and ignored?                                               */
783   /* Unfortunately, we currently are not quite sure how to tell */
784   /* this automatically, and rely largely on user input.        */
785   /* We expect that any mapping with type MEM_MAPPED (which     */
786   /* apparently excludes library data sections) can be safely   */
787   /* ignored.  But we're too chicken to do that in this         */
788   /* version.                                                   */
789   /* Based on a very limited sample, it appears that:           */
790   /*    - Frame buffer mappings appear as mappings of large     */
791   /*      length, usually a bit less than a power of two.       */
792   /*    - The definition of "a bit less" in the above cannot    */
793   /*      be made more precise.                                 */
794   /*    - Have a starting address at best 64K aligned.          */
795   /*    - Have type == MEM_MAPPED.                              */
796   static GC_bool is_frame_buffer(ptr_t start, size_t len, DWORD tp)
797   {
798     static GC_bool initialized = FALSE;
799 #   define MB (1024*1024)
800 #   define DEFAULT_FB_MB 15
801 #   define MIN_FB_MB 3
802
803     if (GC_disallow_ignore_fb || tp != MEM_MAPPED) return FALSE;
804     if (!initialized) {
805       char * ignore_fb_string =  GETENV("GC_IGNORE_FB");
806
807       if (0 != ignore_fb_string) {
808         while (*ignore_fb_string == ' ' || *ignore_fb_string == '\t')
809           ++ignore_fb_string;
810         if (*ignore_fb_string == '\0') {
811           GC_ignore_fb_mb = DEFAULT_FB_MB;
812         } else {
813           GC_ignore_fb_mb = atoi(ignore_fb_string);
814           if (GC_ignore_fb_mb < MIN_FB_MB) {
815             WARN("Bad GC_IGNORE_FB value.  Using %ld\n", DEFAULT_FB_MB);
816             GC_ignore_fb_mb = DEFAULT_FB_MB;
817           }
818         }
819         GC_ignore_fb = TRUE;
820       } else {
821         GC_ignore_fb_mb = DEFAULT_FB_MB;  /* For warning */
822       }
823       initialized = TRUE;
824     }
825     if (len >= ((size_t)GC_ignore_fb_mb << 20)) {
826       if (GC_ignore_fb) {
827         return TRUE;
828       } else {
829         if (GC_warn_fb) {
830           WARN("Possible frame buffer mapping at 0x%lx: \n"
831                "\tConsider setting GC_IGNORE_FB to improve performance.\n",
832                start);
833           GC_warn_fb = FALSE;
834         }
835         return FALSE;
836       }
837     } else {
838       return FALSE;
839     }
840   }
841
842 # ifdef DEBUG_VIRTUALQUERY
843   void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
844   {
845     GC_printf4("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
846                buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
847                buf -> RegionSize);
848     GC_printf4("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
849                "Type = %lx\n",
850                buf -> AllocationProtect, buf -> State, buf -> Protect,
851                buf -> Type);
852   }
853 # endif /* DEBUG_VIRTUALQUERY */
854
855   void GC_register_dynamic_libraries()
856   {
857     MEMORY_BASIC_INFORMATION buf;
858     DWORD result;
859     DWORD protect;
860     LPVOID p;
861     char * base;
862     char * limit, * new_limit;
863
864 #   ifdef MSWIN32
865       if (GC_no_win32_dlls) return;
866 #   endif
867     base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
868 #   if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
869     /* Only the first 32 MB of address space belongs to the current process */
870     while (p < (LPVOID)0x02000000) {
871         result = VirtualQuery(p, &buf, sizeof(buf));
872         if (result == 0) {
873             /* Page is free; advance to the next possible allocation base */
874             new_limit = (char *)
875                 (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
876                  & ~(GC_sysinfo.dwAllocationGranularity-1));
877         } else
878 #   else
879     while (p < GC_sysinfo.lpMaximumApplicationAddress) {
880         result = VirtualQuery(p, &buf, sizeof(buf));
881 #   endif
882         {
883             if (result != sizeof(buf)) {
884                 ABORT("Weird VirtualQuery result");
885             }
886             new_limit = (char *)p + buf.RegionSize;
887             protect = buf.Protect;
888             if (buf.State == MEM_COMMIT
889                 && (protect == PAGE_EXECUTE_READWRITE
890                     || protect == PAGE_READWRITE)
891                 && !GC_is_heap_base(buf.AllocationBase)
892                 /* This used to check for
893                  * !is_frame_buffer(p, buf.RegionSize, buf.Type)
894                  * instead of just checking for MEM_IMAGE.
895                  * If something breaks, change it back. */
896                 && buf.Type == MEM_IMAGE) {  
897 #               ifdef DEBUG_VIRTUALQUERY
898                   GC_dump_meminfo(&buf);
899 #               endif
900                 if ((char *)p != limit) {
901                     GC_cond_add_roots(base, limit);
902                     base = p;
903                 }
904                 limit = new_limit;
905             }
906         }
907         if (p > (LPVOID)new_limit /* overflow */) break;
908         p = (LPVOID)new_limit;
909     }
910     GC_cond_add_roots(base, limit);
911   }
912
913 #endif /* MSWIN32 || MSWINCE */
914   
915 #if defined(ALPHA) && defined(OSF1)
916
917 #include <loader.h>
918
919 void GC_register_dynamic_libraries()
920 {
921   int status;
922   ldr_process_t mypid;
923
924   /* module */
925     ldr_module_t moduleid = LDR_NULL_MODULE;
926     ldr_module_info_t moduleinfo;
927     size_t moduleinfosize = sizeof(moduleinfo);
928     size_t modulereturnsize;    
929
930   /* region */
931     ldr_region_t region; 
932     ldr_region_info_t regioninfo;
933     size_t regioninfosize = sizeof(regioninfo);
934     size_t regionreturnsize;
935
936   /* Obtain id of this process */
937     mypid = ldr_my_process();
938   
939   /* For each module */
940     while (TRUE) {
941
942       /* Get the next (first) module */
943         status = ldr_next_module(mypid, &moduleid);
944
945       /* Any more modules? */
946         if (moduleid == LDR_NULL_MODULE)
947             break;    /* No more modules */
948
949       /* Check status AFTER checking moduleid because */
950       /* of a bug in the non-shared ldr_next_module stub */
951         if (status != 0 ) {
952             GC_printf1("dynamic_load: status = %ld\n", (long)status);
953             {
954                 extern char *sys_errlist[];
955                 extern int sys_nerr;
956                 extern int errno;
957                 if (errno <= sys_nerr) {
958                     GC_printf1("dynamic_load: %s\n", (long)sys_errlist[errno]);
959                } else {
960                     GC_printf1("dynamic_load: %d\n", (long)errno);
961                 }
962         }
963             ABORT("ldr_next_module failed");
964          }
965
966       /* Get the module information */
967         status = ldr_inq_module(mypid, moduleid, &moduleinfo,
968                                 moduleinfosize, &modulereturnsize); 
969         if (status != 0 )
970             ABORT("ldr_inq_module failed");
971
972       /* is module for the main program (i.e. nonshared portion)? */
973           if (moduleinfo.lmi_flags & LDR_MAIN)
974               continue;    /* skip the main module */
975
976 #     ifdef VERBOSE
977           GC_printf("---Module---\n");
978           GC_printf("Module ID            = %16ld\n", moduleinfo.lmi_modid);
979           GC_printf("Count of regions     = %16d\n", moduleinfo.lmi_nregion);
980           GC_printf("flags for module     = %16lx\n", moduleinfo.lmi_flags); 
981           GC_printf("pathname of module   = \"%s\"\n", moduleinfo.lmi_name);
982 #     endif
983
984       /* For each region in this module */
985         for (region = 0; region < moduleinfo.lmi_nregion; region++) {
986
987           /* Get the region information */
988             status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
989                                     regioninfosize, &regionreturnsize);
990             if (status != 0 )
991                 ABORT("ldr_inq_region failed");
992
993           /* only process writable (data) regions */
994             if (! (regioninfo.lri_prot & LDR_W))
995                 continue;
996
997 #         ifdef VERBOSE
998               GC_printf("--- Region ---\n");
999               GC_printf("Region number    = %16ld\n",
1000                         regioninfo.lri_region_no);
1001               GC_printf("Protection flags = %016x\n",  regioninfo.lri_prot);
1002               GC_printf("Virtual address  = %16p\n",   regioninfo.lri_vaddr);
1003               GC_printf("Mapped address   = %16p\n",   regioninfo.lri_mapaddr);
1004               GC_printf("Region size      = %16ld\n",  regioninfo.lri_size);
1005               GC_printf("Region name      = \"%s\"\n", regioninfo.lri_name);
1006 #         endif
1007
1008           /* register region as a garbage collection root */
1009             GC_add_roots_inner (
1010                 (char *)regioninfo.lri_mapaddr,
1011                 (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
1012                 TRUE);
1013
1014         }
1015     }
1016 }
1017 #endif
1018
1019 #if defined(HPUX)
1020
1021 #include <errno.h>
1022 #include <dl.h>
1023
1024 extern int errno;
1025 extern char *sys_errlist[];
1026 extern int sys_nerr;
1027
1028 void GC_register_dynamic_libraries()
1029 {
1030   int status;
1031   int index = 1; /* Ordinal position in shared library search list */
1032   struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
1033
1034   /* For each dynamic library loaded */
1035     while (TRUE) {
1036
1037       /* Get info about next shared library */
1038         status = shl_get(index, &shl_desc);
1039
1040       /* Check if this is the end of the list or if some error occured */
1041         if (status != 0) {
1042 #        ifdef GC_HPUX_THREADS
1043            /* I've seen errno values of 0.  The man page is not clear   */
1044            /* as to whether errno should get set on a -1 return.        */
1045            break;
1046 #        else
1047           if (errno == EINVAL) {
1048               break; /* Moved past end of shared library list --> finished */
1049           } else {
1050               if (errno <= sys_nerr) {
1051                     GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]);
1052               } else {
1053                     GC_printf1("dynamic_load: %d\n", (long) errno);
1054               }
1055               ABORT("shl_get failed");
1056           }
1057 #        endif
1058         }
1059
1060 #     ifdef VERBOSE
1061           GC_printf0("---Shared library---\n");
1062           GC_printf1("\tfilename        = \"%s\"\n", shl_desc->filename);
1063           GC_printf1("\tindex           = %d\n", index);
1064           GC_printf1("\thandle          = %08x\n",
1065                                         (unsigned long) shl_desc->handle);
1066           GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart);
1067           GC_printf1("\ttext seg. end   = %08x\n", shl_desc->tend);
1068           GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart);
1069           GC_printf1("\tdata seg. end   = %08x\n", shl_desc->dend);
1070           GC_printf1("\tref. count      = %lu\n", shl_desc->ref_count);
1071 #     endif
1072
1073       /* register shared library's data segment as a garbage collection root */
1074         GC_add_roots_inner((char *) shl_desc->dstart,
1075                            (char *) shl_desc->dend, TRUE);
1076
1077         index++;
1078     }
1079 }
1080 #endif /* HPUX */
1081
1082 #ifdef RS6000
1083 #pragma alloca
1084 #include <sys/ldr.h>
1085 #include <sys/errno.h>
1086 void GC_register_dynamic_libraries()
1087 {
1088         int len;
1089         char *ldibuf;
1090         int ldibuflen;
1091         struct ld_info *ldi;
1092
1093         ldibuf = alloca(ldibuflen = 8192);
1094
1095         while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
1096                 if (errno != ENOMEM) {
1097                         ABORT("loadquery failed");
1098                 }
1099                 ldibuf = alloca(ldibuflen *= 2);
1100         }
1101
1102         ldi = (struct ld_info *)ldibuf;
1103         while (ldi) {
1104                 len = ldi->ldinfo_next;
1105                 GC_add_roots_inner(
1106                                 ldi->ldinfo_dataorg,
1107                                 (ptr_t)(unsigned long)ldi->ldinfo_dataorg
1108                                 + ldi->ldinfo_datasize,
1109                                 TRUE);
1110                 ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
1111         }
1112 }
1113 #endif /* RS6000 */
1114
1115 #ifdef DARWIN
1116
1117 /* __private_extern__ hack required for pre-3.4 gcc versions.   */
1118 #ifndef __private_extern__
1119 # define __private_extern__ extern
1120 # include <mach-o/dyld.h>
1121 # undef __private_extern__
1122 #else
1123 # include <mach-o/dyld.h>
1124 #endif
1125 #include <mach-o/getsect.h>
1126
1127 /*#define DARWIN_DEBUG*/
1128
1129 const static struct { 
1130         const char *seg;
1131         const char *sect;
1132 } GC_dyld_sections[] = {
1133         { SEG_DATA, SECT_DATA },
1134         { SEG_DATA, SECT_BSS },
1135         { SEG_DATA, SECT_COMMON }
1136 };
1137     
1138 #ifdef DARWIN_DEBUG
1139 static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
1140     unsigned long i,c;
1141     c = _dyld_image_count();
1142     for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
1143         return _dyld_get_image_name(i);
1144     return NULL;
1145 }
1146 #endif
1147         
1148 /* This should never be called by a thread holding the lock */
1149 static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
1150     unsigned long start,end,i;
1151     const struct section *sec;
1152     if (GC_no_dls) return;
1153     for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1154         sec = getsectbynamefromheader(
1155             hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1156         if(sec == NULL || sec->size == 0) continue;
1157         start = slide + sec->addr;
1158         end = start + sec->size;
1159 #       ifdef DARWIN_DEBUG
1160             GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
1161                 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1162 #       endif
1163         GC_add_roots((char*)start,(char*)end);
1164     }
1165 #   ifdef DARWIN_DEBUG
1166         GC_print_static_roots();
1167 #   endif
1168 }
1169
1170 /* This should never be called by a thread holding the lock */
1171 static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
1172     unsigned long start,end,i;
1173     const struct section *sec;
1174     for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1175         sec = getsectbynamefromheader(
1176             hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
1177         if(sec == NULL || sec->size == 0) continue;
1178         start = slide + sec->addr;
1179         end = start + sec->size;
1180 #       ifdef DARWIN_DEBUG
1181             GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
1182                 start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1183 #               endif
1184         GC_remove_roots((char*)start,(char*)end);
1185     }
1186 #   ifdef DARWIN_DEBUG
1187         GC_print_static_roots();
1188 #   endif
1189 }
1190
1191 void GC_register_dynamic_libraries() {
1192     /* Currently does nothing. The callbacks are setup by GC_init_dyld() 
1193     The dyld library takes it from there. */
1194 }
1195
1196 /* The _dyld_* functions have an internal lock so no _dyld functions
1197    can be called while the world is stopped without the risk of a deadlock.
1198    Because of this we MUST setup callbacks BEFORE we ever stop the world.
1199    This should be called BEFORE any thread in created and WITHOUT the
1200    allocation lock held. */
1201    
1202 void GC_init_dyld() {
1203   static GC_bool initialized = FALSE;
1204   char *bind_fully_env = NULL;
1205   
1206   if(initialized) return;
1207   
1208 #   ifdef DARWIN_DEBUG
1209   GC_printf0("Registering dyld callbacks...\n");
1210 #   endif
1211   
1212   /* Apple's Documentation:
1213      When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1214      calls the specified callback (func) once for each of the images that is
1215      currently loaded into the program. When a new image is added to the program,
1216      your callback is called again with the mach_header for the new image, and the      
1217      virtual memory slide amount of the new image. 
1218      
1219      This WILL properly register already linked libraries and libraries 
1220      linked in the future
1221   */
1222   
1223     _dyld_register_func_for_add_image(GC_dyld_image_add);
1224     _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1225
1226     /* Set this early to avoid reentrancy issues. */
1227     initialized = TRUE;
1228
1229     bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
1230     
1231     if (bind_fully_env == NULL) {
1232 #   ifdef DARWIN_DEBUG
1233       GC_printf0("Forcing full bind of GC code...\n");
1234 #   endif
1235       
1236       if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1237         GC_abort("_dyld_bind_fully_image_containing_address failed");
1238     }
1239
1240 }
1241
1242 #define HAVE_REGISTER_MAIN_STATIC_DATA
1243 GC_bool GC_register_main_static_data()
1244 {
1245   /* Already done through dyld callbacks */
1246   return FALSE;
1247 }
1248
1249 #endif /* DARWIN */
1250
1251 #else /* !DYNAMIC_LOADING */
1252
1253 #ifdef PCR
1254
1255 #   include "il/PCR_IL.h"
1256 #   include "th/PCR_ThCtl.h"
1257 #   include "mm/PCR_MM.h"
1258
1259 void GC_register_dynamic_libraries()
1260 {
1261     /* Add new static data areas of dynamically loaded modules. */
1262         {
1263           PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1264           PCR_IL_LoadedSegment * q;
1265           
1266           /* Skip uncommited files */
1267           while (p != NIL && !(p -> lf_commitPoint)) {
1268               /* The loading of this file has not yet been committed    */
1269               /* Hence its description could be inconsistent.           */
1270               /* Furthermore, it hasn't yet been run.  Hence its data   */
1271               /* segments can't possibly reference heap allocated       */
1272               /* objects.                                               */
1273               p = p -> lf_prev;
1274           }
1275           for (; p != NIL; p = p -> lf_prev) {
1276             for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1277               if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1278                   == PCR_IL_SegFlags_Traced_on) {
1279                 GC_add_roots_inner
1280                         ((char *)(q -> ls_addr), 
1281                          (char *)(q -> ls_addr) + q -> ls_bytes,
1282                          TRUE);
1283               }
1284             }
1285           }
1286         }
1287 }
1288
1289
1290 #else /* !PCR */
1291
1292 void GC_register_dynamic_libraries(){}
1293
1294 int GC_no_dynamic_loading;
1295
1296 #endif /* !PCR */
1297
1298 #endif /* !DYNAMIC_LOADING */
1299
1300 #ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1301
1302 /* Do we need to separately register the main static data segment? */
1303 GC_bool GC_register_main_static_data()
1304 {
1305   return TRUE;
1306 }
1307 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
1308