Skip to content
Snippets Groups Projects
os_dep.c 128 KiB
Newer Older
tromey's avatar
tromey committed
/*
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
tromey's avatar
tromey committed
 * 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.
tromey's avatar
tromey committed
 *
 * 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"
tromey's avatar
tromey committed

# if defined(LINUX) && !defined(POWERPC)
#   include <linux/version.h>
#   if (LINUX_VERSION_CODE <= 0x10400)
      /* Ugly hack to get struct sigcontext_struct definition.  Required      */
      /* for some early 1.3.X releases.  Will hopefully go away soon. */
      /* in some later Linux releases, asm/sigcontext.h may have to   */
      /* be included instead.                                         */
#     define __KERNEL__
#     include <asm/signal.h>
#     undef __KERNEL__
#   else
      /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */
      /* struct sigcontext.  libc6 (glibc2) uses "struct sigcontext" in     */
      /* prototypes, so we have to include the top-level sigcontext.h to    */
      /* make sure the former gets defined to be the latter if appropriate. */
#     include <features.h>
#     if 2 <= __GLIBC__
#       if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__
	  /* glibc 2.1 no longer has sigcontext.h.  But signal.h	*/
	  /* has the right declaration for glibc 2.1.			*/
#         include <sigcontext.h>
#       endif /* 0 == __GLIBC_MINOR__ */
tromey's avatar
tromey committed
#     else /* not 2 <= __GLIBC__ */
        /* libc5 doesn't have <sigcontext.h>: go directly with the kernel   */
        /* one.  Check LINUX_VERSION_CODE to see which we should reference. */
#       include <asm/sigcontext.h>
#     endif /* 2 <= __GLIBC__ */
#   endif
# endif
# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
    && !defined(MSWINCE)
tromey's avatar
tromey committed
#   include <sys/types.h>
#   if !defined(MSWIN32) && !defined(SUNOS4)
#   	include <unistd.h>
#   endif
# endif

# include <stdio.h>
# if defined(MSWINCE)
#   define SIGSEGV 0 /* value is irrelevant */
# else
#   include <signal.h>
# endif
tromey's avatar
tromey committed

bryce's avatar
bryce committed
#if defined(LINUX) || defined(LINUX_STACKBOTTOM)
# include <ctype.h>
#endif

tromey's avatar
tromey committed
/* Blatantly OS dependent routines, except for those that are related 	*/
/* to dynamic loading.							*/
tromey's avatar
tromey committed

# if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START)
tromey's avatar
tromey committed
#   define NEED_FIND_LIMIT
# endif

# if !defined(STACKBOTTOM) && defined(HEURISTIC2)
tromey's avatar
tromey committed
#   define NEED_FIND_LIMIT
# endif

# if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR)
tromey's avatar
tromey committed
#   define NEED_FIND_LIMIT
# endif

# if (defined(SVR4) || defined(AUX) || defined(DGUX) \
      || (defined(LINUX) && defined(SPARC))) && !defined(PCR)
tromey's avatar
tromey committed
#   define NEED_FIND_LIMIT
# endif

#if defined(FREEBSD) && (defined(I386) || defined(X86_64) || defined(powerpc) || defined(__powerpc__))
jsturm's avatar
jsturm committed
#  include <machine/trap.h>
#  if !defined(PCR)
#    define NEED_FIND_LIMIT
#  endif
#endif

bryce's avatar
bryce committed
#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) \
    && !defined(NEED_FIND_LIMIT)
   /* Used by GC_init_netbsd_elf() below.	*/
#  define NEED_FIND_LIMIT
#endif

tromey's avatar
tromey committed
#ifdef NEED_FIND_LIMIT
#   include <setjmp.h>
#endif

#ifdef AMIGA
# define GC_AMIGA_DEF
# include "AmigaOS.c"
# undef GC_AMIGA_DEF
tromey's avatar
tromey committed
#endif

davek's avatar
davek committed
#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
tromey's avatar
tromey committed
# define WIN32_LEAN_AND_MEAN
# define NOSERVICE
# include <windows.h>
#endif

#ifdef MACOS
# include <Processes.h>
#endif

#ifdef IRIX5
# include <sys/uio.h>
# include <malloc.h>   /* for locking */
#endif
bryce's avatar
bryce committed
#if defined(USE_MMAP) || defined(USE_MUNMAP)
# ifndef USE_MMAP
    --> USE_MUNMAP requires USE_MMAP
