Blame dyn_load.c

Packit d28291
/*
Packit d28291
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
Packit d28291
 * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
Packit d28291
 *
Packit d28291
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
Packit d28291
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
Packit d28291
 *
Packit d28291
 * Permission is hereby granted to use or copy this program
Packit d28291
 * for any purpose,  provided the above notices are retained on all copies.
Packit d28291
 * Permission to modify the code and to distribute modified code is granted,
Packit d28291
 * provided the above notices are retained, and a notice that the code was
Packit d28291
 * modified is included with the above copyright notice.
Packit d28291
 */
Packit d28291
Packit d28291
#include "private/gc_priv.h"
Packit d28291
Packit d28291
/*
Packit d28291
 * This is incredibly OS specific code for tracking down data sections in
Packit d28291
 * dynamic libraries.  There appears to be no way of doing this quickly
Packit d28291
 * without groveling through undocumented data structures.  We would argue
Packit d28291
 * that this is a bug in the design of the dlopen interface.  THIS CODE
Packit d28291
 * MAY BREAK IN FUTURE OS RELEASES.  If this matters to you, don't hesitate
Packit d28291
 * to let your vendor know ...
Packit d28291
 *
Packit d28291
 * None of this is safe with dlclose and incremental collection.
Packit d28291
 * But then not much of anything is safe in the presence of dlclose.
Packit d28291
 */
Packit d28291
Packit d28291
#if !defined(MACOS) && !defined(_WIN32_WCE) && !defined(__CC_ARM)
Packit d28291
# include <sys/types.h>
Packit d28291
#endif
Packit d28291
Packit d28291
/* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
Packit d28291
#undef GC_MUST_RESTORE_REDEFINED_DLOPEN
Packit d28291
#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) \
Packit d28291
    && !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP)
Packit d28291
  /* To support threads in Solaris, gc.h interposes on dlopen by        */
Packit d28291
  /* defining "dlopen" to be "GC_dlopen", which is implemented below.   */
Packit d28291
  /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the    */
Packit d28291
  /* real system dlopen() in their implementation. We first remove      */
Packit d28291
  /* gc.h's dlopen definition and restore it later, after GC_dlopen().  */
Packit d28291
# undef dlopen
Packit d28291
# define GC_MUST_RESTORE_REDEFINED_DLOPEN
Packit d28291
#endif /* !GC_NO_DLOPEN */
Packit d28291
Packit d28291
/* A user-supplied routine (custom filter) that might be called to      */
Packit d28291
/* determine whether a DSO really needs to be scanned by the GC.        */
Packit d28291
/* 0 means no filter installed.  May be unused on some platforms.       */
Packit d28291
/* FIXME: Add filter support for more platforms.                        */
Packit d28291
STATIC GC_has_static_roots_func GC_has_static_roots = 0;
Packit d28291
Packit d28291
#if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \
Packit d28291
    || defined(CYGWIN32)) && !defined(PCR)
Packit d28291
Packit d28291
#if !defined(DARWIN) && !defined(SCO_ELF) && !defined(SOLARISDL) \
Packit d28291
    && !defined(AIX) && !defined(DGUX) && !defined(IRIX5) && !defined(HPUX) \
Packit d28291
    && !defined(CYGWIN32) && !defined(MSWIN32) && !defined(MSWINCE) \
Packit d28291
    && !(defined(ALPHA) && defined(OSF1)) \
Packit d28291
    && !(defined(FREEBSD) && defined(__ELF__)) \
Packit d28291
    && !((defined(LINUX) || defined(NACL)) && defined(__ELF__)) \
Packit d28291
    && !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) \
Packit d28291
    && !(defined(OPENBSD) && (defined(__ELF__) || defined(M68K))) \
Packit d28291
    && !defined(CPPCHECK)
Packit d28291
# error We only know how to find data segments of dynamic libraries for above.
Packit d28291
# error Additional SVR4 variants might not be too hard to add.
Packit d28291
#endif
Packit d28291
Packit d28291
#include <stdio.h>
Packit d28291
#ifdef SOLARISDL
Packit d28291
#   include <sys/elf.h>
Packit d28291
#   include <dlfcn.h>
Packit d28291
#   include <link.h>
Packit d28291
#endif
Packit d28291
Packit d28291
#if defined(NETBSD)
Packit d28291
#   include <sys/param.h>
Packit d28291
#   include <dlfcn.h>
Packit d28291
#   include <machine/elf_machdep.h>
Packit d28291
#   define ELFSIZE ARCH_ELFSIZE
Packit d28291
#endif
Packit d28291
Packit d28291
#if defined(OPENBSD)
Packit d28291
# include <sys/param.h>
Packit d28291
# if (OpenBSD >= 200519) && !defined(HAVE_DL_ITERATE_PHDR)
Packit d28291
#   define HAVE_DL_ITERATE_PHDR
Packit d28291
# endif
Packit d28291
#endif /* OPENBSD */
Packit d28291
Packit d28291
#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \
Packit d28291
    || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
Packit d28291
                             || defined(NACL) || defined(NETBSD) \
Packit d28291
                             || defined(OPENBSD)))
Packit d28291
# include <stddef.h>
Packit d28291
# if !defined(OPENBSD) && !defined(PLATFORM_ANDROID)
Packit d28291
    /* OpenBSD does not have elf.h file; link.h below is sufficient.    */
Packit d28291
    /* Exclude Android because linker.h below includes its own version. */
Packit d28291
#   include <elf.h>
Packit d28291
# endif
Packit d28291
# ifdef PLATFORM_ANDROID
Packit d28291
    /* If you don't need the "dynamic loading" feature, you may build   */
Packit d28291
    /* the collector with -D IGNORE_DYNAMIC_LOADING.                    */
Packit d28291
#   ifdef BIONIC_ELFDATA_REDEF_BUG
Packit d28291
      /* Workaround a problem in Bionic (as of Android 4.2) which has   */
Packit d28291
      /* mismatching ELF_DATA definitions in sys/exec_elf.h and         */
Packit d28291
      /* asm/elf.h included from linker.h file (similar to EM_ALPHA).   */
Packit d28291
#     include <asm/elf.h>
Packit d28291
#     include <linux/elf-em.h>
Packit d28291
#     undef ELF_DATA
Packit d28291
#     undef EM_ALPHA
Packit d28291
#   endif
Packit d28291
#   include <link.h>
Packit d28291
#   if !defined(GC_DONT_DEFINE_LINK_MAP) && !(__ANDROID_API__ >= 21)
Packit d28291
      /* link_map and r_debug are defined in link.h of NDK r10+.        */
Packit d28291
      /* bionic/linker/linker.h defines them too but the header         */
Packit d28291
      /* itself is a C++ one starting from Android 4.3.                 */
Packit d28291
      struct link_map {
Packit d28291
        uintptr_t l_addr;
Packit d28291
        char* l_name;
Packit d28291
        uintptr_t l_ld;
Packit d28291
        struct link_map* l_next;
Packit d28291
        struct link_map* l_prev;
Packit d28291
      };
Packit d28291
      struct r_debug {
Packit d28291
        int32_t r_version;
Packit d28291
        struct link_map* r_map;
Packit d28291
        void (*r_brk)(void);
Packit d28291
        int32_t r_state;
Packit d28291
        uintptr_t r_ldbase;
Packit d28291
      };
Packit d28291
#   endif
Packit d28291
# else
Packit d28291
#   include <link.h>
Packit d28291
# endif
Packit d28291
#endif
Packit d28291
Packit d28291
/* Newer versions of GNU/Linux define this macro.  We
Packit d28291
 * define it similarly for any ELF systems that don't.  */
Packit d28291
#  ifndef ElfW
Packit d28291
#    if defined(FREEBSD)
Packit d28291
#      if __ELF_WORD_SIZE == 32
Packit d28291
#        define ElfW(type) Elf32_##type
Packit d28291
#      else
Packit d28291
#        define ElfW(type) Elf64_##type
Packit d28291
#      endif
Packit d28291
#    elif defined(NETBSD) || defined(OPENBSD)
Packit d28291
#      if ELFSIZE == 32
Packit d28291
#        define ElfW(type) Elf32_##type
Packit d28291
#      else
Packit d28291
#        define ElfW(type) Elf64_##type
Packit d28291
#      endif
Packit d28291
#    else
Packit d28291
#      if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
Packit d28291
#        define ElfW(type) Elf32_##type
Packit d28291
#      else
Packit d28291
#        define ElfW(type) Elf64_##type
Packit d28291
#      endif
Packit d28291
#    endif
Packit d28291
#  endif
Packit d28291
Packit d28291
#if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES)
Packit d28291
Packit d28291
#ifdef LINT
Packit d28291
    Elf32_Dyn _DYNAMIC;
