Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/hpcgap/extern/gc/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.9.2025 mit Größe 187 kB image not shown  

Quelle  os_dep.c   Sprache: C

 
/*
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
 * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
 * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
 * Copyright (c) 1999 by Hewlett-Packard Company.  All rights reserved.
 * Copyright (c) 2008-2021 Ivan Maidanski
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */


#include "private/gc_priv.h"

#if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
    && !defined(MSWINCE) && !defined(SN_TARGET_ORBIS) \
    && !defined(SN_TARGET_PSP2) && !defined(__CC_ARM)
include <sys/types.h>
if !defined(MSWIN32) && !defined(MSWIN_XBOX1)
#   include <unistd.h>
endif
#endif

#include <stdio.h>
#if defined(MSWINCE) || defined(SN_TARGET_PS3)
define SIGSEGV 0 /* value is irrelevant */
#else
include <signal.h>
#endif

#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL) \
    || defined(SYMBIAN)
include <fcntl.h>
#endif

#if defined(LINUX) || defined(LINUX_STACKBOTTOM)
include <ctype.h>
#endif

/* Blatantly OS dependent routines, except for those that are related   */
/* to dynamic loading.                                                  */

#ifdef AMIGA
define GC_AMIGA_DEF
include "extra/AmigaOS.c"
undef GC_AMIGA_DEF
#endif

#ifdef MACOS
include <Processes.h>
#endif

#ifdef IRIX5
include <sys/uio.h>
include <malloc.h>   /* for locking */
#endif

#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES)
if defined(USE_MUNMAP) && !defined(USE_MMAP) && !defined(CPPCHECK)
#   error Invalid config: USE_MUNMAP requires USE_MMAP
endif
include <sys/types.h>
include <sys/mman.h>
include <sys/stat.h>
#endif

#if defined(ADD_HEAP_GUARD_PAGES) || defined(LINUX_STACKBOTTOM) \
    || defined(MMAP_SUPPORTED) || defined(NEED_PROC_MAPS)
include <errno.h>
#endif

#ifdef DARWIN
  /* for get_etext and friends */
include <mach-o/getsect.h>
#endif

#ifdef DJGPP
  /* Apparently necessary for djgpp 2.01.  May cause problems with      */
  /* other versions.                                                    */
  typedef long unsigned int caddr_t;
#endif

#ifdef PCR
include "il/PCR_IL.h"
include "th/PCR_ThCtl.h"
include "mm/PCR_MM.h"
#endif

#if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB)
  /* Declare GC_mprotect_stop and GC_mprotect_resume as extern "C".     */
include "private/darwin_stop_world.h"
#endif

#if !defined(NO_EXECUTE_PERMISSION)
  STATIC GC_bool GC_pages_executable = TRUE;
#else
  STATIC GC_bool GC_pages_executable = FALSE;
#endif
#define IGNORE_PAGES_EXECUTABLE 1
                        /* Undefined on GC_pages_executable real use.   */

#if ((defined(LINUX_STACKBOTTOM) || defined(NEED_PROC_MAPS) \
      || defined(PROC_VDB) || defined(SOFT_VDB)) && !defined(PROC_READ)) \
    || defined(CPPCHECK)
define PROC_READ read
          /* Should probably call the real read, if read is wrapped.    */
#endif

#if defined(LINUX_STACKBOTTOM) || defined(NEED_PROC_MAPS)
  /* Repeatedly perform a read call until the buffer is filled  */
  /* up, or we encounter EOF or an error.                       */
  STATIC ssize_t GC_repeat_read(int fd, char *buf, size_t count)
  {
    size_t num_read = 0;

    ASSERT_CANCEL_DISABLED();
    while (num_read < count) {
        ssize_t result = PROC_READ(fd, buf + num_read, count - num_read);

        if (result < 0) return result;
        if (result == 0) break;
        num_read += result;
    }
    return num_read;
  }
#endif /* LINUX_STACKBOTTOM || NEED_PROC_MAPS */

#ifdef NEED_PROC_MAPS
/* We need to parse /proc/self/maps, either to find dynamic libraries,  */
/* and/or to find the register backing store base (IA64).  Do it once   */
/* here.                                                                */

#ifdef THREADS
  /* Determine the length of a file by incrementally reading it into a  */
  /* buffer.  This would be silly to use it on a file supporting lseek, */
  /* but Linux /proc files usually do not.                              */
  /* As of Linux 4.15.0, lseek(SEEK_END) fails for /proc/self/maps.     */
  STATIC size_t GC_get_file_len(int f)
  {
    size_t total = 0;
    ssize_t result;
#   define GET_FILE_LEN_BUF_SZ 500
    char buf[GET_FILE_LEN_BUF_SZ];

    do {
        result = PROC_READ(f, buf, sizeof(buf));
        if (result == -1) return 0;
        total += result;
    } while (result > 0);
    return total;
  }

  STATIC size_t GC_get_maps_len(void)
  {
    int f = open("/proc/self/maps", O_RDONLY);
    size_t result;
    if (f < 0) return 0; /* treat missing file as empty */
    result = GC_get_file_len(f);
    close(f);
    return result;
  }
#endif /* THREADS */

/* Copy the contents of /proc/self/maps to a buffer in our address      */
/* space.  Return the address of the buffer.                            */
GC_INNER const char * GC_get_maps(void)
{
    ssize_t result;
    static char *maps_buf = NULL;
    static size_t maps_buf_sz = 1;
    size_t maps_size;
#   ifdef THREADS
      size_t old_maps_size = 0;
#   endif

    /* The buffer is essentially static, so there must be a single client. */
    GC_ASSERT(I_HOLD_LOCK());

    /* Note that in the presence of threads, the maps file can  */
    /* essentially shrink asynchronously and unexpectedly as    */
    /* threads that we already think of as dead release their   */
    /* stacks.  And there is no easy way to read the entire     */
    /* file atomically.  This is arguably a misfeature of the   */
    /* /proc/self/maps interface.                               */
    /* Since we expect the file can grow asynchronously in rare */
    /* cases, it should suffice to first determine              */
    /* the size (using read), and then to reread the file.      */
    /* If the size is inconsistent we have to retry.            */
    /* This only matters with threads enabled, and if we use    */
    /* this to locate roots (not the default).                  */

#   ifdef THREADS
        /* Determine the initial size of /proc/self/maps.       */
        maps_size = GC_get_maps_len();
        if (0 == maps_size)
          ABORT("Cannot determine length of /proc/self/maps");
#   else
        maps_size = 4000;       /* Guess */
#   endif

    /* Read /proc/self/maps, growing maps_buf as necessary.     */
    /* Note that we may not allocate conventionally, and        */
    /* thus can't use stdio.                                    */
        do {
            int f;

            while (maps_size >= maps_buf_sz) {
#             ifdef LINT2
                /* Workaround passing tainted maps_buf to a tainted sink. */
                GC_noop1((word)maps_buf);
#             else
                GC_scratch_recycle_no_gww(maps_buf, maps_buf_sz);
#             endif
              /* Grow only by powers of 2, since we leak "too small" buffers.*/
              while (maps_size >= maps_buf_sz) maps_buf_sz *= 2;
              maps_buf = GC_scratch_alloc(maps_buf_sz);
              if (NULL == maps_buf)
                ABORT_ARG1("Insufficient space for /proc/self/maps buffer",
                        ", %lu bytes requested", (unsigned long)maps_buf_sz);
#             ifdef THREADS
                /* Recompute initial length, since we allocated.        */
                /* This can only happen a few times per program         */
                /* execution.                                           */
                maps_size = GC_get_maps_len();
                if (0 == maps_size)
                  ABORT("Cannot determine length of /proc/self/maps");
#             endif
            }
            GC_ASSERT(maps_buf_sz >= maps_size + 1);
            f = open("/proc/self/maps", O_RDONLY);
            if (-1 == f)
              ABORT_ARG1("Cannot open /proc/self/maps",
                         ": errno= %d", errno);
#           ifdef THREADS
              old_maps_size = maps_size;
#           endif
            maps_size = 0;
            do {
                result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
                if (result < 0) {
                  ABORT_ARG1("Failed to read /proc/self/maps",
                             ": errno= %d", errno);
                }
                maps_size += result;
            } while ((size_t)result == maps_buf_sz-1);
            close(f);
            if (0 == maps_size)
              ABORT("Empty /proc/self/maps");
#           ifdef THREADS
              if (maps_size > old_maps_size) {
                /* This might be caused by e.g. thread creation. */
                WARN("Unexpected asynchronous /proc/self/maps growth"
                     " (to %" WARN_PRIdPTR " bytes)\n", maps_size);
              }
#           endif
        } while (maps_size >= maps_buf_sz
#                ifdef THREADS
                   || maps_size < old_maps_size
#                endif
                );
        maps_buf[maps_size] = '\0';
        return maps_buf;
}

