github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/gofrontend/libffi/src/x86/ffiw64.c (about)

     1  /* -----------------------------------------------------------------------
     2     ffiw64.c - Copyright (c) 2014 Red Hat, Inc.
     3  
     4     x86 win64 Foreign Function Interface
     5  
     6     Permission is hereby granted, free of charge, to any person obtaining
     7     a copy of this software and associated documentation files (the
     8     ``Software''), to deal in the Software without restriction, including
     9     without limitation the rights to use, copy, modify, merge, publish,
    10     distribute, sublicense, and/or sell copies of the Software, and to
    11     permit persons to whom the Software is furnished to do so, subject to
    12     the following conditions:
    13  
    14     The above copyright notice and this permission notice shall be included
    15     in all copies or substantial portions of the Software.
    16  
    17     THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
    18     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    19     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    20     NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    21     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    22     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    23     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    24     DEALINGS IN THE SOFTWARE.
    25     ----------------------------------------------------------------------- */
    26  
    27  #include <ffi.h>
    28  #include <ffi_common.h>
    29  #include <stdlib.h>
    30  #include <stdint.h>
    31  
    32  #ifdef X86_WIN64
    33  
    34  struct win64_call_frame
    35  {
    36    UINT64 rbp;		/* 0 */
    37    UINT64 retaddr;	/* 8 */
    38    UINT64 fn;		/* 16 */
    39    UINT64 flags;		/* 24 */
    40    UINT64 rvalue;	/* 32 */
    41  };
    42  
    43  extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
    44  			    void *closure) FFI_HIDDEN;
    45  
    46  ffi_status
    47  ffi_prep_cif_machdep (ffi_cif *cif)
    48  {
    49    int flags, n;
    50  
    51    if (cif->abi != FFI_WIN64)
    52      return FFI_BAD_ABI;
    53  
    54    flags = cif->rtype->type;
    55    switch (flags)
    56      {
    57      default:
    58        break;
    59      case FFI_TYPE_LONGDOUBLE:
    60        flags = FFI_TYPE_STRUCT;
    61        break;
    62      case FFI_TYPE_COMPLEX:
    63        flags = FFI_TYPE_STRUCT;
    64        /* FALLTHRU */
    65      case FFI_TYPE_STRUCT:
    66        switch (cif->rtype->size)
    67  	{
    68  	case 8:
    69  	  flags = FFI_TYPE_UINT64;
    70  	  break;
    71  	case 4:
    72  	  flags = FFI_TYPE_SMALL_STRUCT_4B;
    73  	  break;
    74  	case 2:
    75  	  flags = FFI_TYPE_SMALL_STRUCT_2B;
    76  	  break;
    77  	case 1:
    78  	  flags = FFI_TYPE_SMALL_STRUCT_1B;
    79  	  break;
    80  	}
    81        break;
    82      }
    83    cif->flags = flags;
    84  
    85    /* Each argument either fits in a register, an 8 byte slot, or is
    86       passed by reference with the pointer in the 8 byte slot.  */
    87    n = cif->nargs;
    88    n += (flags == FFI_TYPE_STRUCT);
    89    if (n < 4)
    90      n = 4;
    91    cif->bytes = n * 8;
    92  
    93    return FFI_OK;
    94  }
    95  
    96  static void
    97  ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
    98  	      void **avalue, void *closure)
    99  {
   100    int i, j, n, flags;
   101    UINT64 *stack;
   102    size_t rsize;
   103    struct win64_call_frame *frame;
   104  
   105    FFI_ASSERT(cif->abi == FFI_WIN64);
   106  
   107    flags = cif->flags;
   108    rsize = 0;
   109  
   110    /* If we have no return value for a structure, we need to create one.
   111       Otherwise we can ignore the return type entirely.  */
   112    if (rvalue == NULL)
   113      {
   114        if (flags == FFI_TYPE_STRUCT)
   115  	rsize = cif->rtype->size;
   116        else
   117  	flags = FFI_TYPE_VOID;
   118      }
   119  
   120    stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
   121    frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
   122    if (rsize)
   123      rvalue = frame + 1;
   124  
   125    frame->fn = (uintptr_t)fn;
   126    frame->flags = flags;
   127    frame->rvalue = (uintptr_t)rvalue;
   128  
   129    j = 0;
   130    if (flags == FFI_TYPE_STRUCT)
   131      {
   132        stack[0] = (uintptr_t)rvalue;
   133        j = 1;
   134      }
   135  
   136    for (i = 0, n = cif->nargs; i < n; ++i, ++j)
   137      {
   138        switch (cif->arg_types[i]->size)
   139  	{
   140  	case 8:
   141  	  stack[j] = *(UINT64 *)avalue[i];
   142  	  break;
   143  	case 4:
   144  	  stack[j] = *(UINT32 *)avalue[i];
   145  	  break;
   146  	case 2:
   147  	  stack[j] = *(UINT16 *)avalue[i];
   148  	  break;
   149  	case 1:
   150  	  stack[j] = *(UINT8 *)avalue[i];
   151  	  break;
   152  	default:
   153  	  stack[j] = (uintptr_t)avalue[i];
   154  	  break;
   155  	}
   156      }
   157  
   158    ffi_call_win64 (stack, frame, closure);
   159  }
   160  
   161  void
   162  ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
   163  {
   164    ffi_call_int (cif, fn, rvalue, avalue, NULL);
   165  }
   166  
   167  void
   168  ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
   169  	     void **avalue, void *closure)
   170  {
   171    ffi_call_int (cif, fn, rvalue, avalue, closure);
   172  }
   173  
   174  
   175  extern void ffi_closure_win64(void) FFI_HIDDEN;
   176  extern void ffi_go_closure_win64(void) FFI_HIDDEN;
   177  
   178  ffi_status
   179  ffi_prep_closure_loc (ffi_closure* closure,
   180  		      ffi_cif* cif,
   181  		      void (*fun)(ffi_cif*, void*, void**, void*),
   182  		      void *user_data,
   183  		      void *codeloc)
   184  {
   185    static const unsigned char trampoline[16] = {
   186      /* leaq  -0x7(%rip),%r10   # 0x0  */
   187      0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff,
   188      /* jmpq  *0x3(%rip)        # 0x10 */
   189      0xff, 0x25, 0x03, 0x00, 0x00, 0x00,
   190      /* nopl  (%rax) */
   191      0x0f, 0x1f, 0x00
   192    };
   193    unsigned char *tramp = closure->tramp;
   194  
   195    if (cif->abi != FFI_WIN64)
   196      return FFI_BAD_ABI;
   197  
   198    memcpy (tramp, trampoline, sizeof(trampoline));
   199    *(UINT64 *)(tramp + 16) = (uintptr_t)ffi_closure_win64;
   200  
   201    closure->cif = cif;
   202    closure->fun = fun;
   203    closure->user_data = user_data;
   204  
   205    return FFI_OK;
   206  }
   207  
   208  ffi_status
   209  ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
   210  		     void (*fun)(ffi_cif*, void*, void**, void*))
   211  {
   212    if (cif->abi != FFI_WIN64)
   213      return FFI_BAD_ABI;
   214  
   215    closure->tramp = ffi_go_closure_win64;
   216    closure->cif = cif;
   217    closure->fun = fun;
   218  
   219    return FFI_OK;
   220  }
   221  
   222  struct win64_closure_frame
   223  {
   224    UINT64 rvalue[2];
   225    UINT64 fargs[4];
   226    UINT64 retaddr;
   227    UINT64 args[];
   228  };
   229  
   230  int FFI_HIDDEN
   231  ffi_closure_win64_inner(ffi_cif *cif,
   232  			void (*fun)(ffi_cif*, void*, void**, void*),
   233  			void *user_data,
   234  			struct win64_closure_frame *frame)
   235  {
   236    void **avalue;
   237    void *rvalue;
   238    int i, n, nreg, flags;
   239  
   240    avalue = alloca(cif->nargs * sizeof(void *));
   241    rvalue = frame->rvalue;
   242    nreg = 0;
   243  
   244    /* When returning a structure, the address is in the first argument.
   245       We must also be prepared to return the same address in eax, so
   246       install that address in the frame and pretend we return a pointer.  */
   247    flags = cif->flags;
   248    if (flags == FFI_TYPE_STRUCT)
   249      {
   250        rvalue = (void *)(uintptr_t)frame->args[0];
   251        frame->rvalue[0] = frame->args[0];
   252        nreg = 1;
   253      }
   254  
   255    for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
   256      {
   257        size_t size = cif->arg_types[i]->size;
   258        size_t type = cif->arg_types[i]->type;
   259        void *a;
   260  
   261        if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
   262  	{
   263  	  if (nreg < 4)
   264  	    a = &frame->fargs[nreg];
   265  	  else
   266  	    a = &frame->args[nreg];
   267  	}
   268        else if (size == 1 || size == 2 || size == 4 || size == 8)
   269  	a = &frame->args[nreg];
   270        else
   271  	a = (void *)(uintptr_t)frame->args[nreg];
   272  
   273        avalue[i] = a;
   274      }
   275  
   276    /* Invoke the closure.  */
   277    fun (cif, rvalue, avalue, user_data);
   278    return flags;
   279  }
   280  
   281  #endif /* X86_WIN64 */