X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FAdjustor.c;h=24fa13a293f34963acf877f8588342716a377be8;hb=efcd3f2b7aabe23e30ab482db1ed2eee5075e095;hp=b976f38b258925031e301fb0acce5ffe74bf9078;hpb=0926a3ca0b5caabc00b8dad7912495a25f63d099;p=ghc-hetmet.git diff --git a/ghc/rts/Adjustor.c b/ghc/rts/Adjustor.c index b976f38..24fa13a 100644 --- a/ghc/rts/Adjustor.c +++ b/ghc/rts/Adjustor.c @@ -41,16 +41,49 @@ Haskell side. #include "RtsFlags.h" /* Heavily arch-specific, I'm afraid.. */ -#if defined(i386_TARGET_ARCH) +#if defined(i386_TARGET_ARCH) || defined(sparc_TARGET_ARCH) + +/* Now here's something obscure for you: + + When generating an adjustor thunk that uses the C calling + convention, we have to make sure that the thunk kicks off + the process of jumping into Haskell with a tail jump. Why? + Because as a result of jumping in into Haskell we may end + up freeing the very adjustor thunk we came from using + freeHaskellFunctionPtr(). Hence, we better not return to + the adjustor code on our way out, since it could by then + point to junk. + + The fix is readily at hand, just include the opcodes + for the C stack fixup code that we need to perform when + returning in some static piece of memory and arrange + to return to it before tail jumping from the adjustor thunk. + + For this to work we make the assumption that bytes in .data + are considered executable. +*/ +static unsigned char __obscure_ccall_ret_code [] = + { 0x83, 0xc4, 0x04 /* addl $0x4, %esp */ + , 0xc3 /* ret */ + }; + + void* createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr) { void *adjustor; unsigned char* adj_code; size_t sizeof_adjustor; - + if (cconv == 0) { /* the adjustor will be _stdcall'ed */ +#if defined(sparc_TARGET_ARCH) + /* SPARC doesn't have a calling convention other than _ccall */ + if (cconv == 0) { + return NULL; + } +#endif + /* Magic constant computed by inspecting the code length of the following assembly language snippet (offset and machine code prefixed): @@ -86,28 +119,34 @@ createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr) } else { /* the adjustor will be _ccall'ed */ +#if defined(i386_TARGET_ARCH) /* Magic constant computed by inspecting the code length of the following assembly language snippet (offset and machine code prefixed): - <00>: 68 ef be ad de pushl $0xdeadbeef # constant is large enough to + <00>: 68 ef be ad de pushl $0xdeadbeef # constant is large enough to # hold a StgStablePtr - <05>: b8 fa ef ff 00 movl $0x00ffeffa, %eax # load up wptr - <0a>: ff d0 call %eax # and call it. - <0c>: 83 c4 04 addl $0x4,%esp # remove stable pointer. - <0f>: c3 ret # return to where you came from. + <05>: b8 fa ef ff 00 movl $0x00ffeffa, %eax # load up wptr + <0a>: 68 ef be ad de pushl $__obscure_ccall_ret_code # push the return address + <0f>: ff e0 jmp *%eax # jump to wptr The ccall'ing version is a tad different, passing in the return address of the caller to the auto-generated C stub (which enters - via the stable pointer.) (The auto-generated C stub is on this + via the stable pointer.) (The auto-generated C stub is in on this game, don't worry :-) - The adjustor makes the assumption that any return value + See the comment next to __obscure_ccall_ret_code why we need to + perform a tail jump instead of a call, followed by some C stack + fixup. + + Note: The adjustor makes the assumption that any return value coming back from the C stub is not stored on the stack. That's (thankfully) the case here with the restricted set of return types that we support. + + */ - sizeof_adjustor = 16*sizeof(char); + sizeof_adjustor = 17*sizeof(char); if ((adjustor = stgMallocBytes(sizeof_adjustor,"createAdjustor")) == NULL) { return NULL; @@ -121,15 +160,59 @@ createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr) adj_code[0x05] = (unsigned char)0xb8; /* movl $wptr, %eax */ *((StgFunPtr*)(adj_code + 0x06)) = (StgFunPtr)wptr; - adj_code[0x0a] = (unsigned char)0xff; /* call %eax */ - adj_code[0x0b] = (unsigned char)0xd0; - - adj_code[0x0c] = (unsigned char)0x83; /* addl $0x4, %esp */ - adj_code[0x0d] = (unsigned char)0xc4; - adj_code[0x0e] = (unsigned char)0x04; + adj_code[0x0a] = (unsigned char)0x68; /* pushl __obscure_ccall_ret_code */ + *((StgFunPtr*)(adj_code + 0x0b)) = (StgFunPtr)__obscure_ccall_ret_code; + + adj_code[0x0f] = (unsigned char)0xff; /* jmp *%eax */ + adj_code[0x10] = (unsigned char)0xe0; + +#elif defined(sparc_TARGET_ARCH) + /* Magic constant computed by inspecting the code length of + the following assembly language snippet + (offset and machine code prefixed): + + <00>: 13 00 3f fb sethi %hi(0x00ffeffa), %o1 # load up wptr (1 of 2) + <04>: 11 37 ab 6f sethi %hi(0xdeadbeef), %o0 # load up hptr (1 of 2) + <08>: 81 c2 63 fa jmp %o1+%lo(0x00ffeffa) # jump to wptr (load 2 of 2) + <0c>: 90 12 22 ef or %o0, %lo(0xdeadbeef), %o0 # load up hptr (2 of 2) + # [in delay slot] + <10>: de ad be ef # Place the value of the StgStablePtr somewhere readable - adj_code[0x0f] = (unsigned char)0xc3; /* ret */ + ccall'ing on a SPARC leaves little to be performed by the caller. + The callee shifts the window on entry and restores it on exit. + Input paramters and results are passed via registers. (%o0 in the + code above contains the input paramter to wptr.) The return address + is stored in %o7/%i7. Since we don't shift the window in this code, + the return address is preserved and wptr will return to our caller. + */ + sizeof_adjustor = 28*sizeof(char); + + if ((adjustor = stgMallocBytes(sizeof_adjustor,"createAdjustor")) == NULL) { + return NULL; + } + + adj_code = (unsigned char*)adjustor; + + /* sethi %hi(wptr), %o1 */ + *((unsigned long*)(adj_code+0x00)) = (unsigned long)0x13000000; + *((unsigned long*)(adj_code+0x00)) |= ((unsigned long)wptr) >> 10; + + /* sethi %hi(hptr), %o0 */ + *((unsigned long*)(adj_code+0x04)) = (unsigned long)0x11000000; + *((unsigned long*)(adj_code+0x04)) |= ((unsigned long)hptr) >> 10; + + /* jmp %o1+%lo(wptr) */ + *((unsigned long*)(adj_code+0x08)) = (unsigned long)0x81c26000; + *((unsigned long*)(adj_code+0x08)) |= ((unsigned long)wptr) & 0x000003ff; + + /* or %o0, %lo(hptr), %o0 */ + *((unsigned long*)(adj_code+0x0c)) = (unsigned long)0x90122000; + *((unsigned long*)(adj_code+0x0c)) |= ((unsigned long)hptr) & 0x000003ff; + + *((StgStablePtr*)(adj_code+0x10)) = (StgStablePtr)hptr; +#endif + } /* Have fun! */ @@ -139,6 +222,7 @@ createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr) void freeHaskellFunctionPtr(void* ptr) { +#if defined(i386_TARGET_ARCH) if ( *(unsigned char*)ptr != 0x68 && *(unsigned char*)ptr != 0x58 ) { fprintf(stderr, "freeHaskellFunctionPtr: not for me, guv! %p\n", ptr); @@ -147,14 +231,23 @@ freeHaskellFunctionPtr(void* ptr) /* Free the stable pointer first..*/ if (*(unsigned char*)ptr == 0x68) { /* Aha, a ccall adjustor! */ - freeStablePointer(*((StgStablePtr*)((unsigned char*)ptr + 0x01))); + freeStablePtr(*((StgStablePtr*)((unsigned char*)ptr + 0x01))); } else { - freeStablePointer(*((StgStablePtr*)((unsigned char*)ptr + 0x02))); + freeStablePtr(*((StgStablePtr*)((unsigned char*)ptr + 0x02))); } +#elif defined(sparc_TARGET_ARCH) + if ( *(unsigned char*)ptr != 0x13 ) { + fprintf(stderr, "freeHaskellFunctionPtr: not for me, guv! %p\n", ptr); + return; + } + + /* Free the stable pointer first..*/ + freeStablePtr(*((StgStablePtr*)((unsigned char*)ptr + 0x10))); +#endif *((unsigned char*)ptr) = '\0'; free(ptr); } -#endif /* i386_TARGET_ARCH */ +#endif /* i386_TARGET_ARCH || sparc_TARGET_ARCH */