Commit c3062251 authored by Simon Marlow's avatar Simon Marlow
Browse files

On Linux use libffi for allocating executable memory (fixed #738)

parent 8f52645b
......@@ -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}
......@@ -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
......
......@@ -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 */
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment