$OpenBSD: patch-rpython_rlib_rmmap_py,v 1.1 2016/08/15 09:16:40 edd Exp $

Make the CPython bootstrap W^X compatible.

--- rpython/rlib/rmmap.py.orig	Tue Jun 14 08:46:04 2016
+++ rpython/rlib/rmmap.py	Thu Aug 11 08:49:22 2016
@@ -155,6 +155,9 @@ if _POSIX:
         c_mremap, _ = external('mremap',
                                [PTR, size_t, size_t, rffi.ULONG], PTR)
 
+    c_mprotect, _ = external('mprotect',
+                             [PTR, size_t, rffi.INT], rffi.INT)
+
     # this one is always safe
     _pagesize = rffi_platform.getintegerfunctionresult('getpagesize',
                                                        includes=includes)
@@ -694,11 +697,29 @@ if _POSIX:
     def alloc_hinted(hintp, map_size):
         flags = MAP_PRIVATE | MAP_ANONYMOUS
         prot = PROT_EXEC | 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 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):
+        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
@@ -714,10 +735,10 @@ if _POSIX:
         pos = -0x4fff0000   # for reproducible results
     hint = Hint()
 
-    def alloc(map_size):
+    def alloc(map_size, no_exec=False):
         """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 (unless no_exec=True)
+        and gets allocated internally in case of a sandboxed process.
         """
         from errno import ENOMEM
         from rpython.rlib import debug
@@ -730,11 +751,17 @@ if _POSIX:
             if res == rffi.cast(PTR, 0):
                 raise MemoryError
             return res
-        res = alloc_hinted(rffi.cast(PTR, hint.pos), map_size)
+        if no_exec:
+            res = alloc_hinted_noexec(rffi.cast(PTR, hint.pos), map_size)
+        else:
+            res = alloc_hinted(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(rffi.cast(PTR, 0), map_size)
+            if no_exec:
+                res = alloc_hinted_noexec(rffi.cast(PTR, 0), map_size)
+            else:
+                res = alloc_hinted(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:
@@ -748,7 +775,7 @@ if _POSIX:
         else:
             hint.pos += map_size
         return res
-    alloc._annenforceargs_ = (int,)
+    alloc._annenforceargs_ = (int, bool)
 
     if _CYGWIN:
         free = c_free_safe
@@ -886,11 +913,13 @@ elif _MS_WINDOWS:
     hint = Hint()
     # XXX this has no effect on windows
 
-    def alloc(map_size):
+    def alloc(map_size, no_exec=False):
         """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.
         """
         null = lltype.nullptr(rffi.VOIDP.TO)
         res = VirtualAlloc_safe(null, map_size, MEM_COMMIT | MEM_RESERVE,
@@ -902,7 +931,7 @@ elif _MS_WINDOWS:
         lltype.free(arg, flavor='raw')
         # ignore errors, just try
         return res
-    alloc._annenforceargs_ = (int,)
+    alloc._annenforceargs_ = (int, bool)
 
     def free(ptr, map_size):
         VirtualFree_safe(ptr, 0, MEM_RELEASE)