/*
 *  GC_parse_map_entry parses an entry from /proc/self/maps so we can
 *  locate all writable data segments that belong to shared libraries.
 *  The format of one of these entries and the fields we care about
 *  is as follows:
 *  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
 *  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
 *  start    end      prot          maj_dev
 *
 *  Note that since about august 2003 kernels, the columns no longer have
 *  fixed offsets on 64-bit kernels.  Hence we no longer rely on fixed offsets
 *  anywhere, which is safer anyway.
 */


/* Assign various fields of the first line in maps_ptr to (*start),     */
/* (*end), (*prot), (*maj_dev) and (*mapping_name).  mapping_name may   */
/* be NULL. (*prot) and (*mapping_name) are assigned pointers into the  */
/* original buffer.                                                     */
#if (defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES)) \
    || defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \
    || (defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS))
  GC_INNER const char *GC_parse_map_entry(const char *maps_ptr,
                                          ptr_t *start, ptr_t *end,
                                          const char **prot, unsigned *maj_dev,
                                          const char **mapping_name)
  {
    const unsigned char *start_start, *end_start, *maj_dev_start;
    const unsigned char *p; /* unsigned for isspace, isxdigit */

    if (maps_ptr == NULL || *maps_ptr == '\0') {
        return NULL;
    }

    p = (const unsigned char *)maps_ptr;
    while (isspace(*p)) ++p;
    start_start = p;
    GC_ASSERT(isxdigit(*start_start));
    *start = (ptr_t)strtoul((const char *)start_start, (char **)&p, 16);
    GC_ASSERT(*p=='-');

    ++p;
    end_start = p;
    GC_ASSERT(isxdigit(*end_start));
    *end = (ptr_t)strtoul((const char *)end_start, (char **)&p, 16);
    GC_ASSERT(isspace(*p));

    while (isspace(*p)) ++p;
    GC_ASSERT(*p == 'r' || *p == '-');
    *prot = (const char *)p;
    /* Skip past protection field to offset field */
    while (!isspace(*p)) ++p;
    while (isspace(*p)) p++;
    GC_ASSERT(isxdigit(*p));
    /* Skip past offset field, which we ignore */
    while (!isspace(*p)) ++p;
    while (isspace(*p)) p++;
    maj_dev_start = p;
    GC_ASSERT(isxdigit(*maj_dev_start));
    *maj_dev = strtoul((const char *)maj_dev_start, NULL, 16);

    if (mapping_name != NULL) {
      while (*p && *p != '\n' && *p != '/' && *p != '[') p++;
      *mapping_name = (const char *)p;
    }
    while (*p && *p++ != '\n');
    return (const char *)p;
  }
#endif /* REDIRECT_MALLOC || DYNAMIC_LOADING || IA64 || ... */

#if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR)
  /* Try to read the backing store base from /proc/self/maps.           */
  /* Return the bounds of the writable mapping with a 0 major device,   */
  /* which includes the address passed as data.                         */
  /* Return FALSE if there is no such mapping.                          */
  GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp,
                                        ptr_t *endp)
  {
    const char *prot;
    ptr_t my_start, my_end;
    unsigned int maj_dev;
    const char *maps_ptr = GC_get_maps();

    for (;;) {
      maps_ptr = GC_parse_map_entry(maps_ptr, &my_start, &my_end,
                                    &prot, &maj_dev, 0);
      if (NULL == maps_ptr) break;

      if (prot[1] == 'w' && maj_dev == 0
          && (word)my_end > (word)addr && (word)my_start <= (word)addr) {
            *startp = my_start;
            *endp = my_end;
            return TRUE;
      }
    }
    return FALSE;
  }
#endif /* IA64 || INCLUDE_LINUX_THREAD_DESCR */

#if defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS)
  /* Find the text(code) mapping for the library whose name, after      */
  /* stripping the directory part, starts with nm.                      */
  GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp)
  {
    size_t nm_len = strlen(nm);
    const char *prot, *map_path;
    ptr_t my_start, my_end;
    unsigned int maj_dev;
    const char *maps_ptr = GC_get_maps();

    for (;;) {
      maps_ptr = GC_parse_map_entry(maps_ptr, &my_start, &my_end,
                                    &prot, &maj_dev, &map_path);
      if (NULL == maps_ptr) break;

      if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x') {
          const char *p = map_path;

          /* Set p to point just past last slash, if any. */
            while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') ++p;
            while (*p != '/' && (word)p >= (word)map_path) --p;
            ++p;
          if (strncmp(nm, p, nm_len) == 0) {
            *startp = my_start;
            *endp = my_end;
            return TRUE;
          }
      }
    }
    return FALSE;
  }
#endif /* REDIRECT_MALLOC */

#ifdef IA64
  static ptr_t backing_store_base_from_proc(void)
  {
    ptr_t my_start, my_end;
    if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) {
        GC_COND_LOG_PRINTF("Failed to find backing store base from /proc\n");
        return 0;
    }
    return my_start;
  }
#endif

#endif /* NEED_PROC_MAPS */

#if defined(SEARCH_FOR_DATA_START)
  /* The I386 case can be handled without a search.  The Alpha case     */
  /* used to be handled differently as well, but the rules changed      */
  /* for recent Linux versions.  This seems to be the easiest way to    */
  /* cover all versions.                                                */

if defined(LINUX) || defined(HURD)
    /* Some Linux distributions arrange to define __data_start.  Some   */
    /* define data_start as a weak symbol.  The latter is technically   */
    /* broken, since the user program may define data_start, in which   */
    /* case we lose.  Nonetheless, we try both, preferring __data_start.*/
    /* We assume gcc-compatible pragmas.                                */
    EXTERN_C_BEGIN
#   pragma weak __data_start
#   pragma weak data_start
    extern int __data_start[], data_start[];
    EXTERN_C_END
endif /* LINUX */

  ptr_t GC_data_start = NULL;

  GC_INNER void GC_init_linux_data_start(void)
  {
    ptr_t data_end = DATAEND;

#   if (defined(LINUX) || defined(HURD)) && defined(USE_PROG_DATA_START)
      /* Try the easy approaches first: */
      /* However, this may lead to wrong data start value if libgc  */
      /* code is put into a shared library (directly or indirectly) */
      /* which is linked with -Bsymbolic-functions option.  Thus,   */
      /* the following is not used by default.                      */
      if (COVERT_DATAFLOW(__data_start) != 0) {
        GC_data_start = (ptr_t)(__data_start);
      } else {
        GC_data_start = (ptr_t)(data_start);
      }
      if (COVERT_DATAFLOW(GC_data_start) != 0) {
        if ((word)GC_data_start > (word)data_end)
          ABORT_ARG2("Wrong __data_start/_end pair",
                     ": %p .. %p", (void *)GC_data_start, (void *)data_end);
        return;
      }
#     ifdef DEBUG_ADD_DEL_ROOTS
        GC_log_printf("__data_start not provided\n");
#     endif
#   endif /* LINUX */

    if (GC_no_dls) {
      /* Not needed, avoids the SIGSEGV caused by       */
      /* GC_find_limit which complicates debugging.     */
      GC_data_start = data_end; /* set data root size to 0 */
      return;
    }

    GC_data_start = (ptr_t)GC_find_limit(data_end, FALSE);
  }
#endif /* SEARCH_FOR_DATA_START */

#ifdef ECOS

