File: any-libffi.c

package info (click to toggle)
squeak-vm 1%3A4.10.2.2614-4.1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 13,284 kB
  • ctags: 15,344
  • sloc: ansic: 75,096; cs: 11,191; objc: 5,494; sh: 3,170; asm: 1,533; cpp: 449; pascal: 372; makefile: 366; awk: 103
file content (509 lines) | stat: -rw-r--r-- 14,181 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
/****************************************************************************
*   PROJECT: Squeak foreign function interface
*   FILE:    sqUnixFFI.c
*   CONTENT: Unix support for the foreign function interface
*
*   AUTHOR:  Andreas Raab (ar)
*   ADDRESS: Walt Disney Imagineering, Glendale, CA
*   EMAIL:   andreasr@wdi.disney.com
*   RCSID:   $Id: any-libffi.c 2096 2009-08-26 19:21:15Z piumarta $
*
*   NOTES:  The Unix version of the FFI support code relies on libffi from
*	    http://sourceware.cygnus.com/libffi/
*
*****************************************************************************/
#include "sq.h"
#include "sqFFI.h"

#ifndef NO_FFI_SUPPORT

#if defined(HAVE_FFI_FFI_H)
# include <ffi/ffi.h>
#else 
# include <ffi.h>
#endif

#ifndef  FFI_TYPE_STRUCT	/* this is private in libffi-2 */
# define FFI_TYPE_STRUCT 13
#endif

#if defined(FFI_TEST)
  static int primitiveFail(void) { puts("primitive fail"); exit(1); return 0; }
#else
  extern struct VirtualMachine *interpreterProxy;
# define primitiveFail() interpreterProxy->primitiveFail();
#endif

#if 1
#define HAVE_LONGLONG
#endif

/* Check if HAVE_LONGLONG is defined (should be figured out by configure */
#ifdef HAVE_LONGLONG
#define HAS_LONGLONG 1
#define LONGLONG long long
#endif

/* Error if LONGLONG is not defined */
#if HAS_LONGLONG
#ifndef LONGLONG
#error "You must define LONGLONG if HAS_LONGLONG is defined"
#endif
#endif

/* Max number of arguments in call */
#define FFI_MAX_ARGS 32

static ffi_type*  ffiTypes[FFI_MAX_ARGS];
static void*      ffiArgs[FFI_MAX_ARGS];

static char   ffiBytes[FFI_MAX_ARGS];
static short  ffiShorts[FFI_MAX_ARGS];
static int    ffiInts[FFI_MAX_ARGS];
static float  ffiFloats[FFI_MAX_ARGS];
static double ffiDoubles[FFI_MAX_ARGS];
static int    ffiArgIndex = 0;

static ffi_type*  ffiStructTypes[FFI_MAX_ARGS];
static int ffiStructIndex = 0;

/* helpers */
#define CHECK_ARGS() if(ffiArgIndex >= FFI_MAX_ARGS) return primitiveFail();
#define PUSH_TYPE(type) { CHECK_ARGS(); ffiTypes[ffiArgIndex] = &type; }
#define PUSH(where, what, type) { \
	PUSH_TYPE(type); where[ffiArgIndex] = what; \
	ffiArgs[ffiArgIndex] = (void*) (where + ffiArgIndex); \
	ffiArgIndex++;\
}


#define BARG_PUSH(value, type) PUSH(ffiBytes, value, type)
#define SARG_PUSH(value, type) PUSH(ffiShorts, value, type)
#define IARG_PUSH(value, type) PUSH(ffiInts, value, type)
#define FARG_PUSH(value) PUSH(ffiFloats, value, ffi_type_float)
#define DARG_PUSH(value) PUSH(ffiDoubles, value, ffi_type_double)

#if HAS_LONGLONG
static LONGLONG ffiLongLongs[FFI_MAX_ARGS];
#define LARG_PUSH(value, type) PUSH(ffiLongLongs, value, type)
#endif

/* The 64bit return value storage area - aligned by the C compiler */
static double   returnValue;
/* Storage area for large structure returns */
static ffi_type* structReturnType = NULL;
static void *structReturnValue = NULL;

/* The area for temporarily allocated strings */
static char *ffiTempStrings[FFI_MAX_ARGS];
/* The number of temporarily allocated strings */
static int   ffiTempStringCount = 0;

/*****************************************************************************/
/*****************************************************************************/