# endif
tromey's avatar
tromey committed
# include <sys/types.h>
# include <sys/mman.h>
# include <sys/stat.h>
bryce's avatar
bryce committed
# include <errno.h>
#endif

#ifdef UNIX_LIKE
tromey's avatar
tromey committed
# include <fcntl.h>
bryce's avatar
bryce committed
# if defined(SUNOS5SIGS) && !defined(FREEBSD)
#  include <sys/siginfo.h>
# endif
bryce's avatar
bryce committed
  /* Define SETJMP and friends to be the version that restores	*/
  /* the signal mask.						*/
# define SETJMP(env) sigsetjmp(env, 1)
# define LONGJMP(env, val) siglongjmp(env, val)
# define JMP_BUF sigjmp_buf
#else
# define SETJMP(env) setjmp(env)
# define LONGJMP(env, val) longjmp(env, val)
# define JMP_BUF jmp_buf
tromey's avatar
tromey committed
#endif

jsturm's avatar
jsturm committed
#ifdef DARWIN
/* for get_etext and friends */
#include <mach-o/getsect.h>
#endif

tromey's avatar
tromey committed
#ifdef DJGPP
  /* Apparently necessary for djgpp 2.01.  May cause problems with	*/
tromey's avatar
tromey committed
  /* 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(NO_EXECUTE_PERMISSION)
# define OPT_PROT_EXEC PROT_EXEC
#else
# define OPT_PROT_EXEC 0
#endif

jsturm's avatar
jsturm committed
#if defined(LINUX) && \
    (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))

/* 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.								*/

#define READ read

/* Repeatedly perform a read call until the buffer is filled or	*/
/* we encounter EOF.						*/
ssize_t GC_repeat_read(int fd, char *buf, size_t count)
{
    ssize_t num_read = 0;
    ssize_t result;
    
    while (num_read < count) {
	result = READ(fd, buf + num_read, count - num_read);
	if (result < 0) return result;
	if (result == 0) break;
	num_read += result;
    }
    return num_read;
}

/*
 * Apply fn to a buffer containing the contents of /proc/self/maps.
 * Return the result of fn or, if we failed, 0.
bryce's avatar
bryce committed
 * We currently do nothing to /proc/self/maps other than simply read
 * it.  This code could be simplified if we could determine its size
 * ahead of time.
jsturm's avatar
jsturm committed
 */

