Skip to content
Snippets Groups Projects
os_dep.c 93.6 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

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

#ifdef NEED_FIND_LIMIT
#   include <setjmp.h>
#endif

#if defined(FREEBSD) && defined(I386)
tromey's avatar
tromey committed
#  include <machine/trap.h>
#endif

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

#if defined(MSWIN32) || defined(MSWINCE)
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
#ifdef USE_MMAP
# include <sys/types.h>
# include <sys/mman.h>
# include <sys/stat.h>
#endif

#ifdef UNIX_LIKE
tromey's avatar
tromey committed
# include <fcntl.h>
#endif

#if defined(SUNOS5SIGS) || defined (HURD) || defined(LINUX)
# ifdef SUNOS5SIGS
#  include <sys/siginfo.h>
# endif
tromey's avatar
tromey committed
# undef setjmp
# undef longjmp
# define setjmp(env) sigsetjmp(env, 1)
# define longjmp(env, val) siglongjmp(env, val)
# define jmp_buf sigjmp_buf
#endif

#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

#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
#   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:	*/
      if (__data_start != 0) {
	  GC_data_start = (ptr_t)__data_start;
      if (data_start != 0) {
	  GC_data_start = (ptr_t)data_start;
	  return;
      }
#   endif /* LINUX */
    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
#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(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
	/* 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))
#   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;

# if defined(MSWIN32) || defined(MSWINCE)
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

#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) || defined(HURD)
tromey's avatar
tromey committed
	static struct sigaction old_segv_act;
#	if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) || defined(HURD)
	    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
    {
green's avatar
green committed
#     if defined(SUNOS5SIGS) || defined(IRIX5)  \
        || defined(OSF1) || defined(HURD)
tromey's avatar
tromey committed
	  struct sigaction	act;

	  act.sa_handler	= h;
#	  ifdef SUNOS5SIGS
            act.sa_flags          = SA_RESTART | SA_NODEFER;
#         else
            act.sa_flags          = SA_RESTART;
#	  endif
tromey's avatar
tromey committed
          /* The presence of SA_NODEFER represents yet another gross    */
          /* hack.  Under Solaris 2.3, siglongjmp doesn't appear to     */
          /* interact correctly with -lthread.  We hide the confusion   */
          /* by making sure that signal handling doesn't affect the     */
          /* signal mask.                                               */

	  (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);
#	  else
	        (void) sigaction(SIGSEGV, &act, &old_segv_act);
#		if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
		   || defined(HPUX) || defined(HURD)
		    /* 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 */
    /* static */ jmp_buf GC_jmp_buf;
    
    /*ARGSUSED*/
    void GC_fault_handler(sig)
    int sig;
    {
        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()
    {
green's avatar
green committed
#     if defined(SUNOS5SIGS) || defined(IRIX5) \
	 || defined(OSF1) || defined(HURD)
	(void) sigaction(SIGSEGV, &old_segv_act, 0);
#	if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
	   || defined(HPUX) || defined(HURD)
	    (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 	*/
    /* the smallest location q s.t. [q,p] is addressible (!up).	*/
    ptr_t GC_find_limit(p, up)
    ptr_t p;
    GC_bool up;
    {
green's avatar
green 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) {
tromey's avatar
tromey committed
	    result += MIN_PAGE_SIZE;
green's avatar
green committed
	  } else {
	    result -= MIN_PAGE_SIZE;
	  }
	  GC_noop1((word)(*result));
	}
      }
      GC_reset_fault_handler();
      if (!up) {
	result += MIN_PAGE_SIZE;
      }
      return(result);
tromey's avatar
tromey committed
    }
# endif

green's avatar
green committed
# if defined(ECOS) || defined(NOSYS)
ptr_t GC_get_stack_base()
{
  return STACKBOTTOM;
}

#else

#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			*/
# 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;

    ptr_t GC_get_register_stack_base(void)
    {
      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 ininitialized during constructor calls.  	*/
	/* Hence we check for both nonzero address and value.		*/
	return __libc_ia64_register_backing_store_base;
      } else {
	word result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
	result += BACKING_STORE_ALIGNMENT - 1;
	result &= ~(BACKING_STORE_ALIGNMENT - 1);
	return (ptr_t)result;
      }
    }
