OSDN Git Service

77193250311bbf98292a052417414bca71bfb713
[pf3gnuchains/gcc-fork.git] / libffi / src / x86 / ffi.c
1 /* -----------------------------------------------------------------------
2    ffi.c - Copyright (c) 1996, 1998, 1999  Cygnus Solutions
3    
4    x86 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, EXPRESS
18    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20    IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23    OTHER DEALINGS IN THE SOFTWARE.
24    ----------------------------------------------------------------------- */
25
26 #ifndef __x86_64__
27
28 #include <ffi.h>
29 #include <ffi_common.h>
30
31 #include <stdlib.h>
32
33 /* ffi_prep_args is called by the assembly routine once stack space
34    has been allocated for the function's arguments */
35
36 /*@-exportheader@*/
37 void ffi_prep_args(char *stack, extended_cif *ecif)
38 /*@=exportheader@*/
39 {
40   register unsigned int i;
41   register void **p_argv;
42   register char *argp;
43   register ffi_type **p_arg;
44
45   argp = stack;
46
47   if (ecif->cif->rtype->type == FFI_TYPE_STRUCT)
48     {
49       *(void **) argp = ecif->rvalue;
50       argp += 4;
51     }
52
53   p_argv = ecif->avalue;
54
55   for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
56        i != 0;
57        i--, p_arg++)
58     {
59       size_t z;
60
61       /* Align if necessary */
62       if (((*p_arg)->alignment - 1) & (unsigned) argp)
63         argp = (char *) ALIGN(argp, (*p_arg)->alignment);
64
65       z = (*p_arg)->size;
66       if (z < sizeof(int))
67         {
68           z = sizeof(int);
69           switch ((*p_arg)->type)
70             {
71             case FFI_TYPE_SINT8:
72               *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
73               break;
74
75             case FFI_TYPE_UINT8:
76               *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
77               break;
78
79             case FFI_TYPE_SINT16:
80               *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
81               break;
82
83             case FFI_TYPE_UINT16:
84               *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
85               break;
86
87             case FFI_TYPE_SINT32:
88               *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv);
89               break;
90
91             case FFI_TYPE_UINT32:
92               *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
93               break;
94
95             case FFI_TYPE_STRUCT:
96               *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
97               break;
98
99             default:
100               FFI_ASSERT(0);
101             }
102         }
103       else
104         {
105           memcpy(argp, *p_argv, z);
106         }
107       p_argv++;
108       argp += z;
109     }
110   
111   return;
112 }
113
114 /* Perform machine dependent cif processing */
115 ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
116 {
117   /* Set the return type flag */
118   switch (cif->rtype->type)
119     {
120     case FFI_TYPE_VOID:
121     case FFI_TYPE_STRUCT:
122     case FFI_TYPE_SINT64:
123     case FFI_TYPE_FLOAT:
124     case FFI_TYPE_DOUBLE:
125     case FFI_TYPE_LONGDOUBLE:
126       cif->flags = (unsigned) cif->rtype->type;
127       break;
128
129     case FFI_TYPE_UINT64:
130       cif->flags = FFI_TYPE_SINT64;
131       break;
132
133     default:
134       cif->flags = FFI_TYPE_INT;
135       break;
136     }
137
138   return FFI_OK;
139 }
140
141 /*@-declundef@*/
142 /*@-exportheader@*/
143 extern void ffi_call_SYSV(void (*)(char *, extended_cif *), 
144                           /*@out@*/ extended_cif *, 
145                           unsigned, unsigned, 
146                           /*@out@*/ unsigned *, 
147                           void (*fn)());
148 /*@=declundef@*/
149 /*@=exportheader@*/
150
151 void ffi_call(/*@dependent@*/ ffi_cif *cif, 
152               void (*fn)(), 
153               /*@out@*/ void *rvalue, 
154               /*@dependent@*/ void **avalue)
155 {
156   extended_cif ecif;
157
158   ecif.cif = cif;
159   ecif.avalue = avalue;
160   
161   /* If the return value is a struct and we don't have a return */
162   /* value address then we need to make one                     */
163
164   if ((rvalue == NULL) && 
165       (cif->rtype->type == FFI_TYPE_STRUCT))
166     {
167       /*@-sysunrecog@*/
168       ecif.rvalue = alloca(cif->rtype->size);
169       /*@=sysunrecog@*/
170     }
171   else
172     ecif.rvalue = rvalue;
173     
174   
175   switch (cif->abi) 
176     {
177     case FFI_SYSV:
178       /*@-usedef@*/
179       ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, 
180                     cif->flags, ecif.rvalue, fn);
181       /*@=usedef@*/
182       break;
183     default:
184       FFI_ASSERT(0);
185       break;
186     }
187 }
188
189
190 /** private members **/
191
192 static void ffi_prep_incoming_args_SYSV (char *stack, void **ret,
193                                          void** args, ffi_cif* cif);
194 static void ffi_closure_SYSV ();
195 static void ffi_closure_raw_SYSV ();
196
197 /* This function is jumped to by the trampoline, on entry, %ecx (a
198  * caller-save register) holds the address of the closure.  
199  * Clearly, this requires __GNUC__, so perhaps we should translate this
200  * into an assembly file if this is to be distributed with ffi.
201  */
202
203 static void
204 ffi_closure_SYSV ()
205 {
206   // this is our return value storage
207   long double    res;
208
209   // our various things...
210   void          *args;
211   ffi_cif       *cif;
212   void         **arg_area;
213   ffi_closure   *closure;
214   unsigned short rtype;
215   void          *resp = (void*)&res;
216
217   /* grab the trampoline context pointer */
218   asm ("movl %%ecx,%0" : "=r" (closure));
219   
220   cif         = closure->cif;
221   arg_area    = (void**) alloca (cif->nargs * sizeof (void*));  
222   asm ("leal 8(%%ebp),%0" : "=q" (args));  
223
224   /* this call will initialize ARG_AREA, such that each
225    * element in that array points to the corresponding 
226    * value on the stack; and if the function returns
227    * a structure, it will re-set RESP to point to the
228    * structure return address.  */
229
230   ffi_prep_incoming_args_SYSV(args, (void**)&resp, arg_area, cif);
231   
232   (closure->fun) (cif, resp, arg_area, closure->user_data);
233
234   rtype = cif->flags;
235
236   /* now, do a generic return based on the value of rtype */
237   if (rtype == FFI_TYPE_INT)
238     {
239       asm ("movl (%0),%%eax" : : "r" (resp) : "eax");
240     }
241   else if (rtype == FFI_TYPE_FLOAT)
242     {
243       asm ("flds (%0)" : : "r" (resp) : "st" );
244     }
245   else if (rtype == FFI_TYPE_DOUBLE)
246     {
247       asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
248     }
249   else if (rtype == FFI_TYPE_LONGDOUBLE)
250     {
251       asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
252     }
253   else if (rtype == FFI_TYPE_SINT64)
254     {
255       asm ("movl 0(%0),%%eax;"
256            "movl 4(%0),%%edx" 
257            : : "r"(resp)
258            : "eax", "edx");
259     }
260 }
261
262 /*@-exportheader@*/
263 static void 
264 ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
265                             void **avalue, ffi_cif *cif)
266 /*@=exportheader@*/
267 {
268   register unsigned int i;
269   register void **p_argv;
270   register char *argp;
271   register ffi_type **p_arg;
272
273   argp = stack;
274
275   if ( cif->rtype->type == FFI_TYPE_STRUCT ) {
276     *rvalue = *(void **) argp;
277     argp += 4;
278   }
279
280   p_argv = avalue;
281
282   for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
283     {
284       size_t z;
285
286       /* Align if necessary */
287       if (((*p_arg)->alignment - 1) & (unsigned) argp) {
288         argp = (char *) ALIGN(argp, (*p_arg)->alignment);
289       }
290
291       z = (*p_arg)->size;
292
293       /* because we're little endian, this is what it turns into.   */
294
295       *p_argv = (void*) argp;
296
297       p_argv++;
298       argp += z;
299     }
300   
301   return;
302 }
303
304 /* How to make a trampoline.  Derived from gcc/config/i386/i386.c. */
305
306 #define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX) \
307 ({ unsigned char *__tramp = (unsigned char*)(TRAMP); \
308    unsigned int  __fun = (unsigned int)(FUN); \
309    unsigned int  __ctx = (unsigned int)(CTX); \
310    unsigned int  __dis = __fun - ((unsigned int) __tramp + 10); \
311    *(unsigned char*) &__tramp[0] = 0xb9; \
312    *(unsigned int*)  &__tramp[1] = __ctx; \
313    *(unsigned char*) &__tramp[5] = 0xe9; \
314    *(unsigned int*)  &__tramp[6] = __dis; \
315  })
316
317
318 /* the cif must already be prep'ed */
319
320 ffi_status
321 ffi_prep_closure (ffi_closure* closure,
322                   ffi_cif* cif,
323                   void (*fun)(ffi_cif*,void*,void**,void*),
324                   void *user_data)
325 {
326   FFI_ASSERT (cif->abi == FFI_SYSV);
327
328   FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
329                        &ffi_closure_SYSV,  \
330                        (void*)closure);
331     
332   closure->cif  = cif;
333   closure->user_data = user_data;
334   closure->fun  = fun;
335
336   return FFI_OK;
337 }
338
339 /* ------- Native raw API support -------------------------------- */
340
341 #if !FFI_NO_RAW_API
342
343 static void
344 ffi_closure_raw_SYSV ()
345 {
346   // this is our return value storage
347   long double    res;
348
349   // our various things...
350   void            *args;
351   ffi_raw         *raw_args;
352   ffi_cif         *cif;
353   ffi_raw_closure *closure;
354   unsigned short   rtype;
355   void            *resp = (void*)&res;
356
357   /* grab the trampoline context pointer */
358   asm ("movl %%ecx,%0" : "=r" (closure));
359
360   /* take the argument pointer */
361   asm ("leal 8(%%ebp),%0" : "=q" (args));  
362
363   /* get the cif */
364   cif = closure->cif;
365
366   /* the SYSV/X86 abi matches the RAW API exactly, well.. almost */
367   raw_args = (ffi_raw*) args;
368
369   (closure->fun) (cif, resp, raw_args, closure->user_data);
370
371   rtype = cif->flags;
372
373   /* now, do a generic return based on the value of rtype */
374   if (rtype == FFI_TYPE_INT)
375     {
376       asm ("movl (%0),%%eax" : : "r" (resp) : "eax");
377     }
378   else if (rtype == FFI_TYPE_FLOAT)
379     {
380       asm ("flds (%0)" : : "r" (resp) : "st" );
381     }
382   else if (rtype == FFI_TYPE_DOUBLE)
383     {
384       asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
385     }
386   else if (rtype == FFI_TYPE_LONGDOUBLE)
387     {
388       asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
389     }
390   else if (rtype == FFI_TYPE_SINT64)
391     {
392       asm ("movl 0(%0),%%eax; movl 4(%0),%%edx" 
393            : : "r"(resp)
394            : "eax", "edx");
395     }
396 }
397
398  
399
400
401 ffi_status
402 ffi_prep_raw_closure (ffi_raw_closure* closure,
403                       ffi_cif* cif,
404                       void (*fun)(ffi_cif*,void*,ffi_raw*,void*),
405                       void *user_data)
406 {
407   int i;
408
409   FFI_ASSERT (cif->abi == FFI_SYSV);
410
411   // we currently don't support certain kinds of arguments for raw
412   // closures.  This should be implemented by a separate assembly language
413   // routine, since it would require argument processing, something we
414   // don't do now for performance.
415
416   for (i = cif->nargs-1; i >= 0; i--)
417     {
418       FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_STRUCT);
419       FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_LONGDOUBLE);
420     }
421   
422
423   FFI_INIT_TRAMPOLINE (&closure->tramp[0], &ffi_closure_raw_SYSV,
424                        (void*)closure);
425     
426   closure->cif  = cif;
427   closure->user_data = user_data;
428   closure->fun  = fun;
429
430   return FFI_OK;
431 }
432
433 static void 
434 ffi_prep_args_raw(char *stack, extended_cif *ecif)
435 {
436   memcpy (stack, ecif->avalue, ecif->cif->bytes);
437 }
438
439 /* we borrow this routine from libffi (it must be changed, though, to
440  * actually call the function passed in the first argument.  as of
441  * libffi-1.20, this is not the case.)
442  */
443
444 extern void 
445 ffi_call_SYSV(void (*)(char *, extended_cif *), 
446               /*@out@*/ extended_cif *, 
447               unsigned, unsigned, 
448               /*@out@*/ unsigned *, 
449               void (*fn)());
450
451 void
452 ffi_raw_call(/*@dependent@*/ ffi_cif *cif, 
453              void (*fn)(), 
454              /*@out@*/ void *rvalue, 
455              /*@dependent@*/ ffi_raw *fake_avalue)
456 {
457   extended_cif ecif;
458   void **avalue = (void **)fake_avalue;
459
460   ecif.cif = cif;
461   ecif.avalue = avalue;
462   
463   /* If the return value is a struct and we don't have a return */
464   /* value address then we need to make one                     */
465
466   if ((rvalue == NULL) && 
467       (cif->rtype->type == FFI_TYPE_STRUCT))
468     {
469       /*@-sysunrecog@*/
470       ecif.rvalue = alloca(cif->rtype->size);
471       /*@=sysunrecog@*/
472     }
473   else
474     ecif.rvalue = rvalue;
475     
476   
477   switch (cif->abi) 
478     {
479     case FFI_SYSV:
480       /*@-usedef@*/
481       ffi_call_SYSV(ffi_prep_args_raw, &ecif, cif->bytes, 
482                     cif->flags, ecif.rvalue, fn);
483       /*@=usedef@*/
484       break;
485     default:
486       FFI_ASSERT(0);
487       break;
488     }
489 }
490
491 #endif
492
493 #endif /* __x86_64__  */