would appreciate help with anon-mmap replacement

Zack Weinberg zackw@stanford.edu
Tue Jan 9 22:02:00 GMT 2001


I'm trying to fix GCC's present problems with cygwin's mmap
implementation.  GCC uses mmap to obtain anonymous memory, and expects
Unix-compatible semantics in detail - these are (as far as I can tell)
unimplementable under Windows due to the way the underlying system
calls work.  This was partially addressed in cygwin 1.1.7; it doesn't
crash anymore, it just leaks memory continuously.

A solution inside cygwin would be a lot of work and wouldn't help
people with older versions of the library.  Therefore, I'm focusing on
a solution inside GCC's support library, using VirtualAlloc()
directly.  I have attempted to write the appropriate routines and a
test program which demonstrates what GCC needs.  When this test
program is run on a modern Unix system it exits successfully.  When
run under cygwin it goes into an infinite loop chewing CPU. cygwin
strace reports this over and over again (with different leading
numbers):

 1034 1296856 [main] test 310 handle_exceptions: In cygwin_except_handler 
	exc 0xC0000005 at 0x4013AB sp 0x246FE7C
  614 1297470 [main] test 310 handle_exceptions: In cygwin_except_handler
	sig = 11 at 0x4013AB
  608 1298078 [main] test 310 handle_exceptions: In cygwin_except_handler
	calling 0x4010E0
  728 1298806 [main] test 310 sig_send: pid 310, signal 11, its_me 1
  608 1299414 [main] test 310 sig_send: Waiting for thiscomplete 0xC
  915 1300329 [sig] test 310 wait_sig: looping
  738 1301067 [sig] test 310 wait_sig: awake
  651 1301718 [sig] test 310 wait_sig: sig 11 blocked
  655 1302373 [main] test 310 sig_send: returning 0 from sending signal 11

I'd appreciate any help anyone can offer.  I do not have access to a
cygwin machine, and my knowledge of the Windows API is limited to the
online docs at msdn.microsoft.com, so it's likely I've made an obvious
mistake.  [I had someone run the test for me.]

Note that the test program won't work on a Unix box that doesn't have
MAP_ANON(YMOUS), which is rather a lot of them.  The real thing will
of course know about /dev/zero; I left that out for simplicity.

Thanks.

zw

#ifdef _WIN32
#include <windows.h>
#define MAP_FAILED 0
#else
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

#ifndef MAP_ANON
# ifdef MAP_ANONYMOUS
#  define MAP_ANON MAP_ANONYMOUS
# else
#  error "Won't work without MAP_ANON(YMOUS)."
# endif
#endif

#ifndef MAP_FAILED
# define MAP_FAILED -1
#endif

#endif /* _WIN32 */

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

#undef perror_exit
#define perror_exit(str, val) \
  do { perror(str); exit(val); } while (0)

/* Anonymous mmap replacement for cygwin environment.  This attempts
   to produce the semantics needed by GCC by careful use of low-level
   Win32 routines.

   The fundamental difficulty is that Unix style anonymous mmap() both
   "reserves" and "commits" memory; it does so with page granularity;
   and munmap() both "decommits" and "releases" exactly what you ask
   it to, irrespective of what chunks were inside or overlapped that
   region.

   Cygwin's existing mmap implementation uses MapViewOfFile and
   UnmapViewOfFile with a -1 HANDLE.  These can only be used in exact
   pairs (i.e.  you have to release exactly the chunk of memory you
   originally allocated).  The same is true of VirtualAlloc and
   VirtualFree "reserving" and "releasing" memory, with the additional
   gotcha of 64K (segment?) granularity.

   VA/VF "committing" and "decommitting" memory appear to be as close
   to Unix semantics as you can get.  So we "reserve" a huge block of
   memory at the beginning, then trust that future VA(0, size,
   MEM_COMMIT ...) calls will use that region without further
   prompting.  We further trust that the reserved region gets
   discarded automatically when the process terminates.  */
   
