This is the mail archive of the guile@sources.redhat.com mailing list for the Guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: GUILE's GC - why we struggling to solve already solved problems ? (was Re: finding the top of the stack in linux)


31-Jul-00 23:32 you wrote:
> 28-Jul-00 03:06 you wrote:
>> "Khimenko Victor" <guile@khim.sch57.msk.ru> writes:

>>> But why the hell we are [...] not even using clearly
>>> appropriate parts (like stack placement finding subroitine) from ALREADY
>>> existing and TESTED solutions.

>> [...]

>>> we still can use parts of it to determine stack placement on
>>> different systems.

>> Would you be willing to cut out and prepare the stack finding code for
>> Guile?

> I will try to do it. It's not trivial, unfortunatelly. It's easy to cut this
> code. It's MUCH harder to do so correct. As you can guess it's big pile of
> #ifdef's (if it's Linux then we do it this way, if it's HP-UX then that way
> and if it's Win32 then still another code is used) and so we have verification
> problem :-/ Plus there are other question: how to do this - manual cut and
> paste with tuning or script to convert from GC_ to scm_GC prefix and strip
> out unneeded code from appropriate .h and .c files ? Manual way is easier
> but then we'll need to repeat this process once every month or two (when new
> version of BGC is out).

After some thought I decided to do this manually. Basically stack detection
stuff in BGC has two parts: big, big heap of #ifdef's in gcconfig.h and this
part is VERY straight and much less pleasant heap of #ifdef's in os_dep to
decide what .h files one need to include for all definitions. This part in
guile should be handled by autoconf, I guess. Core is really simple: and
explained well by Boehm:

/*
 * STACKBOTTOM is the cool end of the stack, which is usually the
 * highest address in the stack.
 * Under PCR or OS/2, we have other ways of finding thread stacks.
 * For each machine, the following should:
 * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and
 * 2) define exactly one of
 *      STACKBOTTOM (should be defined to be an expression)
 *      HEURISTIC1
 *      HEURISTIC2
 * If either of the last two macros are defined, then STACKBOTTOM is computed
 * during collector startup using one of the following two heuristics:
 * HEURISTIC1:  Take an address inside GC_init's frame, and round it up to
 *              the next multiple of STACK_GRAN.
 * HEURISTIC2:  Take an address inside GC_init's frame, increment it repeatedly
 *              in small steps (decrement if STACK_GROWS_UP), and read the value
 *              at each location.  Remember the value when the first
 *              Segmentation violation or Bus error is signalled.  Round that
 *              to the nearest plausible page boundary, and use that instead
 *              of STACKBOTTOM.
 */

Thus implementation is simple enough, but since along with stack bottom
detection there are various interesting things are handled as well (data
segment, heap, etc) I removed most of code from both files. It works here
on Linux (with HEURISTIC1, HEURISTIC2 and LINUX_STACKBOTTOM - special version
with /proc usage) but I'm pretty sure I removed some important check and thus
on other platforms it can be broken.

It needs simple function somewhere in other file:
-- cut --
void scm_stack_noop1(word x)
{
    static volatile word sink;

    sink = x;
}
-- cut --
It must be put in separate file so even VERY "advanced" compiler will not
optimize this call away. Only one function is provided:

void * scm_get_stack_base();

P.S. OS2, Win32 and AMIGA are handled separately and since I have no way to
check them I'm pretty sure right now it does not work there :-/

/* 
 * This file was created by extracting bits from Boehm's GC
 * (see http://somwhere.there/ for more info )
 *
 * If it does not work (or more likely does not compile) for you on system
 * where Boehm's GC works then probably it can be fixed with easy: you need
 * minimal C knowleadge, some brain and last but not least acess to system
 * headers so you can find right headers combination. If Boehm's GC does not
 * work for you then it'll be MUCH harder task.
 */

/* 
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
 * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
 * Copyright (c) 2000 by Hewlett-Packard Company.  All rights reserved.
 *
 * 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.
 */

/* First a unified test for Linux: */
# if defined(linux) || defined(__linux__)
#    define LINUX
# endif

