Make the CPython bootstrap W^X compatible.

Index: rpython/rlib/rmmap.py
--- rpython/rlib/rmmap.py.orig
+++ rpython/rlib/rmmap.py
@@ -166,6 +166,9 @@ if _POSIX:
         _, c_madvise_safe = external('madvise', [PTR, size_t, rffi.INT],
                                      rffi.INT, _nowrapper=True)
 
+    c_mprotect, _ = external('mprotect',
+                             [PTR, size_t, rffi.INT], rffi.INT)
+
     # this one is always safe
     _pagesize = rffi_platform.getintegerfunctionresult('getpagesize',
                                                        includes=includes)
@@ -717,6 +720,25 @@ if _POSIX:
             prot = NonConstant(prot)
         return c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
 
+    def alloc_hinted_noexec(hintp, map_size):
+        """Same as alloc_hinted, but allocates pages non-executable.
+        Duplicated because of constancy constraints on prot."""
+
+        flags = MAP_PRIVATE | MAP_ANONYMOUS
+        prot = PROT_READ | PROT_WRITE
+
+        if we_are_translated():
+            flags = NonConstant(flags)
+            prot = NonConstant(prot)
+        return c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
+
+    def set_pages_executable(addr, size):
+        from rpython.rlib import debug
+
+        rv = c_mprotect(addr, size, PROT_EXEC)
+        if rv < 0:
+            debug.fatalerror_notb("set_pages_executable failed")
+
     def clear_large_memory_chunk_aligned(addr, map_size):
         addr = rffi.cast(PTR, addr)
         flags = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS
@@ -732,10 +754,44 @@ if _POSIX:
         pos = -0x4fff0000   # for reproducible results
     hint = Hint()
 
+    def alloc_noexec(map_size):
+        """The same as `alloc`, but doesn't set the executable flag.
+        Duplicated because of constancy constraints on prot."""
+        from errno import ENOMEM
+        from rpython.rlib import debug
+
+        if _CYGWIN:
+            # XXX: JIT memory should be using mmap MAP_PRIVATE with
+            #      PROT_EXEC but Cygwin's fork() fails.  mprotect()
+            #      cannot be used, but seems to be unnecessary there.
+            res = c_malloc_safe(map_size)
+            if res == rffi.cast(PTR, 0):
+                raise MemoryError
+            return res
+        res = alloc_hinted_noexec(rffi.cast(PTR, hint.pos), map_size)
+        if res == rffi.cast(PTR, -1):
+            # some systems (some versions of OS/X?) complain if they
+            # are passed a non-zero address.  Try again.
+            res = alloc_hinted_noexec(rffi.cast(PTR, 0), map_size)
+            if res == rffi.cast(PTR, -1):
+                # ENOMEM simply raises MemoryError, but other errors are fatal
+                if rposix.get_saved_errno() != ENOMEM:
+                    debug.fatalerror_notb(
+                        "Got an unexpected error trying to allocate some "
+                        "memory for the JIT (tried to do mmap() with "
+                        "PROT_EXEC|PROT_READ|PROT_WRITE).  This can be caused "
+                        "by a system policy like PAX.  You need to find how "
+                        "to work around the policy on your system.")
+                raise MemoryError
+        else:
+            hint.pos += map_size
+        return res
+    alloc_noexec._annenforceargs_ = (int,)
+
     def alloc(map_size):
         """Allocate memory.  This is intended to be used by the JIT,
-        so the memory has the executable bit set and gets allocated
-        internally in case of a sandboxed process.
+        so the memory has the executable bit set.
+        and gets allocated internally in case of a sandboxed process.
         """
         from errno import ENOMEM
         from rpython.rlib import debug
@@ -936,6 +992,17 @@ elif _MS_WINDOWS:
         pos = -0x4fff0000   # for reproducible results
     hint = Hint()
     # XXX this has no effect on windows
+    def alloc_noexec(map_size):
+        """Allocate memory.  This is intended to be used by the JIT,
+        so the memory has the executable bit set.
+        XXX implement me: it should get allocated internally in
+        case of a sandboxed process
+
+        XXX no_exec ignored on windows
+        """
+        return alloc(map_size)
+    alloc_noexec._annenforceargs_ = (int,)
+        
 
     def alloc(map_size):
         """Allocate memory.  This is intended to be used by the JIT,