Packit d28291
#endif
Packit d28291
Packit d28291
STATIC struct link_map *
Packit d28291
GC_FirstDLOpenedLinkMap(void)
Packit d28291
{
Packit d28291
    extern ElfW(Dyn) _DYNAMIC;
Packit d28291
    ElfW(Dyn) *dp;
Packit d28291
    static struct link_map * cachedResult = 0;
Packit d28291
    static ElfW(Dyn) *dynStructureAddr = 0;
Packit d28291
                /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug   */
Packit d28291
Packit d28291
#   ifdef SUNOS53_SHARED_LIB
Packit d28291
        /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */
Packit d28291
        /* up properly in dynamically linked .so's. This means we have  */
Packit d28291
        /* to use its value in the set of original object files loaded  */
Packit d28291
        /* at program startup.                                          */
Packit d28291
        if( dynStructureAddr == 0 ) {
Packit d28291
          void* startupSyms = dlopen(0, RTLD_LAZY);
Packit d28291
          dynStructureAddr = (ElfW(Dyn)*)(word)dlsym(startupSyms, "_DYNAMIC");
Packit d28291
        }
Packit d28291
#   else
Packit d28291
        dynStructureAddr = &_DYNAMIC;
Packit d28291
#   endif
Packit d28291
Packit d28291
    if (0 == COVERT_DATAFLOW(dynStructureAddr)) {
Packit d28291
        /* _DYNAMIC symbol not resolved. */
Packit d28291
        return(0);
Packit d28291
    }
Packit d28291
    if (cachedResult == 0) {
Packit d28291
        int tag;
Packit d28291
        for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
Packit d28291
            if (tag == DT_DEBUG) {
Packit d28291
                struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr;
Packit d28291
                if (rd != NULL) {
Packit d28291
                    struct link_map *lm = rd->r_map;
Packit d28291
                    if (lm != NULL)
Packit d28291
                        cachedResult = lm->l_next; /* might be NULL */
Packit d28291
                }
Packit d28291
                break;
Packit d28291
            }
Packit d28291
        }
Packit d28291
    }
Packit d28291
    return cachedResult;
Packit d28291
}
Packit d28291
Packit d28291
#endif /* SOLARISDL ... */
Packit d28291
Packit d28291
/* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
Packit d28291
# ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN
Packit d28291
#   define dlopen GC_dlopen
Packit d28291
# endif
Packit d28291
Packit d28291
# if defined(SOLARISDL)
Packit d28291
Packit d28291
/* Add dynamic library data sections to the root set.           */
Packit d28291
# if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS) \
Packit d28291
     && !defined(CPPCHECK)
Packit d28291
#   error Fix mutual exclusion with dlopen
Packit d28291
# endif
Packit d28291
Packit d28291
# ifndef USE_PROC_FOR_LIBRARIES
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
  struct link_map *lm;
Packit d28291
Packit d28291
  for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) {
Packit d28291
        ElfW(Ehdr) * e;
Packit d28291
        ElfW(Phdr) * p;
Packit d28291
        unsigned long offset;
Packit d28291
        char * start;
Packit d28291
        int i;
Packit d28291
Packit d28291
        e = (ElfW(Ehdr) *) lm->l_addr;
Packit d28291
        p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
Packit d28291
        offset = ((unsigned long)(lm->l_addr));
Packit d28291
        for( i = 0; i < (int)e->e_phnum; i++, p++ ) {
Packit d28291
          switch( p->p_type ) {
Packit d28291
            case PT_LOAD:
Packit d28291
              {
Packit d28291
                if( !(p->p_flags & PF_W) ) break;
Packit d28291
                start = ((char *)(p->p_vaddr)) + offset;
Packit d28291
                GC_add_roots_inner(
Packit d28291
                  start,
Packit d28291
                  start + p->p_memsz,
Packit d28291
                  TRUE
Packit d28291
                );
Packit d28291
              }
Packit d28291
              break;
Packit d28291
            default:
Packit d28291
              break;
Packit d28291
          }
Packit d28291
        }
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
# endif /* !USE_PROC ... */
Packit d28291
# endif /* SOLARISDL */
Packit d28291
Packit d28291
#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \
Packit d28291
    || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
Packit d28291
                             || defined(NACL) || defined(NETBSD) \
Packit d28291
                             || defined(OPENBSD)))
Packit d28291
Packit d28291
#ifdef USE_PROC_FOR_LIBRARIES
Packit d28291
Packit d28291
#include <string.h>
Packit d28291
Packit d28291
#include <sys/stat.h>
Packit d28291
#include <fcntl.h>
Packit d28291
#include <unistd.h>
Packit d28291
Packit d28291
#define MAPS_BUF_SIZE (32*1024)
Packit d28291
Packit d28291
/* Sort an array of HeapSects by start address.                         */
Packit d28291
/* Unfortunately at least some versions of                              */
Packit d28291
/* Linux qsort end up calling malloc by way of sysconf, and hence can't */
Packit d28291
/* be used in the collector.  Hence we roll our own.  Should be         */
Packit d28291
/* reasonably fast if the array is already mostly sorted, as we expect  */
Packit d28291
/* it to be.                                                            */
Packit d28291
static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements)
Packit d28291
{
Packit d28291
    signed_word n = (signed_word)number_of_elements;
Packit d28291
    signed_word nsorted = 1;
Packit d28291
Packit d28291
    while (nsorted < n) {
Packit d28291
      signed_word i;
Packit d28291
Packit d28291
      while (nsorted < n &&
Packit d28291
             (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start)
Packit d28291
          ++nsorted;
Packit d28291
      if (nsorted == n) break;
Packit d28291
      GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start);
Packit d28291
      i = nsorted - 1;
Packit d28291
      while (i >= 0 && (word)base[i].hs_start > (word)base[i+1].hs_start) {
Packit d28291
        struct HeapSect tmp = base[i];
Packit d28291
        base[i] = base[i+1];
Packit d28291
        base[i+1] = tmp;
Packit d28291
        --i;
Packit d28291
      }
Packit d28291
      GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start);
Packit d28291
      ++nsorted;
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
STATIC word GC_register_map_entries(char *maps)
Packit d28291
{
Packit d28291
    char *prot;
Packit d28291
    char *buf_ptr = maps;
Packit d28291
    ptr_t start, end;
Packit d28291
    unsigned int maj_dev;
Packit d28291
    ptr_t least_ha, greatest_ha;
Packit d28291
    unsigned i;
Packit d28291
Packit d28291
    GC_ASSERT(I_HOLD_LOCK());
Packit d28291
    sort_heap_sects(GC_our_memory, GC_n_memory);
Packit d28291
    least_ha = GC_our_memory[0].hs_start;
Packit d28291
    greatest_ha = GC_our_memory[GC_n_memory-1].hs_start
Packit d28291
                  + GC_our_memory[GC_n_memory-1].hs_bytes;
Packit d28291
Packit d28291
    for (;;) {
Packit d28291
        buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot,
Packit d28291
                                     &maj_dev, 0);
Packit d28291
        if (buf_ptr == NULL) return 1;
Packit d28291
        if (prot[1] == 'w') {
Packit d28291
            /* This is a writable mapping.  Add it to           */
Packit d28291
            /* the root set unless it is already otherwise      */
Packit d28291
            /* accounted for.                                   */
Packit d28291
            if ((word)start <= (word)GC_stackbottom
Packit d28291
                && (word)end >= (word)GC_stackbottom) {
Packit d28291
                /* Stack mapping; discard       */
Packit d28291
                continue;
Packit d28291
            }
Packit d28291
#           ifdef THREADS
Packit d28291
              /* This may fail, since a thread may already be           */
Packit d28291
              /* unregistered, but its thread stack may still be there. */
Packit d28291
              /* That can fail because the stack may disappear while    */
Packit d28291
              /* we're marking.  Thus the marker is, and has to be      */
Packit d28291
              /* prepared to recover from segmentation faults.          */
Packit d28291
Packit d28291
              if (GC_segment_is_thread_stack(start, end)) continue;
Packit d28291
Packit d28291
              /* FIXME: NPTL squirrels                                  */
Packit d28291
              /* away pointers in pieces of the stack segment that we   */
Packit d28291
              /* don't scan.  We work around this                       */
Packit d28291
              /* by treating anything allocated by libpthread as        */
Packit d28291
              /* uncollectible, as we do in some other cases.           */
Packit d28291
              /* A specifically identified problem is that              */
Packit d28291
              /* thread stacks contain pointers to dynamic thread       */
Packit d28291
              /* vectors, which may be reused due to thread caching.    */
Packit d28291
              /* They may not be marked if the thread is still live.    */
Packit d28291
              /* This specific instance should be addressed by          */
Packit d28291
              /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite     */
Packit d28291
              /* seem to suffice.                                       */
Packit d28291
              /* We currently trace entire thread stacks, if they are   */
Packit d28291
              /* are currently cached but unused.  This is              */
Packit d28291
              /* very suboptimal for performance reasons.               */
Packit d28291
#           endif
Packit d28291
            /* We no longer exclude the main data segment.              */
Packit d28291
            if ((word)end <= (word)least_ha
Packit d28291
                || (word)start >= (word)greatest_ha) {
Packit d28291
              /* The easy case; just trace entire segment */
Packit d28291
              GC_add_roots_inner((char *)start, (char *)end, TRUE);
Packit d28291
              continue;
Packit d28291
            }
Packit d28291
            /* Add sections that don't belong to us. */
Packit d28291
              i = 0;
Packit d28291
              while ((word)(GC_our_memory[i].hs_start
Packit d28291
                                + GC_our_memory[i].hs_bytes) < (word)start)
Packit d28291
                  ++i;
Packit d28291
              GC_ASSERT(i < GC_n_memory);
Packit d28291
              if ((word)GC_our_memory[i].hs_start <= (word)start) {
Packit d28291
                  start = GC_our_memory[i].hs_start
Packit d28291
                          + GC_our_memory[i].hs_bytes;
Packit d28291
                  ++i;
Packit d28291
              }
Packit d28291
              while (i < GC_n_memory
Packit d28291
                     && (word)GC_our_memory[i].hs_start < (word)end
Packit d28291
                     && (word)start < (word)end) {
Packit d28291
                  if ((word)start < (word)GC_our_memory[i].hs_start)
Packit d28291
                    GC_add_roots_inner((char *)start,
Packit d28291
                                       GC_our_memory[i].hs_start, TRUE);
Packit d28291
                  start = GC_our_memory[i].hs_start
Packit d28291
                          + GC_our_memory[i].hs_bytes;
Packit d28291
                  ++i;
Packit d28291
              }
Packit d28291
              if ((word)start < (word)end)
Packit d28291
                  GC_add_roots_inner((char *)start, (char *)end, TRUE);
Packit d28291
        }
Packit d28291
    }
Packit d28291
    return 1;
Packit d28291
}
Packit d28291
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
    if (!GC_register_map_entries(GC_get_maps()))
