1  /* -----------------------------------------------------------------------
       2     ffiw64.c - Copyright (c) 2018 Anthony Green
       3                Copyright (c) 2014 Red Hat, Inc.
       4  
       5     x86 win64 Foreign Function Interface
       6  
       7     Permission is hereby granted, free of charge, to any person obtaining
       8     a copy of this software and associated documentation files (the
       9     ``Software''), to deal in the Software without restriction, including
      10     without limitation the rights to use, copy, modify, merge, publish,
      11     distribute, sublicense, and/or sell copies of the Software, and to
      12     permit persons to whom the Software is furnished to do so, subject to
      13     the following conditions:
      14  
      15     The above copyright notice and this permission notice shall be included
      16     in all copies or substantial portions of the Software.
      17  
      18     THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
      19     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      20     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      21     NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      22     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      23     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      24     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25     DEALINGS IN THE SOFTWARE.
      26     ----------------------------------------------------------------------- */
      27  
      28  #if defined(__x86_64__) || defined(_M_AMD64)
      29  #include <ffi.h>
      30  #include <ffi_common.h>
      31  #include <stdlib.h>
      32  #include <stdint.h>
      33  #include <tramp.h>
      34  
      35  #ifdef X86_WIN64
      36  #define EFI64(name) name
      37  #else
      38  #define EFI64(name) FFI_HIDDEN name##_efi64
      39  #endif
      40  
      41  struct win64_call_frame
      42  {
      43    UINT64 rbp;		/* 0 */
      44    UINT64 retaddr;	/* 8 */
      45    UINT64 fn;		/* 16 */
      46    UINT64 flags;		/* 24 */
      47    UINT64 rvalue;	/* 32 */
      48  };
      49  
      50  extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
      51  			    void *closure) FFI_HIDDEN;
      52  
      53  ffi_status FFI_HIDDEN
      54  EFI64(ffi_prep_cif_machdep)(ffi_cif *cif)
      55  {
      56    int flags, n;
      57  
      58    switch (cif->abi)
      59      {
      60      case FFI_WIN64:
      61      case FFI_GNUW64:
      62        break;
      63      default:
      64        return FFI_BAD_ABI;
      65      }
      66  
      67    flags = cif->rtype->type;
      68    switch (flags)
      69      {
      70      default:
      71        break;
      72      case FFI_TYPE_LONGDOUBLE:
      73        /* GCC returns long double values by reference, like a struct */
      74        if (cif->abi == FFI_GNUW64)
      75  	flags = FFI_TYPE_STRUCT;
      76        break;
      77      case FFI_TYPE_COMPLEX:
      78        flags = FFI_TYPE_STRUCT;
      79        /* FALLTHRU */
      80      case FFI_TYPE_STRUCT:
      81        switch (cif->rtype->size)
      82  	{
      83  	case 8:
      84  	  flags = FFI_TYPE_UINT64;
      85  	  break;
      86  	case 4:
      87  	  flags = FFI_TYPE_SMALL_STRUCT_4B;
      88  	  break;
      89  	case 2:
      90  	  flags = FFI_TYPE_SMALL_STRUCT_2B;
      91  	  break;
      92  	case 1:
      93  	  flags = FFI_TYPE_SMALL_STRUCT_1B;
      94  	  break;
      95  	}
      96        break;
      97      }
      98    cif->flags = flags;
      99  
     100    /* Each argument either fits in a register, an 8 byte slot, or is
     101       passed by reference with the pointer in the 8 byte slot.  */
     102    n = cif->nargs;
     103    n += (flags == FFI_TYPE_STRUCT);
     104    if (n < 4)
     105      n = 4;
     106    cif->bytes = n * 8;
     107  
     108    return FFI_OK;
     109  }
     110  
     111  /* We perform some black magic here to use some of the parent's stack frame in
     112   * ffi_call_win64() that breaks with the MSVC compiler with the /RTCs or /GZ
     113   * flags.  Disable the 'Stack frame run time error checking' for this function
     114   * so we don't hit weird exceptions in debug builds. */
     115  #if defined(_MSC_VER)
     116  #pragma runtime_checks("s", off)
     117  #endif
     118  static void
     119  ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
     120  	      void **avalue, void *closure)
     121  {
     122    int i, j, n, flags;
     123    UINT64 *stack;
     124    size_t rsize;
     125    struct win64_call_frame *frame;
     126  
     127    FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64);
     128  
     129    flags = cif->flags;
     130    rsize = 0;
     131  
     132    /* If we have no return value for a structure, we need to create one.
     133       Otherwise we can ignore the return type entirely.  */
     134    if (rvalue == NULL)
     135      {
     136        if (flags == FFI_TYPE_STRUCT)
     137  	rsize = cif->rtype->size;
     138        else
     139  	flags = FFI_TYPE_VOID;
     140      }
     141  
     142    stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
     143    frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
     144    if (rsize)
     145      rvalue = frame + 1;
     146  
     147    frame->fn = (uintptr_t)fn;
     148    frame->flags = flags;
     149    frame->rvalue = (uintptr_t)rvalue;
     150  
     151    j = 0;
     152    if (flags == FFI_TYPE_STRUCT)
     153      {
     154        stack[0] = (uintptr_t)rvalue;
     155        j = 1;
     156      }
     157  
     158    for (i = 0, n = cif->nargs; i < n; ++i, ++j)
     159      {
     160        switch (cif->arg_types[i]->size)
     161  	{
     162  	case 8:
     163  	  stack[j] = *(UINT64 *)avalue[i];
     164  	  break;
     165  	case 4:
     166  	  stack[j] = *(UINT32 *)avalue[i];
     167  	  break;
     168  	case 2:
     169  	  stack[j] = *(UINT16 *)avalue[i];
     170  	  break;
     171  	case 1:
     172  	  stack[j] = *(UINT8 *)avalue[i];
     173  	  break;
     174  	default:
     175  	  stack[j] = (uintptr_t)avalue[i];
     176  	  break;
     177  	}
     178      }
     179  
     180    ffi_call_win64 (stack, frame, closure);
     181  }
     182  #if defined(_MSC_VER)
     183  #pragma runtime_checks("s", restore)
     184  #endif
     185  
     186  void
     187  EFI64(ffi_call)(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
     188  {
     189    ffi_call_int (cif, fn, rvalue, avalue, NULL);
     190  }
     191  
     192  void
     193  EFI64(ffi_call_go)(ffi_cif *cif, void (*fn)(void), void *rvalue,
     194  	     void **avalue, void *closure)
     195  {
     196    ffi_call_int (cif, fn, rvalue, avalue, closure);
     197  }
     198  
     199  
     200  extern void ffi_closure_win64(void) FFI_HIDDEN;
     201  #if defined(FFI_EXEC_STATIC_TRAMP)
     202  extern void ffi_closure_win64_alt(void) FFI_HIDDEN;
     203  #endif
     204  
     205  #ifdef FFI_GO_CLOSURES
     206  extern void ffi_go_closure_win64(void) FFI_HIDDEN;
     207  #endif
     208  
     209  ffi_status
     210  EFI64(ffi_prep_closure_loc)(ffi_closure* closure,
     211  		      ffi_cif* cif,
     212  		      void (*fun)(ffi_cif*, void*, void**, void*),
     213  		      void *user_data,
     214  		      void *codeloc)
     215  {
     216    static const unsigned char trampoline[FFI_TRAMPOLINE_SIZE - 8] = {
     217      /* endbr64 */
     218      0xf3, 0x0f, 0x1e, 0xfa,
     219      /* leaq  -0xb(%rip),%r10   # 0x0  */
     220      0x4c, 0x8d, 0x15, 0xf5, 0xff, 0xff, 0xff,
     221      /* jmpq  *0x7(%rip)        # 0x18 */
     222      0xff, 0x25, 0x07, 0x00, 0x00, 0x00,
     223      /* nopl  0(%rax) */
     224      0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00
     225    };
     226    char *tramp = closure->tramp;
     227  
     228    switch (cif->abi)
     229      {
     230      case FFI_WIN64:
     231      case FFI_GNUW64:
     232        break;
     233      default:
     234        return FFI_BAD_ABI;
     235      }
     236  
     237  #if defined(FFI_EXEC_STATIC_TRAMP)
     238    if (ffi_tramp_is_present(closure))
     239      {
     240        /* Initialize the static trampoline's parameters. */
     241        ffi_tramp_set_parms (closure->ftramp, ffi_closure_win64_alt, closure);
     242        goto out;
     243      }
     244  #endif
     245  
     246    /* Initialize the dynamic trampoline. */
     247    memcpy (tramp, trampoline, sizeof(trampoline));
     248    *(UINT64 *)(tramp + sizeof (trampoline)) = (uintptr_t)ffi_closure_win64;
     249  
     250  out:
     251    closure->cif = cif;
     252    closure->fun = fun;
     253    closure->user_data = user_data;
     254  
     255    return FFI_OK;
     256  }
     257  
     258  #ifdef FFI_GO_CLOSURES
     259  ffi_status
     260  EFI64(ffi_prep_go_closure)(ffi_go_closure* closure, ffi_cif* cif,
     261  		     void (*fun)(ffi_cif*, void*, void**, void*))
     262  {
     263    switch (cif->abi)
     264      {
     265      case FFI_WIN64:
     266      case FFI_GNUW64:
     267        break;
     268      default:
     269        return FFI_BAD_ABI;
     270      }
     271  
     272    closure->tramp = ffi_go_closure_win64;
     273    closure->cif = cif;
     274    closure->fun = fun;
     275  
     276    return FFI_OK;
     277  }
     278  #endif
     279  
     280  struct win64_closure_frame
     281  {
     282    UINT64 rvalue[2];
     283    UINT64 fargs[4];
     284    UINT64 retaddr;
     285    UINT64 args[];
     286  };
     287  
     288  /* Force the inner function to use the MS ABI.  When compiling on win64
     289     this is a nop.  When compiling on unix, this simplifies the assembly,
     290     and places the burden of saving the extra call-saved registers on
     291     the compiler.  */
     292  int FFI_HIDDEN __attribute__((ms_abi))
     293  ffi_closure_win64_inner(ffi_cif *cif,
     294  			void (*fun)(ffi_cif*, void*, void**, void*),
     295  			void *user_data,
     296  			struct win64_closure_frame *frame)
     297  {
     298    void **avalue;
     299    void *rvalue;
     300    int i, n, nreg, flags;
     301  
     302    avalue = alloca(cif->nargs * sizeof(void *));
     303    rvalue = frame->rvalue;
     304    nreg = 0;
     305  
     306    /* When returning a structure, the address is in the first argument.
     307       We must also be prepared to return the same address in eax, so
     308       install that address in the frame and pretend we return a pointer.  */
     309    flags = cif->flags;
     310    if (flags == FFI_TYPE_STRUCT)
     311      {
     312        rvalue = (void *)(uintptr_t)frame->args[0];
     313        frame->rvalue[0] = frame->args[0];
     314        nreg = 1;
     315      }
     316  
     317    for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
     318      {
     319        size_t size = cif->arg_types[i]->size;
     320        size_t type = cif->arg_types[i]->type;
     321        void *a;
     322  
     323        if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
     324  	{
     325  	  if (nreg < 4)
     326  	    a = &frame->fargs[nreg];
     327  	  else
     328  	    a = &frame->args[nreg];
     329  	}
     330        else if (size == 1 || size == 2 || size == 4 || size == 8)
     331  	a = &frame->args[nreg];
     332        else
     333  	a = (void *)(uintptr_t)frame->args[nreg];
     334  
     335        avalue[i] = a;
     336      }
     337  
     338    /* Invoke the closure.  */
     339    fun (cif, rvalue, avalue, user_data);
     340    return flags;
     341  }
     342  
     343  #endif /* __x86_64__ */