/*
* Copyright (c) 1991-1993 by Xerox Corporation. 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 copy this garbage collector for any purpose,
* provided the above notices are retained on all copies.
*/
# include "gc←private.h"
# include <stdio.h>
# include <signal.h>
/* Blatantly OS dependent routines, except for those that are related */
/* dynamic loading. */
# ifdef SUNOS5
# undef sigmask
# endif
/* Disable and enable signals during nontrivial allocations */
# ifdef OS2
# define INCL←DOSEXCEPTIONS
# define INCL←DOSPROCESS
# define INCL←DOSERRORS
# define INCL←DOSMODULEMGR
# include <os2.h>
/* 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>
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
# ifndef PCR
# ifdef sigmask
/* 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 bool mask←initialized = FALSE;
static SIGSET←T new←mask;
static SIGSET←T old←mask;
static SIGSET←T dummy;
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;
}
SIGSETMASK(old←mask,new←mask);
}
void GC←enable←signals()
{
SIGSETMASK(dummy,old←mask);
}
# endif /* !PCR */
# endif /*!OS/2 */
/*
* 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.
*/
# 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));
}
# else
# if !defined(THREADS) && !defined(STACKBOTTOM) && defined(HEURISTIC2)
/* Some tools to implement HEURISTIC2 */
# define MIN←PAGE←SIZE 256 /* Smallest conceivable page size, bytes */
# include <setjmp.h>
/* static */ jmp←buf GC←jmp←buf;
/*ARGSUSED*/
void GC←fault←handler(sig)
int sig;
{
longjmp(GC←jmp←buf, 1);
}
# endif
ptr←t GC←get←stack←base()
{
word dummy;
static 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. */
# ifdef ←←STDC←←
typedef void (*handler)(int);
# else
typedef void (*handler)();
# endif
# ifdef HEURISTIC2
static handler old←segv←handler, old←bus←handler;
/* See above for static declaration. */
# endif
# define STACKBOTTOM←ALIGNMENT←M1 0xffffff
# 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 HEURISTIC2
old←segv←handler = signal(SIGSEGV, GC←fault←handler);
# ifdef SIGBUS
old←bus←handler = signal(SIGBUS, GC←fault←handler);
# endif
if (setjmp(GC←jmp←buf) == 0) {
result = (ptr←t)(((word)(&dummy))
& ~(MIN←PAGE←SIZE-1));
for (;;) {
# ifdef STACK←GROWS←DOWN
result += MIN←PAGE←SIZE;
# else
result -= MIN←PAGE←SIZE;
# endif
GC←noop(*result);
}
}
(void) signal(SIGSEGV, old←segv←handler);
# ifdef SIGBUS
(void) signal(SIGBUS, old←bus←handler);
# endif
# ifdef STACK←GROWS←UP
result += MIN←PAGE←SIZE;
# endif
# endif /* HEURISTIC2 */
return(result);
# endif /* STACKBOTTOM */
}
# endif /* ! OS2 */
/*
* 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));
}
}
# else
void GC←register←data←segments()
{
extern int end;
# ifndef PCR
GC←add←roots←inner(DATASTART, (char *)(&end));
# endif
/* Dynamic libraries are added at every collection, since they may */
/* change. */
}
# endif /* ! OS2 */