ifndef ECOS_GC_MEMORY_SIZE
#   define ECOS_GC_MEMORY_SIZE (448 * 1024)
endif /* ECOS_GC_MEMORY_SIZE */

  /* TODO: This is a simple way of allocating memory which is           */
  /* compatible with ECOS early releases.  Later releases use a more    */
  /* sophisticated means of allocating memory than this simple static   */
  /* allocator, but this method is at least bound to work.              */
  static char ecos_gc_memory[ECOS_GC_MEMORY_SIZE];
  static char *ecos_gc_brk = ecos_gc_memory;

  static void *tiny_sbrk(ptrdiff_t increment)
  {
    void *p = ecos_gc_brk;
    ecos_gc_brk += increment;
    if ((word)ecos_gc_brk > (word)(ecos_gc_memory + sizeof(ecos_gc_memory))) {
      ecos_gc_brk -= increment;
      return NULL;
    }
    return p;
  }
define sbrk tiny_sbrk
#endif /* ECOS */

#if defined(NETBSD) && defined(__ELF__)
  ptr_t GC_data_start = NULL;

  EXTERN_C_BEGIN
  extern char **environ;
  EXTERN_C_END

  GC_INNER void GC_init_netbsd_elf(void)
  {
        /* This may need to be environ, without the underscore, for     */
        /* some versions.                                               */
    GC_data_start = (ptr_t)GC_find_limit(&environ, FALSE);
  }
#endif /* NETBSD */

#if defined(ADDRESS_SANITIZER) && (defined(UNIX_LIKE) \
                    || defined(NEED_FIND_LIMIT) || defined(MPROTECT_VDB)) \
    && !defined(CUSTOM_ASAN_DEF_OPTIONS)
  /* To tell ASan to allow GC to use its own SIGBUS/SEGV handlers.      */
  /* The function is exported just to be visible to ASan library.       */
  GC_API const char *__asan_default_options(void)
  {
    return "allow_user_segv_handler=1";
  }
#endif

#ifdef OPENBSD
  static struct sigaction old_segv_act;
  STATIC JMP_BUF GC_jmp_buf_openbsd;

  STATIC void GC_fault_handler_openbsd(int sig GC_ATTR_UNUSED)
  {
     LONGJMP(GC_jmp_buf_openbsd, 1);
  }

ifdef GC_OPENBSD_UTHREADS
#   include <sys/syscall.h>
    EXTERN_C_BEGIN
    extern sigset_t __syscall(quad_t, ...);
    EXTERN_C_END

  /* Don't use GC_find_limit() because siglongjmp() outside of the      */
  /* signal handler by-passes our userland pthreads lib, leaving        */
  /* SIGSEGV and SIGPROF masked.  Instead, use this custom one that     */
  /* works-around the issues.                                           */

  /* Return the first non-addressable location > p or bound.    */
  /* Requires the allocation lock.                              */
  STATIC ptr_t GC_find_limit_openbsd(ptr_t p, ptr_t bound)
  {
    static volatile ptr_t result;
             /* Safer if static, since otherwise it may not be  */
             /* preserved across the longjmp.  Can safely be    */
             /* static since it's only called with the          */
             /* allocation lock held.                           */

    struct sigaction act;
    word pgsz = (word)sysconf(_SC_PAGESIZE);

    GC_ASSERT((word)bound >= pgsz);
    GC_ASSERT(I_HOLD_LOCK());

    act.sa_handler = GC_fault_handler_openbsd;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_NODEFER | SA_RESTART;
    /* act.sa_restorer is deprecated and should not be initialized. */
    sigaction(SIGSEGV, &act, &old_segv_act);

    if (SETJMP(GC_jmp_buf_openbsd) == 0) {
      result = (ptr_t)((word)p & ~(pgsz-1));
      for (;;) {
        if ((word)result >= (word)bound - pgsz) {
          result = bound;
          break;
        }
        result += pgsz; /* no overflow expected */
        GC_noop1((word)(*result));
      }
    }

#   ifdef THREADS
      /* Due to the siglongjump we need to manually unmask SIGPROF.     */
      __syscall(SYS_sigprocmask, SIG_UNBLOCK, sigmask(SIGPROF));
#   endif

    sigaction(SIGSEGV, &old_segv_act, 0);
    return(result);
  }
endif /* GC_OPENBSD_UTHREADS */

  static volatile int firstpass;

  /* Return first addressable location > p or bound.    */
  /* Requires the allocation lock.                      */
  STATIC ptr_t GC_skip_hole_openbsd(ptr_t p, ptr_t bound)
  {
    static volatile ptr_t result;

    struct sigaction act;
    word pgsz = (word)sysconf(_SC_PAGESIZE);

    GC_ASSERT((word)bound >= pgsz);
    GC_ASSERT(I_HOLD_LOCK());

    act.sa_handler = GC_fault_handler_openbsd;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_NODEFER | SA_RESTART;
    /* act.sa_restorer is deprecated and should not be initialized. */
    sigaction(SIGSEGV, &act, &old_segv_act);

    firstpass = 1;
    result = (ptr_t)((word)p & ~(pgsz-1));
    if (SETJMP(GC_jmp_buf_openbsd) != 0 || firstpass) {
      firstpass = 0;
      if ((word)result >= (word)bound - pgsz) {
        result = bound;
      } else {
        result += pgsz; /* no overflow expected */
        GC_noop1((word)(*result));
      }
    }

    sigaction(SIGSEGV, &old_segv_act, 0);
    return(result);
  }
#endif /* OPENBSD */

ifdef OS2

include <stddef.h>

if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */

struct exe_hdr {
    unsigned short      magic_number;
    unsigned short      padding[29];
    long                new_exe_offset;
};

#define E_MAGIC(x)      (x).magic_number
#define EMAGIC          0x5A4D
#define E_LFANEW(x)     (x).new_exe_offset

struct e32_exe {
    unsigned char       magic_number[2];
    unsigned char       byte_order;
    unsigned char       word_order;
    unsigned long       exe_format_level;
    unsigned short      cpu;
    unsigned short      os;
    unsigned long       padding1[13];
    unsigned long       object_table_offset;
    unsigned long       object_count;
    unsigned long       padding2[31];
};

#define E32_MAGIC1(x)   (x).magic_number[0]
#define E32MAGIC1       'L'
#define E32_MAGIC2(x)   (x).magic_number[1]
#define E32MAGIC2       'X'
#define E32_BORDER(x)   (x).byte_order
#define E32LEBO         0
#define E32_WORDER(x)   (x).word_order
#define E32LEWO         0
#define E32_CPU(x)      (x).cpu
#define E32CPU286       1
#define E32_OBJTAB(x)   (x).object_table_offset
#define E32_OBJCNT(x)   (x).object_count

struct o32_obj {
    unsigned long       size;
    unsigned long       base;
    unsigned long       flags;
    unsigned long       pagemap;
    unsigned long       mapsize;
    unsigned long       reserved;
};

#define O32_FLAGS(x)    (x).flags
#define OBJREAD         0x0001L
#define OBJWRITE        0x0002L
#define OBJINVALID      0x0080L
#define O32_SIZE(x)     (x).size
#define O32_BASE(x)     (x).base

else  /* IBM's compiler */

/* A kludge to get around what appears to be a header file bug */
ifndef WORD
#   define WORD unsigned short
endif
ifndef DWORD
#   define DWORD unsigned long
endif

define EXE386 1
include <newexe.h>
include <exe386.h>

endif  /* __IBMC__ */

define INCL_DOSEXCEPTIONS
define INCL_DOSPROCESS
define INCL_DOSERRORS
define INCL_DOSMODULEMGR
define INCL_DOSMEMMGR
include <os2.h>

endif /* OS/2 */

/* Find the page size */
GC_INNER size_t GC_page_size = 0;

#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
ifndef VER_PLATFORM_WIN32_CE
#   define VER_PLATFORM_WIN32_CE 3
endif

if defined(MSWINCE) && defined(THREADS)
    GC_INNER GC_bool GC_dont_query_stack_min = FALSE;