/*  ffiInitialize:
    Announce that the VM is about to do an external function call. */
int ffiInitialize(void)
{
  ffiArgIndex = 0;
  ffiTempStringCount = 0;
  ffiStructIndex = 0;
  structReturnType = NULL;
  structReturnValue = NULL;
  return 1;
}

/*  ffiSupportsCallingConvention:
    Return true if the support code supports the given calling convention */
int ffiSupportsCallingConvention(int callType)
{
  if(callType == FFICallTypeCDecl) return 1;
  return 0;
}

/*  ffiAlloc:
    Allocate space from the external heap */
int ffiAlloc(int byteSize)
{
  return (int)malloc(byteSize);
}
/*  ffiFree:
    Free space from the external heap */
int ffiFree(int pointer)
{
  if(pointer) free((void*)pointer);
  return 1;
}

/*****************************************************************************/
/*****************************************************************************/

int ffiPushSignedByte(int value)
{
  BARG_PUSH((char)value, ffi_type_sint8);
  return 1;
}

int ffiPushUnsignedByte(int value)
{
  BARG_PUSH((char)value, ffi_type_uint8);
  return 1;
}

int ffiPushSignedShort(int value)
{
  SARG_PUSH((short)value, ffi_type_sint16);
  return 1;
}

int ffiPushUnsignedShort(int value)
{
  SARG_PUSH((short)value, ffi_type_uint16);
  return 1;
}

int ffiPushSignedInt(int value)
{
  IARG_PUSH(value, ffi_type_sint32);
  return 1;
}

int ffiPushUnsignedInt(int value)
{
  IARG_PUSH(value, ffi_type_uint32);
  return 1;
}

int ffiPushSignedLongLong(int low, int high)
{
#if HAS_LONGLONG
  LONGLONG value = (((LONGLONG) high) << 32)  | ((LONGLONG) (unsigned) low);
  LARG_PUSH(value, ffi_type_sint64);
  return 1;
#else
  return primitiveFail();
#endif
}

int ffiPushUnsignedLongLong(int low, int high)
{
#if HAS_LONGLONG
  LONGLONG value = (((LONGLONG) high) << 32)  | ((LONGLONG) (unsigned) low);
  LARG_PUSH(value, ffi_type_uint64);
  return 1;
#else
  return primitiveFail();
#endif
}

int ffiPushSignedChar(int value)
{
  BARG_PUSH(value, ffi_type_sint8);
  return 1;
}

int ffiPushUnsignedChar(int value)
{
  BARG_PUSH(value, ffi_type_uint8);
  return 1;
}

int ffiPushBool(int value)
{
  IARG_PUSH(value, ffi_type_uint8);
  return 1;
}

int ffiPushSingleFloat(double value)
{
  FARG_PUSH((float)value);
  return 1;
}

int ffiPushDoubleFloat(double value)
{
  DARG_PUSH(value);
  return 1;
}