/* Determine the machine type: */
# if defined(sun) && defined(mc68000)
#    define M68K
#    define SUNOS4
#    define mach_type_known
# endif
# if defined(hp9000s300)
#    define M68K
#    define HP
#    define mach_type_known
# endif
# if defined(__OpenBSD__) && defined(m68k)
#    define M68K
#    define OPENBSD
#    define mach_type_known
# endif
# if defined(__OpenBSD__) && defined(__sparc__)
#    define SPARC
#    define OPENBSD
#    define mach_type_known
# endif
# if defined(__NetBSD__) && defined(m68k)
#    define M68K
#    define NETBSD
#    define mach_type_known
# endif
# if defined(__NetBSD__) && defined(arm32)
#    define ARM32
#    define NETBSD
#    define mach_type_known
# endif
# if defined(vax)
#    define VAX
#    ifdef ultrix
#	define ULTRIX
#    else
#	define BSD
#    endif
#    define mach_type_known
# endif
# if defined(mips) || defined(__mips)
#    define MIPS
#    if !defined(LINUX)
#      if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__)
#	 define ULTRIX
#      else
#	 if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) \
	    || defined(__SYSTYPE_SVR4__)
#	   define IRIX5   /* or IRIX 6.X */
#	 else
#	   define RISCOS  /* or IRIX 4.X */
#	 endif
#      endif
#    endif /* !LINUX */
#    define mach_type_known
# endif
# if defined(sequent) && defined(i386)
#    define I386
#    define SEQUENT
#    define mach_type_known
# endif
# if defined(sun) && defined(i386)
#    define I386
#    define SUNOS5
#    define mach_type_known
# endif
# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__)
#    define I386
#    define OS2
#    define mach_type_known
# endif
# if defined(ibm032)
#   define RT
#   define mach_type_known
# endif
# if defined(sun) && (defined(sparc) || defined(__sparc))
#   define SPARC
    /* Test for SunOS 5.x */
#     include <errno.h>
#     ifdef ECHRNG
#       define SUNOS5
#     else
#	define SUNOS4
#     endif
#   define mach_type_known
# endif
# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \
     && !defined(__OpenBSD__)
#   define SPARC
#   define DRSNX
#   define mach_type_known
# endif
# if defined(_IBMR2)
#   define RS6000
#   define mach_type_known
# endif
# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386)
	/* The above test may need refinement	*/
#   define I386
#   if defined(_SCO_ELF)
#     define SCO_ELF
#   else
#     define SCO
#   endif
#   define mach_type_known
# endif
# if defined(_AUX_SOURCE)
#   define M68K
#   define SYSV
#   define mach_type_known
# endif
# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \
     || defined(hppa) || defined(__hppa__)
#   define HP_PA
#   ifndef LINUX
#     define HPUX
#   endif
#   define mach_type_known
# endif
# if defined(LINUX) && (defined(i386) || defined(__i386__))
#    define I386
#    define mach_type_known
# endif
# if defined(LINUX) && (defined(__ia64__) || defined(__ia64))
#    define IA64
#    define mach_type_known
# endif
# if defined(LINUX) && defined(powerpc)
#    define POWERPC
#    define mach_type_known
# endif
# if defined(LINUX) && defined(__mc68000__)
#    define M68K
#    define mach_type_known
# endif
# if defined(LINUX) && (defined(sparc) || defined(__sparc__))
#    define SPARC
#    define mach_type_known
# endif
# if defined(LINUX) && defined(arm)
#    define ARM32
#    define mach_type_known
# endif
# if defined(__alpha) || defined(__alpha__)
#   define ALPHA
#   if !defined(LINUX)
#     define OSF1	/* a.k.a Digital Unix */
#   endif
#   define mach_type_known
# endif
# if defined(_AMIGA) && !defined(AMIGA)
#   define AMIGA
# endif
# ifdef AMIGA 
#   define M68K
#   define mach_type_known
# endif
# if defined(THINK_C) || defined(__MWERKS__) && !defined(__powerc)
#   define M68K
#   define MACOS
#   define mach_type_known
# endif
# if defined(__MWERKS__) && defined(__powerc)
#   define POWERPC
#   define MACOS
#   define mach_type_known
# endif
# if defined(macosx) || \
     defined(__APPLE__) && defined(__MACH__) && defined(__ppc__)
#    define MACOSX
#    define POWERPC
#    define mach_type_known
# endif
# if defined(__APPLE__) && defined(__MACH__) && defined(__i386__)
#    define MACOSX
#    define I386
     --> Not really supported, but at least we recognize it.