endif

  GC_INNER SYSTEM_INFO GC_sysinfo;

  GC_INNER void GC_setpagesize(void)
  {
    GetSystemInfo(&GC_sysinfo);
#   if defined(CYGWIN32) && (defined(MPROTECT_VDB) || defined(USE_MUNMAP))
      /* Allocations made with mmap() are aligned to the allocation     */
      /* granularity, which (at least on 64-bit Windows OS) is not the  */
      /* same as the page size.  Probably a separate variable could     */
      /* be added to distinguish the allocation granularity from the    */
      /* actual page size, but in practice there is no good reason to   */
      /* make allocations smaller than dwAllocationGranularity, so we   */
      /* just use it instead of the actual page size here (as Cygwin    */
      /* itself does in many cases).                                    */
      GC_page_size = (size_t)GC_sysinfo.dwAllocationGranularity;
      GC_ASSERT(GC_page_size >= (size_t)GC_sysinfo.dwPageSize);
#   else
      GC_page_size = (size_t)GC_sysinfo.dwPageSize;
#   endif
#   if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
      {
        OSVERSIONINFO verInfo;
        /* Check the current WinCE version.     */
        verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        if (!GetVersionEx(&verInfo))
          ABORT("GetVersionEx failed");
        if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_CE &&
            verInfo.dwMajorVersion < 6) {
          /* Only the first 32 MB of address space belongs to the       */
          /* current process (unless WinCE 6.0+ or emulation).          */
          GC_sysinfo.lpMaximumApplicationAddress = (LPVOID)((word)32 << 20);
#         ifdef THREADS
            /* On some old WinCE versions, it's observed that           */
            /* VirtualQuery calls don't work properly when used to      */
            /* get thread current stack committed minimum.              */
            if (verInfo.dwMajorVersion < 5)
              GC_dont_query_stack_min = TRUE;
#         endif
        }
      }
#   endif
  }

ifndef CYGWIN32
#   define is_writable(prot) ((prot) == PAGE_READWRITE \
                            || (prot) == PAGE_WRITECOPY \
                            || (prot) == PAGE_EXECUTE_READWRITE \
                            || (prot) == PAGE_EXECUTE_WRITECOPY)
    /* Return the number of bytes that are writable starting at p.      */
    /* The pointer p is assumed to be page aligned.                     */
    /* If base is not 0, *base becomes the beginning of the             */
    /* allocation region containing p.                                  */
    STATIC word GC_get_writable_length(ptr_t p, ptr_t *base)
    {
      MEMORY_BASIC_INFORMATION buf;
      word result;
      word protect;

      result = VirtualQuery(p, &buf, sizeof(buf));
      if (result != sizeof(buf)) ABORT("Weird VirtualQuery result");
      if (base != 0) *base = (ptr_t)(buf.AllocationBase);
      protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE));
      if (!is_writable(protect)) {
        return(0);
      }
      if (buf.State != MEM_COMMIT) return(0);
      return(buf.RegionSize);
    }

    GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
    {
      ptr_t trunc_sp;
      word size;

      /* Set page size if it is not ready (so client can use this       */
      /* function even before GC is initialized).                       */
      if (!GC_page_size) GC_setpagesize();

      trunc_sp = (ptr_t)((word)GC_approx_sp() & ~(GC_page_size - 1));
      /* FIXME: This won't work if called from a deeply recursive       */
      /* client code (and the committed stack space has grown).         */
      size = GC_get_writable_length(trunc_sp, 0);
      GC_ASSERT(size != 0);
      sb -> mem_base = trunc_sp + size;
      return GC_SUCCESS;
    }
else /* CYGWIN32 */
    /* An alternate version for Cygwin (adapted from Dave Korn's        */
    /* gcc version of boehm-gc).                                        */
    GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
    {
#     ifdef X86_64
        sb -> mem_base = ((NT_TIB*)NtCurrentTeb())->StackBase;
#     else
        void * _tlsbase;

        __asm__ ("movl %%fs:4, %0"
                 : "=r" (_tlsbase));
        sb -> mem_base = _tlsbase;
#     endif
      return GC_SUCCESS;
    }
endif /* CYGWIN32 */
define HAVE_GET_STACK_BASE

#else /* !MSWIN32 */
  GC_INNER void GC_setpagesize(void)
  {
#   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(SOFT_VDB) \
       || defined(USE_MMAP)
      GC_page_size = (size_t)GETPAGESIZE();
#     if !defined(CPPCHECK)
        if (0 == GC_page_size)
          ABORT("getpagesize failed");
#     endif
#   else
      /* It's acceptable to fake it.    */
      GC_page_size = HBLKSIZE;
#   endif
  }
#endif /* !MSWIN32 */

#ifdef HAIKU
include <kernel/OS.h>

  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
  {
    thread_info th;
    get_thread_info(find_thread(NULL),&th);
    sb->mem_base = th.stack_end;
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* HAIKU */

#ifdef OS2
  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
  {
    PTIB ptib; /* thread information block */
    PPIB ppib;
    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
      WARN("DosGetInfoBlocks failed\n", 0);
      return GC_UNIMPLEMENTED;
    }
    sb->mem_base = ptib->tib_pstacklimit;
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* OS2 */

ifdef AMIGA
#   define GC_AMIGA_SB
#   include "extra/AmigaOS.c"
#   undef GC_AMIGA_SB
#   define GET_MAIN_STACKBASE_SPECIAL
endif /* AMIGA */

if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)

    typedef void (*GC_fault_handler_t)(int);

#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
       || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \
       || defined(NETBSD)
        static struct sigaction old_segv_act;
#       if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \
           || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
            static struct sigaction old_bus_act;
#       endif
#   else
      static GC_fault_handler_t old_segv_handler;
#     ifdef HAVE_SIGBUS
        static GC_fault_handler_t old_bus_handler;
#     endif
#   endif

    GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t h)
    {
#       if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
           || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \
           || defined(NETBSD) || defined(OPENBSD)
          struct sigaction act;

          act.sa_handler = h;
#         ifdef SIGACTION_FLAGS_NODEFER_HACK
            /* Was necessary for Solaris 2.3 and very temporary */
            /* NetBSD bugs.                                     */
            act.sa_flags = SA_RESTART | SA_NODEFER;
#         else
            act.sa_flags = SA_RESTART;
#         endif

          (void) sigemptyset(&act.sa_mask);
          /* act.sa_restorer is deprecated and should not be initialized. */
#         ifdef GC_IRIX_THREADS
            /* Older versions have a bug related to retrieving and      */
            /* and setting a handler at the same time.                  */
            (void) sigaction(SIGSEGV, 0, &old_segv_act);
            (void) sigaction(SIGSEGV, &act, 0);
#         else
            (void) sigaction(SIGSEGV, &act, &old_segv_act);
#           if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
               || defined(HPUX) || defined(HURD) || defined(NETBSD) \
               || defined(FREEBSD)
              /* Under Irix 5.x or HP/UX, we may get SIGBUS.    */
              /* Pthreads doesn't exist under Irix 5.x, so we   */
              /* don't have to worry in the threads case.       */
              (void) sigaction(SIGBUS, &act, &old_bus_act);
#           endif
#         endif /* !GC_IRIX_THREADS */
#       else
          old_segv_handler = signal(SIGSEGV, h);
#         ifdef HAVE_SIGBUS
            old_bus_handler = signal(SIGBUS, h);
#         endif
#       endif
#       if defined(CPPCHECK) && defined(ADDRESS_SANITIZER)
          GC_noop1((word)&__asan_default_options);
#       endif
    }
endif /* NEED_FIND_LIMIT || UNIX_LIKE */

if defined(NEED_FIND_LIMIT) \
     || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS))
  /* Some tools to implement HEURISTIC2 */