ffi_type* ffiCreateType(int *structSpec, int structSize)
{
  ffi_type *structType, **newTypes;
  int nTypes, i, typeSpec;

  /* count the number of atomic types we need to create */
  nTypes = 0;
  for(i=0; i<structSize; i++) {
    typeSpec = structSpec[i];
    if(typeSpec & FFIFlagPointer) nTypes++;
    else if(typeSpec & FFIFlagAtomic) nTypes++;
  }
  /* note: nTypes == 0 means an invalid structure */
  if(nTypes == 0) {
    printf("Warning: nTypes == 0 in ffiCreateTypes\n");
    return NULL;
  }
  /* allocate the structure type */
  structType = calloc(1, sizeof(ffi_type));
  /* allocate the atomic type refs */
  newTypes = calloc(nTypes+1, sizeof(ffi_type*));
  /* number of elements in type */
  structType->size = (*structSpec) & FFIStructSizeMask;
  structType->alignment = 4;
  structType->type = FFI_TYPE_STRUCT;
  structType->elements = newTypes;

  /* now go over the structure and fill in the fields */
  nTypes = 0;
  for(i=0; i<structSize; i++) {
    typeSpec = structSpec[i];
    if(typeSpec & FFIFlagPointer) {
      newTypes[nTypes++] = &ffi_type_pointer;
      continue;
    }
    if((typeSpec & FFIFlagAtomic) == 0) continue;
    switch((typeSpec & FFIAtomicTypeMask) >> FFIAtomicTypeShift) {
    case FFITypeBool:
      newTypes[nTypes++] = &ffi_type_uint8; break;
    case FFITypeUnsignedByte:
      newTypes[nTypes++] = &ffi_type_uint8; break;
    case FFITypeSignedByte:
      newTypes[nTypes++] = &ffi_type_sint8; break;
    case FFITypeUnsignedShort:
      newTypes[nTypes++] = &ffi_type_uint16; break;
    case FFITypeSignedShort:
      newTypes[nTypes++] = &ffi_type_sint16; break;
    case FFITypeUnsignedInt:
      newTypes[nTypes++] = &ffi_type_uint32; break;
    case FFITypeSignedInt:
      newTypes[nTypes++] = &ffi_type_sint32; break;
    case FFITypeUnsignedLongLong:
      newTypes[nTypes++] = &ffi_type_uint64; break;
    case FFITypeSignedLongLong:
      newTypes[nTypes++] = &ffi_type_sint64; break;
    case FFITypeUnsignedChar:
      newTypes[nTypes++] = &ffi_type_uint8; break;
    case FFITypeSignedChar:
      newTypes[nTypes++] = &ffi_type_sint8; break;
    case FFITypeSingleFloat:
      newTypes[nTypes++] = &ffi_type_float; break;
    case FFITypeDoubleFloat:
      newTypes[nTypes++] = &ffi_type_double; break;
    default:
      printf("Warning: unknown atomic type (%x) in ffiCreateTypes\n", 
	     typeSpec);
      free(newTypes);
      free(structType);
      return NULL;
    };
  }
  newTypes[nTypes++] = NULL;
  return structType;
}

int ffiPushStructureOfLength(int pointer, int* structSpec, int structSize)
{
  ffi_type *structType;

  if(pointer == 0) return primitiveFail();
  CHECK_ARGS(); /* fail early on */
  structType = ffiCreateType(structSpec, structSize);
  if(structType == NULL) return primitiveFail();
  ffiStructTypes[ffiStructIndex++] = structType;
  ffiTypes[ffiArgIndex] = structType;
  ffiArgs[ffiArgIndex] = (void*) pointer;
  ffiArgIndex++;
  return 1;
}

int ffiPushPointer(int pointer)
{
  IARG_PUSH(pointer, ffi_type_pointer);
  return 1;
}

int ffiPushStringOfLength(int srcIndex, int length)
{
  char *ptr;
  ptr = (char*) malloc(length+1);
  if(!ptr) return primitiveFail();
  memcpy(ptr, (void*)srcIndex, length);
  ptr[length] = 0;
  ffiTempStrings[ffiTempStringCount++] = ptr;
  IARG_PUSH((int)ptr, ffi_type_pointer);
  return 1;
}

/*****************************************************************************/
/*****************************************************************************/

/*  ffiCanReturn:
	Return true if the support code can return the given type. */
int ffiCanReturn(int *structSpec, int specSize)
{
  int header = *structSpec;
  if(header & FFIFlagPointer) return 1;
  if(header & FFIFlagStructure) {
    int structSize = header & FFIStructSizeMask;
    structReturnType = ffiCreateType(structSpec, specSize);
    if(!structReturnType) return 0;
    if(structSize > 8) {
      structReturnValue = calloc(1,structSize);
      if(!structReturnValue) return 0;
      return 1;
    }
  }
  return 1;
}

/*  ffiReturnFloatValue:
    Return the value from a previous ffi call with float return type. */
double ffiReturnFloatValue(void)
{
  return returnValue;
}

/*  ffiLongLongResultLow:
    Return the low 32bit from the 64bit result of a call to an external function */
int ffiLongLongResultLow(void)
{
#if HAS_LONGLONG
  return (int) ( (*(LONGLONG*)&returnValue) & (LONGLONG)0xFFFFFFFFU);
#else
  return 0;
#endif
}

/*  ffiLongLongResultHigh:
    Return the high 32bit from the 64bit result of a call to an external function */
int ffiLongLongResultHigh(void)
{
#if HAS_LONGLONG
  return (int) ( (*(LONGLONG*)&returnValue) >> 32);
#else
  return 0;
#endif
}

/*  ffiStoreStructure:
    Store the structure result of a previous ffi call into the given address*/
int ffiStoreStructure(int address, int structSize)
{
  if(structReturnValue) {
    memcpy((void*)address, (void*)structReturnValue, structSize);
  } else {
    memcpy((void*)address, (void*)&returnValue, structSize);
  }
  return 1;
}