# endif
# if defined(NeXT) && defined(mc68000)
#   define M68K
#   define NEXT
#   define mach_type_known
# endif
# if defined(NeXT) && defined(i386)
#   define I386
#   define NEXT
#   define mach_type_known
# endif
# if defined(__OpenBSD__) && defined(i386)
#   define I386
#   define OPENBSD
#   define mach_type_known
# endif
# if defined(__FreeBSD__) && defined(i386)
#   define I386
#   define FREEBSD
#   define mach_type_known
# endif
# if defined(__NetBSD__) && defined(i386)
#   define I386
#   define NETBSD
#   define mach_type_known
# endif
# if defined(bsdi) && defined(i386)
#    define I386
#    define BSDI
#    define mach_type_known
# endif
# if !defined(mach_type_known) && defined(__386BSD__)
#   define I386
#   define THREE86BSD
#   define mach_type_known
# endif
# if defined(_CX_UX) && defined(_M88K)
#   define M88K
#   define CX_UX
#   define mach_type_known
# endif
# if defined(DGUX)
#   define M88K
    /* DGUX defined */
#   define mach_type_known
# endif
# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \
     || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)
#   define I386
#   define MSWIN32	/* or Win32s */
#   define mach_type_known
# endif
# if defined(__DJGPP__)
#   define I386
#   ifndef DJGPP
#     define DJGPP  /* MSDOS running the DJGPP port of GCC */
#   endif
#   define mach_type_known
# endif
# if defined(__CYGWIN32__) || defined(__CYGWIN__)
#   define I386
#   define CYGWIN32
#   define mach_type_known
# endif
# if defined(__MINGW32__)
#   define I386
#   define MSWIN32
#   define mach_type_known
# endif
# if defined(__BORLANDC__)
#   define I386
#   define MSWIN32
#   define mach_type_known
# endif
# if defined(_UTS) && !defined(mach_type_known)
#   define S370
#   define UTS4
#   define mach_type_known
# endif
# if defined(__pj__)
#   define PJ
#   define mach_type_known
# endif
/* Ivan Demakov */
# if defined(__WATCOMC__) && defined(__386__)
#   define I386
#   if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW)
#     if defined(__OS2__)
#       define OS2
#     else
#       if defined(__WINDOWS_386__) || defined(__NT__)
#         define MSWIN32
#       else
#         define DOS4GW
#       endif
#     endif
#   endif
#   define mach_type_known
# endif

/* A machine type is characterized by the architecture.  Some		*/
/* machine types are further subdivided by OS.				*/
/* the macros ULTRIX, RISCOS, and BSD to distinguish.			*/
/* Note that SGI IRIX is treated identically to RISCOS.			*/
/* SYSV on an M68K actually means A/UX.					*/
/* The distinction in these cases is usually the stack starting address */

		    /* Mapping is: M68K       ==> Motorola 680X0	*/
		    /*		   (SUNOS4,HP,NEXT, and SYSV (A/UX),	*/
		    /*		   MACOS and AMIGA variants)		*/
		    /*             I386       ==> Intel 386	 	*/
		    /*		    (SEQUENT, OS2, SCO, LINUX, NETBSD,	*/
		    /*		     FREEBSD, THREE86BSD, MSWIN32,	*/
		    /* 		     BSDI,SUNOS5, NEXT, other variants)	*/
                    /*             NS32K      ==> Encore Multimax 	*/
                    /*             MIPS       ==> R2000 or R3000	*/
                    /*			(RISCOS, ULTRIX variants)	*/
                    /*		   VAX	      ==> DEC VAX		*/
                    /*			(BSD, ULTRIX variants)		*/
                    /*		   RS6000     ==> IBM RS/6000 AIX3.X	*/
                    /*		   RT	      ==> IBM PC/RT		*/
                    /*		   HP_PA      ==> HP9000/700 & /800	*/
                    /*				  HP/UX			*/
		    /*		   SPARC      ==> SPARC	v7/v8/v9	*/
		    /*			(SUNOS4, SUNOS5, LINUX,		*/
		    /*			 DRSNX variants)		*/
		    /* 		   ALPHA      ==> DEC Alpha 		*/
		    /*			(OSF1 and LINUX variants)	*/
		    /* 		   M88K       ==> Motorola 88XX0        */
		    /* 		        (CX_UX and DGUX)		*/
		    /* 		   S370	      ==> 370-like machine	*/
		    /* 			running Amdahl UTS4		*/
		    /* 		   ARM32      ==> Intel StrongARM	*/
		    /* 		   IA64	      ==> Intel IA64		*/
		    /*				  (e.g. Itanium)	*/