Packit d28291
        ABORT("Failed to read /proc for library registration");
Packit d28291
}
Packit d28291
Packit d28291
/* We now take care of the main data segment ourselves: */
Packit d28291
GC_INNER GC_bool GC_register_main_static_data(void)
Packit d28291
{
Packit d28291
    return FALSE;
Packit d28291
}
Packit d28291
Packit d28291
# define HAVE_REGISTER_MAIN_STATIC_DATA
Packit d28291
Packit d28291
#else /* !USE_PROC_FOR_LIBRARIES */
Packit d28291
Packit d28291
/* The following is the preferred way to walk dynamic libraries */
Packit d28291
/* for glibc 2.2.4+.  Unfortunately, it doesn't work for older  */
Packit d28291
/* versions.  Thanks to Jakub Jelinek for most of the code.     */
Packit d28291
Packit d28291
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
Packit d28291
    || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)) \
Packit d28291
    || defined(PLATFORM_ANDROID) /* Are others OK here, too? */
Packit d28291
# ifndef HAVE_DL_ITERATE_PHDR
Packit d28291
#   define HAVE_DL_ITERATE_PHDR
Packit d28291
# endif
Packit d28291
# ifdef PLATFORM_ANDROID
Packit d28291
    /* Android headers might have no such definition for some targets.  */
Packit d28291
    int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *),
Packit d28291
                        void *data);
Packit d28291
# endif
Packit d28291
#endif /* __GLIBC__ >= 2 || PLATFORM_ANDROID */
Packit d28291
Packit d28291
#if (defined(FREEBSD) && __FreeBSD__ >= 7) || defined(__DragonFly__)
Packit d28291
  /* On the FreeBSD system, any target system at major version 7 shall   */
Packit d28291
  /* have dl_iterate_phdr; therefore, we need not make it weak as below. */
Packit d28291
# ifndef HAVE_DL_ITERATE_PHDR
Packit d28291
#   define HAVE_DL_ITERATE_PHDR
Packit d28291
# endif
Packit d28291
# define DL_ITERATE_PHDR_STRONG
Packit d28291
#elif defined(HAVE_DL_ITERATE_PHDR)
Packit d28291
  /* We have the header files for a glibc that includes dl_iterate_phdr.*/
Packit d28291
  /* It may still not be available in the library on the target system. */
Packit d28291
  /* Thus we also treat it as a weak symbol.                            */
Packit d28291
# pragma weak dl_iterate_phdr
Packit d28291
#endif
Packit d28291
Packit d28291
#if defined(HAVE_DL_ITERATE_PHDR)
Packit d28291
Packit d28291
# ifdef PT_GNU_RELRO
Packit d28291
/* Instead of registering PT_LOAD sections directly, we keep them       */
Packit d28291
/* in a temporary list, and filter them by excluding PT_GNU_RELRO       */
Packit d28291
/* segments.  Processing PT_GNU_RELRO sections with                     */
Packit d28291
/* GC_exclude_static_roots instead would be superficially cleaner.  But */
Packit d28291
/* it runs into trouble if a client registers an overlapping segment,   */
Packit d28291
/* which unfortunately seems quite possible.                            */
Packit d28291
Packit d28291
#   define MAX_LOAD_SEGS MAX_ROOT_SETS
Packit d28291
Packit d28291
    static struct load_segment {
Packit d28291
      ptr_t start;
Packit d28291
      ptr_t end;
Packit d28291
      /* Room for a second segment if we remove a RELRO segment */
Packit d28291
      /* from the middle.                                       */
Packit d28291
      ptr_t start2;
Packit d28291
      ptr_t end2;
Packit d28291
    } load_segs[MAX_LOAD_SEGS];
Packit d28291
Packit d28291
    static int n_load_segs;
Packit d28291
    static GC_bool load_segs_overflow;
Packit d28291
# endif /* PT_GNU_RELRO */
Packit d28291
Packit d28291
STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info,
Packit d28291
                                       size_t size, void * ptr)
Packit d28291
{
Packit d28291
  const ElfW(Phdr) * p;
Packit d28291
  ptr_t start, end;
Packit d28291
  int i;
Packit d28291
Packit d28291
  /* Make sure struct dl_phdr_info is at least as big as we need.  */
Packit d28291
  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
Packit d28291
      + sizeof (info->dlpi_phnum))
Packit d28291
    return -1;
Packit d28291
Packit d28291
  p = info->dlpi_phdr;
Packit d28291
  for (i = 0; i < (int)info->dlpi_phnum; i++, p++) {
Packit d28291
    if (p->p_type == PT_LOAD) {
Packit d28291
      GC_has_static_roots_func callback = GC_has_static_roots;
Packit d28291
      if ((p->p_flags & PF_W) == 0) continue;
Packit d28291
Packit d28291
      start = (ptr_t)p->p_vaddr + info->dlpi_addr;
Packit d28291
      end = start + p->p_memsz;
Packit d28291
      if (callback != 0 && !callback(info->dlpi_name, start, p->p_memsz))
Packit d28291
        continue;
Packit d28291
#     ifdef PT_GNU_RELRO
Packit d28291
#       if CPP_WORDSZ == 64
Packit d28291
          /* TODO: GC_push_all eventually does the correct          */
Packit d28291
          /* rounding to the next multiple of ALIGNMENT, so, most   */
Packit d28291
          /* probably, we should remove the corresponding assertion */
Packit d28291
          /* check in GC_add_roots_inner along with this code line. */
Packit d28291
          /* start pointer value may require aligning.              */
Packit d28291
          start = (ptr_t)((word)start & ~(word)(sizeof(word) - 1));
Packit d28291
#       endif
Packit d28291
        if (n_load_segs >= MAX_LOAD_SEGS) {
Packit d28291
          if (!load_segs_overflow) {
Packit d28291
            WARN("Too many PT_LOAD segments;"
Packit d28291
                 " registering as roots directly...\n", 0);
Packit d28291
            load_segs_overflow = TRUE;
Packit d28291
          }
Packit d28291
          GC_add_roots_inner(start, end, TRUE);
Packit d28291
        } else {
Packit d28291
          load_segs[n_load_segs].start = start;
Packit d28291
          load_segs[n_load_segs].end = end;
Packit d28291
          load_segs[n_load_segs].start2 = 0;
Packit d28291
          load_segs[n_load_segs].end2 = 0;
Packit d28291
          ++n_load_segs;
Packit d28291
        }
Packit d28291
#     else
Packit d28291
        GC_add_roots_inner(start, end, TRUE);
Packit d28291
#     endif /* !PT_GNU_RELRO */
Packit d28291
    }
Packit d28291
  }
Packit d28291
Packit d28291
# ifdef PT_GNU_RELRO
Packit d28291
    p = info->dlpi_phdr;
Packit d28291
    for (i = 0; i < (int)info->dlpi_phnum; i++, p++) {
Packit d28291
      if (p->p_type == PT_GNU_RELRO) {
Packit d28291
        /* This entry is known to be constant and will eventually be    */
Packit d28291
        /* remapped as read-only.  However, the address range covered   */
Packit d28291
        /* by this entry is typically a subset of a previously          */
Packit d28291
        /* encountered "LOAD" segment, so we need to exclude it.        */
Packit d28291
        int j;
Packit d28291
Packit d28291
        start = (ptr_t)p->p_vaddr + info->dlpi_addr;
Packit d28291
        end = start + p->p_memsz;
Packit d28291
        for (j = n_load_segs; --j >= 0; ) {
Packit d28291
          if ((word)start >= (word)load_segs[j].start
Packit d28291
              && (word)start < (word)load_segs[j].end) {
Packit d28291
            if (load_segs[j].start2 != 0) {
Packit d28291
              WARN("More than one GNU_RELRO segment per load one\n",0);
Packit d28291
            } else {
Packit d28291
              GC_ASSERT((word)end <= (word)load_segs[j].end);
Packit d28291
              /* Remove from the existing load segment */
Packit d28291
              load_segs[j].end2 = load_segs[j].end;
Packit d28291
              load_segs[j].end = start;
Packit d28291
              load_segs[j].start2 = end;
Packit d28291
            }
Packit d28291
            break;
Packit d28291
          }
Packit d28291
          if (0 == j && 0 == GC_has_static_roots)
Packit d28291
            WARN("Failed to find PT_GNU_RELRO segment"
Packit d28291
                 " inside PT_LOAD region\n", 0);
Packit d28291
            /* No warning reported in case of the callback is present   */
Packit d28291
            /* because most likely the segment has been excluded.       */
Packit d28291
        }
Packit d28291
      }
Packit d28291
    }
Packit d28291
# endif
Packit d28291
Packit d28291
  *(int *)ptr = 1;     /* Signal that we were called */
Packit d28291
  return 0;
