1# ifdef __i386__
2/* -----------------------------------------------------------------------
3   ffi.c - Copyright (c) 1996, 1998, 1999, 2001  Red Hat, Inc.
4           Copyright (c) 2002  Ranjit Mathew
5           Copyright (c) 2002  Bo Thorsen
6           Copyright (c) 2002  Roger Sayle
7
8   x86 Foreign Function Interface
9
10   Permission is hereby granted, free of charge, to any person obtaining
11   a copy of this software and associated documentation files (the
12   ``Software''), to deal in the Software without restriction, including
13   without limitation the rights to use, copy, modify, merge, publish,
14   distribute, sublicense, and/or sell copies of the Software, and to
15   permit persons to whom the Software is furnished to do so, subject to
16   the following conditions:
17
18   The above copyright notice and this permission notice shall be included
19   in all copies or substantial portions of the Software.
20
21   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
22   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27   OTHER DEALINGS IN THE SOFTWARE.
28   ----------------------------------------------------------------------- */
29
30#ifndef __x86_64__
31
32#include <ffi.h>
33#include <ffi_common.h>
34
35#include <stdlib.h>
36
37/* ffi_prep_args is called by the assembly routine once stack space
38   has been allocated for the function's arguments */
39
40/*@-exportheader@*/
41void ffi_prep_args(char *stack, extended_cif *ecif);
42
43static inline int retval_on_stack(ffi_type* tp)
44{
45	if (tp->type == FFI_TYPE_STRUCT) {
46		int sz = tp->size;
47		if (sz > 8) {
48			return 1;
49		}
50		switch (sz) {
51		case 1: case 2: case 4: case 8: return 0;
52		default: return 1;
53		}
54	}
55	return 0;
56}
57
58
59void ffi_prep_args(char *stack, extended_cif *ecif)
60/*@=exportheader@*/
61{
62  register unsigned int i;
63  register void **p_argv;
64  register char *argp;
65  register ffi_type **p_arg;
66
67  argp = stack;
68
69  if (retval_on_stack(ecif->cif->rtype)) {
70      *(void **) argp = ecif->rvalue;
71      argp += 4;
72  }
73
74
75  p_argv = ecif->avalue;
76
77  for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
78       i != 0;
79       i--, p_arg++)
80    {
81      size_t z;
82
83      /* Align if necessary */
84      if ((sizeof(int) - 1) & (unsigned) argp)
85	      argp = (char *) ALIGN(argp, sizeof(int));
86
87      z = (*p_arg)->size;
88      if (z < sizeof(int))
89	{
90	  z = sizeof(int);
91	  switch ((*p_arg)->type)
92	    {
93	    case FFI_TYPE_SINT8:
94	      *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
95	      break;
96
97	    case FFI_TYPE_UINT8:
98	      *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
99	      break;
100
101	    case FFI_TYPE_SINT16:
102	      *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
103	      break;
104
105	    case FFI_TYPE_UINT16:
106	      *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
107	      break;
108
109	    case FFI_TYPE_SINT32:
110	      *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv);
111	      break;
112
113	    case FFI_TYPE_UINT32:
114	      *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
115	      break;
116
117	    case FFI_TYPE_STRUCT:
118	      *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
119	      break;
120
121	    default:
122	      FFI_ASSERT(0);
123	    }
124	}
125      else
126	{
127	  memcpy(argp, *p_argv, z);
128	}
129      p_argv++;
130      argp += z;
131    }
132
133  return;
134}
135
136/* Perform machine dependent cif processing */
137ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
138{
139  /* Set the return type flag */
140  switch (cif->rtype->type)
141    {
142    case FFI_TYPE_VOID:
143#if !defined(X86_WIN32)  && !defined(X86_DARWIN)
144    case FFI_TYPE_STRUCT:
145#endif
146    case FFI_TYPE_SINT64:
147    case FFI_TYPE_FLOAT:
148    case FFI_TYPE_DOUBLE:
149    case FFI_TYPE_LONGDOUBLE:
150      cif->flags = (unsigned) cif->rtype->type;
151      break;
152
153    case FFI_TYPE_UINT64:
154      cif->flags = FFI_TYPE_SINT64;
155      break;
156
157#if defined(X86_WIN32) || defined(X86_DARWIN)
158
159    case FFI_TYPE_STRUCT:
160      if (cif->rtype->size == 1)
161        {
162          cif->flags = FFI_TYPE_SINT8; /* same as char size */
163        }
164      else if (cif->rtype->size == 2)
165        {
166          cif->flags = FFI_TYPE_SINT16; /* same as short size */
167        }
168      else if (cif->rtype->size == 4)
169        {
170          cif->flags = FFI_TYPE_INT; /* same as int type */
171        }
172      else if (cif->rtype->size == 8)
173        {
174          cif->flags = FFI_TYPE_SINT64; /* same as int64 type */
175        }
176      else
177        {
178          cif->flags = FFI_TYPE_STRUCT;
179        }
180      break;
181#endif
182
183    default:
184      cif->flags = FFI_TYPE_INT;
185      break;
186    }
187
188  /* Darwin: The stack needs to be aligned to a multiple of 16 bytes */
189#if 1
190  cif->bytes = (cif->bytes + 15) & ~0xF;
191#endif
192
193
194  return FFI_OK;
195}
196
197/*@-declundef@*/
198/*@-exportheader@*/
199extern void ffi_call_SYSV(void (*)(char *, extended_cif *),
200			  /*@out@*/ extended_cif *,
201			  unsigned, unsigned,
202			  /*@out@*/ unsigned *,
203			  void (*fn)(void));
204/*@=declundef@*/
205/*@=exportheader@*/
206
207#ifdef X86_WIN32
208/*@-declundef@*/
209/*@-exportheader@*/
210extern void ffi_call_STDCALL(void (*)(char *, extended_cif *),
211			  /*@out@*/ extended_cif *,
212			  unsigned, unsigned,
213			  /*@out@*/ unsigned *,
214			  void (*fn)(void));
215/*@=declundef@*/
216/*@=exportheader@*/
217#endif /* X86_WIN32 */
218
219void ffi_call(/*@dependent@*/ ffi_cif *cif,
220	      void (*fn)(),
221	      /*@out@*/ void *rvalue,
222	      /*@dependent@*/ void **avalue)
223{
224  extended_cif ecif;
225
226  ecif.cif = cif;
227  ecif.avalue = avalue;
228
229  /* If the return value is a struct and we don't have a return	*/
230  /* value address then we need to make one		        */
231
232  if ((rvalue == NULL) && retval_on_stack(cif->rtype))
233    {
234      /*@-sysunrecog@*/
235      ecif.rvalue = alloca(cif->rtype->size);
236      /*@=sysunrecog@*/
237    }
238  else
239    ecif.rvalue = rvalue;
240
241  switch (cif->abi)
242    {
243    case FFI_SYSV:
244      /*@-usedef@*/
245      /* To avoid changing the assembly code make sure the size of the argument
246       * block is a multiple of 16. Then add 8 to compensate for local variables
247       * in ffi_call_SYSV.
248       */
249      ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes,
250		    cif->flags, ecif.rvalue, fn);
251      /*@=usedef@*/
252      break;
253#ifdef X86_WIN32
254    case FFI_STDCALL:
255      /*@-usedef@*/
256      ffi_call_STDCALL(ffi_prep_args, &ecif, cif->bytes,
257		    cif->flags, ecif.rvalue, fn);
258      /*@=usedef@*/
259      break;
260#endif /* X86_WIN32 */
261    default:
262      FFI_ASSERT(0);
263      break;
264    }
265}
266
267
268/** private members **/
269
270static void ffi_closure_SYSV (ffi_closure *)
271     __attribute__ ((regparm(1)));
272#if !FFI_NO_RAW_API
273static void ffi_closure_raw_SYSV (ffi_raw_closure *)
274     __attribute__ ((regparm(1)));
275#endif
276
277/*@-exportheader@*/
278static inline void
279ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
280			    void **avalue, ffi_cif *cif)
281/*@=exportheader@*/
282{
283  register unsigned int i;
284  register void **p_argv;
285  register char *argp;
286  register ffi_type **p_arg;
287
288  argp = stack;
289
290  if (retval_on_stack(cif->rtype)) {
291    *rvalue = *(void **) argp;
292    argp += 4;
293  }
294
295  p_argv = avalue;
296
297  for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
298    {
299      size_t z;
300
301      /* Align if necessary */
302      if ((sizeof(int) - 1) & (unsigned) argp) {
303	argp = (char *) ALIGN(argp, sizeof(int));
304      }
305
306      z = (*p_arg)->size;
307
308      /* because we're little endian, this is what it turns into.   */
309
310      *p_argv = (void*) argp;
311
312      p_argv++;
313      argp += z;
314    }
315
316  return;
317}
318
319/* This function is jumped to by the trampoline */
320
321static void
322ffi_closure_SYSV (closure)
323     ffi_closure *closure;
324{
325  // this is our return value storage
326  long double  res;
327
328  // our various things...
329  ffi_cif       *cif;
330  void         **arg_area;
331  void          *resp = (void*)&res;
332  void *args = __builtin_dwarf_cfa ();
333
334
335  cif         = closure->cif;
336  arg_area    = (void**) alloca (cif->nargs * sizeof (void*));
337
338  /* this call will initialize ARG_AREA, such that each
339   * element in that array points to the corresponding
340   * value on the stack; and if the function returns
341   * a structure, it will re-set RESP to point to the
342   * structure return address.  */
343
344  ffi_prep_incoming_args_SYSV(args, (void**)&resp, arg_area, cif);
345
346  (closure->fun) (cif, resp, arg_area, closure->user_data);
347
348  /* now, do a generic return based on the value of rtype */
349  if (cif->flags == FFI_TYPE_INT)
350    {
351      asm ("movl (%0),%%eax" : : "r" (resp) : "eax");
352    }
353  else if (cif->flags == FFI_TYPE_FLOAT)
354    {
355      asm ("flds (%0)" : : "r" (resp) : "st" );
356    }
357  else if (cif->flags == FFI_TYPE_DOUBLE)
358    {
359      asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
360    }
361  else if (cif->flags == FFI_TYPE_LONGDOUBLE)
362    {
363      asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
364    }
365  else if (cif->flags == FFI_TYPE_SINT64)
366    {
367      asm ("movl 0(%0),%%eax;"
368	   "movl 4(%0),%%edx"
369	   : : "r"(resp)
370	   : "eax", "edx");
371    }
372#if defined(X86_WIN32) || defined(X86_DARWIN)
373  else if (cif->flags == FFI_TYPE_SINT8) /* 1-byte struct  */
374    {
375      asm ("movsbl (%0),%%eax" : : "r" (resp) : "eax");
376    }
377  else if (cif->flags == FFI_TYPE_SINT16) /* 2-bytes struct */
378    {
379      asm ("movswl (%0),%%eax" : : "r" (resp) : "eax");
380    }
381#endif
382
383  else if (cif->flags == FFI_TYPE_STRUCT)
384    {
385      asm ("lea -8(%ebp),%esp;"
386	   "pop %esi;"
387	   "pop %edi;"
388	   "pop %ebp;"
389	   "ret $4");
390    }
391}
392
393
394/* How to make a trampoline.  Derived from gcc/config/i386/i386.c. */
395
396#define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX) \
397({ unsigned char *__tramp = (unsigned char*)(TRAMP); \
398   unsigned int  __fun = (unsigned int)(FUN); \
399   unsigned int  __ctx = (unsigned int)(CTX); \
400   unsigned int  __dis = __fun - ((unsigned int) __tramp + FFI_TRAMPOLINE_SIZE); \
401   *(unsigned char*) &__tramp[0] = 0xb8; \
402   *(unsigned int*)  &__tramp[1] = __ctx; /* movl __ctx, %eax */ \
403   *(unsigned char *)  &__tramp[5] = 0xe9; \
404   *(unsigned int*)  &__tramp[6] = __dis; /* jmp __fun  */ \
405 })
406
407
408/* the cif must already be prep'ed */
409
410ffi_status
411ffi_prep_closure (ffi_closure* closure,
412		  ffi_cif* cif,
413		  void (*fun)(ffi_cif*,void*,void**,void*),
414		  void *user_data)
415{
416  FFI_ASSERT (cif->abi == FFI_SYSV);
417
418  FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
419		       &ffi_closure_SYSV,  \
420		       (void*)closure);
421
422  closure->cif  = cif;
423  closure->user_data = user_data;
424  closure->fun  = fun;
425
426  return FFI_OK;
427}
428
429/* ------- Native raw API support -------------------------------- */
430
431#if !FFI_NO_RAW_API
432
433static void
434ffi_closure_raw_SYSV (closure)
435     ffi_raw_closure *closure;
436{
437  // this is our return value storage
438  long double    res;
439
440  // our various things...
441  ffi_raw         *raw_args;
442  ffi_cif         *cif;
443  unsigned short   rtype;
444  void            *resp = (void*)&res;
445
446  /* get the cif */
447  cif = closure->cif;
448
449  /* the SYSV/X86 abi matches the RAW API exactly, well.. almost */
450  raw_args = (ffi_raw*) __builtin_dwarf_cfa ();
451
452  (closure->fun) (cif, resp, raw_args, closure->user_data);
453
454  rtype = cif->flags;
455
456  /* now, do a generic return based on the value of rtype */
457  if (rtype == FFI_TYPE_INT)
458    {
459      asm ("movl (%0),%%eax" : : "r" (resp) : "eax");
460    }
461  else if (rtype == FFI_TYPE_FLOAT)
462    {
463      asm ("flds (%0)" : : "r" (resp) : "st" );
464    }
465  else if (rtype == FFI_TYPE_DOUBLE)
466    {
467      asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
468    }
469  else if (rtype == FFI_TYPE_LONGDOUBLE)
470    {
471      asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
472    }
473  else if (rtype == FFI_TYPE_SINT64)
474    {
475      asm ("movl 0(%0),%%eax; movl 4(%0),%%edx"
476	   : : "r"(resp)
477	   : "eax", "edx");
478    }
479}
480
481
482
483
484ffi_status
485ffi_prep_raw_closure (ffi_raw_closure* closure,
486		      ffi_cif* cif,
487		      void (*fun)(ffi_cif*,void*,ffi_raw*,void*),
488		      void *user_data)
489{
490  int i;
491
492  FFI_ASSERT (cif->abi == FFI_SYSV);
493
494  // we currently don't support certain kinds of arguments for raw
495  // closures.  This should be implemented by a separate assembly language
496  // routine, since it would require argument processing, something we
497  // don't do now for performance.
498
499  for (i = cif->nargs-1; i >= 0; i--)
500    {
501      FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_STRUCT);
502      FFI_ASSERT (cif->arg_types[i]->type != FFI_TYPE_LONGDOUBLE);
503    }
504
505
506  FFI_INIT_TRAMPOLINE (&closure->tramp[0], &ffi_closure_raw_SYSV,
507		       (void*)closure);
508
509  closure->cif  = cif;
510  closure->user_data = user_data;
511  closure->fun  = fun;
512
513  return FFI_OK;
514}
515
516static void
517ffi_prep_args_raw(char *stack, extended_cif *ecif)
518{
519  memcpy (stack, ecif->avalue, ecif->cif->bytes);
520}
521
522/* we borrow this routine from libffi (it must be changed, though, to
523 * actually call the function passed in the first argument.  as of
524 * libffi-1.20, this is not the case.)
525 */
526
527extern void
528ffi_call_SYSV(void (*)(char *, extended_cif *),
529	      /*@out@*/ extended_cif *,
530	      unsigned, unsigned,
531	      /*@out@*/ unsigned *,
532	      void (*fn)());
533
534#ifdef X86_WIN32
535extern void
536ffi_call_STDCALL(void (*)(char *, extended_cif *),
537	      /*@out@*/ extended_cif *,
538	      unsigned, unsigned,
539	      /*@out@*/ unsigned *,
540	      void (*fn)());
541#endif /* X86_WIN32 */
542
543void
544ffi_raw_call(/*@dependent@*/ ffi_cif *cif,
545	     void (*fn)(),
546	     /*@out@*/ void *rvalue,
547	     /*@dependent@*/ ffi_raw *fake_avalue)
548{
549  extended_cif ecif;
550  void **avalue = (void **)fake_avalue;
551
552  ecif.cif = cif;
553  ecif.avalue = avalue;
554
555  /* If the return value is a struct and we don't have a return	*/
556  /* value address then we need to make one		        */
557
558  if ((rvalue == NULL) && retval_on_stack(cif->rtype))
559    {
560      /*@-sysunrecog@*/
561      ecif.rvalue = alloca(cif->rtype->size);
562      /*@=sysunrecog@*/
563    }
564  else
565    ecif.rvalue = rvalue;
566
567
568  switch (cif->abi)
569    {
570    case FFI_SYSV:
571      /*@-usedef@*/
572      ffi_call_SYSV(ffi_prep_args_raw, &ecif, cif->bytes,
573		    cif->flags, ecif.rvalue, fn);
574      /*@=usedef@*/
575      break;
576#ifdef X86_WIN32
577    case FFI_STDCALL:
578      /*@-usedef@*/
579      ffi_call_STDCALL(ffi_prep_args_raw, &ecif, cif->bytes,
580		    cif->flags, ecif.rvalue, fn);
581      /*@=usedef@*/
582      break;
583#endif /* X86_WIN32 */
584    default:
585      FFI_ASSERT(0);
586      break;
587    }
588}
589
590#endif
591
592#endif /* __x86_64__  */
593
594#endif /* __i386__ */
595