/*
 * For each architecture and OS, the following need to be defined:
 *
 * MACH_TYPE is a string representation of the machine type.
 * OS_TYPE is analogous for the OS.
 *
 * STACKBOTTOM is the cool end of the stack, which is usually the
 * highest address in the stack.
 * Under PCR or OS/2, we have other ways of finding thread stacks.
 * For each machine, the following should:
 * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and
 * 2) define exactly one of
 *	STACKBOTTOM (should be defined to be an expression)
 *	HEURISTIC1
 *	HEURISTIC2
 * If either of the last two macros are defined, then STACKBOTTOM is computed
 * during collector startup using one of the following two heuristics:
 * HEURISTIC1:  Take an address inside GC_init's frame, and round it up to
 *		the next multiple of STACK_GRAN.
 * HEURISTIC2:  Take an address inside GC_init's frame, increment it repeatedly
 *		in small steps (decrement if STACK_GROWS_UP), and read the value
 *		at each location.  Remember the value when the first
 *		Segmentation violation or Bus error is signalled.  Round that
 *		to the nearest plausible page boundary, and use that instead
 *		of STACKBOTTOM.
 *
 * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines,
 * the value of environ is a pointer that can serve as STACKBOTTOM.
 * I expect that HEURISTIC2 can be replaced by this approach, which
 * interferes far less with debugging. 
 *
 */
 
# define STACK_GRAN 0x1000000
# ifdef M68K
#   define MACH_TYPE "M68K"
#   ifdef OPENBSD
#	define OS_TYPE "OPENBSD"
#	define HEURISTIC2
#   endif
#   ifdef NETBSD
#	define OS_TYPE "NETBSD"
#	define HEURISTIC2
#   endif
#   ifdef LINUX
#       define OS_TYPE "LINUX"
#       define STACKBOTTOM ((void *)0xf0000000)
#   endif
#   ifdef SUNOS4
#	define OS_TYPE "SUNOS4"
#	define HEURISTIC1	/* differs	*/
#   endif
#   ifdef HP
#	define OS_TYPE "HP"
#       define STACKBOTTOM ((void *) 0xffeffffc)
			      /* empirically determined.  seems to work. */
#  	include <unistd.h>
#	define GETPAGESIZE() sysconf(_SC_PAGE_SIZE)
#   endif
#   ifdef SYSV
#	define OS_TYPE "SYSV"
#	define STACKBOTTOM ((void *)0xFFFFFFFE)
			/* The stack starts at the top of memory, but   */
			/* 0x0 cannot be used as setjump_test complains */
			/* that the stack direction is incorrect.  Two  */
			/* bytes down from 0x0 should be safe enough.   */
			/* 		--Parag				*/
#   	include <sys/mmu.h>
#	define GETPAGESIZE() PAGESIZE	/* Is this still right? */
#   endif
#   ifdef AMIGA
#	define OS_TYPE "AMIGA"
 	    	/* STACKBOTTOM is handled specially	*/
#	define GETPAGESIZE() 4096
#   endif
#   ifdef MACOS
#     ifndef __LOWMEM__
#     include <LowMem.h>
#     endif
#     define OS_TYPE "MACOS"
#     define STACKBOTTOM ((void *) LMGetCurStackBase())
#     define GETPAGESIZE() 4096
#   endif
#   ifdef NEXT
#	define OS_TYPE "NEXT"
#	define STACKBOTTOM ((void *) 0x4000000)
#   endif
# endif

# ifdef POWERPC
#   define MACH_TYPE "POWERPC"
#   ifdef MACOS
#     ifndef __LOWMEM__
#     include <LowMem.h>
#     endif
#     define OS_TYPE "MACOS"
#     define STACKBOTTOM ((void *) LMGetCurStackBase())
#   endif
#   ifdef LINUX
#     define OS_TYPE "LINUX"
#     define HEURISTIC1
#     undef STACK_GRAN
#     define STACK_GRAN 0x10000000
	/* Stack usually starts at 0x80000000 */