Packit d28291
}
Packit d28291
Packit d28291
/* Do we need to separately register the main static data segment? */
Packit d28291
GC_INNER GC_bool GC_register_main_static_data(void)
Packit d28291
{
Packit d28291
# ifdef DL_ITERATE_PHDR_STRONG
Packit d28291
    /* If dl_iterate_phdr is not a weak symbol then don't test against  */
Packit d28291
    /* zero (otherwise a compiler might issue a warning).               */
Packit d28291
    return FALSE;
Packit d28291
# else
Packit d28291
    return 0 == COVERT_DATAFLOW(dl_iterate_phdr);
Packit d28291
# endif
Packit d28291
}
Packit d28291
Packit d28291
/* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
Packit d28291
STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void)
Packit d28291
{
Packit d28291
  int did_something;
Packit d28291
  if (GC_register_main_static_data())
Packit d28291
    return FALSE;
Packit d28291
Packit d28291
# ifdef PT_GNU_RELRO
Packit d28291
    {
Packit d28291
      static GC_bool excluded_segs = FALSE;
Packit d28291
      n_load_segs = 0;
Packit d28291
      load_segs_overflow = FALSE;
Packit d28291
      if (!EXPECT(excluded_segs, TRUE)) {
Packit d28291
        GC_exclude_static_roots_inner((ptr_t)load_segs,
Packit d28291
                                      (ptr_t)load_segs + sizeof(load_segs));
Packit d28291
        excluded_segs = TRUE;
Packit d28291
      }
Packit d28291
    }
Packit d28291
# endif
Packit d28291
Packit d28291
  did_something = 0;
Packit d28291
  dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
Packit d28291
  if (did_something) {
Packit d28291
#   ifdef PT_GNU_RELRO
Packit d28291
      int i;
Packit d28291
Packit d28291
      for (i = 0; i < n_load_segs; ++i) {
Packit d28291
        if ((word)load_segs[i].end > (word)load_segs[i].start) {
Packit d28291
          GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE);
Packit d28291
        }
Packit d28291
        if ((word)load_segs[i].end2 > (word)load_segs[i].start2) {
Packit d28291
          GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE);
Packit d28291
        }
Packit d28291
      }
Packit d28291
#   endif
Packit d28291
  } else {
Packit d28291
      char *datastart;
Packit d28291
      char *dataend;
Packit d28291
#     ifdef DATASTART_IS_FUNC
Packit d28291
        static ptr_t datastart_cached = (ptr_t)(word)-1;
Packit d28291
Packit d28291
        /* Evaluate DATASTART only once.  */
Packit d28291
        if (datastart_cached == (ptr_t)(word)-1) {
Packit d28291
          datastart_cached = DATASTART;
Packit d28291
        }
Packit d28291
        datastart = (char *)datastart_cached;
Packit d28291
#     else
Packit d28291
        datastart = DATASTART;
Packit d28291
#     endif
Packit d28291
#     ifdef DATAEND_IS_FUNC
Packit d28291
        {
Packit d28291
          static ptr_t dataend_cached = 0;
Packit d28291
          /* Evaluate DATAEND only once. */
Packit d28291
          if (dataend_cached == 0) {
Packit d28291
            dataend_cached = DATAEND;
Packit d28291
          }
Packit d28291
          dataend = (char *)dataend_cached;
Packit d28291
        }
Packit d28291
#     else
Packit d28291
        dataend = DATAEND;
Packit d28291
#     endif
Packit d28291
      if (NULL == *(char * volatile *)&datastart
Packit d28291
          || (word)datastart > (word)dataend)
Packit d28291
        ABORT_ARG2("Wrong DATASTART/END pair",
Packit d28291
                   ": %p .. %p", (void *)datastart, (void *)dataend);
Packit d28291
Packit d28291
      /* dl_iterate_phdr may forget the static data segment in  */
Packit d28291
      /* statically linked executables.                         */
Packit d28291
      GC_add_roots_inner(datastart, dataend, TRUE);
Packit d28291
#     ifdef GC_HAVE_DATAREGION2
Packit d28291
        if ((word)DATASTART2 - 1U >= (word)DATAEND2) {
Packit d28291
                        /* Subtract one to check also for NULL  */
Packit d28291
                        /* without a compiler warning.          */
Packit d28291
          ABORT_ARG2("Wrong DATASTART/END2 pair",
Packit d28291
                     ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2);
Packit d28291
        }
Packit d28291
        GC_add_roots_inner(DATASTART2, DATAEND2, TRUE);
Packit d28291
#     endif
Packit d28291
  }
Packit d28291
  return TRUE;
Packit d28291
}
Packit d28291
Packit d28291
# define HAVE_REGISTER_MAIN_STATIC_DATA
Packit d28291
Packit d28291
#else /* !HAVE_DL_ITERATE_PHDR */
Packit d28291
Packit d28291
/* Dynamic loading code for Linux running ELF. Somewhat tested on
Packit d28291
 * Linux/x86, untested but hopefully should work on Linux/Alpha.
Packit d28291
 * This code was derived from the Solaris/ELF support. Thanks to
Packit d28291
 * whatever kind soul wrote that.  - Patrick Bridges */
Packit d28291
Packit d28291
/* This doesn't necessarily work in all cases, e.g. with preloaded
Packit d28291
 * dynamic libraries.                                           */
Packit d28291
Packit d28291
# if defined(NETBSD) || defined(OPENBSD)
Packit d28291
#   include <sys/exec_elf.h>
Packit d28291
   /* for compatibility with 1.4.x */
Packit d28291
#   ifndef DT_DEBUG
Packit d28291
#     define DT_DEBUG   21
Packit d28291
#   endif
Packit d28291
#   ifndef PT_LOAD
Packit d28291
#     define PT_LOAD    1
Packit d28291
#   endif
Packit d28291
#   ifndef PF_W
Packit d28291
#     define PF_W       2
Packit d28291
#   endif
Packit d28291
# elif !defined(PLATFORM_ANDROID)
Packit d28291
#  include <elf.h>
Packit d28291
# endif
Packit d28291
Packit d28291
# ifndef PLATFORM_ANDROID
Packit d28291
#   include <link.h>
Packit d28291
# endif
Packit d28291
Packit d28291
#endif /* !HAVE_DL_ITERATE_PHDR */
Packit d28291
Packit d28291
#ifdef __GNUC__
Packit d28291
# pragma weak _DYNAMIC
Packit d28291
#endif
Packit d28291
extern ElfW(Dyn) _DYNAMIC[];
Packit d28291
Packit d28291
STATIC struct link_map *
Packit d28291
GC_FirstDLOpenedLinkMap(void)
Packit d28291
{
Packit d28291
    static struct link_map *cachedResult = 0;
Packit d28291
Packit d28291
    if (0 == COVERT_DATAFLOW(_DYNAMIC)) {
Packit d28291
        /* _DYNAMIC symbol not resolved. */
Packit d28291
        return(0);
Packit d28291
    }
Packit d28291
    if( cachedResult == 0 ) {
Packit d28291
#     if defined(NETBSD) && defined(RTLD_DI_LINKMAP)
Packit d28291
#       if defined(CPPCHECK)
Packit d28291
#         define GC_RTLD_DI_LINKMAP 2
Packit d28291
#       else
Packit d28291
#         define GC_RTLD_DI_LINKMAP RTLD_DI_LINKMAP
Packit d28291
#       endif
Packit d28291
        struct link_map *lm = NULL;
Packit d28291
        if (!dlinfo(RTLD_SELF, GC_RTLD_DI_LINKMAP, &lm) && lm != NULL) {
Packit d28291
            /* Now lm points link_map object of libgc.  Since it    */
Packit d28291
            /* might not be the first dynamically linked object,    */
Packit d28291
            /* try to find it (object next to the main object).     */
Packit d28291
            while (lm->l_prev != NULL) {
Packit d28291
                lm = lm->l_prev;
Packit d28291
            }
Packit d28291
            cachedResult = lm->l_next;
Packit d28291
        }
Packit d28291
#     else
Packit d28291
        ElfW(Dyn) *dp;
Packit d28291
        int tag;
Packit d28291
Packit d28291
        for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
Packit d28291
            if (tag == DT_DEBUG) {
Packit d28291
                struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr;
Packit d28291
                /* d_ptr could be null if libs are linked statically. */
Packit d28291
                if (rd != NULL) {
Packit d28291
                    struct link_map *lm = rd->r_map;
Packit d28291
                    if (lm != NULL)
Packit d28291
                        cachedResult = lm->l_next; /* might be NULL */
Packit d28291
                }
Packit d28291
                break;
Packit d28291
            }
Packit d28291
        }
Packit d28291
#     endif /* !NETBSD || !RTLD_DI_LINKMAP */
Packit d28291
    }
Packit d28291
    return cachedResult;
Packit d28291
}
Packit d28291
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
  struct link_map *lm;
Packit d28291
Packit d28291
# ifdef HAVE_DL_ITERATE_PHDR
Packit d28291
    if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
Packit d28291
        return;
Packit d28291
    }
Packit d28291
# endif
Packit d28291
  for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next)