# endif

  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
#   if defined(GC_USE_LD_WRAP)
#	define STAT_READ __real_read
#   else
#	define STAT_READ read
#   endif    
    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	*/
      if (0 != &__libc_stack_end) {
	return __libc_stack_end;
      }
    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;
    size_t len = sizeof(ptr_t);
    int r = sysctl(nm, 2, &base, &len, NULL, 0);
    
    if (r) ABORT("Error getting stack base");

  }

#endif /* FREEBSD_STACKBOTTOM */

#if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
green's avatar
green committed
    && !defined(MSWINCE) && !defined(OS2)
tromey's avatar
tromey committed
ptr_t GC_get_stack_base()
{
    word dummy;
    ptr_t result;

#   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)

#   ifdef STACKBOTTOM
	return(STACKBOTTOM);
#   else
#	ifdef HEURISTIC1
#	   ifdef STACK_GROWS_DOWN
	     result = (ptr_t)((((word)(&dummy))
	     		       + STACKBOTTOM_ALIGNMENT_M1)
			      & ~STACKBOTTOM_ALIGNMENT_M1);
#	   else
	     result = (ptr_t)(((word)(&dummy))
			      & ~STACKBOTTOM_ALIGNMENT_M1);
#	   endif
#	endif /* HEURISTIC1 */
#	ifdef LINUX_STACKBOTTOM
	   result = GC_linux_stack_base();
#	endif
#	ifdef FREEBSD_STACKBOTTOM
	   result = GC_freebsd_stack_base();
#	endif
tromey's avatar
tromey committed
#	ifdef HEURISTIC2
#	    ifdef STACK_GROWS_DOWN
		result = GC_find_limit((ptr_t)(&dummy), TRUE);
#           	ifdef HEURISTIC2_LIMIT
		    if (result > HEURISTIC2_LIMIT
		        && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) {
		            result = HEURISTIC2_LIMIT;
		    }
#	        endif
#	    else
		result = GC_find_limit((ptr_t)(&dummy), FALSE);
#           	ifdef HEURISTIC2_LIMIT
		    if (result < HEURISTIC2_LIMIT
		        && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) {
		            result = HEURISTIC2_LIMIT;
		    }
#	        endif
#	    endif

#	endif /* HEURISTIC2 */
#	ifdef STACK_GROWS_DOWN
	    if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t));
#	endif
tromey's avatar
tromey committed
    	return(result);
#   endif /* STACKBOTTOM */
}
green's avatar
green committed
# endif /* NOSYS ECOS */
tromey's avatar
tromey committed

# endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */
tromey's avatar
tromey committed

/*
 * Register static data segment(s) as roots.
 * If more data segments are added later then they need to be registered
 * add 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()
{
    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;	/* Currrent segment */
    int nsegs;
    
    
    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
    	GC_err_printf0("DosGetInfoBlocks failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    module_handle = ppib -> pib_hmte;
    if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
    	GC_err_printf0("DosQueryModuleName failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    myexefile = fopen(path, "rb");
    if (myexefile == 0) {
        GC_err_puts("Couldn't open executable ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Failed to open executable\n");
    }
    if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) {
        GC_err_puts("Couldn't read MSDOS header from ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Couldn't read MSDOS header");
    }
    if (E_MAGIC(hdrdos) != EMAGIC) {
        GC_err_puts("Executable has wrong DOS magic number: ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Bad DOS magic number");
    }
    if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
        GC_err_puts("Seek to new header failed in ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Bad DOS magic number");
    }
    if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) {
        GC_err_puts("Couldn't read MSDOS header from ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Couldn't read OS/2 header");
    }
    if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
        GC_err_puts("Executable has wrong OS/2 magic number:");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Bad OS/2 magic number");
    }
    if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
        GC_err_puts("Executable %s has wrong byte order: ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Bad byte order");
    }
    if ( E32_CPU(hdr386) == E32CPU286) {
        GC_err_puts("GC can't handle 80286 executables: ");
        GC_err_puts(path); GC_err_puts("\n");
        EXIT();
    }
    if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
    	      SEEK_SET) != 0) {
        GC_err_puts("Seek to object table failed: ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Seek to object table failed");
    }
    for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
      int flags;
      if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) {
        GC_err_puts("Couldn't read obj table entry from ");
        GC_err_puts(path); GC_err_puts("\n");
        ABORT("Couldn't read obj table entry");
      }
      flags = O32_FLAGS(seg);
      if (!(flags & OBJWRITE)) continue;
      if (!(flags & OBJREAD)) continue;
      if (flags & OBJINVALID) {
          GC_err_printf0("Object with invalid pages?\n");
          continue;
      } 
      GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE);
    }
}

