Workaround 'redundant initialization for r' cppcheck false positive
[platform/upstream/libgc.git] / 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
15 #include "private/gc_priv.h"
16
17 /*
18  * This is incredibly OS specific code for tracking down data sections in
19  * dynamic libraries.  There appears to be no way of doing this quickly
20  * without groveling through undocumented data structures.  We would argue
21  * that this is a bug in the design of the dlopen interface.  THIS CODE
22  * MAY BREAK IN FUTURE OS RELEASES.  If this matters to you, don't hesitate
23  * to let your vendor know ...
24  *
25  * None of this is safe with dlclose and incremental collection.
26  * But then not much of anything is safe in the presence of dlclose.
27  */
28
29 #if !defined(MACOS) && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) \
30     && !defined(_WIN32_WCE) && !defined(__CC_ARM)
31 # include <sys/types.h>
32 #endif
33
34 /* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
35 #undef GC_MUST_RESTORE_REDEFINED_DLOPEN
36 #if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) \
37     && !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP)
38   /* To support threads in Solaris, gc.h interposes on dlopen by        */
39   /* defining "dlopen" to be "GC_dlopen", which is implemented below.   */
40   /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the    */
41   /* real system dlopen() in their implementation. We first remove      */
42   /* gc.h's dlopen definition and restore it later, after GC_dlopen().  */
43 # undef dlopen
44 # define GC_MUST_RESTORE_REDEFINED_DLOPEN
45 #endif /* !GC_NO_DLOPEN */
46
47 /* A user-supplied routine (custom filter) that might be called to      */
48 /* determine whether a DSO really needs to be scanned by the GC.        */
49 /* 0 means no filter installed.  May be unused on some platforms.       */
50 /* FIXME: Add filter support for more platforms.                        */
51 STATIC GC_has_static_roots_func GC_has_static_roots = 0;
52
53 #if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \
54     || defined(CYGWIN32)) && !defined(PCR)
55
56 #if !defined(DARWIN) && !defined(SCO_ELF) && !defined(SOLARISDL) \
57     && !defined(AIX) && !defined(DGUX) && !defined(IRIX5) && !defined(HPUX) \
58     && !defined(CYGWIN32) && !defined(MSWIN32) && !defined(MSWINCE) \
59     && !(defined(ALPHA) && defined(OSF1)) \
60     && !(defined(FREEBSD) && defined(__ELF__)) \
61     && !(defined(LINUX) && defined(__ELF__)) \
62     && !(defined(NETBSD) && defined(__ELF__)) \
63     && !(defined(OPENBSD) && (defined(__ELF__) || defined(M68K))) \
64     && !defined(HAIKU) && !defined(HURD) && !defined(NACL) \
65     && !defined(CPPCHECK)
66 # error We only know how to find data segments of dynamic libraries for above.
67 # error Additional SVR4 variants might not be too hard to add.
68 #endif
69
70 #include <stdio.h>
71 #ifdef SOLARISDL
72 #   include <sys/elf.h>
73 #   include <dlfcn.h>
74 #   include <link.h>
75 #endif
76
77 #if defined(NETBSD)
78 #   include <sys/param.h>
79 #   include <dlfcn.h>
80 #   include <machine/elf_machdep.h>
81 #   define ELFSIZE ARCH_ELFSIZE
82 #endif
83
84 #if defined(OPENBSD)
85 # include <sys/param.h>
86 # if (OpenBSD >= 200519) && !defined(HAVE_DL_ITERATE_PHDR)
87 #   define HAVE_DL_ITERATE_PHDR
88 # endif
89 #endif /* OPENBSD */
90
91 #if defined(SCO_ELF) || defined(DGUX) || defined(HURD) || defined(NACL) \
92     || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
93                              || defined(NETBSD) || defined(OPENBSD)))
94 # include <stddef.h>
95 # if !defined(OPENBSD) && !defined(HOST_ANDROID)
96     /* OpenBSD does not have elf.h file; link.h below is sufficient.    */
97     /* Exclude Android because linker.h below includes its own version. */
98 #   include <elf.h>
99 # endif
100 # ifdef HOST_ANDROID
101     /* If you don't need the "dynamic loading" feature, you may build   */
102     /* the collector with -D IGNORE_DYNAMIC_LOADING.                    */
103 #   ifdef BIONIC_ELFDATA_REDEF_BUG
104       /* Workaround a problem in Bionic (as of Android 4.2) which has   */
105       /* mismatching ELF_DATA definitions in sys/exec_elf.h and         */
106       /* asm/elf.h included from linker.h file (similar to EM_ALPHA).   */
107 #     include <asm/elf.h>
108 #     include <linux/elf-em.h>
109 #     undef ELF_DATA
110 #     undef EM_ALPHA
111 #   endif
112 #   include <link.h>
113 #   if !defined(GC_DONT_DEFINE_LINK_MAP) && !(__ANDROID_API__ >= 21)
114       /* link_map and r_debug are defined in link.h of NDK r10+.        */
115       /* bionic/linker/linker.h defines them too but the header         */
116       /* itself is a C++ one starting from Android 4.3.                 */
117       struct link_map {
118         uintptr_t l_addr;
119         char* l_name;
120         uintptr_t l_ld;
121         struct link_map* l_next;
122         struct link_map* l_prev;
123       };
124       struct r_debug {
125         int32_t r_version;
126         struct link_map* r_map;
127         void (*r_brk)(void);
128         int32_t r_state;
129         uintptr_t r_ldbase;
130       };
131 #   endif
132 # else
133     EXTERN_C_BEGIN      /* Workaround missing extern "C" around _DYNAMIC */
134                         /* symbol in link.h of some Linux hosts.         */
135 #   include <link.h>
136     EXTERN_C_END
137 # endif
138 #endif
139
140 /* Newer versions of GNU/Linux define this macro.  We
141  * define it similarly for any ELF systems that don't.  */
142 #  ifndef ElfW
143 #    if defined(FREEBSD)
144 #      if __ELF_WORD_SIZE == 32
145 #        define ElfW(type) Elf32_##type
146 #      else
147 #        define ElfW(type) Elf64_##type
148 #      endif
149 #    elif defined(NETBSD) || defined(OPENBSD)
150 #      if ELFSIZE == 32
151 #        define ElfW(type) Elf32_##type
152 #      else
153 #        define ElfW(type) Elf64_##type
154 #      endif
155 #    else
156 #      if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
157 #        define ElfW(type) Elf32_##type
158 #      else
159 #        define ElfW(type) Elf64_##type
160 #      endif
161 #    endif
162 #  endif
163
164 #if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES)
165
166   EXTERN_C_BEGIN
167   extern ElfW(Dyn) _DYNAMIC;
168   EXTERN_C_END
169
170   STATIC struct link_map *
171   GC_FirstDLOpenedLinkMap(void)
172   {
173     ElfW(Dyn) *dp;
174     static struct link_map * cachedResult = 0;
175     static ElfW(Dyn) *dynStructureAddr = 0;
176                 /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug   */
177
178 #   ifdef SUNOS53_SHARED_LIB
179         /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */
180         /* up properly in dynamically linked .so's. This means we have  */
181         /* to use its value in the set of original object files loaded  */
182         /* at program startup.                                          */
183         if( dynStructureAddr == 0 ) {
184           void* startupSyms = dlopen(0, RTLD_LAZY);
185           dynStructureAddr = (ElfW(Dyn)*)(word)dlsym(startupSyms, "_DYNAMIC");
186         }
187 #   else
188         dynStructureAddr = &_DYNAMIC;
189 #   endif
190
191     if (0 == COVERT_DATAFLOW(dynStructureAddr)) {
192         /* _DYNAMIC symbol not resolved. */
193         return(0);
194     }
195     if (cachedResult == 0) {
196         int tag;
197         for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
198             if (tag == DT_DEBUG) {
199                 struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr;
200                 if (rd != NULL) {
201                     struct link_map *lm = rd->r_map;
202                     if (lm != NULL)
203                         cachedResult = lm->l_next; /* might be NULL */
204                 }
205                 break;
206             }
207         }
208     }
209     return cachedResult;
210   }
211
212 #endif /* SOLARISDL ... */
213
214 /* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
215 # ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN
216 #   define dlopen GC_dlopen
217 # endif
218
219 # if defined(SOLARISDL)
220
221 /* Add dynamic library data sections to the root set.           */
222 # if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS) \
223      && !defined(CPPCHECK)
224 #   error Fix mutual exclusion with dlopen
225 # endif
226
227 # ifndef USE_PROC_FOR_LIBRARIES
228 GC_INNER void GC_register_dynamic_libraries(void)
229 {
230   struct link_map *lm;
231
232   for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) {
233         ElfW(Ehdr) * e;
234         ElfW(Phdr) * p;
235         unsigned long offset;
236         char * start;
237         int i;
238
239         e = (ElfW(Ehdr) *) lm->l_addr;
240         p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
241         offset = ((unsigned long)(lm->l_addr));
242         for( i = 0; i < (int)e->e_phnum; i++, p++ ) {
243           switch( p->p_type ) {
244             case PT_LOAD:
245               {
246                 if( !(p->p_flags & PF_W) ) break;
247                 start = ((char *)(p->p_vaddr)) + offset;
248                 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
249               }
250               break;
251             default:
252               break;
253           }
254         }
255     }
256 }
257
258 # endif /* !USE_PROC ... */
259 # endif /* SOLARISDL */
260
261 #if defined(SCO_ELF) || defined(DGUX) || defined(HURD) || defined(NACL) \
262     || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
263                              || defined(NETBSD) || defined(OPENBSD)))
264
265 #ifdef USE_PROC_FOR_LIBRARIES
266
267 #include <string.h>
268
269 #include <sys/stat.h>
270 #include <fcntl.h>
271 #include <unistd.h>
272
273 #define MAPS_BUF_SIZE (32*1024)
274
275 /* Sort an array of HeapSects by start address.                         */
276 /* Unfortunately at least some versions of                              */
277 /* Linux qsort end up calling malloc by way of sysconf, and hence can't */
278 /* be used in the collector.  Hence we roll our own.  Should be         */
279 /* reasonably fast if the array is already mostly sorted, as we expect  */
280 /* it to be.                                                            */
281 static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements)
282 {
283     signed_word n = (signed_word)number_of_elements;
284     signed_word nsorted = 1;
285
286     while (nsorted < n) {
287       signed_word i;
288
289       while (nsorted < n &&
290              (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start)
291           ++nsorted;
292       if (nsorted == n) break;
293       GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start);
294       i = nsorted - 1;
295       while (i >= 0 && (word)base[i].hs_start > (word)base[i+1].hs_start) {
296         struct HeapSect tmp = base[i];
297         base[i] = base[i+1];
298         base[i+1] = tmp;
299         --i;
300       }
301       GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start);
302       ++nsorted;
303     }
304 }
305
306 STATIC void GC_register_map_entries(char *maps)
307 {
308     char *prot;
309     char *buf_ptr = maps;
310     ptr_t start, end;
311     unsigned int maj_dev;
312     ptr_t least_ha, greatest_ha;
313     unsigned i;
314
315     GC_ASSERT(I_HOLD_LOCK());
316     sort_heap_sects(GC_our_memory, GC_n_memory);
317     least_ha = GC_our_memory[0].hs_start;
318     greatest_ha = GC_our_memory[GC_n_memory-1].hs_start
319                   + GC_our_memory[GC_n_memory-1].hs_bytes;
320
321     for (;;) {
322         buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot,
323                                      &maj_dev, 0);
324         if (NULL == buf_ptr)
325             break;
326         if (prot[1] == 'w') {
327             /* This is a writable mapping.  Add it to           */
328             /* the root set unless it is already otherwise      */
329             /* accounted for.                                   */
330             if ((word)start <= (word)GC_stackbottom
331                 && (word)end >= (word)GC_stackbottom) {
332                 /* Stack mapping; discard       */
333                 continue;
334             }
335 #           ifdef THREADS
336               /* This may fail, since a thread may already be           */
337               /* unregistered, but its thread stack may still be there. */
338               /* That can fail because the stack may disappear while    */
339               /* we're marking.  Thus the marker is, and has to be      */
340               /* prepared to recover from segmentation faults.          */
341
342               if (GC_segment_is_thread_stack(start, end)) continue;
343
344               /* FIXME: NPTL squirrels                                  */
345               /* away pointers in pieces of the stack segment that we   */
346               /* don't scan.  We work around this                       */
347               /* by treating anything allocated by libpthread as        */
348               /* uncollectible, as we do in some other cases.           */
349               /* A specifically identified problem is that              */
350               /* thread stacks contain pointers to dynamic thread       */
351               /* vectors, which may be reused due to thread caching.    */
352               /* They may not be marked if the thread is still live.    */
353               /* This specific instance should be addressed by          */
354               /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite     */
355               /* seem to suffice.                                       */
356               /* We currently trace entire thread stacks, if they are   */
357               /* are currently cached but unused.  This is              */
358               /* very suboptimal for performance reasons.               */
359 #           endif
360             /* We no longer exclude the main data segment.              */
361             if ((word)end <= (word)least_ha
362                 || (word)start >= (word)greatest_ha) {
363               /* The easy case; just trace entire segment */
364               GC_add_roots_inner(start, end, TRUE);
365               continue;
366             }
367             /* Add sections that don't belong to us. */
368               i = 0;
369               while ((word)(GC_our_memory[i].hs_start
370                                 + GC_our_memory[i].hs_bytes) < (word)start)
371                   ++i;
372               GC_ASSERT(i < GC_n_memory);
373               if ((word)GC_our_memory[i].hs_start <= (word)start) {
374                   start = GC_our_memory[i].hs_start
375                           + GC_our_memory[i].hs_bytes;
376                   ++i;
377               }
378               while (i < GC_n_memory
379                      && (word)GC_our_memory[i].hs_start < (word)end
380                      && (word)start < (word)end) {
381                   if ((word)start < (word)GC_our_memory[i].hs_start)
382                     GC_add_roots_inner(start,
383                                        GC_our_memory[i].hs_start, TRUE);
384                   start = GC_our_memory[i].hs_start
385                           + GC_our_memory[i].hs_bytes;
386                   ++i;
387               }
388               if ((word)start < (word)end)
389                   GC_add_roots_inner(start, end, TRUE);
390         } else if (prot[0] == '-' && prot[1] == '-' && prot[2] == '-') {
391             /* Even roots added statically might disappear partially    */
392             /* (e.g. the roots added by INCLUDE_LINUX_THREAD_DESCR).    */
393             GC_remove_roots_subregion(start, end);
394         }
395     }
396 }
397
398 GC_INNER void GC_register_dynamic_libraries(void)
399 {
400     char *maps = GC_get_maps();
401
402     if (NULL == maps)
403         ABORT("Failed to read /proc for library registration");
404     GC_register_map_entries(maps);
405 }
406
407 /* We now take care of the main data segment ourselves: */
408 GC_INNER GC_bool GC_register_main_static_data(void)
409 {
410     return FALSE;
411 }
412
413 # define HAVE_REGISTER_MAIN_STATIC_DATA
414
415 #else /* !USE_PROC_FOR_LIBRARIES */
416
417 /* The following is the preferred way to walk dynamic libraries */
418 /* for glibc 2.2.4+.  Unfortunately, it doesn't work for older  */
419 /* versions.  Thanks to Jakub Jelinek for most of the code.     */
420
421 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
422     || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)) \
423     || defined(HOST_ANDROID) /* Are others OK here, too? */
424 # ifndef HAVE_DL_ITERATE_PHDR
425 #   define HAVE_DL_ITERATE_PHDR
426 # endif
427 # ifdef HOST_ANDROID
428     /* Android headers might have no such definition for some targets.  */
429     EXTERN_C_BEGIN
430     extern int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *,
431                                          size_t, void *),
432                                void *data);
433     EXTERN_C_END
434 # endif
435 #endif /* __GLIBC__ >= 2 || HOST_ANDROID */
436
437 #if defined(__DragonFly__) || defined(__FreeBSD_kernel__) \
438     || (defined(FREEBSD) && __FreeBSD__ >= 7)
439   /* On the FreeBSD system, any target system at major version 7 shall   */
440   /* have dl_iterate_phdr; therefore, we need not make it weak as below. */
441 # ifndef HAVE_DL_ITERATE_PHDR
442 #   define HAVE_DL_ITERATE_PHDR
443 # endif
444 # define DL_ITERATE_PHDR_STRONG
445 #elif defined(HAVE_DL_ITERATE_PHDR)
446   /* We have the header files for a glibc that includes dl_iterate_phdr.*/
447   /* It may still not be available in the library on the target system. */
448   /* Thus we also treat it as a weak symbol.                            */
449   EXTERN_C_BEGIN
450 # pragma weak dl_iterate_phdr
451   EXTERN_C_END
452 #endif
453
454 #if defined(HAVE_DL_ITERATE_PHDR)
455
456 # ifdef PT_GNU_RELRO
457 /* Instead of registering PT_LOAD sections directly, we keep them       */
458 /* in a temporary list, and filter them by excluding PT_GNU_RELRO       */
459 /* segments.  Processing PT_GNU_RELRO sections with                     */
460 /* GC_exclude_static_roots instead would be superficially cleaner.  But */
461 /* it runs into trouble if a client registers an overlapping segment,   */
462 /* which unfortunately seems quite possible.                            */
463
464 #   define MAX_LOAD_SEGS MAX_ROOT_SETS
465
466     static struct load_segment {
467       ptr_t start;
468       ptr_t end;
469       /* Room for a second segment if we remove a RELRO segment */
470       /* from the middle.                                       */
471       ptr_t start2;
472       ptr_t end2;
473     } load_segs[MAX_LOAD_SEGS];
474
475     static int n_load_segs;
476     static GC_bool load_segs_overflow;
477 # endif /* PT_GNU_RELRO */
478
479 STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info,
480                                        size_t size, void * ptr)
481 {
482   const ElfW(Phdr) * p;
483   ptr_t start, end;
484   int i;
485
486   /* Make sure struct dl_phdr_info is at least as big as we need.  */
487   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
488       + sizeof (info->dlpi_phnum))
489     return -1;
490
491   p = info->dlpi_phdr;
492   for (i = 0; i < (int)info->dlpi_phnum; i++, p++) {
493     if (p->p_type == PT_LOAD) {
494       GC_has_static_roots_func callback = GC_has_static_roots;
495       if ((p->p_flags & PF_W) == 0) continue;
496
497       start = (ptr_t)p->p_vaddr + info->dlpi_addr;
498       end = start + p->p_memsz;
499       if (callback != 0 && !callback(info->dlpi_name, start, p->p_memsz))
500         continue;
501 #     ifdef PT_GNU_RELRO
502 #       if CPP_WORDSZ == 64
503           /* TODO: GC_push_all eventually does the correct          */
504           /* rounding to the next multiple of ALIGNMENT, so, most   */
505           /* probably, we should remove the corresponding assertion */
506           /* check in GC_add_roots_inner along with this code line. */
507           /* start pointer value may require aligning.              */
508           start = (ptr_t)((word)start & ~(word)(sizeof(word) - 1));
509 #       endif
510         if (n_load_segs >= MAX_LOAD_SEGS) {
511           if (!load_segs_overflow) {
512             WARN("Too many PT_LOAD segments;"
513                  " registering as roots directly...\n", 0);
514             load_segs_overflow = TRUE;
515           }
516           GC_add_roots_inner(start, end, TRUE);
517         } else {
518           load_segs[n_load_segs].start = start;
519           load_segs[n_load_segs].end = end;
520           load_segs[n_load_segs].start2 = 0;
521           load_segs[n_load_segs].end2 = 0;
522           ++n_load_segs;
523         }
524 #     else
525         GC_add_roots_inner(start, end, TRUE);
526 #     endif /* !PT_GNU_RELRO */
527     }
528   }
529
530 # ifdef PT_GNU_RELRO
531     p = info->dlpi_phdr;
532     for (i = 0; i < (int)info->dlpi_phnum; i++, p++) {
533       if (p->p_type == PT_GNU_RELRO) {
534         /* This entry is known to be constant and will eventually be    */
535         /* remapped as read-only.  However, the address range covered   */
536         /* by this entry is typically a subset of a previously          */
537         /* encountered "LOAD" segment, so we need to exclude it.        */
538         int j;
539
540         start = (ptr_t)p->p_vaddr + info->dlpi_addr;
541         end = start + p->p_memsz;
542         for (j = n_load_segs; --j >= 0; ) {
543           if ((word)start >= (word)load_segs[j].start
544               && (word)start < (word)load_segs[j].end) {
545             if (load_segs[j].start2 != 0) {
546               WARN("More than one GNU_RELRO segment per load one\n",0);
547             } else {
548               GC_ASSERT((word)end <= (word)load_segs[j].end);
549               /* Remove from the existing load segment */
550               load_segs[j].end2 = load_segs[j].end;
551               load_segs[j].end = start;
552               load_segs[j].start2 = end;
553             }
554             break;
555           }
556           if (0 == j && 0 == GC_has_static_roots)
557             WARN("Failed to find PT_GNU_RELRO segment"
558                  " inside PT_LOAD region\n", 0);
559             /* No warning reported in case of the callback is present   */
560             /* because most likely the segment has been excluded.       */
561         }
562       }
563     }
564 # endif
565
566   *(int *)ptr = 1;     /* Signal that we were called */
567   return 0;
568 }
569
570 /* Do we need to separately register the main static data segment? */
571 GC_INNER GC_bool GC_register_main_static_data(void)
572 {
573 # ifdef DL_ITERATE_PHDR_STRONG
574     /* If dl_iterate_phdr is not a weak symbol then don't test against  */
575     /* zero (otherwise a compiler might issue a warning).               */
576     return FALSE;
577 # else
578     return 0 == COVERT_DATAFLOW(dl_iterate_phdr);
579 # endif
580 }
581
582 /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
583 STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void)
584 {
585   int did_something;
586   if (GC_register_main_static_data())
587     return FALSE;
588
589 # ifdef PT_GNU_RELRO
590     {
591       static GC_bool excluded_segs = FALSE;
592       n_load_segs = 0;
593       load_segs_overflow = FALSE;
594       if (!EXPECT(excluded_segs, TRUE)) {
595         GC_exclude_static_roots_inner((ptr_t)load_segs,
596                                       (ptr_t)load_segs + sizeof(load_segs));
597         excluded_segs = TRUE;
598       }
599     }
600 # endif
601
602   did_something = 0;
603   dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
604   if (did_something) {
605 #   ifdef PT_GNU_RELRO
606       int i;
607
608       for (i = 0; i < n_load_segs; ++i) {
609         if ((word)load_segs[i].end > (word)load_segs[i].start) {
610           GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE);
611         }
612         if ((word)load_segs[i].end2 > (word)load_segs[i].start2) {
613           GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE);
614         }
615       }
616 #   endif
617   } else {
618       ptr_t datastart, dataend;
619 #     ifdef DATASTART_IS_FUNC
620         static ptr_t datastart_cached = (ptr_t)GC_WORD_MAX;
621
622         /* Evaluate DATASTART only once.  */
623         if (datastart_cached == (ptr_t)GC_WORD_MAX) {
624           datastart_cached = DATASTART;
625         }
626         datastart = datastart_cached;
627 #     else
628         datastart = DATASTART;
629 #     endif
630 #     ifdef DATAEND_IS_FUNC
631         {
632           static ptr_t dataend_cached = 0;
633           /* Evaluate DATAEND only once. */
634           if (dataend_cached == 0) {
635             dataend_cached = DATAEND;
636           }
637           dataend = dataend_cached;
638         }
639 #     else
640         dataend = DATAEND;
641 #     endif
642       if (NULL == *(char * volatile *)&datastart
643           || (word)datastart > (word)dataend)
644         ABORT_ARG2("Wrong DATASTART/END pair",
645                    ": %p .. %p", (void *)datastart, (void *)dataend);
646
647       /* dl_iterate_phdr may forget the static data segment in  */
648       /* statically linked executables.                         */
649       GC_add_roots_inner(datastart, dataend, TRUE);
650 #     ifdef GC_HAVE_DATAREGION2
651         if ((word)DATASTART2 - 1U >= (word)DATAEND2) {
652                         /* Subtract one to check also for NULL  */
653                         /* without a compiler warning.          */
654           ABORT_ARG2("Wrong DATASTART/END2 pair",
655                      ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2);
656         }
657         GC_add_roots_inner(DATASTART2, DATAEND2, TRUE);
658 #     endif
659   }
660   return TRUE;
661 }
662
663 # define HAVE_REGISTER_MAIN_STATIC_DATA
664
665 #else /* !HAVE_DL_ITERATE_PHDR */
666
667 /* Dynamic loading code for Linux running ELF. Somewhat tested on
668  * Linux/x86, untested but hopefully should work on Linux/Alpha.
669  * This code was derived from the Solaris/ELF support. Thanks to
670  * whatever kind soul wrote that.  - Patrick Bridges */
671
672 /* This doesn't necessarily work in all cases, e.g. with preloaded
673  * dynamic libraries.                                           */
674
675 # if defined(NETBSD) || defined(OPENBSD)
676 #   include <sys/exec_elf.h>
677    /* for compatibility with 1.4.x */
678 #   ifndef DT_DEBUG
679 #     define DT_DEBUG   21
680 #   endif
681 #   ifndef PT_LOAD
682 #     define PT_LOAD    1
683 #   endif
684 #   ifndef PF_W
685 #     define PF_W       2
686 #   endif
687 # elif !defined(HOST_ANDROID)
688 #  include <elf.h>
689 # endif
690
691 # ifndef HOST_ANDROID
692 #   include <link.h>
693 # endif
694
695 #endif /* !HAVE_DL_ITERATE_PHDR */
696
697 EXTERN_C_BEGIN
698 #ifdef __GNUC__
699 # pragma weak _DYNAMIC
700 #endif
701 extern ElfW(Dyn) _DYNAMIC[];
702 EXTERN_C_END
703
704 STATIC struct link_map *
705 GC_FirstDLOpenedLinkMap(void)
706 {
707     static struct link_map *cachedResult = 0;
708
709     if (0 == COVERT_DATAFLOW(_DYNAMIC)) {
710         /* _DYNAMIC symbol not resolved. */
711         return(0);
712     }
713     if( cachedResult == 0 ) {
714 #     if defined(NETBSD) && defined(RTLD_DI_LINKMAP)
715 #       if defined(CPPCHECK)
716 #         define GC_RTLD_DI_LINKMAP 2
717 #       else
718 #         define GC_RTLD_DI_LINKMAP RTLD_DI_LINKMAP
719 #       endif
720         struct link_map *lm = NULL;
721         if (!dlinfo(RTLD_SELF, GC_RTLD_DI_LINKMAP, &lm) && lm != NULL) {
722             /* Now lm points link_map object of libgc.  Since it    */
723             /* might not be the first dynamically linked object,    */
724             /* try to find it (object next to the main object).     */
725             while (lm->l_prev != NULL) {
726                 lm = lm->l_prev;
727             }
728             cachedResult = lm->l_next;
729         }
730 #     else
731         ElfW(Dyn) *dp;
732         int tag;
733
734         for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
735             if (tag == DT_DEBUG) {
736                 struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr;
737                 /* d_ptr could be null if libs are linked statically. */
738                 if (rd != NULL) {
739                     struct link_map *lm = rd->r_map;
740                     if (lm != NULL)
741                         cachedResult = lm->l_next; /* might be NULL */
742                 }
743                 break;
744             }
745         }
746 #     endif /* !NETBSD || !RTLD_DI_LINKMAP */
747     }
748     return cachedResult;
749 }
750
751 GC_INNER void GC_register_dynamic_libraries(void)
752 {
753   struct link_map *lm;
754
755 # ifdef HAVE_DL_ITERATE_PHDR
756     if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
757         return;
758     }
759 # endif
760   for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next)
761     {
762         ElfW(Ehdr) * e;
763         ElfW(Phdr) * p;
764         unsigned long offset;
765         char * start;
766         int i;
767
768         e = (ElfW(Ehdr) *) lm->l_addr;
769 #       ifdef HOST_ANDROID
770           if (e == NULL)
771             continue;
772 #       endif
773         p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
774         offset = ((unsigned long)(lm->l_addr));
775         for( i = 0; i < (int)e->e_phnum; i++, p++ ) {
776           switch( p->p_type ) {
777             case PT_LOAD:
778               {
779                 if( !(p->p_flags & PF_W) ) break;
780                 start = ((char *)(p->p_vaddr)) + offset;
781                 GC_add_roots_inner(start, start + p->p_memsz, TRUE);
782               }
783               break;
784             default:
785               break;
786           }
787         }
788     }
789 }
790
791 #endif /* !USE_PROC_FOR_LIBRARIES */
792
793 #endif /* LINUX */
794
795 #if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
796
797 #include <sys/procfs.h>
798 #include <sys/stat.h>
799 #include <fcntl.h>
800 #include <elf.h>
801 #include <errno.h>
802 #include <signal.h>  /* Only for the following test. */
803 #ifndef _sigargs
804 # define IRIX6
805 #endif
806
807 /* We use /proc to track down all parts of the address space that are   */
808 /* mapped by the process, and throw out regions we know we shouldn't    */
809 /* worry about.  This may also work under other SVR4 variants.          */
810 GC_INNER void GC_register_dynamic_libraries(void)
811 {
812     static int fd = -1;
813     char buf[30];
814     static prmap_t * addr_map = 0;
815     static int current_sz = 0;  /* Number of records currently in addr_map */
816     int needed_sz = 0;          /* Required size of addr_map            */
817     int i;
818     long flags;
819     ptr_t start;
820     ptr_t limit;
821     ptr_t heap_start = HEAP_START;
822     ptr_t heap_end = heap_start;
823
824 #   ifdef SOLARISDL
825 #     define MA_PHYS 0
826 #   endif /* SOLARISDL */
827
828     if (fd < 0) {
829       (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid());
830       buf[sizeof(buf) - 1] = '\0';
831       fd = open(buf, O_RDONLY);
832       if (fd < 0) {
833         ABORT("/proc open failed");
834       }
835     }
836     if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
837         ABORT_ARG2("/proc PIOCNMAP ioctl failed",
838                    ": fd = %d, errno = %d", fd, errno);
839     }
840     if (needed_sz >= current_sz) {
841         GC_scratch_recycle_no_gww(addr_map,
842                                   (size_t)current_sz * sizeof(prmap_t));
843         current_sz = needed_sz * 2 + 1;
844                         /* Expansion, plus room for 0 record */
845         addr_map = (prmap_t *)GC_scratch_alloc(
846                                 (size_t)current_sz * sizeof(prmap_t));
847         if (addr_map == NULL)
848           ABORT("Insufficient memory for address map");
849     }
850     if (ioctl(fd, PIOCMAP, addr_map) < 0) {
851         ABORT_ARG3("/proc PIOCMAP ioctl failed",
852                    ": errcode= %d, needed_sz= %d, addr_map= %p",
853                    errno, needed_sz, (void *)addr_map);
854     };
855     if (GC_n_heap_sects > 0) {
856         heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
857                         + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
858         if ((word)heap_end < (word)GC_scratch_last_end_ptr)
859           heap_end = GC_scratch_last_end_ptr;
860     }
861     for (i = 0; i < needed_sz; i++) {
862         flags = addr_map[i].pr_mflags;
863         if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
864                       | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
865         if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
866             goto irrelevant;
867           /* The latter test is empirically useless in very old Irix    */
868           /* versions.  Other than the                                  */
869           /* main data and stack segments, everything appears to be     */
870           /* mapped readable, writable, executable, and shared(!!).     */
871           /* This makes no sense to me. - HB                            */
872         start = (ptr_t)(addr_map[i].pr_vaddr);
873         if (GC_roots_present(start)) goto irrelevant;
874         if ((word)start < (word)heap_end && (word)start >= (word)heap_start)
875                 goto irrelevant;
876
877         limit = start + addr_map[i].pr_size;
878         /* The following seemed to be necessary for very old versions   */
879         /* of Irix, but it has been reported to discard relevant        */
880         /* segments under Irix 6.5.                                     */
881 #       ifndef IRIX6
882           if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
883             /* Discard text segments, i.e. 0-offset mappings against    */
884             /* executable files which appear to have ELF headers.       */
885             caddr_t arg;
886             int obj;
887 #           define MAP_IRR_SZ 10
888             static ptr_t map_irr[MAP_IRR_SZ];
889                                         /* Known irrelevant map entries */
890             static int n_irr = 0;
891             struct stat buf;
892             int j;
893
894             for (j = 0; j < n_irr; j++) {
895                 if (map_irr[j] == start) goto irrelevant;
896             }
897             arg = (caddr_t)start;
898             obj = ioctl(fd, PIOCOPENM, &arg);
899             if (obj >= 0) {
900                 fstat(obj, &buf);
901                 close(obj);
902                 if ((buf.st_mode & 0111) != 0) {
903                     if (n_irr < MAP_IRR_SZ) {
904                         map_irr[n_irr++] = start;
905                     }
906                     goto irrelevant;
907                 }
908             }
909           }
910 #       endif /* !IRIX6 */
911         GC_add_roots_inner(start, limit, TRUE);
912       irrelevant: ;
913     }
914     /* Don't keep cached descriptor, for now.  Some kernels don't like us */
915     /* to keep a /proc file descriptor around during kill -9.             */
916         if (close(fd) < 0) ABORT("Couldn't close /proc file");
917         fd = -1;
918 }
919
920 # endif /* USE_PROC || IRIX5 */
921
922 # if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
923
924 # ifndef WIN32_LEAN_AND_MEAN
925 #   define WIN32_LEAN_AND_MEAN 1
926 # endif
927 # define NOSERVICE
928 # include <windows.h>
929 # include <stdlib.h>
930
931   /* We traverse the entire address space and register all segments     */
932   /* that could possibly have been written to.                          */
933   STATIC void GC_cond_add_roots(char *base, char * limit)
934   {
935 #   ifdef GC_WIN32_THREADS
936       char * curr_base = base;
937       char * next_stack_lo;
938       char * next_stack_hi;
939
940       if (base == limit) return;
941       for(;;) {
942           GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi);
943           if ((word)next_stack_lo >= (word)limit) break;
944           if ((word)next_stack_lo > (word)curr_base)
945             GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
946           curr_base = next_stack_hi;
947       }
948       if ((word)curr_base < (word)limit)
949         GC_add_roots_inner(curr_base, limit, TRUE);
950 #   else
951       char * stack_top
952          = (char *)((word)GC_approx_sp() &
953                     ~(word)(GC_sysinfo.dwAllocationGranularity - 1));
954
955       if (base == limit) return;
956       if ((word)limit > (word)stack_top
957           && (word)base < (word)GC_stackbottom) {
958           /* Part of the stack; ignore it. */
959           return;
960       }
961       GC_add_roots_inner(base, limit, TRUE);
962 #   endif
963   }
964
965 #ifdef DYNAMIC_LOADING
966   /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */
967   GC_INNER GC_bool GC_register_main_static_data(void)
968   {
969 #   if defined(MSWINCE) || defined(CYGWIN32)
970       /* Do we need to separately register the main static data segment? */
971       return FALSE;
972 #   else
973       return GC_no_win32_dlls;
974 #   endif
975   }
976 # define HAVE_REGISTER_MAIN_STATIC_DATA
977 #endif /* DYNAMIC_LOADING */
978
979 # ifdef DEBUG_VIRTUALQUERY
980   void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
981   {
982     GC_printf("BaseAddress = 0x%lx, AllocationBase = 0x%lx,"
983               " RegionSize = 0x%lx(%lu)\n", buf -> BaseAddress,
984               buf -> AllocationBase, buf -> RegionSize, buf -> RegionSize);
985     GC_printf("\tAllocationProtect = 0x%lx, State = 0x%lx, Protect = 0x%lx, "
986               "Type = 0x%lx\n", buf -> AllocationProtect, buf -> State,
987               buf -> Protect, buf -> Type);
988   }
989 # endif /* DEBUG_VIRTUALQUERY */
990
991 # if defined(MSWINCE) || defined(CYGWIN32)
992     /* FIXME: Should we really need to scan MEM_PRIVATE sections?       */
993     /* For now, we don't add MEM_PRIVATE sections to the data roots for */
994     /* WinCE because otherwise SEGV fault sometimes happens to occur in */
995     /* GC_mark_from() (and, even if we use WRAP_MARK_SOME, WinCE prints */
996     /* a "Data Abort" message to the debugging console).                */
997     /* To workaround that, use -DGC_REGISTER_MEM_PRIVATE.               */
998 #   define GC_wnt TRUE
999 # endif
1000
1001   GC_INNER void GC_register_dynamic_libraries(void)
1002   {
1003     MEMORY_BASIC_INFORMATION buf;
1004     DWORD protect;
1005     LPVOID p;
1006     char * base;
1007     char * limit, * new_limit;
1008
1009 #   ifdef MSWIN32
1010       if (GC_no_win32_dlls) return;
1011 #   endif
1012     p = GC_sysinfo.lpMinimumApplicationAddress;
1013     base = limit = (char *)p;
1014     while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) {
1015         size_t result = VirtualQuery(p, &buf, sizeof(buf));
1016
1017 #       ifdef MSWINCE
1018           if (result == 0) {
1019             /* Page is free; advance to the next possible allocation base */
1020             new_limit = (char *)
1021                 (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
1022                  & ~(GC_sysinfo.dwAllocationGranularity-1));
1023           } else
1024 #       endif
1025         /* else */ {
1026             if (result != sizeof(buf)) {
1027                 ABORT("Weird VirtualQuery result");
1028             }
1029             new_limit = (char *)p + buf.RegionSize;
1030             protect = buf.Protect;
1031             if (buf.State == MEM_COMMIT
1032                 && (protect == PAGE_EXECUTE_READWRITE
1033                     || protect == PAGE_EXECUTE_WRITECOPY
1034                     || protect == PAGE_READWRITE
1035                     || protect == PAGE_WRITECOPY)
1036                 && (buf.Type == MEM_IMAGE
1037 #                   ifdef GC_REGISTER_MEM_PRIVATE
1038                       || (protect == PAGE_READWRITE && buf.Type == MEM_PRIVATE)
1039 #                   else
1040                       /* There is some evidence that we cannot always   */
1041                       /* ignore MEM_PRIVATE sections under Windows ME   */
1042                       /* and predecessors.  Hence we now also check for */
1043                       /* that case.                                     */
1044                       || (!GC_wnt && buf.Type == MEM_PRIVATE)
1045 #                   endif
1046                    )
1047                 && !GC_is_heap_base(buf.AllocationBase)) {
1048 #               ifdef DEBUG_VIRTUALQUERY
1049                   GC_dump_meminfo(&buf);
1050 #               endif
1051                 if ((char *)p != limit) {
1052                     GC_cond_add_roots(base, limit);
1053                     base = (char *)p;
1054                 }
1055                 limit = new_limit;
1056             }
1057         }
1058         if ((word)p > (word)new_limit /* overflow */) break;
1059         p = (LPVOID)new_limit;
1060     }
1061     GC_cond_add_roots(base, limit);
1062   }
1063
1064 #endif /* MSWIN32 || MSWINCE || CYGWIN32 */
1065
1066 #if defined(ALPHA) && defined(OSF1)
1067
1068 #include <loader.h>
1069
1070 EXTERN_C_BEGIN
1071 extern char *sys_errlist[];
1072 extern int sys_nerr;
1073 extern int errno;
1074 EXTERN_C_END
1075
1076 GC_INNER void GC_register_dynamic_libraries(void)
1077 {
1078   ldr_module_t moduleid = LDR_NULL_MODULE;
1079   ldr_process_t mypid = ldr_my_process(); /* obtain id of this process */
1080
1081   /* For each module */
1082     while (TRUE) {
1083       ldr_module_info_t moduleinfo;
1084       size_t modulereturnsize;
1085       ldr_region_t region;
1086       ldr_region_info_t regioninfo;
1087       size_t regionreturnsize;
1088       int status = ldr_next_module(mypid, &moduleid);
1089                                 /* Get the next (first) module */
1090
1091       /* Any more modules? */
1092         if (moduleid == LDR_NULL_MODULE)
1093             break;    /* No more modules */
1094
1095       /* Check status AFTER checking moduleid because       */
1096       /* of a bug in the non-shared ldr_next_module stub.   */
1097         if (status != 0) {
1098           ABORT_ARG3("ldr_next_module failed",
1099                      ": status= %d, errcode= %d (%s)", status, errno,
1100                      errno < sys_nerr ? sys_errlist[errno] : "");
1101         }
1102
1103       /* Get the module information */
1104         status = ldr_inq_module(mypid, moduleid, &moduleinfo,
1105                                 sizeof(moduleinfo), &modulereturnsize);
1106         if (status != 0 )
1107             ABORT("ldr_inq_module failed");
1108
1109       /* is module for the main program (i.e. nonshared portion)? */
1110           if (moduleinfo.lmi_flags & LDR_MAIN)
1111               continue;    /* skip the main module */
1112
1113 #     ifdef DL_VERBOSE
1114         GC_log_printf("---Module---\n");
1115         GC_log_printf("Module ID\t = %16ld\n", moduleinfo.lmi_modid);
1116         GC_log_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion);
1117         GC_log_printf("flags for module = %16lx\n", moduleinfo.lmi_flags);
1118         GC_log_printf("module pathname\t = \"%s\"\n", moduleinfo.lmi_name);
1119 #     endif
1120
1121       /* For each region in this module */
1122         for (region = 0; region < moduleinfo.lmi_nregion; region++) {
1123           /* Get the region information */
1124             status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
1125                                     sizeof(regioninfo), &regionreturnsize);
1126             if (status != 0 )
1127                 ABORT("ldr_inq_region failed");
1128
1129           /* only process writable (data) regions */
1130             if (! (regioninfo.lri_prot & LDR_W))
1131                 continue;
1132
1133 #         ifdef DL_VERBOSE
1134             GC_log_printf("--- Region ---\n");
1135             GC_log_printf("Region number\t = %16ld\n",
1136                           regioninfo.lri_region_no);
1137             GC_log_printf("Protection flags = %016x\n", regioninfo.lri_prot);
1138             GC_log_printf("Virtual address\t = %16p\n", regioninfo.lri_vaddr);
1139             GC_log_printf("Mapped address\t = %16p\n",
1140                           regioninfo.lri_mapaddr);
1141             GC_log_printf("Region size\t = %16ld\n", regioninfo.lri_size);
1142             GC_log_printf("Region name\t = \"%s\"\n", regioninfo.lri_name);
1143 #         endif
1144
1145           /* register region as a garbage collection root */
1146           GC_add_roots_inner((char *)regioninfo.lri_mapaddr,
1147                         (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
1148                         TRUE);
1149
1150         }
1151     }
1152 }
1153 #endif
1154
1155 #if defined(HPUX)
1156
1157 #include <errno.h>
1158 #include <dl.h>
1159
1160 EXTERN_C_BEGIN
1161 extern char *sys_errlist[];
1162 extern int sys_nerr;
1163 EXTERN_C_END
1164
1165 GC_INNER void GC_register_dynamic_libraries(void)
1166 {
1167   int index = 1; /* Ordinal position in shared library search list */
1168
1169   /* For each dynamic library loaded */
1170     while (TRUE) {
1171       struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
1172       int status = shl_get(index, &shl_desc);
1173                                 /* Get info about next shared library   */
1174
1175       /* Check if this is the end of the list or if some error occurred */
1176         if (status != 0) {
1177 #        ifdef GC_HPUX_THREADS
1178            /* I've seen errno values of 0.  The man page is not clear   */
1179            /* as to whether errno should get set on a -1 return.        */
1180            break;
1181 #        else
1182           if (errno == EINVAL) {
1183             break; /* Moved past end of shared library list --> finished */
1184           } else {
1185             ABORT_ARG3("shl_get failed",
1186                        ": status= %d, errcode= %d (%s)", status, errno,
1187                        errno < sys_nerr ? sys_errlist[errno] : "");
1188           }
1189 #        endif
1190         }
1191
1192 #     ifdef DL_VERBOSE
1193         GC_log_printf("---Shared library---\n");
1194         GC_log_printf("\tfilename\t= \"%s\"\n", shl_desc->filename);
1195         GC_log_printf("\tindex\t\t= %d\n", index);
1196         GC_log_printf("\thandle\t\t= %08x\n",
1197                       (unsigned long) shl_desc->handle);
1198         GC_log_printf("\ttext seg.start\t= %08x\n", shl_desc->tstart);
1199         GC_log_printf("\ttext seg.end\t= %08x\n", shl_desc->tend);
1200         GC_log_printf("\tdata seg.start\t= %08x\n", shl_desc->dstart);
1201         GC_log_printf("\tdata seg.end\t= %08x\n", shl_desc->dend);
1202         GC_log_printf("\tref.count\t= %lu\n", shl_desc->ref_count);
1203 #     endif
1204
1205       /* register shared library's data segment as a garbage collection root */
1206         GC_add_roots_inner((char *) shl_desc->dstart,
1207                            (char *) shl_desc->dend, TRUE);
1208
1209         index++;
1210     }
1211 }
1212 #endif /* HPUX */
1213
1214 #ifdef AIX
1215 # pragma alloca
1216 # include <sys/ldr.h>
1217 # include <sys/errno.h>
1218   GC_INNER void GC_register_dynamic_libraries(void)
1219   {
1220       int ldibuflen = 8192;
1221
1222       for (;;) {
1223         int len;
1224         struct ld_info *ldi;
1225 #       if defined(CPPCHECK)
1226           char ldibuf[ldibuflen];
1227 #       else
1228           char *ldibuf = alloca(ldibuflen);
1229 #       endif
1230
1231         len = loadquery(L_GETINFO, ldibuf, ldibuflen);
1232         if (len < 0) {
1233                 if (errno != ENOMEM) {
1234                         ABORT("loadquery failed");
1235                 }
1236                 ldibuflen *= 2;
1237                 continue;
1238         }
1239
1240         ldi = (struct ld_info *)ldibuf;
1241         while (ldi) {
1242                 len = ldi->ldinfo_next;
1243                 GC_add_roots_inner(
1244                                 ldi->ldinfo_dataorg,
1245                                 (ptr_t)(unsigned long)ldi->ldinfo_dataorg
1246                                 + ldi->ldinfo_datasize,
1247                                 TRUE);
1248                 ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
1249         }
1250         break;
1251       }
1252   }
1253 #endif /* AIX */
1254
1255 #ifdef DARWIN
1256
1257 /* __private_extern__ hack required for pre-3.4 gcc versions.   */
1258 #ifndef __private_extern__
1259 # define __private_extern__ extern
1260 # include <mach-o/dyld.h>
1261 # undef __private_extern__
1262 #else
1263 # include <mach-o/dyld.h>
1264 #endif
1265 #include <mach-o/getsect.h>
1266
1267 /*#define DARWIN_DEBUG*/
1268
1269 /* Writable sections generally available on Darwin.     */
1270 STATIC const struct dyld_sections_s {
1271     const char *seg;
1272     const char *sect;
1273 } GC_dyld_sections[] = {
1274     { SEG_DATA, SECT_DATA },
1275     /* Used by FSF GCC, but not by OS X system tools, so far.   */
1276     { SEG_DATA, "__static_data" },
1277     { SEG_DATA, SECT_BSS },
1278     { SEG_DATA, SECT_COMMON },
1279     /* FSF GCC - zero-sized object sections for targets         */
1280     /*supporting section anchors.                               */
1281     { SEG_DATA, "__zobj_data" },
1282     { SEG_DATA, "__zobj_bss" }
1283 };
1284
1285 /* Additional writable sections:                                */
1286 /* GCC on Darwin constructs aligned sections "on demand", where */
1287 /* the alignment size is embedded in the section name.          */
1288 /* Furthermore, there are distinctions between sections         */
1289 /* containing private vs. public symbols.  It also constructs   */
1290 /* sections specifically for zero-sized objects, when the       */
1291 /* target supports section anchors.                             */
1292 STATIC const char * const GC_dyld_add_sect_fmts[] = {
1293   "__bss%u",
1294   "__pu_bss%u",
1295   "__zo_bss%u",
1296   "__zo_pu_bss%u"
1297 };
1298
1299 /* Currently, mach-o will allow up to the max of 2^15 alignment */
1300 /* in an object file.                                           */
1301 #ifndef L2_MAX_OFILE_ALIGNMENT
1302 # define L2_MAX_OFILE_ALIGNMENT 15
1303 #endif
1304
1305 STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr)
1306 {
1307     unsigned long i, c;
1308     c = _dyld_image_count();
1309     for (i = 0; i < c; i++)
1310       if ((const struct GC_MACH_HEADER *)_dyld_get_image_header(i) == hdr)
1311         return _dyld_get_image_name(i);
1312     return NULL;
1313 }
1314
1315 /* This should never be called by a thread holding the lock.    */
1316 STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr,
1317                               intptr_t slide)
1318 {
1319   unsigned long start, end;
1320   unsigned i, j;
1321   const struct GC_MACH_SECTION *sec;
1322   const char *name;
1323   GC_has_static_roots_func callback = GC_has_static_roots;
1324   DCL_LOCK_STATE;
1325
1326   if (GC_no_dls) return;
1327 # ifdef DARWIN_DEBUG
1328     name = GC_dyld_name_for_hdr(hdr);
1329 # else
1330     name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL;
1331 # endif
1332   for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) {
1333     sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
1334                            GC_dyld_sections[i].sect);
1335     if (sec == NULL || sec->size < sizeof(word))
1336       continue;
1337     start = slide + sec->addr;
1338     end = start + sec->size;
1339     LOCK();
1340     /* The user callback is called holding the lock.    */
1341     if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) {
1342 #     ifdef DARWIN_DEBUG
1343         GC_log_printf(
1344               "Adding section __DATA,%s at %p-%p (%lu bytes) from image %s\n",
1345                GC_dyld_sections[i].sect, (void*)start, (void*)end,
1346                (unsigned long)sec->size, name);
1347 #     endif
1348       GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE);
1349     }
1350     UNLOCK();
1351   }
1352
1353   /* Sections constructed on demand.    */
1354   for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
1355     const char *fmt = GC_dyld_add_sect_fmts[j];
1356
1357     /* Add our manufactured aligned BSS sections.       */
1358     for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
1359       char secnam[16];
1360
1361       (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
1362       secnam[sizeof(secnam) - 1] = '\0';
1363       sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
1364       if (sec == NULL || sec->size == 0)
1365         continue;
1366       start = slide + sec->addr;
1367       end = start + sec->size;
1368 #     ifdef DARWIN_DEBUG
1369         GC_log_printf("Adding on-demand section __DATA,%s at"
1370                       " %p-%p (%lu bytes) from image %s\n",
1371                       secnam, (void*)start, (void*)end,
1372                       (unsigned long)sec->size, name);
1373 #     endif
1374       GC_add_roots((char*)start, (char*)end);
1375     }
1376   }
1377
1378 # if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING)
1379     LOCK();
1380     GC_print_static_roots();
1381     UNLOCK();
1382 # endif
1383 }
1384
1385 /* This should never be called by a thread holding the lock.    */
1386 STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr,
1387                                  intptr_t slide)
1388 {
1389   unsigned long start, end;
1390   unsigned i, j;
1391   const struct GC_MACH_SECTION *sec;
1392 # if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING)
1393     DCL_LOCK_STATE;
1394 # endif
1395
1396   for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) {
1397     sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
1398                            GC_dyld_sections[i].sect);
1399     if (sec == NULL || sec->size == 0)
1400       continue;
1401     start = slide + sec->addr;
1402     end = start + sec->size;
1403 #   ifdef DARWIN_DEBUG
1404       GC_log_printf(
1405             "Removing section __DATA,%s at %p-%p (%lu bytes) from image %s\n",
1406             GC_dyld_sections[i].sect, (void*)start, (void*)end,
1407             (unsigned long)sec->size, GC_dyld_name_for_hdr(hdr));
1408 #   endif
1409     GC_remove_roots((char*)start, (char*)end);
1410   }
1411
1412   /* Remove our on-demand sections.     */
1413   for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
1414     const char *fmt = GC_dyld_add_sect_fmts[j];
1415
1416     for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
1417       char secnam[16];
1418
1419       (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
1420       secnam[sizeof(secnam) - 1] = '\0';
1421       sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
1422       if (sec == NULL || sec->size == 0)
1423         continue;
1424       start = slide + sec->addr;
1425       end = start + sec->size;
1426 #     ifdef DARWIN_DEBUG
1427         GC_log_printf("Removing on-demand section __DATA,%s at"
1428                       " %p-%p (%lu bytes) from image %s\n", secnam,
1429                       (void*)start, (void*)end, (unsigned long)sec->size,
1430                       GC_dyld_name_for_hdr(hdr));
1431 #     endif
1432       GC_remove_roots((char*)start, (char*)end);
1433     }
1434   }
1435
1436 # if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING)
1437     LOCK();
1438     GC_print_static_roots();
1439     UNLOCK();
1440 # endif
1441 }
1442
1443 GC_INNER void GC_register_dynamic_libraries(void)
1444 {
1445     /* Currently does nothing. The callbacks are setup by GC_init_dyld()
1446     The dyld library takes it from there. */
1447 }
1448
1449 /* The _dyld_* functions have an internal lock so no _dyld functions
1450    can be called while the world is stopped without the risk of a deadlock.
1451    Because of this we MUST setup callbacks BEFORE we ever stop the world.
1452    This should be called BEFORE any thread in created and WITHOUT the
1453    allocation lock held. */
1454
1455 GC_INNER void GC_init_dyld(void)
1456 {
1457   static GC_bool initialized = FALSE;
1458
1459   if (initialized) return;
1460
1461 # ifdef DARWIN_DEBUG
1462     GC_log_printf("Registering dyld callbacks...\n");
1463 # endif
1464
1465   /* Apple's Documentation:
1466      When you call _dyld_register_func_for_add_image, the dynamic linker
1467      runtime calls the specified callback (func) once for each of the images
1468      that is currently loaded into the program. When a new image is added to
1469      the program, your callback is called again with the mach_header for the
1470      new image, and the virtual memory slide amount of the new image.
1471
1472      This WILL properly register already linked libraries and libraries
1473      linked in the future.
1474   */
1475   _dyld_register_func_for_add_image(
1476         (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_add);
1477   _dyld_register_func_for_remove_image(
1478         (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_remove);
1479                         /* Structure mach_header64 has the same fields  */
1480                         /* as mach_header except for the reserved one   */
1481                         /* at the end, so these casts are OK.           */
1482
1483   /* Set this early to avoid reentrancy issues. */
1484   initialized = TRUE;
1485
1486 # ifdef NO_DYLD_BIND_FULLY_IMAGE
1487     /* FIXME: What should we do in this case?   */
1488 # else
1489     if (GC_no_dls) return; /* skip main data segment registration */
1490
1491     /* When the environment variable is set, the dynamic linker binds   */
1492     /* all undefined symbols the application needs at launch time.      */
1493     /* This includes function symbols that are normally bound lazily at */
1494     /* the time of their first invocation.                              */
1495     if (GETENV("DYLD_BIND_AT_LAUNCH") == 0) {
1496       /* The environment variable is unset, so we should bind manually. */
1497 #     ifdef DARWIN_DEBUG
1498         GC_log_printf("Forcing full bind of GC code...\n");
1499 #     endif
1500       /* FIXME: '_dyld_bind_fully_image_containing_address' is deprecated. */
1501       if (!_dyld_bind_fully_image_containing_address(
1502                                                   (unsigned long *)GC_malloc))
1503         ABORT("_dyld_bind_fully_image_containing_address failed");
1504     }
1505 # endif
1506 }
1507
1508 #define HAVE_REGISTER_MAIN_STATIC_DATA
1509 GC_INNER GC_bool GC_register_main_static_data(void)
1510 {
1511   /* Already done through dyld callbacks */
1512   return FALSE;
1513 }
1514
1515 #endif /* DARWIN */
1516
1517 #if defined(HAIKU)
1518 # include <kernel/image.h>
1519
1520   GC_INNER void GC_register_dynamic_libraries(void)
1521   {
1522     image_info info;
1523     int32 cookie = 0;
1524
1525     while (get_next_image_info(0, &cookie, &info) == B_OK) {
1526       ptr_t data = (ptr_t)info.data;
1527       GC_add_roots_inner(data, data + info.data_size, TRUE);
1528     }
1529   }
1530 #endif /* HAIKU */
1531
1532 #elif defined(PCR)
1533
1534 # include "il/PCR_IL.h"
1535 # include "th/PCR_ThCtl.h"
1536 # include "mm/PCR_MM.h"
1537
1538   GC_INNER void GC_register_dynamic_libraries(void)
1539   {
1540     /* Add new static data areas of dynamically loaded modules. */
1541     PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1542     PCR_IL_LoadedSegment * q;
1543
1544     /* Skip uncommitted files */
1545     while (p != NIL && !(p -> lf_commitPoint)) {
1546         /* The loading of this file has not yet been committed    */
1547         /* Hence its description could be inconsistent.           */
1548         /* Furthermore, it hasn't yet been run.  Hence its data   */
1549         /* segments can't possibly reference heap allocated       */
1550         /* objects.                                               */
1551         p = p -> lf_prev;
1552     }
1553     for (; p != NIL; p = p -> lf_prev) {
1554       for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1555         if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1556             == PCR_IL_SegFlags_Traced_on) {
1557           GC_add_roots_inner((ptr_t)q->ls_addr,
1558                              (ptr_t)q->ls_addr + q->ls_bytes, TRUE);
1559         }
1560       }
1561     }
1562   }
1563 #endif /* PCR && !DYNAMIC_LOADING && !MSWIN32 */
1564
1565 #if !defined(HAVE_REGISTER_MAIN_STATIC_DATA) && defined(DYNAMIC_LOADING)
1566   /* Do we need to separately register the main static data segment? */
1567   GC_INNER GC_bool GC_register_main_static_data(void)
1568   {
1569     return TRUE;
1570   }
1571 #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
1572
1573 /* Register a routine to filter dynamic library registration.  */
1574 GC_API void GC_CALL GC_register_has_static_roots_callback(
1575                                         GC_has_static_roots_func callback)
1576 {
1577     GC_has_static_roots = callback;
1578 }