]>
Commit | Line | Data |
---|---|---|
63e5e3e0 | 1 | /* ----------------------------------------------------------------------- |
eb3c46a1 RM |
2 | ffi.c - Copyright (c) 1996, 1998, 1999, 2001 Red Hat, Inc. |
3 | Copyright (c) 2002 Ranjit Mathew | |
4 | Copyright (c) 2002 Bo Thorsen | |
5 | Copyright (c) 2002 Roger Sayle | |
63e5e3e0 AG |
6 | |
7 | x86 Foreign Function Interface | |
8 | ||
63e5e3e0 AG |
9 | Permission is hereby granted, free of charge, to any person obtaining |
10 | a copy of this software and associated documentation files (the | |
11 | ``Software''), to deal in the Software without restriction, including | |
12 | without limitation the rights to use, copy, modify, merge, publish, | |
13 | distribute, sublicense, and/or sell copies of the Software, and to | |
14 | permit persons to whom the Software is furnished to do so, subject to | |
15 | the following conditions: | |
16 | ||
17 | The above copyright notice and this permission notice shall be included | |
18 | in all copies or substantial portions of the Software. | |
19 | ||
20 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
21 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
23 | IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
24 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
25 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
26 | OTHER DEALINGS IN THE SOFTWARE. | |
27 | ----------------------------------------------------------------------- */ | |
28 | ||
c94974ca BT |
29 | #ifndef __x86_64__ |
30 | ||
63e5e3e0 AG |
31 | #include <ffi.h> |
32 | #include <ffi_common.h> | |
33 | ||
34 | #include <stdlib.h> | |
35 | ||
36 | /* ffi_prep_args is called by the assembly routine once stack space | |
37 | has been allocated for the function's arguments */ | |
38 | ||
63e5e3e0 | 39 | void ffi_prep_args(char *stack, extended_cif *ecif) |
63e5e3e0 AG |
40 | { |
41 | register unsigned int i; | |
63e5e3e0 AG |
42 | register void **p_argv; |
43 | register char *argp; | |
44 | register ffi_type **p_arg; | |
45 | ||
63e5e3e0 AG |
46 | argp = stack; |
47 | ||
09cababc | 48 | if (ecif->cif->flags == FFI_TYPE_STRUCT) |
b86aa7b0 BT |
49 | { |
50 | *(void **) argp = ecif->rvalue; | |
51 | argp += 4; | |
52 | } | |
63e5e3e0 | 53 | |
63e5e3e0 AG |
54 | p_argv = ecif->avalue; |
55 | ||
56 | for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; | |
ee4586c5 | 57 | i != 0; |
63e5e3e0 AG |
58 | i--, p_arg++) |
59 | { | |
60 | size_t z; | |
61 | ||
62 | /* Align if necessary */ | |
2c4ed456 HY |
63 | if ((sizeof(int) - 1) & (unsigned) argp) |
64 | argp = (char *) ALIGN(argp, sizeof(int)); | |
63e5e3e0 | 65 | |
b86aa7b0 BT |
66 | z = (*p_arg)->size; |
67 | if (z < sizeof(int)) | |
63e5e3e0 | 68 | { |
b86aa7b0 BT |
69 | z = sizeof(int); |
70 | switch ((*p_arg)->type) | |
63e5e3e0 | 71 | { |
b86aa7b0 BT |
72 | case FFI_TYPE_SINT8: |
73 | *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv); | |
74 | break; | |
75 | ||
76 | case FFI_TYPE_UINT8: | |
77 | *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv); | |
78 | break; | |
79 | ||
80 | case FFI_TYPE_SINT16: | |
81 | *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv); | |
82 | break; | |
83 | ||
84 | case FFI_TYPE_UINT16: | |
85 | *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv); | |
86 | break; | |
87 | ||
88 | case FFI_TYPE_SINT32: | |
89 | *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv); | |
90 | break; | |
91 | ||
92 | case FFI_TYPE_UINT32: | |
93 | *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); | |
94 | break; | |
95 | ||
96 | case FFI_TYPE_STRUCT: | |
97 | *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); | |
98 | break; | |
99 | ||
100 | default: | |
101 | FFI_ASSERT(0); | |
63e5e3e0 | 102 | } |
63e5e3e0 | 103 | } |
b86aa7b0 BT |
104 | else |
105 | { | |
106 | memcpy(argp, *p_argv, z); | |
107 | } | |
108 | p_argv++; | |
109 | argp += z; | |
63e5e3e0 AG |
110 | } |
111 | ||
112 | return; | |
113 | } | |
114 | ||
115 | /* Perform machine dependent cif processing */ | |
116 | ffi_status ffi_prep_cif_machdep(ffi_cif *cif) | |
117 | { | |
118 | /* Set the return type flag */ | |
119 | switch (cif->rtype->type) | |
120 | { | |
121 | case FFI_TYPE_VOID: | |
a3bd50cc | 122 | #ifdef X86 |
63e5e3e0 | 123 | case FFI_TYPE_STRUCT: |
09cababc | 124 | #endif |
63e5e3e0 AG |
125 | case FFI_TYPE_SINT64: |
126 | case FFI_TYPE_FLOAT: | |
127 | case FFI_TYPE_DOUBLE: | |
128 | case FFI_TYPE_LONGDOUBLE: | |
129 | cif->flags = (unsigned) cif->rtype->type; | |
130 | break; | |
131 | ||
132 | case FFI_TYPE_UINT64: | |
133 | cif->flags = FFI_TYPE_SINT64; | |
134 | break; | |
135 | ||
a3bd50cc | 136 | #ifndef X86 |
09cababc HY |
137 | case FFI_TYPE_STRUCT: |
138 | if (cif->rtype->size == 1) | |
139 | { | |
140 | cif->flags = FFI_TYPE_SINT8; /* same as char size */ | |
141 | } | |
142 | else if (cif->rtype->size == 2) | |
143 | { | |
144 | cif->flags = FFI_TYPE_SINT16; /* same as short size */ | |
145 | } | |
146 | else if (cif->rtype->size == 4) | |
147 | { | |
148 | cif->flags = FFI_TYPE_INT; /* same as int type */ | |
149 | } | |
150 | else if (cif->rtype->size == 8) | |
151 | { | |
152 | cif->flags = FFI_TYPE_SINT64; /* same as int64 type */ | |
153 | } | |
154 | else | |
155 | { | |
156 | cif->flags = FFI_TYPE_STRUCT; | |
157 | } | |
158 | break; | |
159 | #endif | |
160 | ||
63e5e3e0 AG |
161 | default: |
162 | cif->flags = FFI_TYPE_INT; | |
163 | break; | |
164 | } | |
165 | ||
a3bd50cc PB |
166 | #ifdef X86_DARWIN |
167 | cif->bytes = (cif->bytes + 15) & ~0xF; | |
168 | #endif | |
169 | ||
63e5e3e0 AG |
170 | return FFI_OK; |
171 | } | |
172 | ||
ac6ed182 AT |
173 | extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, |
174 | unsigned, unsigned, unsigned *, void (*fn)()); | |
63e5e3e0 | 175 | |
eb3c46a1 | 176 | #ifdef X86_WIN32 |
ac6ed182 AT |
177 | extern void ffi_call_STDCALL(void (*)(char *, extended_cif *), extended_cif *, |
178 | unsigned, unsigned, unsigned *, void (*fn)()); | |
179 | ||
eb3c46a1 RM |
180 | #endif /* X86_WIN32 */ |
181 | ||
ac6ed182 | 182 | void ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue) |
63e5e3e0 AG |
183 | { |
184 | extended_cif ecif; | |
185 | ||
186 | ecif.cif = cif; | |
187 | ecif.avalue = avalue; | |
188 | ||
189 | /* If the return value is a struct and we don't have a return */ | |
190 | /* value address then we need to make one */ | |
191 | ||
192 | if ((rvalue == NULL) && | |
09cababc | 193 | (cif->flags == FFI_TYPE_STRUCT)) |
63e5e3e0 | 194 | { |
63e5e3e0 | 195 | ecif.rvalue = alloca(cif->rtype->size); |
63e5e3e0 AG |
196 | } |
197 | else | |
198 | ecif.rvalue = rvalue; | |
199 | ||
200 | ||
201 | switch (cif->abi) | |
202 | { | |
203 | case FFI_SYSV: | |
ac6ed182 AT |
204 | ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, |
205 | fn); | |
63e5e3e0 | 206 | break; |
eb3c46a1 RM |
207 | #ifdef X86_WIN32 |
208 | case FFI_STDCALL: | |
ac6ed182 AT |
209 | ffi_call_STDCALL(ffi_prep_args, &ecif, cif->bytes, cif->flags, |
210 | ecif.rvalue, fn); | |
eb3c46a1 RM |
211 | break; |
212 | #endif /* X86_WIN32 */ | |
63e5e3e0 AG |
213 | default: |
214 | FFI_ASSERT(0); | |
215 | break; | |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | /** private members **/ | |
221 | ||
222 | static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, | |
223 | void** args, ffi_cif* cif); | |
8a42356f | 224 | void FFI_HIDDEN ffi_closure_SYSV (ffi_closure *) |
cd41c847 | 225 | __attribute__ ((regparm(1))); |
8a42356f JJ |
226 | unsigned int FFI_HIDDEN ffi_closure_SYSV_inner (ffi_closure *, void **, void *) |
227 | __attribute__ ((regparm(1))); | |
228 | void FFI_HIDDEN ffi_closure_raw_SYSV (ffi_raw_closure *) | |
cd41c847 | 229 | __attribute__ ((regparm(1))); |
63e5e3e0 | 230 | |
cd41c847 | 231 | /* This function is jumped to by the trampoline */ |
63e5e3e0 | 232 | |
8a42356f JJ |
233 | unsigned int FFI_HIDDEN |
234 | ffi_closure_SYSV_inner (closure, respp, args) | |
cd41c847 | 235 | ffi_closure *closure; |
8a42356f JJ |
236 | void **respp; |
237 | void *args; | |
63e5e3e0 | 238 | { |
63e5e3e0 | 239 | // our various things... |
63e5e3e0 AG |
240 | ffi_cif *cif; |
241 | void **arg_area; | |
63e5e3e0 | 242 | |
63e5e3e0 AG |
243 | cif = closure->cif; |
244 | arg_area = (void**) alloca (cif->nargs * sizeof (void*)); | |
63e5e3e0 AG |
245 | |
246 | /* this call will initialize ARG_AREA, such that each | |
247 | * element in that array points to the corresponding | |
248 | * value on the stack; and if the function returns | |
249 | * a structure, it will re-set RESP to point to the | |
250 | * structure return address. */ | |
251 | ||
8a42356f | 252 | ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); |
63e5e3e0 | 253 | |
8a42356f | 254 | (closure->fun) (cif, *respp, arg_area, closure->user_data); |
63e5e3e0 | 255 | |
8a42356f | 256 | return cif->flags; |
63e5e3e0 AG |
257 | } |
258 | ||
ac6ed182 AT |
259 | static void |
260 | ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue, | |
261 | ffi_cif *cif) | |
63e5e3e0 AG |
262 | { |
263 | register unsigned int i; | |
63e5e3e0 AG |
264 | register void **p_argv; |
265 | register char *argp; | |
266 | register ffi_type **p_arg; | |
267 | ||
63e5e3e0 AG |
268 | argp = stack; |
269 | ||
09cababc | 270 | if ( cif->flags == FFI_TYPE_STRUCT ) { |
63e5e3e0 AG |
271 | *rvalue = *(void **) argp; |
272 | argp += 4; | |
273 | } | |
274 | ||
63e5e3e0 AG |
275 | p_argv = avalue; |
276 | ||
4075db8f | 277 | for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) |
63e5e3e0 AG |
278 | { |
279 | size_t z; | |
280 | ||
281 | /* Align if necessary */ | |
2c4ed456 HY |
282 | if ((sizeof(int) - 1) & (unsigned) argp) { |
283 | argp = (char *) ALIGN(argp, sizeof(int)); | |
63e5e3e0 AG |
284 | } |
285 | ||
4075db8f | 286 | z = (*p_arg)->size; |
63e5e3e0 | 287 | |
4075db8f | 288 | /* because we're little endian, this is what it turns into. */ |
63e5e3e0 | 289 | |
4075db8f | 290 | *p_argv = (void*) argp; |
63e5e3e0 | 291 | |
4075db8f BT |
292 | p_argv++; |
293 | argp += z; | |
63e5e3e0 AG |
294 | } |
295 | ||
296 | return; | |
297 | } | |
298 | ||
299 | /* How to make a trampoline. Derived from gcc/config/i386/i386.c. */ | |
300 | ||
301 | #define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX) \ | |
302 | ({ unsigned char *__tramp = (unsigned char*)(TRAMP); \ | |
303 | unsigned int __fun = (unsigned int)(FUN); \ | |
304 | unsigned int __ctx = (unsigned int)(CTX); \ | |
cd41c847 ZD |
305 | unsigned int __dis = __fun - ((unsigned int) __tramp + FFI_TRAMPOLINE_SIZE); \ |
306 | *(unsigned char*) &__tramp[0] = 0xb8; \ | |
307 | *(unsigned int*) &__tramp[1] = __ctx; /* movl __ctx, %eax */ \ | |
308 | *(unsigned char *) &__tramp[5] = 0xe9; \ | |
309 | *(unsigned int*) &__tramp[6] = __dis; /* jmp __fun */ \ | |
63e5e3e0 AG |
310 | }) |
311 | ||
312 | ||
313 | /* the cif must already be prep'ed */ | |
314 | ||
315 | ffi_status | |
316 | ffi_prep_closure (ffi_closure* closure, | |
317 | ffi_cif* cif, | |
318 | void (*fun)(ffi_cif*,void*,void**,void*), | |
319 | void *user_data) | |
320 | { | |
321 | FFI_ASSERT (cif->abi == FFI_SYSV); | |
322 | ||
323 | FFI_INIT_TRAMPOLINE (&closure->tramp[0], \ | |
324 | &ffi_closure_SYSV, \ | |
325 | (void*)closure); | |
326 | ||
327 | closure->cif = cif; | |
328 | closure->user_data = user_data; | |
329 | closure->fun = fun; | |
330 | ||
331 | return FFI_OK; | |
332 | } | |
333 | ||
334 | /* ------- Native raw API support -------------------------------- */ | |
335 | ||
336 | #if !FFI_NO_RAW_API | |
337 | ||
63e5e3e0 AG |
338 | ffi_status |
339 | ffi_prep_raw_closure (ffi_raw_closure* closure, | |
340 | ffi_cif* cif, | |
341 | void (*fun)(ffi_cif*,void*,ffi_raw*,void*), | |
342 | void *user_data) | |
343 | { | |
344 | int i; | |
345 | ||
346 | FFI_ASSERT (cif->abi == FFI_SYSV); | |
347 | ||
348 | // we currently don't support certain kinds of arguments for raw | |
cc712abf | 349 | // closures. This should be implemented by a separate assembly language |
63e5e3e0 AG |
350 | // routine, since it would require argument processing, something we |
351 | // don't do now for performance. | |
352 | ||
353 | for (i = cif->nargs-1; i >= 0; i--) | |
354 | { | |
355 | FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_STRUCT); | |
356 | FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_LONGDOUBLE); | |
357 | } | |
358 | ||
359 | ||
360 | FFI_INIT_TRAMPOLINE (&closure->tramp[0], &ffi_closure_raw_SYSV, | |
361 | (void*)closure); | |
362 | ||
363 | closure->cif = cif; | |
364 | closure->user_data = user_data; | |
365 | closure->fun = fun; | |
366 | ||
367 | return FFI_OK; | |
368 | } | |
369 | ||
370 | static void | |
371 | ffi_prep_args_raw(char *stack, extended_cif *ecif) | |
372 | { | |
373 | memcpy (stack, ecif->avalue, ecif->cif->bytes); | |
374 | } | |
375 | ||
376 | /* we borrow this routine from libffi (it must be changed, though, to | |
377 | * actually call the function passed in the first argument. as of | |
378 | * libffi-1.20, this is not the case.) | |
379 | */ | |
380 | ||
ac6ed182 AT |
381 | extern void |
382 | ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, unsigned, | |
383 | unsigned, unsigned *, void (*fn)()); | |
63e5e3e0 | 384 | |
eb3c46a1 RM |
385 | #ifdef X86_WIN32 |
386 | extern void | |
ac6ed182 AT |
387 | ffi_call_STDCALL(void (*)(char *, extended_cif *), extended_cif *, unsigned, |
388 | unsigned, unsigned *, void (*fn)()); | |
eb3c46a1 RM |
389 | #endif /* X86_WIN32 */ |
390 | ||
63e5e3e0 | 391 | void |
ac6ed182 | 392 | ffi_raw_call(ffi_cif *cif, void (*fn)(), void *rvalue, ffi_raw *fake_avalue) |
63e5e3e0 AG |
393 | { |
394 | extended_cif ecif; | |
395 | void **avalue = (void **)fake_avalue; | |
396 | ||
397 | ecif.cif = cif; | |
398 | ecif.avalue = avalue; | |
399 | ||
400 | /* If the return value is a struct and we don't have a return */ | |
401 | /* value address then we need to make one */ | |
402 | ||
403 | if ((rvalue == NULL) && | |
404 | (cif->rtype->type == FFI_TYPE_STRUCT)) | |
405 | { | |
63e5e3e0 | 406 | ecif.rvalue = alloca(cif->rtype->size); |
63e5e3e0 AG |
407 | } |
408 | else | |
409 | ecif.rvalue = rvalue; | |
410 | ||
411 | ||
412 | switch (cif->abi) | |
413 | { | |
414 | case FFI_SYSV: | |
ac6ed182 AT |
415 | ffi_call_SYSV(ffi_prep_args_raw, &ecif, cif->bytes, cif->flags, |
416 | ecif.rvalue, fn); | |
63e5e3e0 | 417 | break; |
eb3c46a1 RM |
418 | #ifdef X86_WIN32 |
419 | case FFI_STDCALL: | |
ac6ed182 AT |
420 | ffi_call_STDCALL(ffi_prep_args_raw, &ecif, cif->bytes, cif->flags, |
421 | ecif.rvalue, fn); | |
eb3c46a1 RM |
422 | break; |
423 | #endif /* X86_WIN32 */ | |
63e5e3e0 AG |
424 | default: |
425 | FFI_ASSERT(0); | |
426 | break; | |
427 | } | |
428 | } | |
429 | ||
430 | #endif | |
c94974ca BT |
431 | |
432 | #endif /* __x86_64__ */ |