/*  ffiCleanup:
    Cleanup after a foreign function call has completed.
    The generic support code only frees the temporarily
    allocated strings. */
int ffiCleanup(void)
{
  int i;
  for(i=0; i<ffiTempStringCount; i++)
    free(ffiTempStrings[i]);
  for(i=0; i<ffiStructIndex; i++) {
    free(ffiStructTypes[i]->elements);
    free(ffiStructTypes[i]);
    ffiStructTypes[i]=NULL;
  }
  if(structReturnType) {
    free(structReturnType->elements);
    free(structReturnType);
    structReturnType = NULL;
  }
  if(structReturnValue) {
    free(structReturnValue);
    structReturnValue = NULL;
  }
  ffiTempStringCount = 0;
  ffiStructIndex = 0;
  return 1;
}

/*****************************************************************************/
/*****************************************************************************/
int ffiCallAddress(int fn, ffi_type *returnType, int atomicArgType)
{
  ffi_cif cif;
  ffi_status result;
  int retVal;

  result = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, ffiArgIndex, 
			returnType, ffiTypes);
  if(result != FFI_OK) return primitiveFail();
  if(structReturnValue) {
    ffi_call(&cif, (void *)fn, (void *)structReturnValue, (void **)ffiArgs);
    return (int) structReturnValue;
  }
  ffi_call(&cif, (void *)fn, (void *)&returnValue, (void **)ffiArgs);
  retVal = *(int*)&returnValue;
#ifdef FFI_MIPS_N32
  /* Note: MIPS N32 ABI returns 64bit for integer/pointer whatever.
     This seems to be a bug in the fficall implementation. */
  retVal = ((int*)(&returnValue))[1];
#endif
  /* Promote certain return types to integral size */
  switch(atomicArgType) {
  case FFITypeUnsignedChar:
  case FFITypeUnsignedByte: retVal = *(unsigned char*) &retVal; break;
  case FFITypeSignedChar:
  case FFITypeSignedByte: retVal = *(signed char*) &retVal; break;
  case FFITypeUnsignedShort: retVal = *(unsigned short*) &retVal; break;
  case FFITypeSignedShort: retVal = *(signed short*) &retVal; break;
  case FFITypeSingleFloat: returnValue = *(float*)&returnValue; break;
  }
  return retVal;
}

int ffiCallAddressOfWithPointerReturn(int fn, int callType)
{
  return ffiCallAddress(fn, &ffi_type_pointer,-1);
}

int ffiCallAddressOfWithStructReturn(int fn, int callType, 
				     int *structSpec, int specSize)
{
  if(!structReturnType) return primitiveFail();
  return ffiCallAddress(fn, structReturnType,-1);
}

int ffiCallAddressOfWithReturnType(int fn, int callType, int typeSpec)
{
  ffi_type *returnType;
  int atomicType;
  atomicType = (typeSpec & FFIAtomicTypeMask) >> FFIAtomicTypeShift;
  switch(atomicType) {
  case FFITypeVoid:		returnType = &ffi_type_void; break;
  case FFITypeBool:		returnType = &ffi_type_uint8; break;
  case FFITypeUnsignedByte:	returnType = &ffi_type_uint8; break;
  case FFITypeSignedByte:	returnType = &ffi_type_sint8; break;
  case FFITypeUnsignedShort:	returnType = &ffi_type_uint16; break;
  case FFITypeSignedShort:	returnType = &ffi_type_sint16; break;
  case FFITypeUnsignedInt:	returnType = &ffi_type_uint32; break;
  case FFITypeSignedInt:       	returnType = &ffi_type_sint32; break;
  case FFITypeUnsignedLongLong: returnType = &ffi_type_uint64; break;
  case FFITypeSignedLongLong:	returnType = &ffi_type_sint64; break;
  case FFITypeUnsignedChar:	returnType = &ffi_type_uint8; break;
  case FFITypeSignedChar:      	returnType = &ffi_type_sint8; break;
  case FFITypeSingleFloat:	returnType = &ffi_type_float; break;
  case FFITypeDoubleFloat:	returnType = &ffi_type_double; break;
  default:
    return primitiveFail();
  }
  return ffiCallAddress(fn, returnType, atomicType);
}


#if defined(FFI_TEST)
void ffiDoAssertions(void) {}
#endif


#endif /* NO_FFI_SUPPORT */