Packit d28291
    {
Packit d28291
        ElfW(Ehdr) * e;
Packit d28291
        ElfW(Phdr) * p;
Packit d28291
        unsigned long offset;
Packit d28291
        char * start;
Packit d28291
        int i;
Packit d28291
Packit d28291
        e = (ElfW(Ehdr) *) lm->l_addr;
Packit d28291
#       ifdef PLATFORM_ANDROID
Packit d28291
          if (e == NULL)
Packit d28291
            continue;
Packit d28291
#       endif
Packit d28291
        p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
Packit d28291
        offset = ((unsigned long)(lm->l_addr));
Packit d28291
        for( i = 0; i < (int)e->e_phnum; i++, p++ ) {
Packit d28291
          switch( p->p_type ) {
Packit d28291
            case PT_LOAD:
Packit d28291
              {
Packit d28291
                if( !(p->p_flags & PF_W) ) break;
Packit d28291
                start = ((char *)(p->p_vaddr)) + offset;
Packit d28291
                GC_add_roots_inner(start, start + p->p_memsz, TRUE);
Packit d28291
              }
Packit d28291
              break;
Packit d28291
            default:
Packit d28291
              break;
Packit d28291
          }
Packit d28291
        }
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
#endif /* !USE_PROC_FOR_LIBRARIES */
Packit d28291
Packit d28291
#endif /* LINUX */
Packit d28291
Packit d28291
#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
Packit d28291
Packit d28291
#include <sys/procfs.h>
Packit d28291
#include <sys/stat.h>
Packit d28291
#include <fcntl.h>
Packit d28291
#include <elf.h>
Packit d28291
#include <errno.h>
Packit d28291
#include <signal.h>  /* Only for the following test. */
Packit d28291
#ifndef _sigargs
Packit d28291
# define IRIX6
Packit d28291
#endif
Packit d28291
Packit d28291
/* We use /proc to track down all parts of the address space that are   */
Packit d28291
/* mapped by the process, and throw out regions we know we shouldn't    */
Packit d28291
/* worry about.  This may also work under other SVR4 variants.          */
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
    static int fd = -1;
Packit d28291
    char buf[30];
Packit d28291
    static prmap_t * addr_map = 0;
Packit d28291
    static int current_sz = 0;  /* Number of records currently in addr_map */
Packit d28291
    int needed_sz = 0;          /* Required size of addr_map            */
Packit d28291
    int i;
Packit d28291
    long flags;
Packit d28291
    ptr_t start;
Packit d28291
    ptr_t limit;
Packit d28291
    ptr_t heap_start = HEAP_START;
Packit d28291
    ptr_t heap_end = heap_start;
Packit d28291
Packit d28291
#   ifdef SOLARISDL
Packit d28291
#     define MA_PHYS 0
Packit d28291
#   endif /* SOLARISDL */
Packit d28291
Packit d28291
    if (fd < 0) {
Packit d28291
      (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid());
Packit d28291
      buf[sizeof(buf) - 1] = '\0';
Packit d28291
        /* The above generates a lint complaint, since pid_t varies.    */
Packit d28291
        /* It's unclear how to improve this.                            */
Packit d28291
      fd = open(buf, O_RDONLY);
Packit d28291
      if (fd < 0) {
Packit d28291
        ABORT("/proc open failed");
Packit d28291
      }
Packit d28291
    }
Packit d28291
    if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
Packit d28291
        ABORT_ARG2("/proc PIOCNMAP ioctl failed",
Packit d28291
                   ": fd = %d, errno = %d", fd, errno);
Packit d28291
    }
Packit d28291
    if (needed_sz >= current_sz) {
Packit d28291
        GC_scratch_recycle_no_gww(addr_map,
Packit d28291
                                  (size_t)current_sz * sizeof(prmap_t));
Packit d28291
        current_sz = needed_sz * 2 + 1;
Packit d28291
                        /* Expansion, plus room for 0 record */
Packit d28291
        addr_map = (prmap_t *)GC_scratch_alloc(
Packit d28291
                                (size_t)current_sz * sizeof(prmap_t));
Packit d28291
        if (addr_map == NULL)
Packit d28291
          ABORT("Insufficient memory for address map");
Packit d28291
    }
Packit d28291
    if (ioctl(fd, PIOCMAP, addr_map) < 0) {
Packit d28291
        ABORT_ARG3("/proc PIOCMAP ioctl failed",
Packit d28291
                   ": errcode= %d, needed_sz= %d, addr_map= %p",
Packit d28291
                   errno, needed_sz, (void *)addr_map);
Packit d28291
    };
Packit d28291
    if (GC_n_heap_sects > 0) {
Packit d28291
        heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
Packit d28291
                        + GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
Packit d28291
        if ((word)heap_end < (word)GC_scratch_last_end_ptr)
Packit d28291
          heap_end = GC_scratch_last_end_ptr;
Packit d28291
    }
Packit d28291
    for (i = 0; i < needed_sz; i++) {
Packit d28291
        flags = addr_map[i].pr_mflags;
Packit d28291
        if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
Packit d28291
                      | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
Packit d28291
        if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
Packit d28291
            goto irrelevant;
Packit d28291
          /* The latter test is empirically useless in very old Irix    */
Packit d28291
          /* versions.  Other than the                                  */
Packit d28291
          /* main data and stack segments, everything appears to be     */
Packit d28291
          /* mapped readable, writable, executable, and shared(!!).     */
Packit d28291
          /* This makes no sense to me. - HB                            */
Packit d28291
        start = (ptr_t)(addr_map[i].pr_vaddr);
Packit d28291
        if (GC_roots_present(start)) goto irrelevant;
Packit d28291
        if ((word)start < (word)heap_end && (word)start >= (word)heap_start)
Packit d28291
                goto irrelevant;
Packit d28291
Packit d28291
        limit = start + addr_map[i].pr_size;
Packit d28291
        /* The following seemed to be necessary for very old versions   */
Packit d28291
        /* of Irix, but it has been reported to discard relevant        */
Packit d28291
        /* segments under Irix 6.5.                                     */
Packit d28291
#       ifndef IRIX6
Packit d28291
          if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
Packit d28291
            /* Discard text segments, i.e. 0-offset mappings against    */
Packit d28291
            /* executable files which appear to have ELF headers.       */
Packit d28291
            caddr_t arg;
Packit d28291
            int obj;
Packit d28291
#           define MAP_IRR_SZ 10
Packit d28291
            static ptr_t map_irr[MAP_IRR_SZ];
Packit d28291
                                        /* Known irrelevant map entries */
Packit d28291
            static int n_irr = 0;
Packit d28291
            struct stat buf;
Packit d28291
            register int j;
Packit d28291
Packit d28291
            for (j = 0; j < n_irr; j++) {
Packit d28291
                if (map_irr[j] == start) goto irrelevant;
Packit d28291
            }
Packit d28291
            arg = (caddr_t)start;
Packit d28291
            obj = ioctl(fd, PIOCOPENM, &arg;;
Packit d28291
            if (obj >= 0) {
Packit d28291
                fstat(obj, &buf;;
Packit d28291
                close(obj);
Packit d28291
                if ((buf.st_mode & 0111) != 0) {
Packit d28291
                    if (n_irr < MAP_IRR_SZ) {
Packit d28291
                        map_irr[n_irr++] = start;
Packit d28291
                    }
Packit d28291
                    goto irrelevant;
Packit d28291
                }
Packit d28291
            }
Packit d28291
          }
Packit d28291
#       endif /* !IRIX6 */
Packit d28291
        GC_add_roots_inner(start, limit, TRUE);
Packit d28291
      irrelevant: ;
Packit d28291
    }
Packit d28291
    /* Don't keep cached descriptor, for now.  Some kernels don't like us */
Packit d28291
    /* to keep a /proc file descriptor around during kill -9.             */
Packit d28291
        if (close(fd) < 0) ABORT("Couldn't close /proc file");
Packit d28291
        fd = -1;
Packit d28291
}
Packit d28291
Packit d28291
# endif /* USE_PROC || IRIX5 */
Packit d28291
Packit d28291
# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
Packit d28291
Packit d28291
# ifndef WIN32_LEAN_AND_MEAN
Packit d28291
#   define WIN32_LEAN_AND_MEAN 1
Packit d28291
# endif
Packit d28291
# define NOSERVICE
Packit d28291
# include <windows.h>
Packit d28291
# include <stdlib.h>
Packit d28291
Packit d28291
  /* We traverse the entire address space and register all segments     */
Packit d28291
  /* that could possibly have been written to.                          */
Packit d28291
  STATIC void GC_cond_add_roots(char *base, char * limit)
Packit d28291
  {
Packit d28291
#   ifdef GC_WIN32_THREADS
Packit d28291
      char * curr_base = base;
Packit d28291
      char * next_stack_lo;
Packit d28291
      char * next_stack_hi;
Packit d28291
Packit d28291
      if (base == limit) return;
Packit d28291
      for(;;) {
Packit d28291
          GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi);
Packit d28291
          if ((word)next_stack_lo >= (word)limit) break;
Packit d28291
          if ((word)next_stack_lo > (word)curr_base)
Packit d28291
            GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
Packit d28291
          curr_base = next_stack_hi;
Packit d28291
      }
Packit d28291
      if ((word)curr_base < (word)limit)
Packit d28291
        GC_add_roots_inner(curr_base, limit, TRUE);
Packit d28291
#   else
Packit d28291
      char * stack_top
Packit d28291
         = (char *)((word)GC_approx_sp() &
Packit d28291
                    ~(word)(GC_sysinfo.dwAllocationGranularity - 1));
Packit d28291
Packit d28291
      if (base == limit) return;
Packit d28291
      if ((word)limit > (word)stack_top
Packit d28291
          && (word)base < (word)GC_stackbottom) {
Packit d28291
          /* Part of the stack; ignore it. */
Packit d28291
          return;
Packit d28291
      }
Packit d28291
      GC_add_roots_inner(base, limit, TRUE);
Packit d28291
#   endif
Packit d28291
  }
Packit d28291
Packit d28291
#ifdef DYNAMIC_LOADING
Packit d28291
  /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */
Packit d28291
  GC_INNER GC_bool GC_register_main_static_data(void)
Packit d28291
  {
Packit d28291
#   if defined(MSWINCE) || defined(CYGWIN32)
Packit d28291
      /* Do we need to separately register the main static data segment? */
Packit d28291
      return FALSE;
Packit d28291
#   else
Packit d28291
      return GC_no_win32_dlls;
Packit d28291
#   endif
Packit d28291
  }
