On Linux use libffi for allocating executable memory (fixed #738)
authorSimon Marlow <marlowsd@gmail.com>
Fri, 19 Sep 2008 13:46:02 +0000 (13:46 +0000)
committerSimon Marlow <marlowsd@gmail.com>
Fri, 19 Sep 2008 13:46:02 +0000 (13:46 +0000)
compiler/ghci/ByteCodeFFI.lhs
includes/RtsExternal.h
includes/Storage.h
rts/Adjustor.c
rts/Makefile
rts/sm/Storage.c

index 5c2b35f..649efc0 100644 (file)
@@ -28,14 +28,16 @@ moan64 msg pp_rep
 
 newExec :: Storable a => [a] -> IO (FunPtr ())
 newExec code
-   = do ptr <- _allocateExec (fromIntegral $ codeSize undefined code)
+   = alloca $ \pcode -> do
+        ptr <- _allocateExec (fromIntegral $ codeSize undefined code) pcode
         pokeArray ptr code
-        return (castPtrToFunPtr ptr)
+        code <- peek pcode
+        return (castPtrToFunPtr code)
    where
    codeSize :: Storable a => a -> [a] -> Int
    codeSize dummy array = sizeOf(dummy) * length array
 
 foreign import ccall unsafe "allocateExec"
-  _allocateExec :: CUInt -> IO (Ptr a)  
+  _allocateExec :: CUInt -> Ptr (Ptr a) -> IO (Ptr a)  
 \end{code}
 
index c6fd74a..31cae1a 100644 (file)
@@ -111,7 +111,7 @@ void sendIOManagerEvent (HsWord32 event);
 extern void setIOManagerPipe (int fd);
 #endif
 
-extern void* allocateExec(unsigned int len);
+extern void* allocateExec(unsigned int len, void **exec_addr);
 
 // Breakpoint stuff
 
index 2b8f107..d431298 100644 (file)
@@ -204,7 +204,7 @@ doYouWantToGC( void )
 }
 
 /* memory allocator for executable memory */
-extern void *allocateExec (nat bytes);
+extern void* allocateExec(unsigned int len, void **exec_addr);
 extern void freeExec (void *p);
 
 /* for splitting blocks groups in two */
