Synopsis - Cross-Reference

File: /src/Synopsis/gc/os_dep.c
   1/*
   2 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
   3 * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
   4 * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
   5 * Copyright (c) 1999 by Hewlett-Packard Company.  All rights reserved.
   6 *
   7 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   8 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   9 *
  10 * Permission is hereby granted to use or copy this program
  11 * for any purpose,  provided the above notices are retained on all copies.
  12 * Permission to modify the code and to distribute modified code is granted,
  13 * provided the above notices are retained, and a notice that the code was
  14 * modified is included with the above copyright notice.
  15 */
  16
  17# include "private/gc_priv.h"
  18# ifdef THREADS
  19#   include "atomic_ops.h"
  20# endif
  21
  22# if defined(LINUX) && !defined(POWERPC)
  23#   include <linux/version.h>
  24#   if (LINUX_VERSION_CODE <= 0x10400)
  25      /* Ugly hack to get struct sigcontext_struct definition.  Required      */
  26      /* for some early 1.3.X releases.  Will hopefully go away soon. */
  27      /* in some later Linux releases, asm/sigcontext.h may have to   */
  28      /* be included instead.                                         */
  29#     define __KERNEL__
  30#     include <asm/signal.h>
  31#     undef __KERNEL__
  32#   else
  33      /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */
  34      /* struct sigcontext.  libc6 (glibc2) uses "struct sigcontext" in     */
  35      /* prototypes, so we have to include the top-level sigcontext.h to    */
  36      /* make sure the former gets defined to be the latter if appropriate. */
  37#     include <features.h>
  38#     if 2 <= __GLIBC__
  39#       if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__
  40	  /* glibc 2.1 no longer has sigcontext.h.  But signal.h	*/
  41	  /* has the right declaration for glibc 2.1.			*/
  42#         include <sigcontext.h>
  43#       endif /* 0 == __GLIBC_MINOR__ */
  44#     else /* not 2 <= __GLIBC__ */
  45        /* libc5 doesn't have <sigcontext.h>: go directly with the kernel   */
  46        /* one.  Check LINUX_VERSION_CODE to see which we should reference. */
  47#       include <asm/sigcontext.h>
  48#     endif /* 2 <= __GLIBC__ */
  49#   endif
  50# endif
  51# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
  52    && !defined(MSWINCE)
  53#   include <sys/types.h>
  54#   if !defined(MSWIN32)
  55#   	include <unistd.h>
  56#   endif
  57# endif
  58
  59# include <stdio.h>
  60# if defined(MSWINCE)
  61#   define SIGSEGV 0 /* value is irrelevant */
  62# else
  63#   include <signal.h>
  64# endif
  65
  66#ifdef UNIX_LIKE
  67# include <fcntl.h>
  68#endif
  69
  70#if defined(LINUX) || defined(LINUX_STACKBOTTOM)
  71# include <ctype.h>
  72#endif
  73
  74/* Blatantly OS dependent routines, except for those that are related 	*/
  75/* to dynamic loading.							*/
  76
  77#ifdef AMIGA
  78# define GC_AMIGA_DEF
  79# include "AmigaOS.c"
  80# undef GC_AMIGA_DEF
  81#endif
  82
  83#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
  84# define WIN32_LEAN_AND_MEAN
  85# define NOSERVICE
  86# include <windows.h>
  87  /* It's not clear this is completely kosher under Cygwin.  But it	*/
  88  /* allows us to get a working GC_get_stack_base.			*/
  89#endif
  90
  91#ifdef MACOS
  92# include <Processes.h>
  93#endif
  94
  95#ifdef IRIX5
  96# include <sys/uio.h>
  97# include <malloc.h>   /* for locking */
  98#endif
  99
 100#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX5) \
 101	|| defined(USE_MMAP) || defined(USE_MUNMAP)
 102# define MMAP_SUPPORTED
 103#endif
 104
 105#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES)
 106# if defined(USE_MUNMAP) && !defined(USE_MMAP)
 107    --> USE_MUNMAP requires USE_MMAP
 108# endif
 109# include <sys/types.h>
 110# include <sys/mman.h>
 111# include <sys/stat.h>
 112# include <errno.h>
 113#endif
 114
 115#ifdef DARWIN
 116/* for get_etext and friends */
 117#include <mach-o/getsect.h>
 118#endif
 119
 120#ifdef DJGPP
 121  /* Apparently necessary for djgpp 2.01.  May cause problems with	*/
 122  /* other versions.							*/
 123  typedef long unsigned int caddr_t;
 124#endif
 125
 126#ifdef PCR
 127# include "il/PCR_IL.h"
 128# include "th/PCR_ThCtl.h"
 129# include "mm/PCR_MM.h"
 130#endif
 131
 132#if !defined(NO_EXECUTE_PERMISSION)
 133# define OPT_PROT_EXEC PROT_EXEC
 134#else
 135# define OPT_PROT_EXEC 0
 136#endif
 137
 138#if defined(LINUX) && \
 139    (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))
 140# define NEED_PROC_MAPS
 141#endif
 142
 143#ifdef NEED_PROC_MAPS
 144/* We need to parse /proc/self/maps, either to find dynamic libraries,	*/
 145/* and/or to find the register backing store base (IA64).  Do it once	*/
 146/* here.								*/
 147
 148#define READ read
 149
 150/* Repeatedly perform a read call until the buffer is filled or	*/
 151/* we encounter EOF.						*/
 152ssize_t GC_repeat_read(int fd, char *buf, size_t count)
 153{
 154    ssize_t num_read = 0;
 155    ssize_t result;
 156    
 157    while (num_read < count) {
 158	result = READ(fd, buf + num_read, count - num_read);
 159	if (result < 0) return result;
 160	if (result == 0) break;
 161	num_read += result;
 162    }
 163    return num_read;
 164}
 165
 166/* Determine the length of a file by incrementally reading it into a 	*/
 167/* This would be sily to use on a file supporting lseek, but Linux	*/
 168/* /proc files usually do not.						*/
 169size_t GC_get_file_len(int f)
 170{
 171    size_t total = 0;
 172    ssize_t result;
 173#   define GET_FILE_LEN_BUF_SZ 500
 174    char buf[GET_FILE_LEN_BUF_SZ];
 175
 176    do {
 177	result = read(f, buf, GET_FILE_LEN_BUF_SZ);
 178	if (result == -1) return 0;
 179	total += result;
 180    } while (result > 0);
 181    return total;
 182}
 183
 184size_t GC_get_maps_len(void)
 185{
 186    int f = open("/proc/self/maps", O_RDONLY);
 187    size_t result = GC_get_file_len(f);
 188    close(f);
 189    return result;
 190}
 191
 192/*
 193 * Copy the contents of /proc/self/maps to a buffer in our address space.
 194 * Return the address of the buffer, or zero on failure.
 195 * This code could be simplified if we could determine its size
 196 * ahead of time.
 197 */
 198char * GC_get_maps(void)
 199{
 200    int f;
 201    int result;
 202    static char init_buf[1];
 203    static char *maps_buf = init_buf;
 204    static size_t maps_buf_sz = 1;
 205    size_t maps_size, old_maps_size = 0;
 206
 207    /* The buffer is essentially static, so there must be a single client. */
 208    GC_ASSERT(I_HOLD_LOCK());
 209
 210    /* Note that in the presence of threads, the maps file can	*/
 211    /* essentially shrink asynchronously and unexpectedly as 	*/
 212    /* threads that we already think of as dead release their	*/
 213    /* stacks.  And there is no easy way to read the entire	*/
 214    /* file atomically.  This is arguably a misfeature of the	*/
 215    /* /proc/.../maps interface.				*/
 216
 217    /* Since we dont believe the file can grow			*/
 218    /* asynchronously, it should suffice to first determine	*/
 219    /* the size (using lseek or read), and then to reread the	*/
 220    /* file.  If the size is inconsistent we have to retry.	*/
 221    /* This only matters with threads enabled, and if we use 	*/
 222    /* this to locate roots (not the default).			*/
 223
 224    /* Determine the initial size of /proc/self/maps.		*/
 225    /* Note that lseek doesn't work, at least as of 2.6.15.	*/
 226#   ifdef THREADS
 227	maps_size = GC_get_maps_len();
 228	if (0 == maps_size) return 0;
 229#   else
 230	maps_size = 4000;	/* Guess */
 231#   endif
 232
 233    /* Read /proc/self/maps, growing maps_buf as necessary.	*/
 234    /* Note that we may not allocate conventionally, and	*/
 235    /* thus can't use stdio.					*/
 236	do {
 237	    while (maps_size >= maps_buf_sz) {
 238	      /* Grow only by powers of 2, since we leak "too small" buffers. */
 239	      while (maps_size >= maps_buf_sz) maps_buf_sz *= 2;
 240	      maps_buf = GC_scratch_alloc(maps_buf_sz);
 241#	      ifdef THREADS
 242	        /* Recompute initial length, since we allocated.	*/
 243	        /* This can only happen a few times per program		*/
 244	        /* execution.						*/
 245	        maps_size = GC_get_maps_len();
 246	        if (0 == maps_size) return 0;
 247#	      endif
 248	      if (maps_buf == 0) return 0;
 249	    }
 250	    GC_ASSERT(maps_buf_sz >= maps_size + 1);
 251	    f = open("/proc/self/maps", O_RDONLY);
 252	    if (-1 == f) return 0;
 253#	    ifdef THREADS
 254	      old_maps_size = maps_size;
 255#	    endif
 256	    maps_size = 0;
 257	    do {
 258	        result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
 259	        if (result <= 0) return 0;
 260	        maps_size += result;
 261	    } while (result == maps_buf_sz-1);
 262	    close(f);
 263#	    ifdef THREADS
 264	      if (maps_size > old_maps_size) {
 265		GC_err_printf("Old maps size = %d, new maps size = %d\n",
 266			      old_maps_size, maps_size);
 267		ABORT("Unexpected asynchronous /proc/self/maps growth: "
 268		      "Unregistered thread?");
 269	      }
 270#	    endif
 271	} while (maps_size >= maps_buf_sz || maps_size < old_maps_size);
 272		/* In the single-threaded case, the second clause is false.	*/
 273        maps_buf[maps_size] = '\0';
 274	
 275    /* Apply fn to result. */
 276	return maps_buf;
 277}
 278
 279//
 280//  GC_parse_map_entry parses an entry from /proc/self/maps so we can
 281//  locate all writable data segments that belong to shared libraries.
 282//  The format of one of these entries and the fields we care about
 283//  is as follows:
 284//  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
 285//  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
 286//  start    end      prot          maj_dev
 287//
 288//  Note that since about august 2003 kernels, the columns no longer have
 289//  fixed offsets on 64-bit kernels.  Hence we no longer rely on fixed offsets
 290//  anywhere, which is safer anyway.
 291//
 292
 293/*
 294 * Assign various fields of the first line in buf_ptr to *start, *end,
 295 * *prot, *maj_dev and *mapping_name.  Mapping_name may be NULL.
 296 * *prot and *mapping_name are assigned pointers into the original
 297 * buffer.
 298 */
 299char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end,
 300                                char **prot, unsigned int *maj_dev,
 301				char **mapping_name)
 302{
 303    char *start_start, *end_start, *maj_dev_start;
 304    char *p;
 305    char *endp;
 306
 307    if (buf_ptr == NULL || *buf_ptr == '\0') {
 308        return NULL;
 309    }
 310
 311    p = buf_ptr;
 312    while (isspace(*p)) ++p;
 313    start_start = p;
 314    GC_ASSERT(isxdigit(*start_start));
 315    *start = (ptr_t)strtoul(start_start, &endp, 16); p = endp;
 316    GC_ASSERT(*p=='-');
 317
 318    ++p;
 319    end_start = p;
 320    GC_ASSERT(isxdigit(*end_start));
 321    *end = (ptr_t)strtoul(end_start, &endp, 16); p = endp;
 322    GC_ASSERT(isspace(*p));
 323
 324    while (isspace(*p)) ++p;
 325    GC_ASSERT(*p == 'r' || *p == '-');
 326    *prot = p;
 327    /* Skip past protection field to offset field */
 328       while (!isspace(*p)) ++p; while (isspace(*p)) ++p;
 329    GC_ASSERT(isxdigit(*p));
 330    /* Skip past offset field, which we ignore */
 331          while (!isspace(*p)) ++p; while (isspace(*p)) ++p;
 332    maj_dev_start = p;
 333    GC_ASSERT(isxdigit(*maj_dev_start));
 334    *maj_dev = strtoul(maj_dev_start, NULL, 16);
 335
 336    if (mapping_name == 0) {
 337      while (*p && *p++ != '\n');
 338    } else {
 339      while (*p && *p != '\n' && *p != '/' && *p != '[') p++;
 340      *mapping_name = p;
 341      while (*p && *p++ != '\n');
 342    }
 343
 344    return p;
 345}
 346
 347/* Try to read the backing store base from /proc/self/maps.		*/
 348/* Return the bounds of the writable mapping with a 0 major device,	*/
 349/* which includes the address passed as data.				*/
 350/* Return FALSE if there is no such mapping.				*/
 351GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp)
 352{
 353  char *prot;
 354  ptr_t my_start, my_end;
 355  unsigned int maj_dev;
 356  char *maps = GC_get_maps();
 357  char *buf_ptr = maps;
 358  
 359  if (0 == maps) return(FALSE);
 360  for (;;) {
 361    buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end,
 362		    	         &prot, &maj_dev, 0);
 363
 364    if (buf_ptr == NULL) return FALSE;
 365    if (prot[1] == 'w' && maj_dev == 0) {
 366        if (my_end > addr && my_start <= addr) {
 367    	  *startp = my_start;
 368	  *endp = my_end;
 369	  return TRUE;
 370	}
 371    }
 372  }
 373  return FALSE;
 374}
 375
 376/* Find the text(code) mapping for the library whose name starts with nm. */
 377GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp)
 378{
 379  size_t nm_len = strlen(nm);
 380  char *prot;
 381  char *map_path;
 382  ptr_t my_start, my_end;
 383  unsigned int maj_dev;
 384  char *maps = GC_get_maps();
 385  char *buf_ptr = maps;
 386  
 387  if (0 == maps) return(FALSE);
 388  for (;;) {
 389    buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end,
 390		    	         &prot, &maj_dev, &map_path);
 391
 392    if (buf_ptr == NULL) return FALSE;
 393    if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x' &&
 394	strncmp(nm, map_path, nm_len) == 0) {
 395    	  *startp = my_start;
 396	  *endp = my_end;
 397	  return TRUE;
 398    }
 399  }
 400  return FALSE;
 401}
 402
 403#ifdef IA64
 404static ptr_t backing_store_base_from_proc(void)
 405{
 406    ptr_t my_start, my_end;
 407    if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) {
 408	if (GC_print_stats) {
 409	    GC_log_printf("Failed to find backing store base from /proc\n");
 410	}
 411	return 0;
 412    }
 413    return my_start;
 414}
 415#endif
 416
 417#endif /* NEED_PROC_MAPS */	
 418
 419#if defined(SEARCH_FOR_DATA_START)
 420  /* The I386 case can be handled without a search.  The Alpha case	*/
 421  /* used to be handled differently as well, but the rules changed	*/
 422  /* for recent Linux versions.  This seems to be the easiest way to	*/
 423  /* cover all versions.						*/
 424
 425# if defined(LINUX) || defined(HURD)
 426    /* Some Linux distributions arrange to define __data_start.  Some	*/
 427    /* define data_start as a weak symbol.  The latter is technically	*/
 428    /* broken, since the user program may define data_start, in which	*/
 429    /* case we lose.  Nonetheless, we try both, prefering __data_start.	*/
 430    /* We assume gcc-compatible pragmas.	*/
 431#   pragma weak __data_start
 432    extern int __data_start[];
 433#   pragma weak data_start
 434    extern int data_start[];
 435# endif /* LINUX */
 436  extern int _end[];
 437
 438  ptr_t GC_data_start;
 439
 440  void GC_init_linux_data_start()
 441  {
 442    extern ptr_t GC_find_limit(ptr_t, GC_bool);
 443
 444#   if defined(LINUX) || defined(HURD)
 445      /* Try the easy approaches first:	*/
 446      if ((ptr_t)__data_start != 0) {
 447	  GC_data_start = (ptr_t)(__data_start);
 448	  return;
 449      }
 450      if ((ptr_t)data_start != 0) {
 451	  GC_data_start = (ptr_t)(data_start);
 452	  return;
 453      }
 454#   endif /* LINUX */
 455    GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
 456  }
 457#endif
 458
 459# ifdef ECOS
 460
 461# ifndef ECOS_GC_MEMORY_SIZE
 462# define ECOS_GC_MEMORY_SIZE (448 * 1024)
 463# endif /* ECOS_GC_MEMORY_SIZE */
 464
 465// FIXME: This is a simple way of allocating memory which is
 466// compatible with ECOS early releases.  Later releases use a more
 467// sophisticated means of allocating memory than this simple static
 468// allocator, but this method is at least bound to work.
 469static char memory[ECOS_GC_MEMORY_SIZE];
 470static char *brk = memory;
 471
 472static void *tiny_sbrk(ptrdiff_t increment)
 473{
 474  void *p = brk;
 475
 476  brk += increment;
 477
 478  if (brk >  memory + sizeof memory)
 479    {
 480      brk -= increment;
 481      return NULL;
 482    }
 483
 484  return p;
 485}
 486#define sbrk tiny_sbrk
 487# endif /* ECOS */
 488
 489#if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
 490  ptr_t GC_data_start;
 491
 492  void GC_init_netbsd_elf(void)
 493  {
 494    extern ptr_t GC_find_limit(ptr_t, GC_bool);
 495    extern char **environ;
 496	/* This may need to be environ, without the underscore, for	*/
 497	/* some versions.						*/
 498    GC_data_start = GC_find_limit((ptr_t)&environ, FALSE);
 499  }
 500#endif
 501
 502# ifdef OS2
 503
 504# include <stddef.h>
 505
 506# if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */
 507
 508struct exe_hdr {
 509    unsigned short      magic_number;
 510    unsigned short      padding[29];
 511    long                new_exe_offset;
 512};
 513
 514#define E_MAGIC(x)      (x).magic_number
 515#define EMAGIC          0x5A4D  
 516#define E_LFANEW(x)     (x).new_exe_offset
 517
 518struct e32_exe {
 519    unsigned char       magic_number[2]; 
 520    unsigned char       byte_order; 
 521    unsigned char       word_order; 
 522    unsigned long       exe_format_level;
 523    unsigned short      cpu;       
 524    unsigned short      os;
 525    unsigned long       padding1[13];
 526    unsigned long       object_table_offset;
 527    unsigned long       object_count;    
 528    unsigned long       padding2[31];
 529};
 530
 531#define E32_MAGIC1(x)   (x).magic_number[0]
 532#define E32MAGIC1       'L'
 533#define E32_MAGIC2(x)   (x).magic_number[1]
 534#define E32MAGIC2       'X'
 535#define E32_BORDER(x)   (x).byte_order
 536#define E32LEBO         0
 537#define E32_WORDER(x)   (x).word_order
 538#define E32LEWO         0
 539#define E32_CPU(x)      (x).cpu
 540#define E32CPU286       1
 541#define E32_OBJTAB(x)   (x).object_table_offset
 542#define E32_OBJCNT(x)   (x).object_count
 543
 544struct o32_obj {
 545    unsigned long       size;  
 546    unsigned long       base;
 547    unsigned long       flags;  
 548    unsigned long       pagemap;
 549    unsigned long       mapsize; 
 550    unsigned long       reserved;
 551};
 552
 553#define O32_FLAGS(x)    (x).flags
 554#define OBJREAD         0x0001L
 555#define OBJWRITE        0x0002L
 556#define OBJINVALID      0x0080L
 557#define O32_SIZE(x)     (x).size
 558#define O32_BASE(x)     (x).base
 559
 560# else  /* IBM's compiler */
 561
 562/* A kludge to get around what appears to be a header file bug */
 563# ifndef WORD
 564#   define WORD unsigned short
 565# endif
 566# ifndef DWORD
 567#   define DWORD unsigned long
 568# endif
 569
 570# define EXE386 1
 571# include <newexe.h>
 572# include <exe386.h>
 573
 574# endif  /* __IBMC__ */
 575
 576# define INCL_DOSEXCEPTIONS
 577# define INCL_DOSPROCESS
 578# define INCL_DOSERRORS
 579# define INCL_DOSMODULEMGR
 580# define INCL_DOSMEMMGR
 581# include <os2.h>
 582
 583
 584/* Disable and enable signals during nontrivial allocations	*/
 585
 586void GC_disable_signals(void)
 587{
 588    ULONG nest;
 589    
 590    DosEnterMustComplete(&nest);
 591    if (nest != 1) ABORT("nested GC_disable_signals");
 592}
 593
 594void GC_enable_signals(void)
 595{
 596    ULONG nest;
 597    
 598    DosExitMustComplete(&nest);
 599    if (nest != 0) ABORT("GC_enable_signals");
 600}
 601
 602
 603# else
 604
 605#  if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
 606      && !defined(MSWINCE) \
 607      && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
 608      && !defined(NOSYS) && !defined(ECOS)
 609
 610#   if 0
 611	/* Use the traditional BSD interface */
 612#	define SIGSET_T int
 613#	define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
 614#	define SIG_FILL(set)  (set) = 0x7fffffff
 615    	  /* Setting the leading bit appears to provoke a bug in some	*/
 616    	  /* longjmp implementations.  Most systems appear not to have	*/
 617    	  /* a signal 32.						*/
 618#	define SIGSETMASK(old, new) (old) = sigsetmask(new)
 619#   endif
 620
 621    /* Use POSIX/SYSV interface	*/
 622#   define SIGSET_T sigset_t
 623#   define SIG_DEL(set, signal) sigdelset(&(set), (signal))
 624#   define SIG_FILL(set) sigfillset(&set)
 625#   define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old))
 626
 627
 628static GC_bool mask_initialized = FALSE;
 629
 630static SIGSET_T new_mask;
 631
 632static SIGSET_T old_mask;
 633
 634static SIGSET_T dummy;
 635
 636#if defined(GC_ASSERTIONS) && !defined(THREADS)
 637# define CHECK_SIGNALS
 638  int GC_sig_disabled = 0;
 639#endif
 640
 641void GC_disable_signals(void)
 642{
 643    if (!mask_initialized) {
 644    	SIG_FILL(new_mask);
 645
 646	SIG_DEL(new_mask, SIGSEGV);
 647	SIG_DEL(new_mask, SIGILL);
 648	SIG_DEL(new_mask, SIGQUIT);
 649#	ifdef SIGBUS
 650	    SIG_DEL(new_mask, SIGBUS);
 651#	endif
 652#	ifdef SIGIOT
 653	    SIG_DEL(new_mask, SIGIOT);
 654#	endif
 655#	ifdef SIGEMT
 656	    SIG_DEL(new_mask, SIGEMT);
 657#	endif
 658#	ifdef SIGTRAP
 659	    SIG_DEL(new_mask, SIGTRAP);
 660#	endif 
 661	mask_initialized = TRUE;
 662    }
 663#   ifdef CHECK_SIGNALS
 664	if (GC_sig_disabled != 0) ABORT("Nested disables");
 665	GC_sig_disabled++;
 666#   endif
 667    SIGSETMASK(old_mask,new_mask);
 668}
 669
 670void GC_enable_signals(void)
 671{
 672#   ifdef CHECK_SIGNALS
 673	if (GC_sig_disabled != 1) ABORT("Unmatched enable");
 674	GC_sig_disabled--;
 675#   endif
 676    SIGSETMASK(dummy,old_mask);
 677}
 678
 679#  endif  /* !PCR */
 680
 681# endif /*!OS/2 */
 682
 683/* Ivan Demakov: simplest way (to me) */
 684#if defined (DOS4GW)
 685  void GC_disable_signals() { }
 686  void GC_enable_signals() { }
 687#endif
 688
 689/* Find the page size */
 690word GC_page_size;
 691
 692# if defined(MSWIN32) || defined(MSWINCE)
 693  void GC_setpagesize(void)
 694  {
 695    GetSystemInfo(&GC_sysinfo);
 696    GC_page_size = GC_sysinfo.dwPageSize;
 697  }
 698
 699# else
 700#   if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP)
 701	void GC_setpagesize(void)
 702	{
 703	    GC_page_size = GETPAGESIZE();
 704	}
 705#   else
 706	/* It's acceptable to fake it. */
 707	void GC_setpagesize(void)
 708	{
 709	    GC_page_size = HBLKSIZE;
 710	}
 711#   endif
 712# endif
 713
 714/* 
 715 * Find the base of the stack. 
 716 * Used only in single-threaded environment.
 717 * With threads, GC_mark_roots needs to know how to do this.
 718 * Called with allocator lock held.
 719 */
 720# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
 721# define is_writable(prot) ((prot) == PAGE_READWRITE \
 722			    || (prot) == PAGE_WRITECOPY \
 723			    || (prot) == PAGE_EXECUTE_READWRITE \
 724			    || (prot) == PAGE_EXECUTE_WRITECOPY)
 725/* Return the number of bytes that are writable starting at p.	*/
 726/* The pointer p is assumed to be page aligned.			*/
 727/* If base is not 0, *base becomes the beginning of the 	*/
 728/* allocation region containing p.				*/
 729word GC_get_writable_length(ptr_t p, ptr_t *base)
 730{
 731    MEMORY_BASIC_INFORMATION buf;
 732    word result;
 733    word protect;
 734    
 735    result = VirtualQuery(p, &buf, sizeof(buf));
 736    if (result != sizeof(buf)) ABORT("Weird VirtualQuery result");
 737    if (base != 0) *base = (ptr_t)(buf.AllocationBase);
 738    protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE));
 739    if (!is_writable(protect)) {
 740        return(0);
 741    }
 742    if (buf.State != MEM_COMMIT) return(0);
 743    return(buf.RegionSize);
 744}
 745
 746int GC_get_stack_base(struct GC_stack_base *sb)
 747{
 748    int dummy;
 749    ptr_t sp = (ptr_t)(&dummy);
 750    ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
 751    word size = GC_get_writable_length(trunc_sp, 0);
 752   
 753    sb -> mem_base = trunc_sp + size;
 754    return GC_SUCCESS;
 755}
 756
 757#define HAVE_GET_STACK_BASE
 758
 759/* This is always called from the main thread.	*/
 760ptr_t GC_get_main_stack_base(void)
 761{
 762    struct GC_stack_base sb;
 763
 764    GC_get_stack_base(&sb);
 765    return (ptr_t)sb.mem_base;
 766}
 767
 768# endif /* MS Windows */
 769
 770# ifdef BEOS
 771# include <kernel/OS.h>
 772ptr_t GC_get_main_stack_base(void){
 773	thread_info th;
 774	get_thread_info(find_thread(NULL),&th);
 775	return th.stack_end;
 776}
 777# endif /* BEOS */
 778
 779
 780# ifdef OS2
 781
 782ptr_t GC_get_main_stack_base(void)
 783{
 784    PTIB ptib;
 785    PPIB ppib;
 786    
 787    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
 788    	GC_err_printf("DosGetInfoBlocks failed\n");
 789    	ABORT("DosGetInfoBlocks failed\n");
 790    }
 791    return((ptr_t)(ptib -> tib_pstacklimit));
 792}
 793
 794# endif /* OS2 */
 795
 796# ifdef AMIGA
 797#   define GC_AMIGA_SB
 798#   include "AmigaOS.c"
 799#   undef GC_AMIGA_SB
 800# endif /* AMIGA */
 801
 802# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
 803
 804    typedef void (*handler)(int);
 805
 806#   if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
 807    || defined(HURD) || defined(NETBSD)
 808	static struct sigaction old_segv_act;
 809#	if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \
 810	|| defined(HURD) || defined(NETBSD)
 811	    static struct sigaction old_bus_act;
 812#	endif
 813#   else
 814        static handler old_segv_handler, old_bus_handler;
 815#   endif
 816    
 817    void GC_set_and_save_fault_handler(handler h)
 818    {
 819#	if defined(SUNOS5SIGS) || defined(IRIX5)  \
 820        || defined(OSF1) || defined(HURD) || defined(NETBSD)
 821	  struct sigaction	act;
 822
 823	  act.sa_handler	= h;
 824#	  if 0 /* Was necessary for Solaris 2.3 and very temporary 	*/
 825	       /* NetBSD bugs.						*/
 826            act.sa_flags          = SA_RESTART | SA_NODEFER;
 827#         else
 828            act.sa_flags          = SA_RESTART;
 829#	  endif
 830
 831	  (void) sigemptyset(&act.sa_mask);
 832#	  ifdef GC_IRIX_THREADS
 833		/* Older versions have a bug related to retrieving and	*/
 834		/* and setting a handler at the same time.		*/
 835	        (void) sigaction(SIGSEGV, 0, &old_segv_act);
 836	        (void) sigaction(SIGSEGV, &act, 0);
 837#	  else
 838	        (void) sigaction(SIGSEGV, &act, &old_segv_act);
 839#		if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
 840		   || defined(HPUX) || defined(HURD) || defined(NETBSD)
 841		    /* Under Irix 5.x or HP/UX, we may get SIGBUS.	*/
 842		    /* Pthreads doesn't exist under Irix 5.x, so we	*/
 843		    /* don't have to worry in the threads case.		*/
 844		    (void) sigaction(SIGBUS, &act, &old_bus_act);
 845#		endif
 846#	  endif	/* GC_IRIX_THREADS */
 847#	else
 848    	  old_segv_handler = signal(SIGSEGV, h);
 849#	  ifdef SIGBUS
 850	    old_bus_handler = signal(SIGBUS, h);
 851#	  endif
 852#	endif
 853    }
 854# endif /* NEED_FIND_LIMIT || UNIX_LIKE */
 855
 856# if defined(NEED_FIND_LIMIT) || \
 857     defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)
 858  /* Some tools to implement HEURISTIC2	*/
 859#   define MIN_PAGE_SIZE 256	/* Smallest conceivable page size, bytes */
 860    
 861    /*ARGSUSED*/
 862    void GC_fault_handler(int sig)
 863    {
 864        LONGJMP(GC_jmp_buf, 1);
 865    }
 866
 867    void GC_setup_temporary_fault_handler(void)
 868    {
 869	/* Handler is process-wide, so this should only happen in */
 870	/* one thread at a time.				  */
 871	GC_ASSERT(I_HOLD_LOCK());
 872	GC_set_and_save_fault_handler(GC_fault_handler);
 873    }
 874    
 875    void GC_reset_fault_handler(void)
 876    {
 877#       if defined(SUNOS5SIGS) || defined(IRIX5) \
 878	   || defined(OSF1) || defined(HURD) || defined(NETBSD)
 879	  (void) sigaction(SIGSEGV, &old_segv_act, 0);
 880#	  if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
 881	     || defined(HPUX) || defined(HURD) || defined(NETBSD)
 882	      (void) sigaction(SIGBUS, &old_bus_act, 0);
 883#	  endif
 884#       else
 885  	  (void) signal(SIGSEGV, old_segv_handler);
 886#	  ifdef SIGBUS
 887	    (void) signal(SIGBUS, old_bus_handler);
 888#	  endif
 889#       endif
 890    }
 891
 892    /* Return the first nonaddressible location > p (up) or 	*/
 893    /* the smallest location q s.t. [q,p) is addressable (!up).	*/
 894    /* We assume that p (up) or p-1 (!up) is addressable.	*/
 895    /* Requires allocation lock.				*/
 896    ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound)
 897    {
 898        static volatile ptr_t result;
 899    		/* Safer if static, since otherwise it may not be	*/
 900    		/* preserved across the longjmp.  Can safely be 	*/
 901    		/* static since it's only called with the		*/
 902    		/* allocation lock held.				*/
 903
 904	GC_ASSERT(I_HOLD_LOCK());
 905	GC_setup_temporary_fault_handler();
 906	if (SETJMP(GC_jmp_buf) == 0) {
 907	    result = (ptr_t)(((word)(p))
 908			      & ~(MIN_PAGE_SIZE-1));
 909	    for (;;) {
 910 	        if (up) {
 911		    result += MIN_PAGE_SIZE;
 912		    if (result >= bound) return bound;
 913 	        } else {
 914		    result -= MIN_PAGE_SIZE;
 915		    if (result <= bound) return bound;
 916 	        }
 917		GC_noop1((word)(*result));
 918	    }
 919	}
 920	GC_reset_fault_handler();
 921 	if (!up) {
 922	    result += MIN_PAGE_SIZE;
 923 	}
 924	return(result);
 925    }
 926
 927    ptr_t GC_find_limit(ptr_t p, GC_bool up)
 928    {
 929      if (up) {
 930	return GC_find_limit_with_bound(p, up, (ptr_t)(word)(-1));
 931      } else {
 932	return GC_find_limit_with_bound(p, up, 0);
 933      }
 934    }
 935# endif
 936
 937#if defined(ECOS) || defined(NOSYS)
 938  ptr_t GC_get_main_stack_base(void)
 939  {
 940    return STACKBOTTOM;
 941  }
 942#endif
 943
 944#ifdef HPUX_STACKBOTTOM
 945
 946#include <sys/param.h>
 947#include <sys/pstat.h>
 948
 949  ptr_t GC_get_register_stack_base(void)
 950  {
 951    struct pst_vm_status vm_status;
 952
 953    int i = 0;
 954    while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) {
 955      if (vm_status.pst_type == PS_RSESTACK) {
 956        return (ptr_t) vm_status.pst_vaddr;
 957      }
 958    }
 959
 960    /* old way to get the register stackbottom */
 961    return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1)
 962                   & ~(BACKING_STORE_ALIGNMENT - 1));
 963  }
 964
 965#endif /* HPUX_STACK_BOTTOM */
 966
 967#ifdef LINUX_STACKBOTTOM
 968
 969#include <sys/types.h>
 970#include <sys/stat.h>
 971
 972# define STAT_SKIP 27   /* Number of fields preceding startstack	*/
 973			/* field in /proc/self/stat			*/
 974
 975#ifdef USE_LIBC_PRIVATES
 976# pragma weak __libc_stack_end
 977  extern ptr_t __libc_stack_end;
 978#endif
 979
 980# ifdef IA64
 981#   ifdef USE_LIBC_PRIVATES
 982#     pragma weak __libc_ia64_register_backing_store_base
 983      extern ptr_t __libc_ia64_register_backing_store_base;
 984#   endif
 985
 986    ptr_t GC_get_register_stack_base(void)
 987    {
 988      ptr_t result;
 989
 990#     ifdef USE_LIBC_PRIVATES
 991        if (0 != &__libc_ia64_register_backing_store_base
 992	    && 0 != __libc_ia64_register_backing_store_base) {
 993	  /* Glibc 2.2.4 has a bug such that for dynamically linked	*/
 994	  /* executables __libc_ia64_register_backing_store_base is 	*/
 995	  /* defined but uninitialized during constructor calls.  	*/
 996	  /* Hence we check for both nonzero address and value.		*/
 997	  return __libc_ia64_register_backing_store_base;
 998        }
 999#     endif
1000      result = backing_store_base_from_proc();
1001      if (0 == result) {
1002	  result = GC_find_limit(GC_save_regs_in_stack(), FALSE);
1003	  /* Now seems to work better than constant displacement	*/
1004	  /* heuristic used in 6.X versions.  The latter seems to	*/
1005	  /* fail for 2.6 kernels.					*/
1006      }
1007      return result;
1008    }
1009# endif
1010
1011  ptr_t GC_linux_stack_base(void)
1012  {
1013    /* We read the stack base value from /proc/self/stat.  We do this	*/
1014    /* using direct I/O system calls in order to avoid calling malloc   */
1015    /* in case REDIRECT_MALLOC is defined.				*/ 
1016#   define STAT_BUF_SIZE 4096
1017#   define STAT_READ read
1018	  /* Should probably call the real read, if read is wrapped.	*/
1019    char stat_buf[STAT_BUF_SIZE];
1020    int f;
1021    char c;
1022    word result = 0;
1023    size_t i, buf_offset = 0;
1024
1025    /* First try the easy way.  This should work for glibc 2.2	*/
1026    /* This fails in a prelinked ("prelink" command) executable */
1027    /* since the correct value of __libc_stack_end never	*/
1028    /* becomes visible to us.  The second test works around 	*/
1029    /* this.							*/  
1030#   ifdef USE_LIBC_PRIVATES
1031      if (0 != &__libc_stack_end && 0 != __libc_stack_end ) {
1032#       if defined(IA64)
1033	  /* Some versions of glibc set the address 16 bytes too	*/
1034	  /* low while the initialization code is running.		*/
1035	  if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) {
1036	    return __libc_stack_end + 0x10;
1037	  } /* Otherwise it's not safe to add 16 bytes and we fall	*/
1038	    /* back to using /proc.					*/
1039#	elif defined(SPARC)
1040	  /* Older versions of glibc for 64-bit Sparc do not set
1041	   * this variable correctly, it gets set to either zero
1042	   * or one.
1043	   */
1044	  if (__libc_stack_end != (ptr_t) (unsigned long)0x1)
1045	    return __libc_stack_end;
1046#	else
1047	  return __libc_stack_end;
1048#	endif
1049      }
1050#   endif
1051    f = open("/proc/self/stat", O_RDONLY);
1052    if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
1053	ABORT("Couldn't read /proc/self/stat");
1054    }
1055    c = stat_buf[buf_offset++];
1056    /* Skip the required number of fields.  This number is hopefully	*/
1057    /* constant across all Linux implementations.			*/
1058      for (i = 0; i < STAT_SKIP; ++i) {
1059	while (isspace(c)) c = stat_buf[buf_offset++];
1060	while (!isspace(c)) c = stat_buf[buf_offset++];
1061      }
1062    while (isspace(c)) c = stat_buf[buf_offset++];
1063    while (isdigit(c)) {
1064      result *= 10;
1065      result += c - '0';
1066      c = stat_buf[buf_offset++];
1067    }
1068    close(f);
1069    if (result < 0x10000000) ABORT("Absurd stack bottom value");
1070    return (ptr_t)result;
1071  }
1072
1073#endif /* LINUX_STACKBOTTOM */
1074
1075#ifdef FREEBSD_STACKBOTTOM
1076
1077/* This uses an undocumented sysctl call, but at least one expert 	*/
1078/* believes it will stay.						*/
1079
1080#include <unistd.h>
1081#include <sys/types.h>
1082#include <sys/sysctl.h>
1083
1084  ptr_t GC_freebsd_stack_base(void)
1085  {
1086    int nm[2] = {CTL_KERN, KERN_USRSTACK};
1087    ptr_t base;
1088    size_t len = sizeof(ptr_t);
1089    int r = sysctl(nm, 2, &base, &len, NULL, 0);
1090    
1091    if (r) ABORT("Error getting stack base");
1092
1093    return base;
1094  }
1095
1096#endif /* FREEBSD_STACKBOTTOM */
1097
1098#if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
1099    && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS) \
1100    && !defined(CYGWIN32)
1101
1102ptr_t GC_get_main_stack_base(void)
1103{
1104#   if defined(HEURISTIC1) || defined(HEURISTIC2)
1105      word dummy;
1106#   endif
1107    ptr_t result;
1108
1109#   define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
1110
1111#   ifdef STACKBOTTOM
1112	return(STACKBOTTOM);
1113#   else
1114#	ifdef HEURISTIC1
1115#	   ifdef STACK_GROWS_DOWN
1116	     result = (ptr_t)((((word)(&dummy))
1117	     		       + STACKBOTTOM_ALIGNMENT_M1)
1118			      & ~STACKBOTTOM_ALIGNMENT_M1);
1119#	   else
1120	     result = (ptr_t)(((word)(&dummy))
1121			      & ~STACKBOTTOM_ALIGNMENT_M1);
1122#	   endif
1123#	endif /* HEURISTIC1 */
1124#	ifdef LINUX_STACKBOTTOM
1125	   result = GC_linux_stack_base();
1126#	endif
1127#	ifdef FREEBSD_STACKBOTTOM
1128	   result = GC_freebsd_stack_base();
1129#	endif
1130#	ifdef HEURISTIC2
1131#	    ifdef STACK_GROWS_DOWN
1132		result = GC_find_limit((ptr_t)(&dummy), TRUE);
1133#           	ifdef HEURISTIC2_LIMIT
1134		    if (result > HEURISTIC2_LIMIT
1135		        && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) {
1136		            result = HEURISTIC2_LIMIT;
1137		    }
1138#	        endif
1139#	    else
1140		result = GC_find_limit((ptr_t)(&dummy), FALSE);
1141#           	ifdef HEURISTIC2_LIMIT
1142		    if (result < HEURISTIC2_LIMIT
1143		        && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) {
1144		            result = HEURISTIC2_LIMIT;
1145		    }
1146#	        endif
1147#	    endif
1148
1149#	endif /* HEURISTIC2 */
1150#	ifdef STACK_GROWS_DOWN
1151	    if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t));
1152#	endif
1153    	return(result);
1154#   endif /* STACKBOTTOM */
1155}
1156
1157# endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS */
1158
1159#if defined(GC_LINUX_THREADS) && !defined(HAVE_GET_STACK_BASE)
1160
1161#include <pthread.h>
1162
1163#ifdef IA64
1164  ptr_t GC_greatest_stack_base_below(ptr_t bound);
1165  	/* From pthread_support.c */
1166#endif
1167
1168int GC_get_stack_base(struct GC_stack_base *b)
1169{
1170    pthread_attr_t attr;
1171    size_t size;
1172
1173    if (pthread_getattr_np(pthread_self(), &attr) != 0) {
1174	WARN("pthread_getattr_np failed\n", 0);
1175	return GC_UNIMPLEMENTED;
1176    }
1177    if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) {
1178	ABORT("pthread_attr_getstack failed");
1179    }
1180#   ifdef STACK_GROWS_DOWN
1181        b -> mem_base = (char *)(b -> mem_base) + size;
1182#   endif
1183#   ifdef IA64
1184      /* We could try backing_store_base_from_proc, but that's safe	*/
1185      /* only if no mappings are being asynchronously created.		*/
1186      /* Subtracting the size from the stack base doesn't work for at	*/
1187      /* least the main thread.						*/
1188      LOCK();
1189      {
1190	ptr_t bsp = GC_save_regs_in_stack();
1191	ptr_t next_stack = GC_greatest_stack_base_below(bsp);
1192	if (0 == next_stack) {
1193          b -> reg_base = GC_find_limit(bsp, FALSE);
1194	} else {
1195	  /* Avoid walking backwards into preceding memory stack and	*/
1196	  /* growing it.						*/
1197          b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack);
1198	}
1199      }
1200      UNLOCK();
1201#   endif
1202    return GC_SUCCESS;
1203}
1204
1205#define HAVE_GET_STACK_BASE
1206
1207#endif /* GC_LINUX_THREADS */
1208
1209#ifndef HAVE_GET_STACK_BASE
1210/* Retrieve stack base.						*/
1211/* Using the GC_find_limit version is risky.			*/
1212/* On IA64, for example, there is no guard page between the	*/
1213/* stack of one thread and the register backing store of the 	*/
1214/* next.  Thus this is likely to identify way too large a	*/
1215/* "stack" and thus at least result in disastrous performance.	*/
1216/* FIXME - Implement better strategies here.			*/
1217int GC_get_stack_base(struct GC_stack_base *b)
1218{
1219    int dummy;
1220
1221#   ifdef NEED_FIND_LIMIT
1222#     ifdef STACK_GROWS_DOWN
1223    	b -> mem_base = GC_find_limit((ptr_t)(&dummy), TRUE);
1224#       ifdef IA64
1225	  b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE);
1226#       endif
1227#     else
1228	b -> mem_base = GC_find_limit(&dummy, FALSE);
1229#     endif
1230      return GC_SUCCESS;
1231#   else
1232      return GC_UNIMPLEMENTED;
1233#   endif
1234}
1235#endif
1236
1237/*
1238 * Register static data segment(s) as roots.
1239 * If more data segments are added later then they need to be registered
1240 * add that point (as we do with SunOS dynamic loading),
1241 * or GC_mark_roots needs to check for them (as we do with PCR).
1242 * Called with allocator lock held.
1243 */
1244
1245# ifdef OS2
1246
1247void GC_register_data_segments(void)
1248{
1249    PTIB ptib;
1250    PPIB ppib;
1251    HMODULE module_handle;
1252#   define PBUFSIZ 512
1253    UCHAR path[PBUFSIZ];
1254    FILE * myexefile;
1255    struct exe_hdr hdrdos;	/* MSDOS header.	*/
1256    struct e32_exe hdr386;	/* Real header for my executable */
1257    struct o32_obj seg;	/* Currrent segment */
1258    int nsegs;
1259    
1260    
1261    if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
1262    	GC_err_printf("DosGetInfoBlocks failed\n");
1263    	ABORT("DosGetInfoBlocks failed\n");
1264    }
1265    module_handle = ppib -> pib_hmte;
1266    if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
1267    	GC_err_printf("DosQueryModuleName failed\n");
1268    	ABORT("DosGetInfoBlocks failed\n");
1269    }
1270    myexefile = fopen(path, "rb");
1271    if (myexefile == 0) {
1272        GC_err_puts("Couldn't open executable ");
1273        GC_err_puts(path); GC_err_puts("\n");
1274        ABORT("Failed to open executable\n");
1275    }
1276    if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) {
1277        GC_err_puts("Couldn't read MSDOS header from ");
1278        GC_err_puts(path); GC_err_puts("\n");
1279        ABORT("Couldn't read MSDOS header");
1280    }
1281    if (E_MAGIC(hdrdos) != EMAGIC) {
1282        GC_err_puts("Executable has wrong DOS magic number: ");
1283        GC_err_puts(path); GC_err_puts("\n");
1284        ABORT("Bad DOS magic number");
1285    }
1286    if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
1287        GC_err_puts("Seek to new header failed in ");
1288        GC_err_puts(path); GC_err_puts("\n");
1289        ABORT("Bad DOS magic number");
1290    }
1291    if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) {
1292        GC_err_puts("Couldn't read MSDOS header from ");
1293        GC_err_puts(path); GC_err_puts("\n");
1294        ABORT("Couldn't read OS/2 header");
1295    }
1296    if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
1297        GC_err_puts("Executable has wrong OS/2 magic number:");
1298        GC_err_puts(path); GC_err_puts("\n");
1299        ABORT("Bad OS/2 magic number");
1300    }
1301    if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
1302        GC_err_puts("Executable %s has wrong byte order: ");
1303        GC_err_puts(path); GC_err_puts("\n");
1304        ABORT("Bad byte order");
1305    }
1306    if ( E32_CPU(hdr386) == E32CPU286) {
1307        GC_err_puts("GC can't handle 80286 executables: ");
1308        GC_err_puts(path); GC_err_puts("\n");
1309        EXIT();
1310    }
1311    if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
1312    	      SEEK_SET) != 0) {
1313        GC_err_puts("Seek to object table failed: ");
1314        GC_err_puts(path); GC_err_puts("\n");
1315        ABORT("Seek to object table failed");
1316    }
1317    for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
1318      int flags;
1319      if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) {
1320        GC_err_puts("Couldn't read obj table entry from ");
1321        GC_err_puts(path); GC_err_puts("\n");
1322        ABORT("Couldn't read obj table entry");
1323      }
1324      flags = O32_FLAGS(seg);
1325      if (!(flags & OBJWRITE)) continue;
1326      if (!(flags & OBJREAD)) continue;
1327      if (flags & OBJINVALID) {
1328          GC_err_printf("Object with invalid pages?\n");
1329          continue;
1330      } 
1331      GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE);
1332    }
1333}
1334
1335# else /* !OS2 */
1336
1337# if defined(MSWIN32) || defined(MSWINCE)
1338
1339# ifdef MSWIN32
1340  /* Unfortunately, we have to handle win32s very differently from NT, 	*/
1341  /* Since VirtualQuery has very different semantics.  In particular,	*/
1342  /* under win32s a VirtualQuery call on an unmapped page returns an	*/
1343  /* invalid result.  Under NT, GC_register_data_segments is a noop and	*/
1344  /* all real work is done by GC_register_dynamic_libraries.  Under	*/
1345  /* win32s, we cannot find the data segments associated with dll's.	*/
1346  /* We register the main data segment here.				*/
1347  GC_bool GC_no_win32_dlls = FALSE;	 
1348  	/* This used to be set for gcc, to avoid dealing with		*/
1349  	/* the structured exception handling issues.  But we now have	*/
1350  	/* assembly code to do that right.				*/
1351
1352# if defined(GWW_VDB)
1353
1354#   ifndef _BASETSD_H_
1355      typedef ULONG * PULONG_PTR;
1356#   endif
1357    typedef UINT (WINAPI * GetWriteWatch_type)(
1358      DWORD, PVOID, SIZE_T, PVOID*, PULONG_PTR, PULONG);
1359    static GetWriteWatch_type GetWriteWatch_func;
1360    static DWORD GetWriteWatch_alloc_flag;
1361
1362#   define GC_GWW_AVAILABLE() (GetWriteWatch_func != NULL)
1363
1364    static void detect_GetWriteWatch(void)
1365    {
1366      static GC_bool done;
1367      if (done)
1368        return;
1369
1370      GetWriteWatch_func = (GetWriteWatch_type)
1371        GetProcAddress(GetModuleHandle("kernel32.dll"), "GetWriteWatch");
1372      if (GetWriteWatch_func != NULL) {
1373        /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH,   */
1374        /* as some versions of kernel32.dll have one but not the      */
1375        /* other, making the feature completely broken.               */
1376        void * page = VirtualAlloc(NULL, GC_page_size,
1377                                    MEM_WRITE_WATCH | MEM_RESERVE,
1378                                    PAGE_READWRITE);
1379        if (page != NULL) {
1380          PVOID pages[16];
1381          ULONG_PTR count = 16;
1382          DWORD page_size;
1383          /* Check that it actually works.  In spite of some 		*/
1384	  /* documentation it actually seems to exist on W2K.		*/
1385	  /* This test may be unnecessary, but ...			*/
1386          if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET,
1387                                 page, GC_page_size,
1388                                 pages,
1389                                 &count,
1390                                 &page_size) != 0) {
1391            /* GetWriteWatch always fails. */
1392            GetWriteWatch_func = NULL;
1393          } else {
1394            GetWriteWatch_alloc_flag = MEM_WRITE_WATCH;
1395          }
1396          VirtualFree(page, GC_page_size, MEM_RELEASE);
1397        } else {
1398          /* GetWriteWatch will be useless. */
1399          GetWriteWatch_func = NULL;
1400        }
1401      }
1402      if (GC_print_stats) {
1403        if (GetWriteWatch_func == NULL) {
1404          GC_log_printf("Did not find a usable GetWriteWatch()\n");
1405        } else {
1406          GC_log_printf("Using GetWriteWatch()\n");
1407        }
1408      }
1409      done = TRUE;
1410    }
1411
1412# endif /* GWW_VDB */
1413
1414  GC_bool GC_wnt = FALSE;
1415         /* This is a Windows NT derivative, i.e. NT, W2K, XP or later.  */
1416  
1417  void GC_init_win32(void)
1418  {
1419    /* Set GC_wnt.							 */
1420    /* If we're running under win32s, assume that no DLLs will be loaded */
1421    /* I doubt anyone still runs win32s, but ...			 */
1422    DWORD v = GetVersion();
1423    GC_wnt = !(v & 0x80000000);
1424    GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3);
1425  }
1426
1427  /* Return the smallest address a such that VirtualQuery		*/
1428  /* returns correct results for all addresses