#   define MIN_PAGE_SIZE 256    /* Smallest conceivable page size, bytes */

    GC_INNER JMP_BUF GC_jmp_buf;

    STATIC void GC_fault_handler(int sig GC_ATTR_UNUSED)
    {
        LONGJMP(GC_jmp_buf, 1);
    }

    GC_INNER void GC_setup_temporary_fault_handler(void)
    {
        /* Handler is process-wide, so this should only happen in       */
        /* one thread at a time.                                        */
        GC_ASSERT(I_HOLD_LOCK());
        GC_set_and_save_fault_handler(GC_fault_handler);
    }

    GC_INNER void GC_reset_fault_handler(void)
    {
#       if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
           || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \
           || defined(NETBSD) || defined(OPENBSD)
          (void) sigaction(SIGSEGV, &old_segv_act, 0);
#         if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
             || defined(HPUX) || defined(HURD) || defined(NETBSD) \
             || defined(FREEBSD)
              (void) sigaction(SIGBUS, &old_bus_act, 0);
#         endif
#       else
          (void) signal(SIGSEGV, old_segv_handler);
#         ifdef HAVE_SIGBUS
            (void) signal(SIGBUS, old_bus_handler);
#         endif
#       endif
    }

    /* Return the first non-addressable location > p (up) or    */
    /* the smallest location q s.t. [q,p) is addressable (!up). */
    /* We assume that p (up) or p-1 (!up) is addressable.       */
    /* Requires allocation lock.                                */
    GC_ATTR_NO_SANITIZE_ADDR
    STATIC ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound)
    {
        static volatile ptr_t result;
                /* Safer if static, since otherwise it may not be       */
                /* preserved across the longjmp.  Can safely be         */
                /* static since it's only called with the               */
                /* allocation lock held.                                */

        GC_ASSERT(up ? (word)bound >= MIN_PAGE_SIZE
                     : (word)bound <= ~(word)MIN_PAGE_SIZE);
        GC_ASSERT(I_HOLD_LOCK());
        GC_setup_temporary_fault_handler();
        if (SETJMP(GC_jmp_buf) == 0) {
            result = (ptr_t)(((word)(p))
                              & ~(MIN_PAGE_SIZE-1));
            for (;;) {
                if (up) {
                    if ((word)result >= (word)bound - MIN_PAGE_SIZE) {
                      result = bound;
                      break;
                    }
                    result += MIN_PAGE_SIZE; /* no overflow expected */
                } else {
                    if ((word)result <= (word)bound + MIN_PAGE_SIZE) {
                      result = bound - MIN_PAGE_SIZE;
                                        /* This is to compensate        */
                                        /* further result increment (we */
                                        /* do not modify "up" variable  */
                                        /* since it might be clobbered  */
                                        /* by setjmp otherwise).        */
                      break;
                    }
                    result -= MIN_PAGE_SIZE; /* no underflow expected */
                }
                GC_noop1((word)(*result));
            }
        }
        GC_reset_fault_handler();
        if (!up) {
            result += MIN_PAGE_SIZE;
        }
        return(result);
    }

    void * GC_find_limit(void * p, int up)
    {
        return GC_find_limit_with_bound((ptr_t)p, (GC_bool)up,
                                        up ? (ptr_t)GC_WORD_MAX : 0);
    }
endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */

#ifdef HPUX_MAIN_STACKBOTTOM
include <sys/param.h>
include <sys/pstat.h>

  STATIC ptr_t GC_hpux_main_stack_base(void)
  {
    struct pst_vm_status vm_status;
    int i = 0;

    while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) {
      if (vm_status.pst_type == PS_STACK)
        return (ptr_t)vm_status.pst_vaddr;
    }

    /* Old way to get the stack bottom. */
#   ifdef STACK_GROWS_UP
      return (ptr_t)GC_find_limit(GC_approx_sp(), /* up= */ FALSE);
#   else /* not HP_PA */
      return (ptr_t)GC_find_limit(GC_approx_sp(), TRUE);
#   endif
  }
#endif /* HPUX_MAIN_STACKBOTTOM */

#ifdef HPUX_STACKBOTTOM

#include <sys/param.h>
#include <sys/pstat.h>

  GC_INNER ptr_t GC_get_register_stack_base(void)
  {
    struct pst_vm_status vm_status;

    int i = 0;
    while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) {
      if (vm_status.pst_type == PS_RSESTACK) {
        return (ptr_t) vm_status.pst_vaddr;
      }
    }

    /* old way to get the register stackbottom */
    return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1)
                   & ~(BACKING_STORE_ALIGNMENT - 1));
  }

#endif /* HPUX_STACK_BOTTOM */

#ifdef LINUX_STACKBOTTOM

include <sys/types.h>
include <sys/stat.h>

define STAT_SKIP 27   /* Number of fields preceding startstack        */
                        /* field in /proc/self/stat                     */

ifdef USE_LIBC_PRIVATES
    EXTERN_C_BEGIN
#   pragma weak __libc_stack_end
    extern ptr_t __libc_stack_end;
#   ifdef IA64
#     pragma weak __libc_ia64_register_backing_store_base
      extern ptr_t __libc_ia64_register_backing_store_base;
#   endif
    EXTERN_C_END
endif

ifdef IA64
    GC_INNER ptr_t GC_get_register_stack_base(void)
    {
      ptr_t result;

#     ifdef USE_LIBC_PRIVATES
        if (0 != &__libc_ia64_register_backing_store_base
            && 0 != __libc_ia64_register_backing_store_base) {
          /* Glibc 2.2.4 has a bug such that for dynamically linked     */
          /* executables __libc_ia64_register_backing_store_base is     */
          /* defined but uninitialized during constructor calls.        */
          /* Hence we check for both nonzero address and value.         */
          return __libc_ia64_register_backing_store_base;
        }
#     endif
      result = backing_store_base_from_proc();
      if (0 == result) {
          result = (ptr_t)GC_find_limit(GC_save_regs_in_stack(), FALSE);
          /* This works better than a constant displacement heuristic.  */
      }
      return result;
    }
endif /* IA64 */

  STATIC ptr_t GC_linux_main_stack_base(void)
  {
    /* We read the stack bottom value from /proc/self/stat.  We do this */
    /* using direct I/O system calls in order to avoid calling malloc   */
    /* in case REDIRECT_MALLOC is defined.                              */
#     define STAT_BUF_SIZE 4096
    char stat_buf[STAT_BUF_SIZE];
    int f;
    word result;
    ssize_t i, buf_offset = 0, len;

    /* First try the easy way.  This should work for glibc 2.2  */
    /* This fails in a prelinked ("prelink" command) executable */
    /* since the correct value of __libc_stack_end never        */
    /* becomes visible to us.  The second test works around     */
    /* this.                                                    */
#   ifdef USE_LIBC_PRIVATES
      if (0 != &__libc_stack_end && 0 != __libc_stack_end ) {
#       if defined(IA64)
          /* Some versions of glibc set the address 16 bytes too        */
          /* low while the initialization code is running.              */
          if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) {
            return __libc_stack_end + 0x10;
          } /* Otherwise it's not safe to add 16 bytes and we fall      */
            /* back to using /proc.                                     */
#       elif defined(SPARC)
          /* Older versions of glibc for 64-bit SPARC do not set this   */
          /* variable correctly, it gets set to either zero or one.     */
          if (__libc_stack_end != (ptr_t) (unsigned long)0x1)
            return __libc_stack_end;
#       else
          return __libc_stack_end;
#       endif
      }
#   endif

    f = open("/proc/self/stat", O_RDONLY);
    if (-1 == f)
      ABORT_ARG1("Could not open /proc/self/stat"": errno= %d", errno);
    len = GC_repeat_read(f, stat_buf, sizeof(stat_buf));
    if (len < 0)
      ABORT_ARG1("Failed to read /proc/self/stat",
                 ": errno= %d", errno);
    close(f);

    /* Skip the required number of fields.  This number is hopefully    */
    /* constant across all Linux implementations.                       */
    for (i = 0; i < STAT_SKIP; ++i) {
      while (buf_offset < len && isspace(stat_buf[buf_offset++])) {
        /* empty */
      }
      while (buf_offset < len && !isspace(stat_buf[buf_offset++])) {
        /* empty */
      }
    }
    /* Skip spaces.     */
    while (buf_offset < len && isspace(stat_buf[buf_offset])) {
      buf_offset++;
    }
    /* Find the end of the number and cut the buffer there.     */
    for (i = 0; buf_offset + i < len; i++) {
      if (!isdigit(stat_buf[buf_offset + i])) break;
    }
    if (buf_offset + i >= len) ABORT("Could not parse /proc/self/stat");
    stat_buf[buf_offset + i] = '\0';

    result = (word)STRTOULL(&stat_buf[buf_offset], NULL, 10);
    if (result < 0x100000 || (result & (sizeof(word) - 1)) != 0)
      ABORT_ARG1("Absurd stack bottom value",
                 ": 0x%lx", (unsigned long)result);
    return (ptr_t)result;
  }
#endif /* LINUX_STACKBOTTOM */

#ifdef FREEBSD_STACKBOTTOM
  /* This uses an undocumented sysctl call, but at least one expert     */
  /* believes it will stay.                                             */

