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