From: Simon Marlow Date: Fri, 19 Sep 2008 13:46:02 +0000 (+0000) Subject: On Linux use libffi for allocating executable memory (fixed #738) X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=commitdiff_plain;h=c3062251034f54944061e816ed018b0b2db1b849 On Linux use libffi for allocating executable memory (fixed #738) --- diff --git a/compiler/ghci/ByteCodeFFI.lhs b/compiler/ghci/ByteCodeFFI.lhs index 5c2b35f..649efc0 100644 --- a/compiler/ghci/ByteCodeFFI.lhs +++ b/compiler/ghci/ByteCodeFFI.lhs @@ -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} diff --git a/includes/RtsExternal.h b/includes/RtsExternal.h index c6fd74a..31cae1a 100644 --- a/includes/RtsExternal.h +++ b/includes/RtsExternal.h @@ -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 diff --git a/includes/Storage.h b/includes/Storage.h index 2b8f107..d431298 100644 --- a/includes/Storage.h +++ b/includes/Storage.h @@ -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 */ diff --git a/rts/Adjustor.c b/rts/Adjustor.c index a4c6abe..e021487 100644 --- a/rts/Adjustor.c +++ b/rts/Adjustor.c @@ -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, : 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); } diff --git a/rts/Makefile b/rts/Makefile index 9d5d6ec..d6a4e45 100644 --- a/rts/Makefile +++ b/rts/Makefile @@ -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 diff --git a/rts/sm/Storage.c b/rts/sm/Storage.c index e10304c..c9c9c1f 100644 --- a/rts/sm/Storage.c +++ b/rts/sm/Storage.c @@ -35,6 +35,8 @@ #include #include +#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