include <unistd.h>
include <sys/types.h>
include <sys/sysctl.h>

  STATIC ptr_t GC_freebsd_main_stack_base(void)
  {
    int nm[2] = {CTL_KERN, KERN_USRSTACK};
    ptr_t base;
    size_t len = sizeof(ptr_t);
    int r = sysctl(nm, 2, &base, &len, NULL, 0);
    if (r) ABORT("Error getting main stack base");
    return base;
  }
#endif /* FREEBSD_STACKBOTTOM */

#if defined(ECOS) || defined(NOSYS)
  ptr_t GC_get_main_stack_base(void)
  {
    return STACKBOTTOM;
  }
define GET_MAIN_STACKBASE_SPECIAL
#elif defined(SYMBIAN)
  EXTERN_C_BEGIN
  extern int GC_get_main_symbian_stack_base(void);
  EXTERN_C_END

  ptr_t GC_get_main_stack_base(void)
  {
    return (ptr_t)GC_get_main_symbian_stack_base();
  }
define GET_MAIN_STACKBASE_SPECIAL
#elif defined(EMSCRIPTEN)
include <emscripten.h>

  static void* emscripten_stack_base;

  static void scan_stack_cb(void *begin, void *end)
  {
    (void)begin;
    emscripten_stack_base = end;
  }

  ptr_t GC_get_main_stack_base(void)
  {
    emscripten_scan_stack(scan_stack_cb);
    return (ptr_t)emscripten_stack_base;
  }
define GET_MAIN_STACKBASE_SPECIAL
#elif !defined(AMIGA) && !defined(HAIKU) && !defined(OS2) \
      && !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) \
      && !defined(GC_OPENBSD_THREADS) \
      && (!defined(GC_SOLARIS_THREADS) || defined(_STRICT_STDC))

if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \
     && (defined(THREADS) || defined(USE_GET_STACKBASE_FOR_MAIN))
#   include <pthread.h>
#   ifdef HAVE_PTHREAD_NP_H
#     include <pthread_np.h> /* for pthread_attr_get_np() */
#   endif
elif defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP)
    /* We could use pthread_get_stackaddr_np even in case of a  */
    /* single-threaded gclib (there is no -lpthread on Darwin). */
#   include <pthread.h>
#   undef STACKBOTTOM
#   define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self())
endif

  ptr_t GC_get_main_stack_base(void)
  {
    ptr_t result;
#   if (defined(HAVE_PTHREAD_ATTR_GET_NP) \
        || defined(HAVE_PTHREAD_GETATTR_NP)) \
       && (defined(USE_GET_STACKBASE_FOR_MAIN) \
           || (defined(THREADS) && !defined(REDIRECT_MALLOC)))
      pthread_attr_t attr;
      void *stackaddr;
      size_t size;

#     ifdef HAVE_PTHREAD_ATTR_GET_NP
        if (pthread_attr_init(&attr) == 0
            && (pthread_attr_get_np(pthread_self(), &attr) == 0
                ? TRUE : (pthread_attr_destroy(&attr), FALSE)))
#     else /* HAVE_PTHREAD_GETATTR_NP */
        if (pthread_getattr_np(pthread_self(), &attr) == 0)
#     endif
      {
        if (pthread_attr_getstack(&attr, &stackaddr, &size) == 0
            && stackaddr != NULL) {
          (void)pthread_attr_destroy(&attr);
#         ifdef STACK_GROWS_DOWN
            stackaddr = (char *)stackaddr + size;
#         endif
          return (ptr_t)stackaddr;
        }
        (void)pthread_attr_destroy(&attr);
      }
      WARN("pthread_getattr_np or pthread_attr_getstack failed"
           " for main thread\n", 0);
#   endif
#   ifdef STACKBOTTOM
      result = STACKBOTTOM;
#   else
#     ifdef HEURISTIC1
#       define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
#       ifdef STACK_GROWS_DOWN
          result = (ptr_t)(((word)GC_approx_sp() + STACKBOTTOM_ALIGNMENT_M1)
                           & ~STACKBOTTOM_ALIGNMENT_M1);
#       else
          result = (ptr_t)((word)GC_approx_sp() & ~STACKBOTTOM_ALIGNMENT_M1);
#       endif
#     elif defined(HPUX_MAIN_STACKBOTTOM)
        result = GC_hpux_main_stack_base();
#     elif defined(LINUX_STACKBOTTOM)
        result = GC_linux_main_stack_base();
#     elif defined(FREEBSD_STACKBOTTOM)
        result = GC_freebsd_main_stack_base();
#     elif defined(HEURISTIC2)
        {
          ptr_t sp = GC_approx_sp();
#         ifdef STACK_GROWS_DOWN
            result = (ptr_t)GC_find_limit(sp, TRUE);
#           if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK)
              if ((word)result > (word)HEURISTIC2_LIMIT
                  && (word)sp < (word)HEURISTIC2_LIMIT) {
                result = HEURISTIC2_LIMIT;
              }
#           endif
#         else
            result = (ptr_t)GC_find_limit(sp, FALSE);
#           if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK)
              if ((word)result < (word)HEURISTIC2_LIMIT
                  && (word)sp > (word)HEURISTIC2_LIMIT) {
                result = HEURISTIC2_LIMIT;
              }
#           endif
#         endif
        }
#     elif defined(STACK_NOT_SCANNED) || defined(CPPCHECK)
        result = NULL;
#     else
#       error None of HEURISTIC* and *STACKBOTTOM defined!
#     endif
#     if defined(STACK_GROWS_DOWN) && !defined(CPPCHECK)
        if (result == 0)
          result = (ptr_t)(signed_word)(-sizeof(ptr_t));
#     endif
#   endif
#   if !defined(CPPCHECK)
      GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)result);
#   endif
    return(result);
  }
define GET_MAIN_STACKBASE_SPECIAL
#endif /* !AMIGA, !HAIKU, !OPENBSD, !OS2, !Windows */

#if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \
    && defined(THREADS) && !defined(HAVE_GET_STACK_BASE)
include <pthread.h>
ifdef HAVE_PTHREAD_NP_H
#   include <pthread_np.h>
endif

  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
  {
    pthread_attr_t attr;
    size_t size;
#   ifdef IA64
      DCL_LOCK_STATE;
#   endif

#   ifdef HAVE_PTHREAD_ATTR_GET_NP
      if (pthread_attr_init(&attr) != 0)
        ABORT("pthread_attr_init failed");
      if (pthread_attr_get_np(pthread_self(), &attr) != 0) {
        WARN("pthread_attr_get_np failed\n", 0);
        (void)pthread_attr_destroy(&attr);
        return GC_UNIMPLEMENTED;
      }
#   else /* HAVE_PTHREAD_GETATTR_NP */
      if (pthread_getattr_np(pthread_self(), &attr) != 0) {
        WARN("pthread_getattr_np failed\n", 0);
        return GC_UNIMPLEMENTED;
      }
#   endif
    if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) {
        ABORT("pthread_attr_getstack failed");
    }
    (void)pthread_attr_destroy(&attr);
#   ifdef STACK_GROWS_DOWN
        b -> mem_base = (char *)(b -> mem_base) + size;
#   endif
#   ifdef IA64
      /* We could try backing_store_base_from_proc, but that's safe     */
      /* only if no mappings are being asynchronously created.          */
      /* Subtracting the size from the stack base doesn't work for at   */
      /* least the main thread.                                         */
      LOCK();
      {
        IF_CANCEL(int cancel_state;)
        ptr_t bsp;
        ptr_t next_stack;

        DISABLE_CANCEL(cancel_state);
        bsp = GC_save_regs_in_stack();
        next_stack = GC_greatest_stack_base_below(bsp);
        if (0 == next_stack) {
          b -> reg_base = GC_find_limit(bsp, FALSE);
        } else {
          /* Avoid walking backwards into preceding memory stack and    */
          /* growing it.                                                */
          b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack);
        }
        RESTORE_CANCEL(cancel_state);
      }
      UNLOCK();
#   elif defined(E2K)
      b -> reg_base = NULL;
#   endif
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* THREADS && (HAVE_PTHREAD_ATTR_GET_NP || HAVE_PTHREAD_GETATTR_NP) */