word GC_apply_to_maps(word (*fn)(char *))
{
    int f;
    int result;
bryce's avatar
bryce committed
    size_t maps_size = 4000;  /* Initial guess. 	*/
    static char init_buf[1];
    static char *maps_buf = init_buf;
    static size_t maps_buf_sz = 1;

    /* Read /proc/self/maps, growing maps_buf as necessary.	*/
        /* Note that we may not allocate conventionally, and	*/
        /* thus can't use stdio.				*/
jsturm's avatar
jsturm committed
	do {
bryce's avatar
bryce committed
	    if (maps_size >= maps_buf_sz) {
	      /* 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 (maps_buf == 0) return 0;
	    }
jsturm's avatar
jsturm committed
	    f = open("/proc/self/maps", O_RDONLY);
	    if (-1 == f) return 0;
bryce's avatar
bryce committed
	    maps_size = 0;
	    do {
	        result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
	        if (result <= 0) return 0;
	        maps_size += result;
	    } while (result == maps_buf_sz-1);
	    close(f);
	} while (maps_size >= maps_buf_sz);
        maps_buf[maps_size] = '\0';
jsturm's avatar
jsturm committed
	
    /* Apply fn to result. */
	return fn(maps_buf);
}

#endif /* Need GC_apply_to_maps */

#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64))
//
//  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
//
bryce's avatar
bryce committed
//  Note that since about auguat 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.
jsturm's avatar
jsturm committed
//

/*
 * Assign various fields of the first line in buf_ptr to *start, *end,
 * *prot_buf and *maj_dev.  Only *prot_buf may be set for unwritable maps.
 */
char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
                                char *prot_buf, unsigned int *maj_dev)
{
bryce's avatar
bryce committed
    char *start_start, *end_start, *prot_start, *maj_dev_start;
    char *p;
    char *endp;
jsturm's avatar
jsturm committed

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

bryce's avatar
bryce committed
    p = buf_ptr;
    while (isspace(*p)) ++p;
    start_start = p;
    GC_ASSERT(isxdigit(*start_start));
    *start = strtoul(start_start, &endp, 16); p = endp;
    GC_ASSERT(*p=='-');
jsturm's avatar
jsturm committed

bryce's avatar
bryce committed
    ++p;
    end_start = p;
    GC_ASSERT(isxdigit(*end_start));
    *end = strtoul(end_start, &endp, 16); p = endp;
    GC_ASSERT(isspace(*p));
jsturm's avatar
jsturm committed

bryce's avatar
bryce committed
    while (isspace(*p)) ++p;
    prot_start = p;
    GC_ASSERT(*prot_start == 'r' || *prot_start == '-');
    memcpy(prot_buf, prot_start, 4);
    prot_buf[4] = '\0';
    if (prot_buf[1] == 'w') {/* we can skip the rest if it's not writable. */
	/* 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(maj_dev_start, NULL, 16);
jsturm's avatar
jsturm committed
    }

bryce's avatar
bryce committed
    while (*p && *p++ != '\n');
jsturm's avatar
jsturm committed

bryce's avatar
bryce committed
    return p;
jsturm's avatar
jsturm committed
}

#endif /* Need to parse /proc/self/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.						*/
tromey's avatar
tromey committed

# ifdef LINUX
jsturm's avatar
jsturm committed
    /* 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, prefering __data_start.	*/
    /* We assume gcc-compatible pragmas.	*/
#   pragma weak __data_start
    extern int __data_start[];
#   pragma weak data_start
    extern int data_start[];
# endif /* LINUX */

  ptr_t GC_data_start;
tromey's avatar
tromey committed

  void GC_init_linux_data_start()
    extern ptr_t GC_find_limit();
#   ifdef LINUX
      /* Try the easy approaches first:	*/
jsturm's avatar
jsturm committed
      if ((ptr_t)__data_start != 0) {
	  GC_data_start = (ptr_t)(__data_start);
jsturm's avatar
jsturm committed
      if ((ptr_t)data_start != 0) {
	  GC_data_start = (ptr_t)(data_start);
	  return;
      }
#   endif /* LINUX */
jsturm's avatar
jsturm committed
    GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
# ifdef ECOS

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

// setjmp() function, as described in ANSI para 7.6.1.1
bryce's avatar
bryce committed
#undef SETJMP
#define SETJMP( __env__ )  hal_setjmp( __env__ )

// FIXME: 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 memory[ECOS_GC_MEMORY_SIZE];
static char *brk = memory;

static void *tiny_sbrk(ptrdiff_t increment)
{
  void *p = brk;

  brk += increment;

  if (brk >  memory + sizeof memory)
    {
      brk -= increment;
      return NULL;
    }

  return p;
}
#define sbrk tiny_sbrk
# endif /* ECOS */

#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
  ptr_t GC_data_start;

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

tromey's avatar
tromey committed
# 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>


/* Disable and enable signals during nontrivial allocations	*/

void GC_disable_signals(void)
{
    ULONG nest;
    
    DosEnterMustComplete(&nest);
    if (nest != 1) ABORT("nested GC_disable_signals");
}

void GC_enable_signals(void)
{
    ULONG nest;
    
    DosExitMustComplete(&nest);
    if (nest != 0) ABORT("GC_enable_signals");
}


# else

#  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
      && !defined(MSWINCE) \
green's avatar
green committed
      && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
      && !defined(NOSYS) && !defined(ECOS)
tromey's avatar
tromey committed

#   if defined(SIG_BLOCK)
	/* Use POSIX/SYSV interface */
#	define SIGSET_T sigset_t
#	define SIG_DEL(set, signal) sigdelset(&(set), (signal))
#	define SIG_FILL(set) sigfillset(&set)
#	define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old))
#   elif defined(sigmask) && !defined(UTS4) && !defined(HURD)
tromey's avatar
tromey committed
	/* Use the traditional BSD interface */
#	define SIGSET_T int
#	define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
#	define SIG_FILL(set)  (set) = 0x7fffffff
    	  /* Setting the leading bit appears to provoke a bug in some	*/
    	  /* longjmp implementations.  Most systems appear not to have	*/
    	  /* a signal 32.						*/
#	define SIGSETMASK(old, new) (old) = sigsetmask(new)
#   else
#       error undetectable signal API
tromey's avatar
tromey committed
#   endif

static GC_bool mask_initialized = FALSE;

static SIGSET_T new_mask;

static SIGSET_T old_mask;

static SIGSET_T dummy;

#if defined(PRINTSTATS) && !defined(THREADS)
# define CHECK_SIGNALS
  int GC_sig_disabled = 0;
#endif

void GC_disable_signals()
{
    if (!mask_initialized) {
    	SIG_FILL(new_mask);

	SIG_DEL(new_mask, SIGSEGV);
	SIG_DEL(new_mask, SIGILL);
	SIG_DEL(new_mask, SIGQUIT);
#	ifdef SIGBUS
	    SIG_DEL(new_mask, SIGBUS);
#	endif
#	ifdef SIGIOT
	    SIG_DEL(new_mask, SIGIOT);
#	endif
#	ifdef SIGEMT
	    SIG_DEL(new_mask, SIGEMT);
#	endif
#	ifdef SIGTRAP
	    SIG_DEL(new_mask, SIGTRAP);
#	endif 
	mask_initialized = TRUE;
    }
#   ifdef CHECK_SIGNALS
	if (GC_sig_disabled != 0) ABORT("Nested disables");
	GC_sig_disabled++;
#   endif
    SIGSETMASK(old_mask,new_mask);
}

void GC_enable_signals()
{
#   ifdef CHECK_SIGNALS
	if (GC_sig_disabled != 1) ABORT("Unmatched enable");
	GC_sig_disabled--;
#   endif
    SIGSETMASK(dummy,old_mask);
}

#  endif  /* !PCR */

# endif /*!OS/2 */

/* Ivan Demakov: simplest way (to me) */
#if defined (DOS4GW)
tromey's avatar
tromey committed
  void GC_disable_signals() { }
  void GC_enable_signals() { }
#endif

/* Find the page size */
word GC_page_size;

davek's avatar
davek committed
# if defined(MSWIN32) || defined(MSWINCE) || defined (CYGWIN32)
tromey's avatar
tromey committed
  void GC_setpagesize()
  {
    GetSystemInfo(&GC_sysinfo);
    GC_page_size = GC_sysinfo.dwPageSize;
tromey's avatar
tromey committed
  }

# else
#   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) \
       || defined(USE_MUNMAP)
tromey's avatar
tromey committed
	void GC_setpagesize()
	{
	    GC_page_size = GETPAGESIZE();
	}
#   else
	/* It's acceptable to fake it. */
	void GC_setpagesize()
	{
	    GC_page_size = HBLKSIZE;
	}
#   endif
# endif

/* 
 * Find the base of the stack. 
 * Used only in single-threaded environment.
 * With threads, GC_mark_roots needs to know how to do this.
 * Called with allocator lock held.
 */
# if defined(MSWIN32) || defined(MSWINCE)
tromey's avatar
tromey committed
# 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.				*/
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);
}

ptr_t GC_get_stack_base()
{
    int dummy;
    ptr_t sp = (ptr_t)(&dummy);
    ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
    word size = GC_get_writable_length(trunc_sp, 0);
   
    return(trunc_sp + size);
}


# endif /* MS Windows */

# ifdef BEOS
# include <kernel/OS.h>
ptr_t GC_get_stack_base(){
	thread_info th;
	get_thread_info(find_thread(NULL),&th);
	return th.stack_end;
}
# endif /* BEOS */

tromey's avatar
tromey committed

# ifdef OS2

ptr_t GC_get_stack_base()
{
    PTIB ptib;
    PPIB ppib;
    
    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
    	GC_err_printf0("DosGetInfoBlocks failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    return((ptr_t)(ptib -> tib_pstacklimit));
}

# endif /* OS2 */
tromey's avatar
tromey committed

# ifdef AMIGA
#   define GC_AMIGA_SB
#   include "AmigaOS.c"
#   undef GC_AMIGA_SB
# endif /* AMIGA */
tromey's avatar
tromey committed

green's avatar
green committed
# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
tromey's avatar
tromey committed

#   ifdef __STDC__
	typedef void (*handler)(int);
#   else
	typedef void (*handler)();
#   endif

bryce's avatar
bryce committed
#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
    || defined(HURD) || defined(NETBSD)
tromey's avatar
tromey committed
	static struct sigaction old_segv_act;
bryce's avatar
bryce committed
#	if defined(IRIX5) || defined(HPUX) \
bryce's avatar
bryce committed
	|| defined(HURD) || defined(NETBSD)
	    static struct sigaction old_bus_act;
#	endif
tromey's avatar
tromey committed
#   else
        static handler old_segv_handler, old_bus_handler;
#   endif
    
#   ifdef __STDC__
      void GC_set_and_save_fault_handler(handler h)
#   else
      void GC_set_and_save_fault_handler(h)
      handler h;
#   endif
tromey's avatar
tromey committed
    {
bryce's avatar
bryce committed
#	if defined(SUNOS5SIGS) || defined(IRIX5)  \
        || defined(OSF1) || defined(HURD) || defined(NETBSD)
tromey's avatar
tromey committed
	  struct sigaction	act;

	  act.sa_handler	= h;
bryce's avatar
bryce committed
#	  if 0 /* 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
tromey's avatar
tromey committed

	  (void) sigemptyset(&act.sa_mask);
tromey's avatar
tromey committed
		/* 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);
bryce's avatar
bryce committed
	        (void) sigaction(SIGBUS, 0, &old_bus_act);
	        (void) sigaction(SIGBUS, &act, 0);
tromey's avatar
tromey committed
#	  else
	        (void) sigaction(SIGSEGV, &act, &old_segv_act);
bryce's avatar
bryce committed
#		if defined(IRIX5) \
bryce's avatar
bryce committed
		   || defined(HPUX) || defined(HURD) || defined(NETBSD)
		    /* 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.		*/
tromey's avatar
tromey committed
		    (void) sigaction(SIGBUS, &act, &old_bus_act);
#		endif
#	  endif	/* GC_IRIX_THREADS */
tromey's avatar
tromey committed
#	else
    	  old_segv_handler = signal(SIGSEGV, h);
tromey's avatar
tromey committed
#	  ifdef SIGBUS
	    old_bus_handler = signal(SIGBUS, h);
tromey's avatar
tromey committed
#	  endif
#	endif
    }
# endif /* NEED_FIND_LIMIT || UNIX_LIKE */

# ifdef NEED_FIND_LIMIT
  /* Some tools to implement HEURISTIC2	*/
#   define MIN_PAGE_SIZE 256	/* Smallest conceivable page size, bytes */
bryce's avatar
bryce committed
    /* static */ JMP_BUF GC_jmp_buf;
    
    /*ARGSUSED*/
    void GC_fault_handler(sig)
    int sig;
    {
bryce's avatar
bryce committed
        LONGJMP(GC_jmp_buf, 1);
    }

    void GC_setup_temporary_fault_handler()
    {
	GC_set_and_save_fault_handler(GC_fault_handler);
    }
tromey's avatar
tromey committed
    
    void GC_reset_fault_handler()
    {
bryce's avatar
bryce committed
#       if defined(SUNOS5SIGS) || defined(IRIX5) \
	   || defined(OSF1) || defined(HURD) || defined(NETBSD)
	  (void) sigaction(SIGSEGV, &old_segv_act, 0);
bryce's avatar
bryce committed
#	  if defined(IRIX5) \
bryce's avatar
bryce committed
	     || defined(HPUX) || defined(HURD) || defined(NETBSD)
	      (void) sigaction(SIGBUS, &old_bus_act, 0);
#	  endif
#       else
  	  (void) signal(SIGSEGV, old_segv_handler);
#	  ifdef SIGBUS
	    (void) signal(SIGBUS, old_bus_handler);
#	  endif
#       endif
tromey's avatar
tromey committed
    }

    /* Return the first nonaddressible location > p (up) or 	*/
jsturm's avatar
jsturm committed
    /* the smallest location q s.t. [q,p) is addressable (!up).	*/
    /* We assume that p (up) or p-1 (!up) is addressable.	*/
tromey's avatar
tromey committed
    ptr_t GC_find_limit(p, up)
    ptr_t p;
    GC_bool up;
    {
bryce's avatar
bryce committed
        static VOLATILE ptr_t result;
    		/* Needs to be static, since otherwise it may not be	*/
    		/* preserved across the longjmp.  Can safely be 	*/
    		/* static since it's only called once, with the		*/
    		/* allocation lock held.				*/


	GC_setup_temporary_fault_handler();
	if (SETJMP(GC_jmp_buf) == 0) {
	    result = (ptr_t)(((word)(p))
			      & ~(MIN_PAGE_SIZE-1));
	    for (;;) {
 	        if (up) {
		    result += MIN_PAGE_SIZE;
 	        } else {
		    result -= MIN_PAGE_SIZE;
 	        }
		GC_noop1((word)(*result));
	    }
green's avatar
green committed
	}
bryce's avatar
bryce committed
	GC_reset_fault_handler();
 	if (!up) {
	    result += MIN_PAGE_SIZE;
 	}
	return(result);
tromey's avatar
tromey committed
    }
# endif

jsturm's avatar
jsturm committed
#if defined(ECOS) || defined(NOSYS)
  ptr_t GC_get_stack_base()
  {
    return STACKBOTTOM;
  }
#endif
bryce's avatar
bryce committed
#ifdef HPUX_STACKBOTTOM

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

  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			*/
bryce's avatar
bryce committed
#ifdef USE_LIBC_PRIVATES
# pragma weak __libc_stack_end
  extern ptr_t __libc_stack_end;
bryce's avatar
bryce committed
#endif

# ifdef IA64
jsturm's avatar
jsturm committed
    /* Try to read the backing store base from /proc/self/maps.	*/
    /* We look for the writable mapping with a 0 major device,  */
    /* which is	as close to our frame as possible, but below it.*/
    static word backing_store_base_from_maps(char *maps)
    {
      char prot_buf[5];
      char *buf_ptr = maps;
      word start, end;
      unsigned int maj_dev;
      word current_best = 0;
      word dummy;
  
      for (;;) {
        buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
	if (buf_ptr == NULL) return current_best;
	if (prot_buf[1] == 'w' && maj_dev == 0) {
	    if (end < (word)(&dummy) && start > current_best) current_best = start;
	}
      }
      return current_best;
    }

    static word backing_store_base_from_proc(void)
    {
        return GC_apply_to_maps(backing_store_base_from_maps);
    }

bryce's avatar
bryce committed
#   ifdef USE_LIBC_PRIVATES
#     pragma weak __libc_ia64_register_backing_store_base
      extern ptr_t __libc_ia64_register_backing_store_base;
#   endif

    ptr_t GC_get_register_stack_base(void)
    {
bryce's avatar
bryce committed
#     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
      word result = backing_store_base_from_proc();
      if (0 == result) {
jsturm's avatar
jsturm committed
	  /* Use dumb heuristics.  Works only for default configuration. */
	  result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
	  result += BACKING_STORE_ALIGNMENT - 1;
	  result &= ~(BACKING_STORE_ALIGNMENT - 1);
	  /* Verify that it's at least readable.  If not, we goofed. */
	  GC_noop1(*(word *)result); 
bryce's avatar
bryce committed
      return (ptr_t)result;
  ptr_t GC_linux_stack_base(void)
  {
    /* We read the stack base 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
jsturm's avatar
jsturm committed
#   define STAT_READ read
	  /* Should probably call the real read, if read is wrapped.	*/
    char stat_buf[STAT_BUF_SIZE];
    int f;
    char c;
    word result = 0;
    size_t i, buf_offset = 0;
    /* First try the easy way.  This should work for glibc 2.2	*/
bryce's avatar
bryce committed
    /* 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.							*/  
bryce's avatar
bryce committed
#   ifdef USE_LIBC_PRIVATES
bryce's avatar
bryce committed
      if (0 != &__libc_stack_end && 0 != __libc_stack_end ) {
jsturm's avatar
jsturm committed
#       ifdef 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.					*/
#	else 
#	ifdef 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
jsturm's avatar
jsturm committed
	  return __libc_stack_end;
jsturm's avatar
jsturm committed
#	endif
bryce's avatar
bryce committed
#   endif
    f = open("/proc/self/stat", O_RDONLY);
    if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
	ABORT("Couldn't read /proc/self/stat");
    }
    c = stat_buf[buf_offset++];
    /* Skip the required number of fields.  This number is hopefully	*/
    /* constant across all Linux implementations.			*/
      for (i = 0; i < STAT_SKIP; ++i) {
	while (isspace(c)) c = stat_buf[buf_offset++];
	while (!isspace(c)) c = stat_buf[buf_offset++];
    while (isspace(c)) c = stat_buf[buf_offset++];
    while (isdigit(c)) {
      result *= 10;
      result += c - '0';
      c = stat_buf[buf_offset++];
    if (result < 0x10000000) ABORT("Absurd stack bottom value");
    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>

  ptr_t GC_freebsd_stack_base(void)
  {
    int nm[2] = {CTL_KERN, KERN_USRSTACK};
    ptr_t base;