newlisp/nl-import.c

1185 lines
34 KiB
C

/* nl-import.c --- shared library interface for newLISP
Copyright (C) 2016 Lutz Mueller
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "newlisp.h"
#include "protos.h"
#ifndef WINDOWS
#include <dlfcn.h>
#endif
#ifdef MAC_OSX
#include <sys/mman.h>
#endif
UINT cdeclFunction(UINT fAddress, UINT * args, int count);
extern int evalCatchFlag;
#ifdef FFI
CELL * ffiPreparation(CELL * pCell, CELL * params, int type);
CELL * ffiTypeToCell(ffi_type *type, void * src);
CELL * unpackFFI(ffi_type * ffi, char * data);
void ffi_trampoline(ffi_cif *cif, void *ret, void **args, void *symbol);
#endif
#if defined(WINDOWS) || defined(CYGWIN)
UINT stdcallFunction(UINT fAddress, UINT * args, int count);
#endif
#ifdef WINDOWS
CELL * p_importLib(CELL * params)
{
char * libName;
char * funcName;
char * options = NULL;
HINSTANCE hLibrary;
CELL * pCell;
SYMBOL * symbol;
FARPROC initProc;
int type = CELL_IMPORT_DLL;
params = getString(params, &libName);
params = getString(params, &funcName);
if(params != nilCell)
{
if(params->next == nilCell)
params = getString(params, &options);
#ifdef FFI
else
type = CELL_IMPORT_FFI;
#endif
}
if( (UINT)(hLibrary = LoadLibrary(libName)) < 32)
return(errorProcExt2(ERR_IMPORT_LIB_NOT_FOUND, stuffString(libName)));
if(options != NULL && strcmp(options, "cdecl") == 0)
type = CELL_IMPORT_CDECL;
symbol = translateCreateSymbol(funcName, type, currentContext, TRUE);
if(isFFIsymbol(symbol->flags)) /* don't redefine return current def */
return (copyCell((CELL *)symbol->contents));
if(isProtected(symbol->flags))
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
pCell = getCell(type);
deleteList((CELL *)symbol->contents);
symbol->contents = (UINT)pCell;
if((pCell->contents = (UINT)GetProcAddress(hLibrary, (LPCSTR)funcName)) == 0)
return(errorProcExt2(ERR_IMPORT_FUNC_NOT_FOUND, stuffString(funcName)));
/* put name of imported DLL into DLLs space for loadStartup() */
initProc = GetProcAddress(hLibrary, (LPCSTR)"dllName");
if(initProc != 0) (*initProc)(libName);
#ifdef FFI
symbol->flags |= SYMBOL_FFI | SYMBOL_PROTECTED;
if(pCell->type == CELL_IMPORT_FFI)
{
pCell->aux = (UINT)calloc(sizeof(FFIMPORT), 1);
((FFIMPORT *)pCell->aux)->name = symbol->name;
return(copyCell(ffiPreparation(pCell, params, FFI_FUNCTION)));
}
#endif
pCell->aux = (UINT)symbol->name;
return(copyCell(pCell));
}
#else /* UNIX and compatible operating systems */
CELL * p_importLib(CELL * params)
{
char * libName;
char * funcName;
void * hLibrary;
CELL * pCell;
SYMBOL * symbol;
char * error;
#ifdef CYGWIN
char * options = NULL;
int type = CELL_IMPORT_DLL;
#else
int type = CELL_IMPORT_CDECL;
#endif
params = getString(params, &libName);
if(params != nilCell)
params = getString(params, &funcName);
else funcName = NULL;
#ifdef CYGWIN
if(params != nilCell)
{
if(params->next == nilCell)
{
params = getString(params, &options);
if(strcmp(options, "cdecl") == 0)
type = CELL_IMPORT_CDECL;
}
#ifdef FFI
else type = CELL_IMPORT_FFI;
#endif
}
#else
if(params->next != nilCell)
type = CELL_IMPORT_FFI;
#endif
hLibrary = 0;
#ifdef TRU64
if((hLibrary = dlopen(libName, RTLD_LAZY)) == 0)
#else
if((hLibrary = dlopen(libName, RTLD_GLOBAL|RTLD_LAZY)) == 0)
#endif
return(errorProcExt2(ERR_IMPORT_LIB_NOT_FOUND, stuffString((char *)dlerror())));
if(funcName == NULL)
return(trueCell);
symbol = translateCreateSymbol(funcName, type, currentContext, TRUE);
if(isFFIsymbol(symbol->flags)) /* don't redefine */
return (copyCell((CELL *)symbol->contents));
if(isProtected(symbol->flags))
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
pCell = getCell(type);
deleteList((CELL *)symbol->contents);
symbol->contents = (UINT)pCell;
dlerror(); /* clear potential error */
pCell->contents = (UINT)dlsym(hLibrary, funcName);
if((error = (char *)dlerror()) != NULL)
return(errorProcExt2(ERR_IMPORT_FUNC_NOT_FOUND, stuffString(error)));
#ifdef FFI
symbol->flags |= SYMBOL_FFI | SYMBOL_PROTECTED;
if(pCell->type == CELL_IMPORT_FFI)
{
pCell->aux = (UINT)calloc(sizeof(FFIMPORT), 1);
((FFIMPORT *)pCell->aux)->name = symbol->name;
return(copyCell(ffiPreparation(pCell, params, FFI_FUNCTION)));
}
#endif
pCell->aux = (UINT)symbol->name;
return(copyCell(pCell));
}
#endif
CELL * executeLibfunction(CELL * pCell, CELL * params)
{
CELL * arg;
UINT args[14];
int count;
#ifdef FFI
if(pCell->type == CELL_IMPORT_FFI)
if(((FFIMPORT *)pCell->aux)->type != 0)
return executeLibFFI(pCell, params);
#endif
count = 0;
while(params->type != CELL_NIL && count < 14)
{
arg = evaluateExpression(params);
switch(arg->type)
{
case CELL_LONG:
case CELL_STRING:
case CELL_PRIMITIVE:
args[count++] = arg->contents;
break;
#ifndef NEWLISP64
/* change 64-bit to 32-bit */
case CELL_INT64:
args[count++] = *(INT64 *)&arg->aux;
break;
#endif
case CELL_FLOAT:
#ifndef NEWLISP64
args[count++] = arg->aux;
#endif
args[count++] = arg->contents;
break;
default:
args[count++] = (UINT)arg;
break;
}
params = (CELL *)params->next;
}
#if defined(WINDOWS) || defined(CYGWIN)
if(pCell->type == CELL_IMPORT_DLL)
return(stuffInteger(stdcallFunction(pCell->contents, args, count)));
else
#endif
return(stuffInteger(cdeclFunction(pCell->contents, args, count)));
}
UINT cdeclFunction(UINT fAddress, UINT * args, int count)
{
UINT (*function)();
function = (UINT (*)())fAddress;
switch(count)
{
case 0:
return (*function)();
case 1:
return (*function)(args[0]);
case 2:
return (*function)(args[0], args[1]);
case 3:
/* printf("args[0] %llx, args[1] %llx, args[2] %llx, args[1]-args[2] %llx\n ",
args[0], args[1], args[2], args[1] - args[2]); */
return (*function)(args[0], args[1], args[2]);
case 4:
return (*function)(args[0], args[1], args[2], args[3]);
case 5:
return (*function)(args[0], args[1], args[2], args[3],
args[4]);
case 6:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5]);
case 7:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6]);
case 8:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7]);
case 9:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8]);
case 10:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8], args[9]);
case 11:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10]);
case 12:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11]);
case 13:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12]);
case 14:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12], args[13]);
default:
break;
}
return(0);
}
#if defined(WINDOWS) || defined(CYGWIN)
UINT stdcallFunction(UINT fAddress, UINT * args, int count)
{
UINT _stdcall (*function)();
function = (UINT _stdcall (*)())fAddress;
switch(count)
{
case 0:
return (*function)();
case 1:
return (*function)(args[0]);
case 2:
return (*function)(args[0], args[1]);
case 3:
return (*function)(args[0], args[1], args[2]);
case 4:
return (*function)(args[0], args[1], args[2], args[3]);
case 5:
return (*function)(args[0], args[1], args[2], args[3],
args[4]);
case 6:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5]);
case 7:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6]);
case 8:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7]);
case 9:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8]);
case 10:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7], args[8], args[9]);
case 11:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10]);
case 12:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11]);
case 13:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12]);
case 14:
return (*function)(args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12], args[13]);
default:
break;
}
return(0);
}
#endif
/* 16 callback functions for up to 8 parameters */
INT template(INT n, INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8);
INT callback0(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(0, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback1(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(1, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback2(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(2, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback3(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(3, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback4(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(4, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback5(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(5, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback6(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(6, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback7(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(7, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback8(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(8, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback9(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(9, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback10(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(10, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback11(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(11, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback12(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(12, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback13(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(13, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback14(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(14, p1, p2, p3, p4, p5, p6, p7, p8);}
INT callback15(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{return template(15, p1, p2, p3, p4, p5, p6, p7, p8);}
typedef INT (*lib_callback_t)(INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8);
typedef struct {
SYMBOL * sym;
lib_callback_t func;
} LIBCALLBACK;
LIBCALLBACK callback[] = {
{ NULL, callback0 },
{ NULL, callback1 },
{ NULL, callback2 },
{ NULL, callback3 },
{ NULL, callback4 },
{ NULL, callback5 },
{ NULL, callback6 },
{ NULL, callback7 },
{ NULL, callback8 },
{ NULL, callback9 },
{ NULL, callback10 },
{ NULL, callback11 },
{ NULL, callback12 },
{ NULL, callback13 },
{ NULL, callback14 },
{ NULL, callback15 },
};
INT template(INT n, INT p1, INT p2, INT p3, INT p4, INT p5, INT p6, INT p7, INT p8)
{
CELL * args;
CELL * cell;
INT result;
jmp_buf errorJumpSave;
memcpy(errorJumpSave, errorJump, sizeof(errorJump));
if(setjmp(errorJump))
{
reset();
initStacks();
result = -1;
goto FINISH_CALLBACK;
}
args = stuffIntegerList(8, p1, p2, p3, p4, p5, p6, p7, p8);
executeSymbol(callback[n].sym, (CELL *)args->contents, &cell);
#ifndef NEWLISP64
if(cell->type == CELL_INT64)
result = *(INT64 *)&cell->aux;
else
#endif
result = (INT)cell->contents;
args->contents = (UINT)nilCell;
deleteList(args);
/* before 10.4.4 this was pushResult(cell) but caused resultStack overflow
in 10.4.4 changed to deleteList(cell), but now return value on
simple callback not available anymore, use callback with libffi instead
available on -DFFI compiled versions. See also:
qa-specific-tests/qa-simplecallback
*/
deleteList(cell);
FINISH_CALLBACK:
memcpy(errorJump, errorJumpSave, sizeof(errorJump));
return(result);
}
CELL * p_callback(CELL * params)
{
CELL * cell;
SYMBOL * sPtr;
UINT n;
#ifdef FFI
SYMBOL * symbol;
CELL * ffiCell;
FFIMPORT *ffi;
char * cb_name;
int result, len;
#ifdef MAC_OSX
ffi_closure *closure;
#endif
#endif /* FFI */
cell = evaluateExpression(params);
if(cell->type == CELL_SYMBOL)
sPtr = (SYMBOL *)cell->contents;
else
goto CALLBACK_SIMPLE;
#ifdef FFI
len = strlen(sPtr->name);
cb_name = calloc(sizeof(char) * (len + 6), 1);
/*
strncpy(cb_name, "$ffi-", 6);
strncat(cb_name, sPtr->name, len);
*/
memcpy(cb_name, "$ffi-", 5);
memcpy(cb_name + 5, sPtr->name, len + 1);
symbol = translateCreateSymbol(cb_name, CELL_NIL, mainContext, TRUE);
if(isFFIsymbol(symbol->flags)) /* already defined */
{
ffiCell = (CELL *)symbol->contents;
ffi = (FFIMPORT *) ffiCell->aux;
return(stuffInteger((UINT) ffi->code));
}
if(isProtected(symbol->flags))
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
symbol->flags |= SYMBOL_FFI | SYMBOL_PROTECTED;
if(params->next == nilCell)
return(errorProcExt(ERR_FFI_PREP_FAILED,nilCell));
ffiCell = getCell(CELL_IMPORT_FFI);
ffiCell->aux = (UINT)calloc(sizeof(FFIMPORT), 1);
((FFIMPORT *)ffiCell->aux)->name = cb_name;
ffiCell = ffiPreparation(ffiCell,params->next,FFI_CLOSURE);
if(ffiCell != nilCell)
{
ffi = (FFIMPORT *) ffiCell->aux;
ffi->data = calloc(sizeof(ffi_closure_data), 1);
#ifndef MAC_OSX
ffi->clos = ffi_closure_alloc(sizeof(ffi_closure),&ffi->code);
if(!ffi->clos)
{
free(ffi->data);
return(errorProcExt(ERR_FFI_PREP_FAILED,nilCell));
}
result = ffi_prep_closure_loc(ffi->clos, &ffi->cif, ffi_trampoline, ffi->data, ffi->code);
if(result != FFI_OK)
{
free(ffi->data);
return(errorProcExt(ERR_FFI_PREP_FAILED, stuffSymbol(sPtr)));
}
#else /* MAC_OSX */
if((closure = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0)) == (void*)-1)
{
free(ffi->data);
return(errorProcExt(ERR_FFI_PREP_FAILED, stuffSymbol(sPtr)));
}
ffi->clos = closure;
if((result = ffi_prep_closure(closure, &ffi->cif, ffi_trampoline, ffi->data)) != FFI_OK)
{
free(ffi->data);
munmap(closure, sizeof(closure));
return(errorProcExt(ERR_FFI_PREP_FAILED, stuffSymbol(sPtr)));
}
if(mprotect(closure, sizeof(closure), PROT_READ | PROT_EXEC) == -1)
{
free(ffi->data);
munmap(closure, sizeof(closure));
return(errorProcExt(ERR_FFI_PREP_FAILED, stuffSymbol(sPtr)));
}
/* ffi->code = ffi_trampoline; */
ffi->code = closure;
#endif
ffi->data->symbol = sPtr;
ffi->data->code = ffi->code;
deleteList((CELL *)symbol->contents);
symbol->contents = (UINT)ffiCell;
return(stuffInteger((UINT) ffi->code));
}
else
return(errorProcExt(ERR_FFI_PREP_FAILED, stuffString(sPtr->name)));
#endif /* FFI */
CALLBACK_SIMPLE:
getIntegerExt(cell, &n, FALSE);
if(n > 15) return(errorProc(ERR_NUMBER_OUT_OF_RANGE));
getSymbol(params->next, &sPtr);
callback[n].sym = sPtr;
return(stuffInteger((UINT)callback[n].func));
}
/* ========================= FFFI using ffilib ========================== */
/* because of the non-standard cell in FFI symbol->contents, it cannot be
memory managed in an efficient way. This is why FFI functions and structs
can only be defined once. A repeated definitions will return nil and leave
the symbol's original definition untouched.
Thanks to Stefan Sonnenberg for doing most of the coding in this section.
*/
#ifdef FFI
typedef struct
{
char * name;
ffi_type * type;
int size;
} FFITYPE;
/* of the native C-types only types, which are the same
on LP64, LLP64 and ILP32, are supported
*/
/* custom ffi_type_charpointer see initFFI() */
ffi_type ffi_type_charpointer = {0, 0, 0, NULL};
FFITYPE ffi_types[] =
{
{"void", &ffi_type_void},
{"char", &ffi_type_sint8},
{"byte", &ffi_type_uint8},
{"unsigned short int", &ffi_type_uint16},
{"short int", &ffi_type_sint16},
{"unsigned int",&ffi_type_uint32},
{"int", &ffi_type_sint32},
#ifdef NEWLISP64
{"long", &ffi_type_sint64},
#else
{"long", &ffi_type_sint32},
#endif
{"long long", &ffi_type_sint64},
{"float", &ffi_type_float},
{"double", &ffi_type_double},
{"char*", &ffi_type_charpointer}, /* zero terminated string buffers with textual info */
{"void*", &ffi_type_pointer}, /* string or address with binary info, address return */
{NULL, NULL}
};
void initFFI(void)
{
memcpy(&ffi_type_charpointer, &ffi_type_pointer, sizeof(ffi_type));
}
/* creates ffi usable struct information */
CELL * p_struct(CELL * params)
{
CELL * ffiCell;
SYMBOL * symbol;
FFIMPORT * ffi;
int i;
params = getSymbol(params, &symbol);
if(isFFIsymbol(symbol->flags)) /* don't redefine */
return(stuffSymbol(symbol));
if(isProtected(symbol->flags))
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
if(params == nilCell)
return(errorProc(ERR_MISSING_ARGUMENT));
symbol->flags |= SYMBOL_FFI | SYMBOL_PROTECTED;
ffiCell = getCell(CELL_IMPORT_FFI);
ffiCell->aux = (UINT)calloc(sizeof(FFIMPORT), 1);
ffi = (FFIMPORT *) ffiCell->aux;
ffi->name = symbol->name;
ffiCell = ffiPreparation(ffiCell, params, FFI_STRUCT);
ffi->cstruct = calloc(sizeof(ffi_type),1);
ffi->cstruct->elements = calloc(sizeof(ffi_type *) * (ffi->cif.nargs + 1), 1);
for(i = 0; i < ffi->cif.nargs; i++)
ffi->cstruct->elements[i] = ffi->cif.arg_types[i];
/* last elements[nargs] must be left 0, NULL */
/* size and alignment are already 0, to let ffi know, it has to calculate */
ffi->cstruct->type = FFI_TYPE_STRUCT;
if(ffi_prep_cif(&ffi->cif, FFI_DEFAULT_ABI, 0, ffi->cstruct, 0) != FFI_OK)
return(errorProc(ERR_FFI_PREP_FAILED));
deleteList((CELL *)symbol->contents);
symbol->contents = (UINT)ffiCell;
return(stuffSymbol(symbol));
}
CELL * packFFIstruct(CELL * cell, CELL * params)
{
char chrV;
unsigned char byteV;
short int shortV;
unsigned short int uint16V; /* 16 bit */
unsigned int uint32V; /* 32 bit */
int int32V; /* 32 bit */
unsigned long long uint64V; /* 64 bit */
float floatV;
double doubleV;
ffi_type ** elements;
ffi_type *elem;
size_t offset = 0;
unsigned short pad;
char * data;
int listFlag = 0;
FFIMPORT * ffi;
ffi = (FFIMPORT *) cell->aux;
if(ffi->cstruct && ffi->cstruct->type != FFI_TYPE_STRUCT)
return(errorProc(ERR_FFI_STRUCT_EXPECTED));
elements = (ffi_type **) ffi->cstruct->elements;
data = allocMemory(ffi->cstruct->size + 1);
memset(data, 0, ffi->cstruct->size + 1);
/* check if data come in as a list like in traditional pack */
/* computing offsets and copy data */
elements = (ffi_type **) ffi->cstruct->elements;
while( (elem = *elements++) != NULL)
{
if(params->type == CELL_NIL) break;
if(listFlag)
cell = params;
else
cell = evaluateExpression(params);
/* accept data in a list */
if(isList(cell->type))
{
params = cell = (CELL *)cell->contents;
listFlag = 1;
}
/* aligned ? */
pad = offset % elem->alignment;
/* no, add size of alignment boundary - padding bytes */
if (pad != 0) offset += elem->alignment - pad;
#ifndef NEWLISP64
if(cell->type == CELL_FLOAT || cell->type == CELL_INT64)
uint64V = *(INT64 *)&cell->aux;
else /* CELL_LONG and CELL_STRING */
uint64V = cell->contents;
#else
uint64V = cell->contents;
#endif
if( elem == &ffi_type_sint8 )
{
chrV = (char)uint64V;
memcpy(data + offset, &chrV, sizeof(char));
}
else if( elem == &ffi_type_uint8)
{
byteV = (char)uint64V;
memcpy(data + offset, &byteV, sizeof(unsigned char));
}
else if( elem == &ffi_type_uint16)
{
uint16V = (unsigned short int)uint64V;
memcpy(data + offset, &uint16V, sizeof(unsigned short int));
}
else if( elem == &ffi_type_sint16)
{
shortV = (short int)uint64V;
memcpy(data + offset, &shortV, sizeof(short int));
}
else if( elem == &ffi_type_uint32)
{
uint32V = (unsigned int)uint64V;
memcpy(data + offset, &uint32V, sizeof(unsigned int));
}
else if(elem == &ffi_type_sint32)
{
int32V = (int)uint64V;
memcpy(data + offset, &int32V, sizeof(int));
}
else if(elem == &ffi_type_sint64)
memcpy(data + offset, &uint64V, sizeof(long long));
else if(elem == &ffi_type_float)
{
floatV = (float) *(double *)&cell->aux;
memcpy(data + offset, &floatV, sizeof(float));
}
else if(elem == &ffi_type_double)
{
doubleV = getDirectFloat(cell);
memcpy(data+offset, &doubleV, sizeof(double));
}
else if(elem == &ffi_type_pointer || elem == &ffi_type_charpointer)
memcpy(data + offset, (void *)&cell->contents, sizeof(void *));
else /* just copy what's there, must be struct */
memcpy(data + offset, (void *)cell->contents, elem->size);
/* grow offset by size of last element */
offset += elem->size;
/* go to next param and repeat */
params = params->next;
}
cell = makeStringCell(data, ffi->cstruct->size);
return(cell);
}
CELL * unpackFFIstruct(CELL * cell, char * data)
{
FFIMPORT * ffi;
ffi = (FFIMPORT *) cell->aux;
if(ffi->cstruct && ffi->cstruct->type != FFI_TYPE_STRUCT)
return(errorProc(ERR_FFI_STRUCT_EXPECTED));
/* This is redundant? works with or without it on OSX, Windows, Linux.
if(ffi->cstruct->size != 0)
if(ffi_prep_cif(&ffi->cif, FFI_DEFAULT_ABI, 0, ffi->cstruct,0) != FFI_OK)
return(errorProc(ERR_FFI_PREP_FAILED));
*/
return(unpackFFI(ffi->cstruct, data));
}
CELL * unpackFFI(ffi_type * ffiPtr, char * data)
{
ffi_type * elem;
ffi_type ** elements;
CELL * cell;
size_t offset = 0;
unsigned short pad;
cell = getCell(CELL_EXPRESSION);
elements = ffiPtr->elements;
while( (elem = *elements++) != NULL )
{
pad = offset % elem->alignment;
if (pad != 0) offset += elem->alignment - pad;
addList(cell, (CELL *) ffiTypeToCell(elem, data + offset));
offset += elem->size;
}
return(cell);
}
ffi_type * getFFIType(char * type)
{
int i = 0;
char * name;
SYMBOL * sPtr;
CELL * sType;
FFIMPORT * tmp;
while((name = ffi_types[i].name))
{
if(strcmp(name, type) == 0)
return(ffi_types[i].type);
++i;
}
/* ok - no primitive type found: try to lookup struct definition */
sPtr = lookupSymbol(type,currentContext);
if(sPtr)
{
sType = (CELL *)sPtr->contents;
tmp = (FFIMPORT *)sType->aux;
return( (ffi_type *)tmp->cstruct );
}
errorProcExt2(ERR_FFI_INVALID_TYPE, stuffString(type) );
return(NULL);
}
#define MAX_TYPE_ARGS 32
CELL * ffiPreparation(CELL * pCell, CELL * params, int type)
{
CELL * arg;
FFIMPORT * ffi;
ffi_type **atypes;
unsigned int nargs;
ffi_type *rtype;
if(pCell->type != CELL_IMPORT_FFI)
errorProcExt(ERR_INVALID_PARAMETER, pCell);
ffi = (FFIMPORT *)pCell->aux;
ffi->func = FFI_FN(pCell->contents);
ffi->type = type;
atypes = calloc(sizeof(ffi_type) * MAX_TYPE_ARGS, 1);
rtype = &ffi_type_void;
/* get return type if not a structure */
if(type != FFI_STRUCT)
{
arg = evaluateExpression(params);
if(arg->type == CELL_STRING)
rtype = getFFIType((char *) arg->contents);
else
errorProcExt(ERR_STRING_EXPECTED, arg);
params = params->next;
}
nargs=0;
while((arg = evaluateExpression(params)) != nilCell)
{
if(arg->type == CELL_STRING)
{
/* skip void arguments */
if(strcmp((char *)arg->contents,"void") != 0)
{
atypes[nargs] = getFFIType((char *) arg->contents);
nargs++;
}
}
else
errorProcExt(ERR_INVALID_PARAMETER,pCell);
params = params->next;
if(nargs == MAX_TYPE_ARGS)
break;
}
ffi->cif.nargs = nargs;
ffi->cif.arg_types = atypes;
if(ffi_prep_cif(&ffi->cif, FFI_DEFAULT_ABI, nargs, rtype, atypes) != FFI_OK)
errorProcExt(ERR_FFI_PREP_FAILED, pCell);
return(pCell);
}
CELL * executeLibFFI(CELL * pCell, CELL * params)
{
FFIMPORT * ffi;
int c = 0;
ffi_type * ffiType;
INT64 value64;
UINT value;
void **avalues;
CELL * cell;
void * result;
double valueDouble;
ffi = (FFIMPORT *)pCell->aux;
if(ffi->type == FFI_STRUCT)
return(pCell);
avalues = alloca(sizeof(void *) * 16);
while(params != nilCell)
{
if(c >= ffi->cif.nargs) /* too much args */
errorProc(ERR_NUM_ARGS);
ffiType = ffi->cif.arg_types[c];
if(ffiType != &ffi_type_void)
avalues[c] = alloca(ffiType->size);
/* printf("c: %d size:%ld\n", c, ffiType->size); */
if(ffiType->type == FFI_TYPE_STRUCT)
{
cell = evaluateExpression(params);
if(cell->type == CELL_STRING)
memcpy(avalues[c],(void*) cell->contents,ffiType->size);
else
{
getIntegerExt(cell, (UINT *) &value, FALSE);
memcpy(avalues[c], (void **) value, ffiType->size);
}
params = params->next;
}
else if(ffiType == &ffi_type_pointer) /* "void*" */
{
cell = evaluateExpression(params);
if(cell->type == CELL_STRING)
*(UINT *)avalues[c] = cell->contents;
else
getIntegerExt(cell, avalues[c], FALSE);
params = params->next;
}
else if(ffiType == &ffi_type_charpointer) /* "char*" */
{
params = getString(params, avalues[c]);
}
else if(ffiType == &ffi_type_float) /* float 64-bit */
{
params = getFloat(params, &valueDouble);
*(float *)avalues[c] = valueDouble;
}
else if(ffiType == &ffi_type_double) /* double 64-bit */
params = getFloat(params, avalues[c]);
else if(ffiType == &ffi_type_sint64
|| ffiType == &ffi_type_uint64) /* int 64-bit unsiged 64-bit in newLISP */
{
params = getInteger64Ext(params, &value64, TRUE);
avalues[c] = alloca(sizeof(INT64));
*(INT64 *)avalues[c] = value64;
}
else
{
params = getInteger(params, &value);
if(ffiType == &ffi_type_sint8) /* char 8-bit */
*(char *)avalues[c] = value;
else if(ffiType == &ffi_type_uint8) /* unsigned char 8-bit */
*(unsigned char *)avalues[c] = value;
else if(ffiType == &ffi_type_sint16) /* short int 16-bit */
*(short int *)avalues[c] = value;
else if(ffiType == &ffi_type_uint16) /* unsigned short int 16-bit */
*(unsigned short int *)avalues[c] = value;
else if(ffiType == &ffi_type_sint32) /* int 32-bit */
*(int *)avalues[c] = value;
else if(ffiType == &ffi_type_uint32) /* unsigned int 32-bit */
*(unsigned int *)avalues[c] = value;
}
c++;
}
if(c < ffi->cif.nargs) /* not enough args */
errorProc(ERR_NUM_ARGS);
result = (ffi_type *) alloca(sizeof(ffi->cif.rtype->size));
ffi_call(&ffi->cif, FFI_FN(pCell->contents), result, avalues);
return ffiTypeToCell(ffi->cif.rtype, result);
}
CELL * ffiTypeToCell(ffi_type *type, void * result)
{
double valueDouble;
char * cPtr;
/* returning a structure */
if(type->type == FFI_TYPE_STRUCT)
return(unpackFFI(type, result));
/*
return stuffStringN(result, type->size);
*/
/* for C displayable strings use return type "char*"
for destructuring pointers to binary info
use "void*" as return type */
else if(type == &ffi_type_charpointer)
{
cPtr = *(char **)result;
if(cPtr == NULL)
errorProc(ERR_CANNOT_CONVERT_NULL);
return stuffString(cPtr);
}
else if(type == &ffi_type_pointer)
return stuffInteger(*((UINT *)result));
else if(type == &ffi_type_double)
return stuffFloat(*(double *)result);
else if(type == &ffi_type_float)
{
valueDouble = *(float *)result;
return stuffFloat(valueDouble);
}
else if(type == &ffi_type_sint8)
return stuffInteger(*((char *)result));
else if(type == &ffi_type_uint8)
return stuffInteger(*((unsigned char *)result));
else if(type == &ffi_type_sint16)
return stuffInteger(*((short int *)result));
else if(type == &ffi_type_uint16)
return stuffInteger(*((unsigned short int *)result));
else if(type == &ffi_type_sint32)
return stuffInteger(*((int *)result));
else if(type == &ffi_type_uint32)
return stuffInteger64(*((unsigned int *)result));
else if(type == &ffi_type_sint64 || type == &ffi_type_uint64)
return stuffInteger64(*((INT64 *)result));
return nilCell;
}
/* This function gets called by external C functions/libraries */
void ffi_trampoline(ffi_cif *cif, void *ret, void **args, void *data)
{
SYMBOL *symbol;
CELL *arg = NULL;
CELL *next;
CELL *result;
int c;
INT64 value64;
UINT value;
if(!cif)
return;
if(ret)
memset(ret, 0, cif->rtype->size);
symbol = (SYMBOL *) ((ffi_closure_data*) data)->symbol;
if(!symbol)
return;
if(cif->nargs)
{
arg = next = ffiTypeToCell(cif->arg_types[0], args[0]);
for(c = 1;c < cif->nargs; c++)
next = next->next = ffiTypeToCell(cif->arg_types[c], args[c]);
}
executeSymbol(symbol,(CELL *) arg, &result);
if((cif->rtype == &ffi_type_pointer) || (cif->rtype->type == FFI_TYPE_STRUCT) ) /* string or void pointer, or struct */
*(UINT *) ret = (UINT) result->contents;
else if(cif->rtype == &ffi_type_float) /* float 64-bit */
*(float *)ret = (float) getDirectFloat(result);
else if(cif->rtype == &ffi_type_double) /* double 64-bit */
*(double *)ret = getDirectFloat(result);
/* int 64-bit in newLISP */
else if(cif->rtype == &ffi_type_sint64 || cif->rtype == &ffi_type_uint64)
{
getInteger64Ext(result, &value64, TRUE);
*(INT64 *)ret = value64;
}
else if(cif->rtype == &ffi_type_void)
goto TRAMPOLINE_FINISH;
else
{
getIntegerExt(result, &value, FALSE);
if(cif->rtype == &ffi_type_sint8) /* char 8-bit */
*(char *)ret = (char) value;
else if(cif->rtype == &ffi_type_uint8) /* unsigned char 8-bit */
*(unsigned char *)ret = (unsigned char) value;
else if(cif->rtype == &ffi_type_sint16) /* short int 16-bit */
*(short int *)ret = (short int) value;
else if(cif->rtype == &ffi_type_uint16) /* unsigned short int 16-bit */
*(unsigned short int *)ret = (unsigned short int) value;
else if(cif->rtype == &ffi_type_sint32) /* int 32-bit */
*(int *)ret = (int) value;
else if(cif->rtype == &ffi_type_uint32) /* unsigned int 32-bit */
*(unsigned int *)ret = (unsigned int) value;
else
*(double *)ret = sqrt(-1.0); /* return NaN; never happens */
}
TRAMPOLINE_FINISH:
deleteList(result);
return;
}
#endif /* FFI */
/* end of file */