#   endif
#   ifdef MACOSX
      /* There are reasons to suspect this may not be reliable. 	*/
#     define OS_TYPE "MACOSX"
#     define STACKBOTTOM ((void *) 0xc0000000)
#   endif
# endif

# ifdef VAX
#   define MACH_TYPE "VAX"
#   ifdef BSD
#	define OS_TYPE "BSD"
#	define HEURISTIC1
			/* HEURISTIC2 may be OK, but it's hard to test. */
#   endif
#   ifdef ULTRIX
#	define OS_TYPE "ULTRIX"
#	define STACKBOTTOM ((void *) 0x7fffc800)
#   endif
# endif

# ifdef RT
#   define MACH_TYPE "RT"
#   define STACKBOTTOM ((void *) 0x1fffd800)
# endif

# ifdef SPARC
#   define MACH_TYPE "SPARC"
#   ifdef SUNOS5
#	define OS_TYPE "SUNOS5"
/*	HEURISTIC1 reportedly no longer works under 2.7.  Thus we	*/
/* 	switched to HEURISTIC2, eventhough it creates some debugging	*/
/*	issues.								*/
#	define HEURISTIC2
#	include <unistd.h>
#       define GETPAGESIZE()  sysconf(_SC_PAGESIZE)
		/* getpagesize() appeared to be missing from at least one */
		/* Solaris 5.4 installation.  Weird.			  */
#   endif
#   ifdef SUNOS4
#	define OS_TYPE "SUNOS4"
#	define HEURISTIC1
#   endif
#   ifdef DRSNX
#	define OS_TYPE "DRSNX"
#       define STACKBOTTOM ((void *) 0xdfff0000)
#   endif
#   ifdef LINUX
#     define OS_TYPE "LINUX"
#     ifdef __ELF__
#       define DYNAMIC_LOADING
#     else
          Linux Sparc/a.out not supported
#     endif
#     define SVR4
#     ifdef __arch64__
#       define STACKBOTTOM ((void *) 0x80000000000ULL)
#     else
#       define STACKBOTTOM ((void *) 0xf0000000)
#     endif
#   endif
#   ifdef OPENBSD
#     define OS_TYPE "OPENBSD"
#     define STACKBOTTOM ((void *) 0xf8000000)
#   endif
# endif

# ifdef I386
#   define MACH_TYPE "I386"
#   ifdef SEQUENT
#	define OS_TYPE "SEQUENT"
#       define STACKBOTTOM ((void *) 0x3ffff000) 
#   endif
#   ifdef SUNOS5
#	define OS_TYPE "SUNOS5"
  	extern int _start;
#	define STACKBOTTOM ((void *)(&_start))
#   endif
#   ifdef SCO
#	define OS_TYPE "SCO"
#	define STACKBOTTOM ((void *) 0x7ffffffc)
#   endif
#   ifdef SCO_ELF
#       define OS_TYPE "SCO_ELF"
#       define STACKBOTTOM ((void *) 0x08048000)
#   endif
#   ifdef LINUX
#	define OS_TYPE "LINUX"
#       define LINUX_STACKBOTTOM
#	if 0
#	  define HEURISTIC1
#         undef STACK_GRAN
#         define STACK_GRAN 0x10000000
	  /* STACKBOTTOM is usually 0xc0000000, but this changes with	*/
	  /* different kernel configurations.  In particular, systems	*/
	  /* with 2GB physical memory will usually move the user	*/
	  /* address space limit, and hence initial SP to 0x80000000.	*/
#       endif
#   endif
#   ifdef CYGWIN32
#       define OS_TYPE "CYGWIN32"
#	undef STACK_GRAN
#       define STACK_GRAN 0x10000
#       define HEURISTIC1
#   endif
#   ifdef OS2
#	define OS_TYPE "OS2"
 	    	/* STACKBOTTOM is handled specially			*/
		/* OS2 actually has the right system call!		*/
#   endif
#   ifdef MSWIN32
#	define OS_TYPE "MSWIN32"
		/* STACKBOTTOM is handled specially 			*/
#   endif
#   ifdef DJGPP
#       define OS_TYPE "DJGPP"
#       include "stubinfo.h"
        extern int _stklen;
        extern int __djgpp_stack_limit;
/* #       define STACKBOTTOM ((void *)((word) _stubinfo + _stubinfo->size \
                                                     + _stklen)) */