Packit d28291
# define HAVE_REGISTER_MAIN_STATIC_DATA
Packit d28291
#endif /* DYNAMIC_LOADING */
Packit d28291
Packit d28291
# ifdef DEBUG_VIRTUALQUERY
Packit d28291
  void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
Packit d28291
  {
Packit d28291
    GC_printf("BaseAddress = 0x%lx, AllocationBase = 0x%lx,"
Packit d28291
              " RegionSize = 0x%lx(%lu)\n", buf -> BaseAddress,
Packit d28291
              buf -> AllocationBase, buf -> RegionSize, buf -> RegionSize);
Packit d28291
    GC_printf("\tAllocationProtect = 0x%lx, State = 0x%lx, Protect = 0x%lx, "
Packit d28291
              "Type = 0x%lx\n", buf -> AllocationProtect, buf -> State,
Packit d28291
              buf -> Protect, buf -> Type);
Packit d28291
  }
Packit d28291
# endif /* DEBUG_VIRTUALQUERY */
Packit d28291
Packit d28291
# if defined(MSWINCE) || defined(CYGWIN32)
Packit d28291
    /* FIXME: Should we really need to scan MEM_PRIVATE sections?       */
Packit d28291
    /* For now, we don't add MEM_PRIVATE sections to the data roots for */
Packit d28291
    /* WinCE because otherwise SEGV fault sometimes happens to occur in */
Packit d28291
    /* GC_mark_from() (and, even if we use WRAP_MARK_SOME, WinCE prints */
Packit d28291
    /* a "Data Abort" message to the debugging console).                */
Packit d28291
    /* To workaround that, use -DGC_REGISTER_MEM_PRIVATE.               */
Packit d28291
#   define GC_wnt TRUE
Packit d28291
# endif
Packit d28291
Packit d28291
  GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
  {
Packit d28291
    MEMORY_BASIC_INFORMATION buf;
Packit d28291
    DWORD protect;
Packit d28291
    LPVOID p;
Packit d28291
    char * base;
Packit d28291
    char * limit, * new_limit;
Packit d28291
Packit d28291
#   ifdef MSWIN32
Packit d28291
      if (GC_no_win32_dlls) return;
Packit d28291
#   endif
Packit d28291
    base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
Packit d28291
    while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) {
Packit d28291
        size_t result = VirtualQuery(p, &buf, sizeof(buf));
Packit d28291
Packit d28291
#       ifdef MSWINCE
Packit d28291
          if (result == 0) {
Packit d28291
            /* Page is free; advance to the next possible allocation base */
Packit d28291
            new_limit = (char *)
Packit d28291
                (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
Packit d28291
                 & ~(GC_sysinfo.dwAllocationGranularity-1));
Packit d28291
          } else
Packit d28291
#       endif
Packit d28291
        /* else */ {
Packit d28291
            if (result != sizeof(buf)) {
Packit d28291
                ABORT("Weird VirtualQuery result");
Packit d28291
            }
Packit d28291
            new_limit = (char *)p + buf.RegionSize;
Packit d28291
            protect = buf.Protect;
Packit d28291
            if (buf.State == MEM_COMMIT
Packit d28291
                && (protect == PAGE_EXECUTE_READWRITE
Packit d28291
                    || protect == PAGE_READWRITE)
Packit d28291
                && (buf.Type == MEM_IMAGE
Packit d28291
#                   ifdef GC_REGISTER_MEM_PRIVATE
Packit d28291
                      || (protect == PAGE_READWRITE && buf.Type == MEM_PRIVATE)
Packit d28291
#                   else
Packit d28291
                      /* There is some evidence that we cannot always   */
Packit d28291
                      /* ignore MEM_PRIVATE sections under Windows ME   */
Packit d28291
                      /* and predecessors.  Hence we now also check for */
Packit d28291
                      /* that case.                                     */
Packit d28291
                      || (!GC_wnt && buf.Type == MEM_PRIVATE)
Packit d28291
#                   endif
Packit d28291
                   )
Packit d28291
                && !GC_is_heap_base(buf.AllocationBase)) {
Packit d28291
#               ifdef DEBUG_VIRTUALQUERY
Packit d28291
                  GC_dump_meminfo(&buf;;
Packit d28291
#               endif
Packit d28291
                if ((char *)p != limit) {
Packit d28291
                    GC_cond_add_roots(base, limit);
Packit d28291
                    base = p;
Packit d28291
                }
Packit d28291
                limit = new_limit;
Packit d28291
            }
Packit d28291
        }
Packit d28291
        if ((word)p > (word)new_limit /* overflow */) break;
Packit d28291
        p = (LPVOID)new_limit;
Packit d28291
    }
Packit d28291
    GC_cond_add_roots(base, limit);
Packit d28291
  }
Packit d28291
Packit d28291
#endif /* MSWIN32 || MSWINCE || CYGWIN32 */
Packit d28291
Packit d28291
#if defined(ALPHA) && defined(OSF1)
Packit d28291
Packit d28291
#include <loader.h>
Packit d28291
Packit d28291
extern char *sys_errlist[];
Packit d28291
extern int sys_nerr;
Packit d28291
extern int errno;
Packit d28291
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
  ldr_module_t moduleid = LDR_NULL_MODULE;
Packit d28291
  ldr_process_t mypid = ldr_my_process(); /* obtain id of this process */
Packit d28291
Packit d28291
  /* For each module */
Packit d28291
    while (TRUE) {
Packit d28291
      ldr_module_info_t moduleinfo;
Packit d28291
      size_t modulereturnsize;
Packit d28291
      ldr_region_t region;
Packit d28291
      ldr_region_info_t regioninfo;
Packit d28291
      size_t regionreturnsize;
Packit d28291
      int status = ldr_next_module(mypid, &moduleid);
Packit d28291
                                /* Get the next (first) module */
Packit d28291
Packit d28291
      /* Any more modules? */
Packit d28291
        if (moduleid == LDR_NULL_MODULE)
Packit d28291
            break;    /* No more modules */
Packit d28291
Packit d28291
      /* Check status AFTER checking moduleid because       */
Packit d28291
      /* of a bug in the non-shared ldr_next_module stub.   */
Packit d28291
        if (status != 0) {
Packit d28291
          ABORT_ARG3("ldr_next_module failed",
Packit d28291
                     ": status= %d, errcode= %d (%s)", status, errno,
Packit d28291
                     errno < sys_nerr ? sys_errlist[errno] : "");
Packit d28291
        }
Packit d28291
Packit d28291
      /* Get the module information */
Packit d28291
        status = ldr_inq_module(mypid, moduleid, &moduleinfo,
Packit d28291
                                sizeof(moduleinfo), &modulereturnsize);
Packit d28291
        if (status != 0 )
Packit d28291
            ABORT("ldr_inq_module failed");
Packit d28291
Packit d28291
      /* is module for the main program (i.e. nonshared portion)? */
Packit d28291
          if (moduleinfo.lmi_flags & LDR_MAIN)
Packit d28291
              continue;    /* skip the main module */
Packit d28291
Packit d28291
#     ifdef DL_VERBOSE
Packit d28291
        GC_log_printf("---Module---\n");
Packit d28291
        GC_log_printf("Module ID\t = %16ld\n", moduleinfo.lmi_modid);
Packit d28291
        GC_log_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion);
Packit d28291
        GC_log_printf("flags for module = %16lx\n", moduleinfo.lmi_flags);
Packit d28291
        GC_log_printf("module pathname\t = \"%s\"\n", moduleinfo.lmi_name);
Packit d28291
#     endif
Packit d28291
Packit d28291
      /* For each region in this module */
Packit d28291
        for (region = 0; region < moduleinfo.lmi_nregion; region++) {
Packit d28291
          /* Get the region information */
Packit d28291
            status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
Packit d28291
                                    sizeof(regioninfo), &regionreturnsize);
Packit d28291
            if (status != 0 )
Packit d28291
                ABORT("ldr_inq_region failed");
Packit d28291
Packit d28291
          /* only process writable (data) regions */
Packit d28291
            if (! (regioninfo.lri_prot & LDR_W))
Packit d28291
                continue;
Packit d28291
Packit d28291
#         ifdef DL_VERBOSE
Packit d28291
            GC_log_printf("--- Region ---\n");
Packit d28291
            GC_log_printf("Region number\t = %16ld\n",
Packit d28291
                          regioninfo.lri_region_no);
Packit d28291
            GC_log_printf("Protection flags = %016x\n", regioninfo.lri_prot);
Packit d28291
            GC_log_printf("Virtual address\t = %16p\n", regioninfo.lri_vaddr);
Packit d28291
            GC_log_printf("Mapped address\t = %16p\n",
Packit d28291
                          regioninfo.lri_mapaddr);
Packit d28291
            GC_log_printf("Region size\t = %16ld\n", regioninfo.lri_size);
Packit d28291
            GC_log_printf("Region name\t = \"%s\"\n", regioninfo.lri_name);
Packit d28291
#         endif
Packit d28291
Packit d28291
          /* register region as a garbage collection root */
Packit d28291
          GC_add_roots_inner((char *)regioninfo.lri_mapaddr,
Packit d28291
                        (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
Packit d28291
                        TRUE);
Packit d28291
Packit d28291
        }
Packit d28291
    }
Packit d28291
}
Packit d28291
#endif
Packit d28291
Packit d28291
#if defined(HPUX)
Packit d28291
Packit d28291
#include <errno.h>
Packit d28291
#include <dl.h>
Packit d28291
Packit d28291
extern char *sys_errlist[];
Packit d28291
extern int sys_nerr;
Packit d28291
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
  int index = 1; /* Ordinal position in shared library search list */