# else /* !OS2 */

# if defined(MSWIN32) || defined(MSWINCE)
tromey's avatar
tromey committed

# 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 GC_register_data_segments is a noop and	*/
  /* all real work is done by GC_register_dynamic_libraries.  Under	*/
  /* win32s, we cannot find the data segments associated with dll's.	*/
  /* We rgister the main data segment here.				*/
#  ifdef __GCC__
  GC_bool GC_no_win32_dlls = TRUE;	 /* GCC can't do SEH, so we can't use VirtualQuery */
#  else
  GC_bool GC_no_win32_dlls = FALSE;	 
#  endif
tromey's avatar
tromey committed
  
  void GC_init_win32()
  {
    /* if we're running under win32s, assume that no DLLs will be loaded */
    DWORD v = GetVersion();
    GC_no_win32_dlls |= ((v & 0x80000000) && (v & 0xff) <= 3);
tromey's avatar
tromey committed
  }
tromey's avatar
tromey committed
  /* 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.	*/
  ptr_t GC_least_described_address(ptr_t start)
  {  
    MEMORY_BASIC_INFORMATION buf;
    DWORD result;
    LPVOID limit;
    ptr_t p;
    LPVOID q;
    
    limit = GC_sysinfo.lpMinimumApplicationAddress;
tromey's avatar
tromey committed
    p = (ptr_t)((word)start & ~(GC_page_size - 1));
    for (;;) {
    	q = (LPVOID)(p - GC_page_size);
    	if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break;
    	result = VirtualQuery(q, &buf, sizeof(buf));
    	if (result != sizeof(buf) || buf.AllocationBase == 0) break;
    	p = (ptr_t)(buf.AllocationBase);
    }
    return(p);
  }
# endif
tromey's avatar
tromey committed
  
  /* Is p the start of either the malloc heap, or of one of our */
  /* heap sections?						*/
  GC_bool GC_is_heap_base (ptr_t p)
  {
     
     register unsigned i;
     
#    ifndef REDIRECT_MALLOC
       static ptr_t malloc_heap_pointer = 0;
     
       if (0 == malloc_heap_pointer) {
         MEMORY_BASIC_INFORMATION buf;
         void *pTemp = malloc( 1 );
         register DWORD result = VirtualQuery(pTemp, &buf, sizeof(buf));
           
         free( pTemp );

tromey's avatar
tromey committed
         
         if (result != sizeof(buf)) {
             ABORT("Weird VirtualQuery result");
         }
         malloc_heap_pointer = (ptr_t)(buf.AllocationBase);
       }
       if (p == malloc_heap_pointer) return(TRUE);
#    endif
     for (i = 0; i < GC_n_heap_bases; i++) {
         if (GC_heap_bases[i] == p) return(TRUE);
     }
     return(FALSE);
  }

# ifdef MSWIN32
tromey's avatar
tromey committed
  void GC_register_root_section(ptr_t static_root)