static void
anonmap_init ()
{
#ifdef _WIN32
  /* Reserve one gigabyte of address space.  This should be
     satisfiable by all existing versions of Windows.  */

  char *arena = (char *) VirtualAlloc (0, 1024 * 1024 * 1024,
				       MEM_RESERVE, 0);
  if (arena == NULL)
    perror_exit ("reserve", 127);
#endif
}

static char *
anonmap (size, loc)
     size_t size;
     char *loc;
{
#ifdef _WIN32
  return (char *) VirtualAlloc (loc, size, MEM_COMMIT, PAGE_READWRITE);
#else
  return (char *) mmap (loc, size, PROT_READ|PROT_WRITE,
			(loc ? MAP_FIXED : 0)|MAP_PRIVATE|MAP_ANON, -1, 0);
#endif
}

static void
anonfree (loc, size)
     char *loc;
     size_t size;
{
#ifdef _WIN32
  VirtualFree (loc, size, MEM_DECOMMIT);
#else
  munmap (loc, size);
#endif
}
     
  
static jmp_buf r;
static size_t pg;

static void
sigsegv (unused)
     int unused;
{
  longjmp (r, 1);
}

/* 1. If we map a 2-page region and unmap its second page, the first page
   must remain.  */
void
test_1 ()
{
  char *x = anonmap (pg * 2, 0);
  if (x == (char *)MAP_FAILED)
    perror_exit ("test 1 mmap", 1);

  signal (SIGSEGV, sigsegv);
  if (setjmp (r))
    perror_exit ("test 1 fault", 2);

  x[0] = 1;
  x[pg] = 1;

  anonfree (x + pg, pg);
  x[0] = 2;

  if (setjmp (r))
    {
      anonfree (x, pg);
      return;
    }
  x[pg] = 1;
  perror_exit ("test 1 no fault", 3);
}

/* 2. If we map a 2-page region and unmap its first page, the second
   page must remain.  */
void
test_2 ()
{
  char *x = anonmap (pg * 2, 0);
  if (x == (char *)MAP_FAILED)
    perror_exit ("test 2 mmap", 4);

  signal (SIGSEGV, sigsegv);
  if (setjmp (r))
    perror_exit ("test 2 fault", 5);

  x[0] = 1;
  x[pg] = 1;

  anonfree (x, pg);
  x[pg] = 2;

  if (setjmp (r))
    {
      anonfree (x + pg, pg);
      return;
    }
  x[0] = 1;
  perror_exit ("test 2 no fault", 6);
}

/* 3. If we map two consecutive 1-page regions and unmap them both with
   one munmap, both must go away.  */

void
test_3 ()
{
  char *x, *y;
  x = anonmap (pg, 0);
  if (x == (char *)MAP_FAILED)
    perror_exit ("test 3 mmap 1", 7);
  y = anonmap (pg, x+pg);
  if (y == (char *)MAP_FAILED || x + pg != y)
    perror_exit ("test 3 mmap 2", 7);

  signal (SIGSEGV, sigsegv);
  if (setjmp (r))
    perror_exit ("test 3 fault", 8);

  x[0] = 1;
  x[pg] = 1;

  anonfree (x, pg * 2);

  if (setjmp (r) == 0)
    {
      x[0] = 1;
      perror_exit ("test 3 no fault 1", 9);
    }
  
  signal (SIGSEGV, sigsegv);
  if (setjmp (r) == 0)
    {
      x[pg] = 1;
      perror_exit ("test 3 no fault 2", 10);
    }
}


int
main ()
{
  pg = getpagesize ();

  anonmap_init ();
  test_1();
  test_2();
  test_3();

  exit(0);
}


--
Want to unsubscribe from this list?
Check out: http://cygwin.com/ml/#unsubscribe-simple



More information about the Cygwin mailing list