/* ----------------------------------------------------------------------------- * AMD64 architecture adjustor thunk logic. * ---------------------------------------------------------------------------*/ #include "rts/PosixSource.h" #include "Rts.h" #include "RtsUtils.h" #include "StablePtr.h" #include "Adjustor.h" #include "AdjustorPool.h" #define DECLARE_ADJUSTOR_TEMPLATE(NAME) \ extern uint8_t NAME ## _adjustor; \ extern uint8_t NAME ## _adjustor_context; \ extern uint8_t NAME ## _adjustor_end; \ const struct AdjustorTemplate NAME ## _adjustor_template = { \ .code_start = (uint8_t *) &NAME ## _adjustor, \ .code_end = (uint8_t *) &NAME ## _adjustor_end, \ .context_ptr = (const struct AdjustorContext **) &NAME ## _adjustor_context, \ }; /* adjustors to handle calls with less than 6 integer arguments */ DECLARE_ADJUSTOR_TEMPLATE(simple_ccall); static struct AdjustorPool *simple_ccall_pool; /* adjustors to handle calls with 6 or more integer arguments */ DECLARE_ADJUSTOR_TEMPLATE(complex_ccall); static struct AdjustorPool *complex_ccall_pool; void initAdjustors() { simple_ccall_pool = new_adjustor_pool_from_template(&simple_ccall_adjustor_template); complex_ccall_pool = new_adjustor_pool_from_template(&complex_ccall_adjustor_template); } void* createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr, char *typeString ) { struct AdjustorContext context = { .hptr = hptr, .wptr = wptr, }; switch (cconv) { case 1: /* _ccall */ /* stack at call: argn ... arg7 return address %rdi,%rsi,%rdx,%rcx,%r8,%r9 = arg1..arg6 if there are <6 integer args, then we can just push the StablePtr into %edi and shuffle the other args up. If there are >=6 integer args, then we have to flush one arg to the stack, and arrange to adjust the stack ptr on return. The stack will be rearranged to this: argn ... arg7 return address *** <-- dummy arg in stub fn. arg6 obscure_ccall_ret_code This unfortunately means that the type of the stub function must have a dummy argument for the original return address pointer inserted just after the 6th integer argument. */ { int n_int_args = 0; // determine whether we have 6 or more integer arguments, // and therefore need to flush one to the stack. for (char *c = typeString; *c != '\0'; c++) { if (*c != 'f' && *c != 'd') { n_int_args++; } if (n_int_args == 6) { break; } } if (n_int_args < 6) { return alloc_adjustor(simple_ccall_pool, &context); } else { return alloc_adjustor(complex_ccall_pool, &context); } break; } default: barf("createAdjustor: Unsupported calling convention"); break; } } void freeHaskellFunctionPtr(void* ptr) { struct AdjustorContext context; free_adjustor(ptr, &context); freeStablePtr(context.hptr); }