#if defined(GC_DARWIN_THREADS) && !defined(NO_PTHREAD_GET_STACKADDR_NP)
include <pthread.h>

  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
  {
    /* pthread_get_stackaddr_np() should return stack bottom (highest   */
    /* stack address plus 1).                                           */
    b->mem_base = pthread_get_stackaddr_np(pthread_self());
    GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)b->mem_base);
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* GC_DARWIN_THREADS */

#ifdef GC_OPENBSD_THREADS
include <sys/signal.h>
include <pthread.h>
include <pthread_np.h>

  /* Find the stack using pthread_stackseg_np(). */
  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
  {
    stack_t stack;
    if (pthread_stackseg_np(pthread_self(), &stack))
      ABORT("pthread_stackseg_np(self) failed");
    sb->mem_base = stack.ss_sp;
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* GC_OPENBSD_THREADS */

#if defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC)

include <thread.h>
include <signal.h>
include <pthread.h>

  /* These variables are used to cache ss_sp value for the primordial   */
  /* thread (it's better not to call thr_stksegment() twice for this    */
  /* thread - see JDK bug #4352906).                                    */
  static pthread_t stackbase_main_self = 0;
                        /* 0 means stackbase_main_ss_sp value is unset. */
  static void *stackbase_main_ss_sp = NULL;

  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
  {
    stack_t s;
    pthread_t self = pthread_self();

    if (self == stackbase_main_self)
      {
        /* If the client calls GC_get_stack_base() from the main thread */
        /* then just return the cached value.                           */
        b -> mem_base = stackbase_main_ss_sp;
        GC_ASSERT(b -> mem_base != NULL);
        return GC_SUCCESS;
      }

    if (thr_stksegment(&s)) {
      /* According to the manual, the only failure error code returned  */
      /* is EAGAIN meaning "the information is not available due to the */
      /* thread is not yet completely initialized or it is an internal  */
      /* thread" - this shouldn't happen here.                          */
      ABORT("thr_stksegment failed");
    }
    /* s.ss_sp holds the pointer to the stack bottom. */
    GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)s.ss_sp);

    if (!stackbase_main_self && thr_main() != 0)
      {
        /* Cache the stack bottom pointer for the primordial thread     */
        /* (this is done during GC_init, so there is no race).          */
        stackbase_main_ss_sp = s.ss_sp;
        stackbase_main_self = self;
      }

    b -> mem_base = s.ss_sp;
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* GC_SOLARIS_THREADS */

#ifdef GC_RTEMS_PTHREADS
  GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
  {
    sb->mem_base = rtems_get_stack_bottom();
    return GC_SUCCESS;
  }
define HAVE_GET_STACK_BASE
#endif /* GC_RTEMS_PTHREADS */

#ifndef HAVE_GET_STACK_BASE
ifdef NEED_FIND_LIMIT
    /* Retrieve the stack bottom.                                       */
    /* Using the GC_find_limit version is risky.                        */
    /* On IA64, for example, there is no guard page between the         */
    /* stack of one thread and the register backing store of the        */
    /* next.  Thus this is likely to identify way too large a           */
    /* "stack" and thus at least result in disastrous performance.      */
    /* TODO: Implement better strategies here. */
    GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
    {
      IF_CANCEL(int cancel_state;)
      DCL_LOCK_STATE;

      LOCK();
      DISABLE_CANCEL(cancel_state);  /* May be unnecessary? */
#     ifdef STACK_GROWS_DOWN
        b -> mem_base = GC_find_limit(GC_approx_sp(), TRUE);
#     else
        b -> mem_base = GC_find_limit(GC_approx_sp(), FALSE);
#     endif
#     ifdef IA64
        b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE);
#     elif defined(E2K)
        b -> reg_base = NULL;
#     endif
      RESTORE_CANCEL(cancel_state);
      UNLOCK();
      return GC_SUCCESS;
    }
else
    GC_API int GC_CALL GC_get_stack_base(
                                struct GC_stack_base *b GC_ATTR_UNUSED)
    {
#     if defined(GET_MAIN_STACKBASE_SPECIAL) && !defined(THREADS) \
         && !defined(IA64)
        b->mem_base = GC_get_main_stack_base();
        return GC_SUCCESS;
#     else
        return GC_UNIMPLEMENTED;
#     endif
    }
endif /* !NEED_FIND_LIMIT */
#endif /* !HAVE_GET_STACK_BASE */

#ifndef GET_MAIN_STACKBASE_SPECIAL
  /* This is always called from the main thread.  Default implementation. */
  ptr_t GC_get_main_stack_base(void)
  {
    struct GC_stack_base sb;

    if (GC_get_stack_base(&sb) != GC_SUCCESS)
      ABORT("GC_get_stack_base failed");
    GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)sb.mem_base);
    return (ptr_t)sb.mem_base;
  }
#endif /* !GET_MAIN_STACKBASE_SPECIAL */

/* Register static data segment(s) as roots.  If more data segments are */
/* added later then they need to be registered at that point (as we do  */
/* with SunOS dynamic loading), or GC_mark_roots needs to check for     */
/* them (as we do with PCR).  Called with allocator lock held.          */
ifdef OS2

void GC_register_data_segments(void)
{
    PTIB ptib;
    PPIB ppib;
    HMODULE module_handle;
#   define PBUFSIZ 512
    UCHAR path[PBUFSIZ];
    FILE * myexefile;
    struct exe_hdr hdrdos;      /* MSDOS header.        */
    struct e32_exe hdr386;      /* Real header for my executable */
    struct o32_obj seg;         /* Current segment */
    int nsegs;

#   if defined(CPPCHECK)
        hdrdos.padding[0] = 0; /* to prevent "field unused" warnings */
        hdr386.exe_format_level = 0;
        hdr386.os = 0;
        hdr386.padding1[0] = 0;
        hdr386.padding2[0] = 0;
        seg.pagemap = 0;
        seg.mapsize = 0;
        seg.reserved = 0;
#   endif
    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
        ABORT("DosGetInfoBlocks failed");
    }
    module_handle = ppib -> pib_hmte;
    if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
        ABORT("DosQueryModuleName failed");
    }
    myexefile = fopen(path, "rb");
    if (myexefile == 0) {
        ABORT_ARG1("Failed to open executable"": %s", path);
    }
    if (fread((char *)(&hdrdos), 1, sizeof(hdrdos), myexefile)
          < sizeof(hdrdos)) {
        ABORT_ARG1("Could not read MSDOS header"" from: %s", path);
    }
    if (E_MAGIC(hdrdos) != EMAGIC) {
        ABORT_ARG1("Bad DOS magic number"" in file: %s", path);
    }
    if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
        ABORT_ARG1("Bad DOS magic number"" in file: %s", path);
    }
    if (fread((char *)(&hdr386), 1, sizeof(hdr386), myexefile)
          < sizeof(hdr386)) {
        ABORT_ARG1("Could not read OS/2 header"" from: %s", path);
    }
    if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
        ABORT_ARG1("Bad OS/2 magic number"" in file: %s", path);
    }
    if (E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
        ABORT_ARG1("Bad byte order in executable"" file: %s", path);
    }
    if (E32_CPU(hdr386) == E32CPU286) {
        ABORT_ARG1("GC cannot handle 80286 executables"": %s", path);
    }
    if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
              SEEK_SET) != 0) {
        ABORT_ARG1("Seek to object table failed"" in file: %s", path);
    }
    for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
      int flags;
      if (fread((char *)(&seg), 1, sizeof(seg), myexefile) < sizeof(seg)) {
        ABORT_ARG1("Could not read obj table entry"" from file: %s", path);
      }
      flags = O32_FLAGS(seg);
      if (!(flags & OBJWRITE)) continue;
      if (!(flags & OBJREAD)) continue;
      if (flags & OBJINVALID) {
          GC_err_printf("Object with invalid pages?\n");
          continue;
      }
      GC_add_roots_inner((ptr_t)O32_BASE(seg),
                         (ptr_t)(O32_BASE(seg)+O32_SIZE(seg)), FALSE);
    }
    (void)fclose(myexefile);
}

else /* !OS2 */

