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