index a4c6abe..e021487 100644 (file)
@@ -90,6 +90,7 @@ createAdjustor (int cconv,
     ffi_type *result_type;
     ffi_closure *cl;
     int r, abi;
+    void *code;
 
     n_args = strlen(typeString) - 1;
     cif = stgMallocBytes(sizeof(ffi_cif), "createAdjustor");
@@ -115,13 +116,15 @@ createAdjustor (int cconv,
     r = ffi_prep_cif(cif, abi, n_args, result_type, arg_types);
     if (r != FFI_OK) barf("ffi_prep_cif failed: %d", r);
     
-    // ToDo: use ffi_closure_alloc()
-    cl = allocateExec(sizeof(ffi_closure));
+    cl = allocateExec(sizeof(ffi_closure), &code);
+    if (cl == NULL) {
+        barf("createAdjustor: failed to allocate memory");
+    }
 
     r = ffi_prep_closure(cl, cif, (void*)wptr, hptr/*userdata*/);
     if (r != FFI_OK) barf("ffi_prep_closure failed: %d", r);
 
-    return (void*)cl;
+    return (void*)code;
 }
 
 #else // To end of file...
@@ -329,6 +332,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
               )
 {
   void *adjustor = NULL;
+  void *code;
 
   switch (cconv)
   {
@@ -346,7 +350,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
      <c>:      ff e0             jmp    %eax              # and jump to it.
                # the callee cleans up the stack
     */
-    adjustor = allocateExec(14);
+    adjustor = allocateExec(14,&code);
     {
        unsigned char *const adj_code = (unsigned char *)adjustor;
        adj_code[0x00] = (unsigned char)0x58;  /* popl %eax  */
@@ -391,7 +395,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
     That's (thankfully) the case here with the restricted set of 
     return types that we support.
   */
-    adjustor = allocateExec(17);
+    adjustor = allocateExec(17,&code);
     {
        unsigned char *const adj_code = (unsigned char *)adjustor;
 
@@ -416,7 +420,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
           
           We offload most of the work to AdjustorAsm.S.
         */
-        AdjustorStub *adjustorStub = allocateExec(sizeof(AdjustorStub));
+        AdjustorStub *adjustorStub = allocateExec(sizeof(AdjustorStub),&code);
         adjustor = adjustorStub;
 
         extern void adjustorCode(void);
@@ -514,7 +518,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
        }
 
        if (i < 6) {
-           adjustor = allocateExec(0x30);
+           adjustor = allocateExec(0x30,&code);
 
            *(StgInt32 *)adjustor        = 0x49c1894d;
            *(StgInt32 *)(adjustor+0x4)  = 0x8948c889;
@@ -528,7 +532,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
        }
        else
        {
-           adjustor = allocateExec(0x40);
+           adjustor = allocateExec(0x40,&code);
 
            *(StgInt32 *)adjustor        = 0x35ff5141;
            *(StgInt32 *)(adjustor+0x4)  = 0x00000020;
@@ -575,7 +579,7 @@ createAdjustor(int cconv, StgStablePtr hptr,
      similarly, and local variables should be accessed via %fp, not %sp. In a
      nutshell: This should work! (Famous last words! :-)
   */
-    adjustor = allocateExec(4*(11+1));
+    adjustor = allocateExec(4*(11+1),&code);
     {
         unsigned long *const adj_code = (unsigned long *)adjustor;
 
@@ -652,7 +656,7 @@ TODO: Depending on how much allocation overhead stgMallocBytes uses for
       4 bytes (getting rid of the nop), hence saving memory. [ccshan]
   */
     ASSERT(((StgWord64)wptr & 3) == 0);
-    adjustor = allocateExec(48);
+    adjustor = allocateExec(48,&code);
     {
        StgWord64 *const code = (StgWord64 *)adjustor;
 
@@ -757,7 +761,7 @@ TODO: Depending on how much allocation overhead stgMallocBytes uses for
             */
                     // allocate space for at most 4 insns per parameter
                     // plus 14 more instructions.
-        adjustor = allocateExec(4 * (4*n + 14));
+        adjustor = allocateExec(4 * (4*n + 14),&code);
         code = (unsigned*)adjustor;
         
         *code++ = 0x48000008; // b *+8
@@ -916,7 +920,7 @@ TODO: Depending on how much allocation overhead stgMallocBytes uses for
 #ifdef FUNDESCS
         adjustorStub = stgMallocBytes(sizeof(AdjustorStub), "createAdjustor");
 #else
-        adjustorStub = allocateExec(sizeof(AdjustorStub));
+        adjustorStub = allocateExec(sizeof(AdjustorStub),&code);
 #endif
         adjustor = adjustorStub;
             
@@ -1089,7 +1093,7 @@ TODO: Depending on how much allocation overhead stgMallocBytes uses for
   }
 
   /* Have fun! */
-  return adjustor;
+  return code;
 }
 
 
@@ -1167,7 +1171,8 @@ if ( *(unsigned char*)ptr != 0xe8 ) {
 #else
  ASSERT(0);
 #endif
- *((unsigned char*)ptr) = '\0';
+ // Can't write to this memory, it is only executable:
+ // *((unsigned char*)ptr) = '\0';
 
  freeExec(ptr);
 }
index 9d5d6ec..d6a4e45 100644 (file)
@@ -208,6 +208,7 @@ RtsUtils_CC_OPTS += -DGhcEnableTablesNextToCode=$(DQ)$(GhcEnableTablesNextToCode
 # ffi.h triggers prototype warnings, so disable them here:
 Interpreter_CC_OPTS += -Wno-strict-prototypes
 Adjustor_CC_OPTS += -Wno-strict-prototypes
+sm/Storage_CC_OPTS += -Wno-strict-prototypes
 
 StgCRun_CC_OPTS += -w
 Typeable_CC_OPTS += -w
index e10304c..c9c9c1f 100644 (file)
@@ -35,6 +35,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "ffi.h"
+
 /* 
  * All these globals require sm_mutex to access in THREADED_RTS mode.
  */
@@ -46,6 +48,8 @@ bdescr *pinned_object_block;    /* allocate pinned objects into this block */
 nat alloc_blocks;              /* number of allocate()d blocks since GC */
 nat alloc_blocks_lim;          /* approximate limit on alloc_blocks */
 
+static bdescr *exec_block;
+
 generation *generations = NULL;        /* all the generations */
 generation *g0         = NULL; /* generation 0, for convenience */
 generation *oldest_gen  = NULL; /* oldest generation, for convenience */
@@ -261,6 +265,8 @@ initStorage( void )
   alloc_blocks = 0;
   alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
 
+  exec_block = NULL;
+
   /* Tell GNU multi-precision pkg about our custom alloc functions */
   mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP);
 
@@ -1134,9 +1140,37 @@ calcNeeded(void)
          should be modified to use allocateExec instead of VirtualAlloc.
    ------------------------------------------------------------------------- */
 
-static bdescr *exec_block;
+#if defined(linux_HOST_OS)
+
+// On Linux we need to use libffi for allocating executable memory,
+// because it knows how to work around the restrictions put in place
+// by SELinux.
+
+void *allocateExec (nat bytes, void **exec_ret)
+{
+    void **ret, **exec;
+    ACQUIRE_SM_LOCK;
+    ret = ffi_closure_alloc (sizeof(void *) + (size_t)bytes, (void**)&exec);
+    RELEASE_SM_LOCK;
+    if (ret == NULL) return ret;
+    *ret = ret; // save the address of the writable mapping, for freeExec().
+    *exec_ret = exec + 1;
+    return (ret + 1);
+}
+
+// freeExec gets passed the executable address, not the writable address. 
+void freeExec (void *addr)
+{
+    void *writable;
+    writable = *((void**)addr - 1);
+    ACQUIRE_SM_LOCK;
+    ffi_closure_free (writable);
+    RELEASE_SM_LOCK
+}
 
-void *allocateExec (nat bytes)
+#else
+
+void *allocateExec (nat bytes, void **exec_ret)
 {
     void *ret;
     nat n;
@@ -1172,6 +1206,7 @@ void *allocateExec (nat bytes)
     exec_block->free += n + 1;
 
     RELEASE_SM_LOCK
+    *exec_ret = ret;
     return ret;
 }
 
@@ -1209,6 +1244,8 @@ void freeExec (void *addr)
     RELEASE_SM_LOCK
 }    
 
+#endif /* mingw32_HOST_OS */
+
 /* -----------------------------------------------------------------------------
    Debugging