if defined(GWW_VDB)
#   ifndef MEM_WRITE_WATCH
#     define MEM_WRITE_WATCH 0x200000
#   endif
#   ifndef WRITE_WATCH_FLAG_RESET
#     define WRITE_WATCH_FLAG_RESET 1
#   endif

    /* Since we can't easily check whether ULONG_PTR and SIZE_T are     */
    /* defined in Win32 basetsd.h, we define own ULONG_PTR.             */
#   define GC_ULONG_PTR word

    typedef UINT (WINAPI * GetWriteWatch_type)(
                                DWORD, PVOID, GC_ULONG_PTR /* SIZE_T */,
                                PVOID *, GC_ULONG_PTR *, PULONG);
    static FARPROC GetWriteWatch_func;
    static DWORD GetWriteWatch_alloc_flag;

#   define GC_GWW_AVAILABLE() (GetWriteWatch_func != 0)

    static void detect_GetWriteWatch(void)
    {
      static GC_bool done;
      HMODULE hK32;
      if (done)
        return;

#     if defined(MPROTECT_VDB)
        {
          char * str = GETENV("GC_USE_GETWRITEWATCH");
#         if defined(GC_PREFER_MPROTECT_VDB)
            if (str == NULL || (*str == '0' && *(str + 1) == '\0')) {
              /* GC_USE_GETWRITEWATCH is unset or set to "0".           */
              done = TRUE/* falling back to MPROTECT_VDB strategy.    */
              /* This should work as if GWW_VDB is undefined. */
              return;
            }
#         else
            if (str != NULL && *str == '0' && *(str + 1) == '\0') {
              /* GC_USE_GETWRITEWATCH is set "0".                       */
              done = TRUE/* falling back to MPROTECT_VDB strategy.    */
              return;
            }
#         endif
        }
#     endif

#     ifdef MSWINRT_FLAVOR
        {
          MEMORY_BASIC_INFORMATION memInfo;
          SIZE_T result = VirtualQuery((void*)(word)GetProcAddress,
                                       &memInfo, sizeof(memInfo));
          if (result != sizeof(memInfo))
            ABORT("Weird VirtualQuery result");
          hK32 = (HMODULE)memInfo.AllocationBase;
        }
#     else
        hK32 = GetModuleHandle(TEXT("kernel32.dll"));
#     endif
      if (hK32 != (HMODULE)0 &&
          (GetWriteWatch_func = GetProcAddress(hK32, "GetWriteWatch")) != 0) {
        /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH,   */
        /* as some versions of kernel32.dll have one but not the      */
        /* other, making the feature completely broken.               */
        void * page;

        GC_ASSERT(GC_page_size != 0);
        page = VirtualAlloc(NULL, GC_page_size, MEM_WRITE_WATCH | MEM_RESERVE,
                            PAGE_READWRITE);
        if (page != NULL) {
          PVOID pages[16];
          GC_ULONG_PTR count = 16;
          DWORD page_size;
          /* Check that it actually works.  In spite of some            */
          /* documentation it actually seems to exist on Win2K.         */
          /* This test may be unnecessary, but ...                      */
          if ((*(GetWriteWatch_type)(word)GetWriteWatch_func)(
                                        WRITE_WATCH_FLAG_RESET, page,
                                        GC_page_size, pages, &count,
                                        &page_size) != 0) {
            /* GetWriteWatch always fails. */
            GetWriteWatch_func = 0;
          } else {
            GetWriteWatch_alloc_flag = MEM_WRITE_WATCH;
          }
          VirtualFree(page, 0 /* dwSize */, MEM_RELEASE);
        } else {
          /* GetWriteWatch will be useless. */
          GetWriteWatch_func = 0;
        }
      }
      done = TRUE;
    }

else
#   define GetWriteWatch_alloc_flag 0
endif /* !GWW_VDB */

if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)

ifdef MSWIN32
  /* Unfortunately, we have to handle win32s very differently from NT,  */
  /* Since VirtualQuery has very different semantics.  In particular,   */
  /* under win32s a VirtualQuery call on an unmapped page returns an    */
  /* invalid result.  Under NT, GC_register_data_segments is a no-op    */
  /* and all real work is done by GC_register_dynamic_libraries.  Under */
  /* win32s, we cannot find the data segments associated with dll's.    */
  /* We register the main data segment here.                            */
  GC_INNER GC_bool GC_no_win32_dlls = FALSE;
        /* This used to be set for gcc, to avoid dealing with           */
        /* the structured exception handling issues.  But we now have   */
        /* assembly code to do that right.                              */

  GC_INNER GC_bool GC_wnt = FALSE;
         /* This is a Windows NT derivative, i.e. NT, Win2K, XP or later. */

  GC_INNER void GC_init_win32(void)
  {
#   if defined(_WIN64) || (defined(_MSC_VER) && _MSC_VER >= 1800)
      /* MS Visual Studio 2013 deprecates GetVersion, but on the other  */
      /* hand it cannot be used to target pre-Win2K.                    */
      GC_wnt = TRUE;
#   else
      /* Set GC_wnt.  If we're running under win32s, assume that no     */
      /* DLLs will be loaded.  I doubt anyone still runs win32s, but... */
      DWORD v = GetVersion();

      GC_wnt = !(v & 0x80000000);
      GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3);
#   endif
#   ifdef USE_MUNMAP
      if (GC_no_win32_dlls) {
        /* Turn off unmapping for safety (since may not work well with  */
        /* GlobalAlloc).                                                */
        GC_unmap_threshold = 0;
      }
#   endif
  }

  /* Return the smallest address a such that VirtualQuery               */
  /* returns correct results for all addresses between a and start.     */
  /* Assumes VirtualQuery returns correct information for start.        */
  STATIC ptr_t GC_least_described_address(ptr_t start)
  {
    MEMORY_BASIC_INFORMATION buf;
    LPVOID limit = GC_sysinfo.lpMinimumApplicationAddress;
    ptr_t p = (ptr_t)((word)start & ~(GC_page_size - 1));

    GC_ASSERT(GC_page_size != 0);
    for (;;) {
        size_t result;
        LPVOID q = (LPVOID)(p - GC_page_size);

        if ((word)q > (word)p /* underflow */ || (word)q < (word)limit) break;
        result = VirtualQuery(q, &buf, sizeof(buf));
        if (result != sizeof(buf) || buf.AllocationBase == 0) break;
        p = (ptr_t)(buf.AllocationBase);
    }
    return p;
  }
endif /* MSWIN32 */

if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC)
  /* We maintain a linked list of AllocationBase values that we know    */
  /* correspond to malloc heap sections.  Currently this is only called */
  /* during a GC.  But there is some hope that for long running         */
  /* programs we will eventually see most heap sections.                */

  /* In the long run, it would be more reliable to occasionally walk    */
  /* the malloc heap with HeapWalk on the default heap.  But that       */
  /* apparently works only for NT-based Windows.                        */

  STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size.  */

  /* In the long run, a better data structure would also be nice ...    */
  STATIC struct GC_malloc_heap_list {
    void * allocation_base;
    struct GC_malloc_heap_list *next;
  } *GC_malloc_heap_l = 0;

  /* Is p the base of one of the malloc heap sections we already know   */
  /* about?                                                             */
  STATIC GC_bool GC_is_malloc_heap_base(void *p)
  {
    struct GC_malloc_heap_list *q = GC_malloc_heap_l;

    while (0 != q) {
      if (q -> allocation_base == p) return TRUE;
      q = q -> next;
    }
    return FALSE;
  }

  STATIC void *GC_get_allocation_base(void *p)
  {
    MEMORY_BASIC_INFORMATION buf;
    size_t result = VirtualQuery(p, &buf, sizeof(buf));
    if (result != sizeof(buf)) {
      ABORT("Weird VirtualQuery result");
    }
    return buf.AllocationBase;
  }

  GC_INNER void GC_add_current_malloc_heap(void)
  {
    struct GC_malloc_heap_list *new_l = (struct GC_malloc_heap_list *)
                 malloc(sizeof(struct GC_malloc_heap_list));
    void *candidate;

    if (NULL == new_l) return;
    new_l -> allocation_base = NULL;
                        /* to suppress maybe-uninitialized gcc warning  */

    candidate = GC_get_allocation_base(new_l);
--> --------------------

--> maximum size reached

--> --------------------

91%


¤ Dauer der Verarbeitung: 0.38 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.