summaryrefslogtreecommitdiff
path: root/rts
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2008-09-19 13:46:02 +0000
committerSimon Marlow <marlowsd@gmail.com>2008-09-19 13:46:02 +0000
commitc3062251034f54944061e816ed018b0b2db1b849 (patch)
tree13376f17332ac575f637489ed9f6910b9d071225 /rts
parent8f52645bd99ee3e636a34826c0cbfc5939920da1 (diff)
downloadhaskell-c3062251034f54944061e816ed018b0b2db1b849.tar.gz
On Linux use libffi for allocating executable memory (fixed #738)
Diffstat (limited to 'rts')
-rw-r--r--rts/Adjustor.c33
-rw-r--r--rts/Makefile1
-rw-r--r--rts/sm/Storage.c41
3 files changed, 59 insertions, 16 deletions
diff --git a/rts/Adjustor.c b/rts/Adjustor.c
index a4c6abeddb..e02148740d 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,
<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);
}
diff --git a/rts/Makefile b/rts/Makefile
index 9d5d6ec539..d6a4e4549d 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 e10304c5a1..c9c9c1fc01 100644
--- a/rts/sm/Storage.c
+++ b/rts/sm/Storage.c
@@ -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