Packit d28291
Packit d28291
  /* For each dynamic library loaded */
Packit d28291
    while (TRUE) {
Packit d28291
      struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
Packit d28291
      int status = shl_get(index, &shl_desc);
Packit d28291
                                /* Get info about next shared library   */
Packit d28291
Packit d28291
      /* Check if this is the end of the list or if some error occurred */
Packit d28291
        if (status != 0) {
Packit d28291
#        ifdef GC_HPUX_THREADS
Packit d28291
           /* I've seen errno values of 0.  The man page is not clear   */
Packit d28291
           /* as to whether errno should get set on a -1 return.        */
Packit d28291
           break;
Packit d28291
#        else
Packit d28291
          if (errno == EINVAL) {
Packit d28291
            break; /* Moved past end of shared library list --> finished */
Packit d28291
          } else {
Packit d28291
            ABORT_ARG3("shl_get failed",
Packit d28291
                       ": status= %d, errcode= %d (%s)", status, errno,
Packit d28291
                       errno < sys_nerr ? sys_errlist[errno] : "");
Packit d28291
          }
Packit d28291
#        endif
Packit d28291
        }
Packit d28291
Packit d28291
#     ifdef DL_VERBOSE
Packit d28291
        GC_log_printf("---Shared library---\n");
Packit d28291
        GC_log_printf("\tfilename\t= \"%s\"\n", shl_desc->filename);
Packit d28291
        GC_log_printf("\tindex\t\t= %d\n", index);
Packit d28291
        GC_log_printf("\thandle\t\t= %08x\n",
Packit d28291
                      (unsigned long) shl_desc->handle);
Packit d28291
        GC_log_printf("\ttext seg.start\t= %08x\n", shl_desc->tstart);
Packit d28291
        GC_log_printf("\ttext seg.end\t= %08x\n", shl_desc->tend);
Packit d28291
        GC_log_printf("\tdata seg.start\t= %08x\n", shl_desc->dstart);
Packit d28291
        GC_log_printf("\tdata seg.end\t= %08x\n", shl_desc->dend);
Packit d28291
        GC_log_printf("\tref.count\t= %lu\n", shl_desc->ref_count);
Packit d28291
#     endif
Packit d28291
Packit d28291
      /* register shared library's data segment as a garbage collection root */
Packit d28291
        GC_add_roots_inner((char *) shl_desc->dstart,
Packit d28291
                           (char *) shl_desc->dend, TRUE);
Packit d28291
Packit d28291
        index++;
Packit d28291
    }
Packit d28291
}
Packit d28291
#endif /* HPUX */
Packit d28291
Packit d28291
#ifdef AIX
Packit d28291
# pragma alloca
Packit d28291
# include <sys/ldr.h>
Packit d28291
# include <sys/errno.h>
Packit d28291
  GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
  {
Packit d28291
      int ldibuflen = 8192;
Packit d28291
Packit d28291
      for (;;) {
Packit d28291
        int len;
Packit d28291
        struct ld_info *ldi;
Packit d28291
#       if defined(CPPCHECK)
Packit d28291
          char ldibuf[ldibuflen];
Packit d28291
#       else
Packit d28291
          char *ldibuf = alloca(ldibuflen);
Packit d28291
#       endif
Packit d28291
Packit d28291
        len = loadquery(L_GETINFO, ldibuf, ldibuflen);
Packit d28291
        if (len < 0) {
Packit d28291
                if (errno != ENOMEM) {
Packit d28291
                        ABORT("loadquery failed");
Packit d28291
                }
Packit d28291
                ldibuflen *= 2;
Packit d28291
                continue;
Packit d28291
        }
Packit d28291
Packit d28291
        ldi = (struct ld_info *)ldibuf;
Packit d28291
        while (ldi) {
Packit d28291
                len = ldi->ldinfo_next;
Packit d28291
                GC_add_roots_inner(
Packit d28291
                                ldi->ldinfo_dataorg,
Packit d28291
                                (ptr_t)(unsigned long)ldi->ldinfo_dataorg
Packit d28291
                                + ldi->ldinfo_datasize,
Packit d28291
                                TRUE);
Packit d28291
                ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
Packit d28291
        }
Packit d28291
        break;
Packit d28291
      }
Packit d28291
  }
Packit d28291
#endif /* AIX */
Packit d28291
Packit d28291
#ifdef DARWIN
Packit d28291
Packit d28291
/* __private_extern__ hack required for pre-3.4 gcc versions.   */
Packit d28291
#ifndef __private_extern__
Packit d28291
# define __private_extern__ extern
Packit d28291
# include <mach-o/dyld.h>
Packit d28291
# undef __private_extern__
Packit d28291
#else
Packit d28291
# include <mach-o/dyld.h>
Packit d28291
#endif
Packit d28291
#include <mach-o/getsect.h>
Packit d28291
Packit d28291
/*#define DARWIN_DEBUG*/
Packit d28291
Packit d28291
/* Writable sections generally available on Darwin.     */
Packit d28291
STATIC const struct {
Packit d28291
    const char *seg;
Packit d28291
    const char *sect;
Packit d28291
} GC_dyld_sections[] = {
Packit d28291
    { SEG_DATA, SECT_DATA },
Packit d28291
    /* Used by FSF GCC, but not by OS X system tools, so far.   */
Packit d28291
    { SEG_DATA, "__static_data" },
Packit d28291
    { SEG_DATA, SECT_BSS },
Packit d28291
    { SEG_DATA, SECT_COMMON },
Packit d28291
    /* FSF GCC - zero-sized object sections for targets         */
Packit d28291
    /*supporting section anchors.                               */
Packit d28291
    { SEG_DATA, "__zobj_data" },
Packit d28291
    { SEG_DATA, "__zobj_bss" }
Packit d28291
};
Packit d28291
Packit d28291
/* Additional writable sections:                                */
Packit d28291
/* GCC on Darwin constructs aligned sections "on demand", where */
Packit d28291
/* the alignment size is embedded in the section name.          */
Packit d28291
/* Furthermore, there are distinctions between sections         */
Packit d28291
/* containing private vs. public symbols.  It also constructs   */
Packit d28291
/* sections specifically for zero-sized objects, when the       */
Packit d28291
/* target supports section anchors.                             */
Packit d28291
STATIC const char * const GC_dyld_add_sect_fmts[] = {
Packit d28291
  "__bss%u",
Packit d28291
  "__pu_bss%u",
Packit d28291
  "__zo_bss%u",
Packit d28291
  "__zo_pu_bss%u"
Packit d28291
};
Packit d28291
Packit d28291
/* Currently, mach-o will allow up to the max of 2^15 alignment */
Packit d28291
/* in an object file.                                           */
Packit d28291
#ifndef L2_MAX_OFILE_ALIGNMENT
Packit d28291
# define L2_MAX_OFILE_ALIGNMENT 15
Packit d28291
#endif
Packit d28291
Packit d28291
STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr)
Packit d28291
{
Packit d28291
    unsigned long i, c;
Packit d28291
    c = _dyld_image_count();
Packit d28291
    for (i = 0; i < c; i++)
Packit d28291
      if ((const struct GC_MACH_HEADER *)_dyld_get_image_header(i) == hdr)
Packit d28291
        return _dyld_get_image_name(i);
Packit d28291
    return NULL;
Packit d28291
}
Packit d28291
Packit d28291
/* This should never be called by a thread holding the lock.    */
Packit d28291
STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr,
Packit d28291
                              intptr_t slide)
Packit d28291
{
Packit d28291
  unsigned long start, end;
Packit d28291
  unsigned i, j;
Packit d28291
  const struct GC_MACH_SECTION *sec;
Packit d28291
  const char *name;
Packit d28291
  GC_has_static_roots_func callback = GC_has_static_roots;
Packit d28291
  DCL_LOCK_STATE;
Packit d28291
Packit d28291
  if (GC_no_dls) return;
Packit d28291
# ifdef DARWIN_DEBUG
Packit d28291
    name = GC_dyld_name_for_hdr(hdr);
Packit d28291
# else
Packit d28291
    name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL;
Packit d28291
# endif
Packit d28291
  for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) {
Packit d28291
    sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
Packit d28291
                           GC_dyld_sections[i].sect);
Packit d28291
    if (sec == NULL || sec->size < sizeof(word))
Packit d28291
      continue;
Packit d28291
    start = slide + sec->addr;
Packit d28291
    end = start + sec->size;
Packit d28291
    LOCK();
Packit d28291
    /* The user callback is called holding the lock.    */
Packit d28291
    if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) {
Packit d28291
#     ifdef DARWIN_DEBUG
Packit d28291
        GC_log_printf(
Packit d28291
              "Adding section __DATA,%s at %p-%p (%lu bytes) from image %s\n",
Packit d28291
               GC_dyld_sections[i].sect, (void*)start, (void*)end,
Packit d28291
               (unsigned long)sec->size, name);
Packit d28291
#     endif
Packit d28291
      GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE);
Packit d28291
    }
Packit d28291
    UNLOCK();
Packit d28291
  }
Packit d28291
Packit d28291
  /* Sections constructed on demand.    */
Packit d28291
  for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
Packit d28291
    const char *fmt = GC_dyld_add_sect_fmts[j];
Packit d28291
Packit d28291
    /* Add our manufactured aligned BSS sections.       */
Packit d28291
    for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
Packit d28291
      char secnam[16];
Packit d28291
Packit d28291
      (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
Packit d28291
      secnam[sizeof(secnam) - 1] = '\0';
Packit d28291
      sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
Packit d28291
      if (sec == NULL || sec->size == 0)
Packit d28291
        continue;
Packit d28291
      start = slide + sec->addr;
Packit d28291
      end = start + sec->size;
Packit d28291
#     ifdef DARWIN_DEBUG
Packit d28291
        GC_log_printf("Adding on-demand section __DATA,%s at"
Packit d28291
                      " %p-%p (%lu bytes) from image %s\n",
Packit d28291
                      secnam, (void*)start, (void*)end,
Packit d28291
                      (unsigned long)sec->size, name);
Packit d28291
#     endif
Packit d28291
      GC_add_roots((char*)start, (char*)end);
Packit d28291
    }
Packit d28291
  }
Packit d28291
Packit d28291
# ifdef DARWIN_DEBUG
Packit d28291
    GC_print_static_roots();
Packit d28291
# endif
Packit d28291
}
Packit d28291
Packit d28291
/* This should never be called by a thread holding the lock.    */
Packit d28291
STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr,
Packit d28291
                                 intptr_t slide)
Packit d28291
{
Packit d28291
  unsigned long start, end;
Packit d28291
  unsigned i, j;
Packit d28291
  const struct GC_MACH_SECTION *sec;
Packit d28291
Packit d28291
  for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) {
Packit d28291
    sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
Packit d28291
                           GC_dyld_sections[i].sect);
Packit d28291
    if (sec == NULL || sec->size == 0)
Packit d28291
      continue;
Packit d28291
    start = slide + sec->addr;
Packit d28291
    end = start + sec->size;
Packit d28291
#   ifdef DARWIN_DEBUG
Packit d28291
      GC_log_printf(
Packit d28291
            "Removing section __DATA,%s at %p-%p (%lu bytes) from image %s\n",
Packit d28291
            GC_dyld_sections[i].sect, (void*)start, (void*)end,
Packit d28291
            (unsigned long)sec->size, GC_dyld_name_for_hdr(hdr));
Packit d28291
#   endif
Packit d28291
    GC_remove_roots((char*)start, (char*)end);
Packit d28291
  }
Packit d28291
Packit d28291
  /* Remove our on-demand sections.     */
Packit d28291
  for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
Packit d28291
    const char *fmt = GC_dyld_add_sect_fmts[j];
Packit d28291
Packit d28291
    for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
Packit d28291
      char secnam[16];
Packit d28291
Packit d28291
      (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
Packit d28291
      secnam[sizeof(secnam) - 1] = '\0';
Packit d28291
      sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
Packit d28291
      if (sec == NULL || sec->size == 0)
Packit d28291
        continue;
Packit d28291
      start = slide + sec->addr;
Packit d28291
      end = start + sec->size;
Packit d28291
#     ifdef DARWIN_DEBUG
Packit d28291
        GC_log_printf("Removing on-demand section __DATA,%s at"
Packit d28291
                      " %p-%p (%lu bytes) from image %s\n", secnam,
Packit d28291
                      (void*)start, (void*)end, (unsigned long)sec->size,
Packit d28291
                      GC_dyld_name_for_hdr(hdr));
Packit d28291
#     endif
Packit d28291
      GC_remove_roots((char*)start, (char*)end);
Packit d28291
    }
Packit d28291
  }
Packit d28291
Packit d28291
# ifdef DARWIN_DEBUG
Packit d28291
    GC_print_static_roots();
Packit d28291
# endif
Packit d28291
}
Packit d28291
Packit d28291
GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
{
Packit d28291
    /* Currently does nothing. The callbacks are setup by GC_init_dyld()
Packit d28291
    The dyld library takes it from there. */
Packit d28291
}
Packit d28291
Packit d28291
/* The _dyld_* functions have an internal lock so no _dyld functions
Packit d28291
   can be called while the world is stopped without the risk of a deadlock.
Packit d28291
   Because of this we MUST setup callbacks BEFORE we ever stop the world.
Packit d28291
   This should be called BEFORE any thread in created and WITHOUT the
Packit d28291
   allocation lock held. */
Packit d28291
Packit d28291
GC_INNER void GC_init_dyld(void)
Packit d28291
{
Packit d28291
  static GC_bool initialized = FALSE;
Packit d28291
Packit d28291
  if (initialized) return;
Packit d28291
Packit d28291
# ifdef DARWIN_DEBUG
Packit d28291
    GC_log_printf("Registering dyld callbacks...\n");
Packit d28291
# endif
Packit d28291
Packit d28291
  /* Apple's Documentation:
Packit d28291
     When you call _dyld_register_func_for_add_image, the dynamic linker
Packit d28291
     runtime calls the specified callback (func) once for each of the images
Packit d28291
     that is currently loaded into the program. When a new image is added to
Packit d28291
     the program, your callback is called again with the mach_header for the
Packit d28291
     new image, and the virtual memory slide amount of the new image.
Packit d28291
Packit d28291
     This WILL properly register already linked libraries and libraries
Packit d28291
     linked in the future.
Packit d28291
  */
Packit d28291
  _dyld_register_func_for_add_image(
Packit d28291
        (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_add);
Packit d28291
  _dyld_register_func_for_remove_image(
Packit d28291
        (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_remove);
Packit d28291
                        /* Structure mach_header64 has the same fields  */
Packit d28291
                        /* as mach_header except for the reserved one   */
Packit d28291
                        /* at the end, so these casts are OK.           */
Packit d28291
Packit d28291
  /* Set this early to avoid reentrancy issues. */
Packit d28291
  initialized = TRUE;
Packit d28291
Packit d28291
# ifdef NO_DYLD_BIND_FULLY_IMAGE
Packit d28291
    /* FIXME: What should we do in this case?   */
Packit d28291
# else
Packit d28291
    if (GC_no_dls) return; /* skip main data segment registration */
Packit d28291
Packit d28291
    /* When the environment variable is set, the dynamic linker binds   */
Packit d28291
    /* all undefined symbols the application needs at launch time.      */
Packit d28291
    /* This includes function symbols that are normally bound lazily at */
Packit d28291
    /* the time of their first invocation.                              */
Packit d28291
    if (GETENV("DYLD_BIND_AT_LAUNCH") == 0) {
Packit d28291
      /* The environment variable is unset, so we should bind manually. */
Packit d28291
#     ifdef DARWIN_DEBUG
Packit d28291
        GC_log_printf("Forcing full bind of GC code...\n");
Packit d28291
#     endif
Packit d28291
      /* FIXME: '_dyld_bind_fully_image_containing_address' is deprecated. */
Packit d28291
      if (!_dyld_bind_fully_image_containing_address(
Packit d28291
                                                  (unsigned long *)GC_malloc))
Packit d28291
        ABORT("_dyld_bind_fully_image_containing_address failed");
Packit d28291
    }
Packit d28291
# endif
Packit d28291
}
Packit d28291
Packit d28291
#define HAVE_REGISTER_MAIN_STATIC_DATA
Packit d28291
GC_INNER GC_bool GC_register_main_static_data(void)
Packit d28291
{
Packit d28291
  /* Already done through dyld callbacks */
Packit d28291
  return FALSE;
Packit d28291
}
Packit d28291
Packit d28291
#endif /* DARWIN */
Packit d28291
Packit d28291
#elif defined(PCR)
Packit d28291
Packit d28291
# include "il/PCR_IL.h"
Packit d28291
# include "th/PCR_ThCtl.h"
Packit d28291
# include "mm/PCR_MM.h"
Packit d28291
Packit d28291
  GC_INNER void GC_register_dynamic_libraries(void)
Packit d28291
  {
Packit d28291
    /* Add new static data areas of dynamically loaded modules. */
Packit d28291
    PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
Packit d28291
    PCR_IL_LoadedSegment * q;
Packit d28291
Packit d28291
    /* Skip uncommitted files */
Packit d28291
    while (p != NIL && !(p -> lf_commitPoint)) {
Packit d28291
        /* The loading of this file has not yet been committed    */
Packit d28291
        /* Hence its description could be inconsistent.           */
Packit d28291
        /* Furthermore, it hasn't yet been run.  Hence its data   */
Packit d28291
        /* segments can't possibly reference heap allocated       */
Packit d28291
        /* objects.                                               */
Packit d28291
        p = p -> lf_prev;
Packit d28291
    }
Packit d28291
    for (; p != NIL; p = p -> lf_prev) {
Packit d28291
      for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
Packit d28291
        if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
Packit d28291
            == PCR_IL_SegFlags_Traced_on) {
Packit d28291
          GC_add_roots_inner((char *)(q -> ls_addr),
Packit d28291
                             (char *)(q -> ls_addr) + q -> ls_bytes, TRUE);
Packit d28291
        }
Packit d28291
      }
Packit d28291
    }
Packit d28291
  }
Packit d28291
#endif /* PCR && !DYNAMIC_LOADING && !MSWIN32 */
Packit d28291
Packit d28291
#if !defined(HAVE_REGISTER_MAIN_STATIC_DATA) && defined(DYNAMIC_LOADING)
Packit d28291
  /* Do we need to separately register the main static data segment? */
Packit d28291
  GC_INNER GC_bool GC_register_main_static_data(void)
Packit d28291
  {
Packit d28291
    return TRUE;
Packit d28291
  }
Packit d28291
#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
Packit d28291
Packit d28291
/* Register a routine to filter dynamic library registration.  */
Packit d28291
GC_API void GC_CALL GC_register_has_static_roots_callback(
Packit d28291
                                        GC_has_static_roots_func callback)
Packit d28291
{
Packit d28291
    GC_has_static_roots = callback;
Packit d28291
}