#       define STACKBOTTOM ((void *)((word) __djgpp_stack_limit + _stklen))
		/* This may not be right.  */
#   endif
#   ifdef OPENBSD
#	define OS_TYPE "OPENBSD"
#   endif
#   ifdef FREEBSD
#	define OS_TYPE "FREEBSD"
#   endif
#   ifdef NETBSD
#	define OS_TYPE "NETBSD"
#   endif
#   ifdef THREE86BSD
#	define OS_TYPE "THREE86BSD"
#   endif
#   ifdef BSDI
#	define OS_TYPE "BSDI"
#   endif
#   if defined(OPENBSD) || defined(FREEBSD) || defined(NETBSD) \
        || defined(THREE86BSD) || defined(BSDI)
#	define HEURISTIC2
#   endif
#   ifdef NEXT
#	define OS_TYPE "NEXT"
#	define STACKBOTTOM ((void *)0xc0000000)
#   endif
#   ifdef DOS4GW
#     define OS_TYPE "DOS4GW"
      extern char *_STACKTOP;
#     define STACKBOTTOM ((void *) _STACKTOP)
#   endif
# endif

# ifdef NS32K
#   define MACH_TYPE "NS32K"
#   define STACKBOTTOM ((void *) 0xfffff000) /* for Encore */
# endif

# ifdef MIPS
#   define MACH_TYPE "MIPS"
/* #   define STACKBOTTOM ((void *)0x7fff8000)  sometimes also works.  */
#   ifdef LINUX
      /* This was developed for a linuxce style platform.  Probably	*/
      /* needs to be tweaked for workstation class machines.		*/
#     define OS_TYPE "LINUX"
#     define STACKBOTTOM 0x80000000
	/* In many cases, this should probably use LINUX_STACKBOTTOM 	*/
	/* instead. But some kernel versions seem to give the wrong	*/
	/* value from /proc.						*/
#   endif /* Linux */
#   ifdef ULTRIX
#	define OS_TYPE "ULTRIX"
#	define HEURISTIC2
#   endif
#   ifdef RISCOS
#	define OS_TYPE "RISCOS"
#	define HEURISTIC2
#   endif
#   ifdef IRIX5
#	define OS_TYPE "IRIX5"
#	define HEURISTIC2
#   endif
# endif

# ifdef RS6000
#   define MACH_TYPE "RS6000"
    extern int errno;
#   define STACKBOTTOM ((void *)((ulong)&errno))
# endif

# ifdef HP_PA
    /* OS is assumed to be HP/UX	*/
#   define MACH_TYPE "HP_PA"
#   define OS_TYPE "HPUX"
#   if 0
	/* The following appears to work for 7xx systems running HP/UX	*/
	/* 9.xx Furthermore, it might result in much faster		*/
	/* collections than HEURISTIC2, which may involve scanning	*/
	/* segments that directly precede the stack.  It is not the	*/
	/* default, since it may not work on older machine/OS		*/
	/* combinations. (Thanks to Raymond X.T. Nijssen for uncovering	*/
	/* this.)							*/
#       define STACKBOTTOM ((void *) 0x7b033000)  /* from /etc/conf/h/param.h */
#   else
	/* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2	*/
	/* to this.  We'll probably do this on other platforms, too.	*/
	/* For now I'll use it where I can test it.			*/
	extern char ** environ;
#       define STACKBOTTOM ((void *)environ)
#   endif
#   define STACK_GROWS_UP
#   include <unistd.h>
#   define GETPAGESIZE() sysconf(_SC_PAGE_SIZE)
# endif

# ifdef ALPHA
#   define MACH_TYPE "ALPHA"
#   ifdef OSF1
#	define OS_TYPE "OSF1"
#   	define HEURISTIC2
	/* Normally HEURISTIC2 is too conervative, since		*/
	/* the text segment immediately follows the stack.		*/
	/* Hence we give an upper pound.				*/
    	extern int __start;
#   	define HEURISTIC2_LIMIT ((void *)((word)(&__start) & ~(getpagesize()-1)))
#   endif
#   ifdef LINUX
#       define OS_TYPE "LINUX"
#       define STACKBOTTOM ((void *) 0x120000000)
#   endif
# endif

# ifdef IA64
#   define MACH_TYPE "IA64"
#   ifdef HPUX
	--> needs work
