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 */