#   endif
#   ifdef LINUX
#       define OS_TYPE "LINUX"
#       define STACKBOTTOM ((void *) 0xa000000000000000l)
#   endif
# endif

# ifdef M88K
#   define MACH_TYPE "M88K"
#   ifdef CX_UX
#	define OS_TYPE "CX_UX"
#   endif
#   ifdef  DGUX
#	define OS_TYPE "DGUX"
#   endif
#   define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */
# endif

# ifdef S370
#   define MACH_TYPE "S370"
#   define OS_TYPE "UTS4"
#	define HEURISTIC2
# endif

# if defined(PJ)
#   define HEURISTIC1
# endif

# ifdef ARM32
#   define MACH_TYPE "ARM32"
#   ifdef NETBSD
#       define OS_TYPE "NETBSD"
#       define HEURISTIC2
#   endif
#   ifdef LINUX
#       define OS_TYPE "LINUX"
#       define HEURISTIC1
#       undef STACK_GRAN
#       define STACK_GRAN 0x10000000
#   endif
#endif

# ifndef STACK_GROWS_UP
#   define STACK_GROWS_DOWN
# endif

# if defined(SVR4) && !defined(GETPAGESIZE)
#    include <unistd.h>
#    define GETPAGESIZE()  sysconf(_SC_PAGESIZE)
# endif

# ifndef GETPAGESIZE
#   if defined(SUNOS5) || defined(IRIX5)
#	include <unistd.h>
#   endif
#   define GETPAGESIZE() getpagesize()
# endif

# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4)
    /* OS has SVR4 generic features.  Probably others also qualify.	*/
#   define SVR4
# endif

# if defined(SUNOS5) || defined(DRSNX) || defined(HPUX)
    /* OS has SUNOS5 style signal handlers.				*/
#   define SUNOS5SIGS
# endif

#if !defined(FALSE)
#define FALSE 0
#endif
#if !defined(TRUE)
#define TRUE !FALSE
#endif
#if !defined(NULL)
#define NULL (void *)0
#endif

typedef unsigned long word;
typedef long signed_word;

# ifdef MSWIN32 

#ifdef MSWIN32
# define WIN32_LEAN_AND_MEAN
# define NOSERVICE
# include <windows.h>
#endif

# 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 shm_stack_get_writable_length(void * p, void * *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 = (void *)(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);
}

void * shm_stack_get_stack_base()
{
    int dummy;
    void * sp = (void *)(&dummy);
    void * trunc_sp = (void *)((word)sp & ~(shm_stack_page_size - 1));
    word size = shm_stack_get_writable_length(trunc_sp, 0);
   
    return(trunc_sp + size);
}

# else

# ifdef OS2

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

void * shm_stack_get_stack_base()
{
    PTIB ptib;
    PPIB ppib;
    
    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
    	shm_stack_err_printf0("DosGetInfoBlocks failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    return((void *)(ptib -> tib_pstacklimit));
}

# else

# ifdef AMIGA

# include <proto/exec.h>
# include <proto/dos.h>
# include <dos/dosextens.h>
# include <workbench/startup.h>

void * shm_stack_get_stack_base()
{
    struct Process *proc = (struct Process*)SysBase->ThisTask;
 
    /* Reference: Amiga Guru Book Pages: 42,567,574 */
    if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS
        && proc->pr_CLI != NULL) {
	/* first ULONG is StackSize */
	/*longPtr = proc->pr_ReturnAddr;
	size = longPtr[0];*/

	return (char *)proc->pr_ReturnAddr + sizeof(ULONG);
    } else {
	return (char *)proc->pr_Task.tc_SPUpper;
    }
}

#else

#ifdef HEURISTIC2

#include <signal.h>
#include <setjmp.h>

  /* Some tools to implement HEURISTIC2	*/
#   define MIN_PAGE_SIZE 256	/* Smallest conceivable page size, bytes */
    /* static */ jmp_buf scm_stack_jmp_buf;
    
    /*ARGSUSED*/
    void scm_stack_fault_handler(int sig)
    {
        longjmp(scm_stack_jmp_buf, 1);
    }

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

#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1)
	static struct sigaction old_segv_act;
#	if defined(_sigargs) || defined(HPUX) /* !Irix6.x */
	    static struct sigaction old_bus_act;
#	endif
#   else
        static handler old_segv_handler, old_bus_handler;
#   endif
    
    void scm_stack_setup_temporary_fault_handler()
    {
#	if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1)
	  struct sigaction	act;

	  act.sa_handler	= schm_stack_fault_handler;
          act.sa_flags          = SA_RESTART | SA_NODEFER;
          /* 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);
#	  ifdef 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)
		    /* 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	/* IRIX_THREADS */
#	else
    	  old_segv_handler = signal(SIGSEGV, scm_stack_fault_handler);
#	  ifdef SIGBUS
	    old_bus_handler = signal(SIGBUS, scm_stack_fault_handler);
#	  endif
#	endif
    }
    
    void scm_stack_reset_fault_handler()
    {
#       if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1)
	  (void) sigaction(SIGSEGV, &old_segv_act, 0);
#	  if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
	     || defined(HPUX)
	      (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
    }

    /* Return the first nonaddressible location > p (up) or 	*/
    /* the smallest location q s.t. [q,p] is addressible (!up).	*/
    void * scm_stack_find_limit(void *p, int up)
    {
        static volatile void *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.				*/


	scm_stack_setup_temporary_fault_handler();
	if (setjmp(scm_stack_jmp_buf) == 0) {
	    result = (void *)(((word)(p))
			      & ~(MIN_PAGE_SIZE-1));
	    for (;;) {
 	        if (up) {
		    result += MIN_PAGE_SIZE;
 	        } else {
		    result -= MIN_PAGE_SIZE;
 	        }
		scm_stack_noop1((word)(*((word *)result)));
	    }
	}
	scm_stack_reset_fault_handler();
 	if (!up) {
	    result += MIN_PAGE_SIZE;
 	}
	return((void *)result);
    }

#endif


#ifdef LINUX_STACKBOTTOM

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

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

void *scm_stack_linux_stack_base(void)
{
#   define STAT_BUF_SIZE 4096
    char stat_buf[STAT_BUF_SIZE];
    int f;
    char c;
    word result = 0;
    size_t i, buf_offset = 0;

    f = open("/proc/self/stat", O_RDONLY);
    if (f < 0 || 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++];
    }
    close(f);
    if (result < 0x10000000) abort("Absurd stack bottom value");
    return (void *)result;
}

#endif /* LINUX_STACKBOTTOM */

void * scm_get_stack_base() {
    word dummy;
    void *result;
    
#   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
#   ifdef STACKBOTTOM
	return(STACKBOTTOM);
#   else
#	ifdef HEURISTIC1
#	   ifdef STACK_GROWS_DOWN
	     result = (void *)((((word)(&dummy))
	     		       + STACKBOTTOM_ALIGNMENT_M1)
			      & ~STACKBOTTOM_ALIGNMENT_M1);
#	   else
	     result = (void *)((()(&dummy))
			      & ~STACKBOTTOM_ALIGNMENT_M1);
#	   endif
#	endif /* HEURISTIC1 */
#	ifdef LINUX_STACKBOTTOM
	   result = scm_stack_linux_stack_base();
#	endif
#	ifdef HEURISTIC2
#	    ifdef STACK_GROWS_DOWN
		result = 
		scm_stack_find_limit((void *)(&dummy), TRUE);
#           	ifdef HEURISTIC2_LIMIT
		    if (result > HEURISTIC2_LIMIT
		        && (void *)(&dummy) < HEURISTIC2_LIMIT) {
		            result = HEURISTIC2_LIMIT;
		    }
#	        endif
#	    else
		result = scm_stack_find_limit((void *)(&dummy), FALSE);
#           	ifdef HEURISTIC2_LIMIT
		    if (result < HEURISTIC2_LIMIT
		        && (void *)(&dummy) > HEURISTIC2_LIMIT) {
		            result = HEURISTIC2_LIMIT;
		    }
#	        endif
#	    endif

#	endif /* HEURISTIC2 */
#	ifdef STACK_GROWS_DOWN
	    if (result == 0) result = (void *)(signed_word)(-sizeof(void *));
#	endif
    	return(result);
#   endif /* STACKBOTTOM */
}

# endif /* ! AMIGA */
# endif /* ! OS2 */
# endif /* ! MSWIN32 */

/* Single argument version, robust against whole program analysis. */
void scm_stack_noop1(word x)
{
    static volatile word sink;

    sink = x;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]