home *** CD-ROM | disk | FTP | other *** search
- /* xsdynload - Dynamic loading and C function calling routines. */
- /* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */
- /* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */
- /* You may give out copies of this software; for conditions see the */
- /* file COPYING included with this distribution. */
-
- /* Calling conventions are based on the conventions given in the New S */
- /* book. Calling conventions for dyn-load are based on a combination */
- /* KCL's si:faslink conventions and Bill Dunlap's dyn.load2 for New S. */
- /* */
- /* The dynamic loading code is based on the KCL faslink function and */
- /* Bill Dunlap's dynamic loader for New S. */
-
- #ifdef FOREIGNCALL
- #include "xlisp.h"
-
- #define nil 0L
-
- #define seqlen(x) ((vectorp(x)) ? getsize(x) : llength(x))
-
- extern char buf[];
- extern char *progname;
- extern LVAL s_true, k_verbose;
-
- extern LVAL newvector(), make_string(), mklist(), getnextelement();
- extern double makedouble();
-
- typedef int (*pfi_t)(); /* pointer to function returning integer. */
- typedef LVAL (*pfl_t)(); /* pointer to function returning LVAL. */
-
- #define HASHSIZE 397
-
- static int verbose;
- static LVAL s_cfun_table = NIL;
- static char lbuf[100];
-
- #include "foreign.h"
-
- /************************************************************************/
- /** **/
- /** Public Allocation and Error Signalling Functions **/
- /** **/
- /************************************************************************/
-
- static LVAL current_allocs;
-
- /* allocate space that will be garbage collected after return */
- char *xscall_alloc(n, m)
- int n, m;
- {
- LVAL adata;
- char *p;
-
- adata = newadata(n, m, FALSE);
- if (adata == NIL || (p = getadaddr(adata)) == nil)
- xlfail("allocation failed");
- current_allocs = cons(adata, current_allocs);
- return(p);
- }
-
- /* error routint for use within C functions */
- xscall_fail(s) char *s; { xlfail(s); }
-
- /************************************************************************/
- /** **/
- /** Lisp to C/FORTRAN Data Conversion **/
- /** **/
- /************************************************************************/
-
- #define IN 0
- #define RE 1
- #define MAXARGS 15
-
- typedef struct {
- int type, size;
- char *addr;
- } call_arg;
-
- /* convert lisp argument to allocated pointer */
- static call_arg lisp2arg(x)
- LVAL x;
- {
- call_arg a;
- LVAL elem, data;
- int i;
-
- xlprot1(x);
-
- /* make sure x is a sequence and find its length */
- if (! sequencep(x)) x = consa(x);
- a.size = seqlen(x);
-
- /* determine the mode of the data */
- for (i = 0, a.type = IN, data = x; i < a.size; i++) {
- elem = getnextelement(&data, i);
- if (floatp(elem)) a.type = RE;
- else if (! fixp(elem)) xlerror("not a real number", elem);
- }
-
- /* allocate space for the data */
- a.addr = xscall_alloc(a.size, (a.type == IN) ? sizeof(int) : sizeof(double));
-
- /* fill the space */
- for (i = 0, data = x; i < a.size; i++) {
- elem = getnextelement(&data, i);
- if (a.type == IN) ((int *) a.addr)[i] = getfixnum(elem);
- #if !(defined(ibm032) && defined(__HIGHC__))
- else ((double *) a.addr)[i] = makedouble(elem);
- #else /* avoid bug in hc 2.1n C compiler on IBM RT running AOS 4.3 */
- else {
- double *dbl = &((double *)a.addr)[i] ;
- *dbl = makedouble(elem) ;
- }
- #endif
- }
-
- xlpop();
- return(a);
- }
-
- /* copy allocated pointer back to new lisp list */
- static LVAL arg2lisp(a)
- call_arg a;
- {
- LVAL x, next;
- int i;
-
- xlsave1(x);
- x = mklist(a.size, NIL);
- for (i = 0, next = x; i < a.size; i++, next = cdr(next)) {
- if (a.type == IN) rplaca(next, cvfixnum((FIXTYPE) ((int *) a.addr)[i]));
- else rplaca(next, cvflonum((FLOTYPE) ((double *) a.addr)[i]));
- }
- xlpop();
- return(x);
- }
-
- /************************************************************************/
- /** **/
- /** Foreign Function Call Functions **/
- /** **/
- /************************************************************************/
-
- static LVAL call_foreign(which)
- int which;
- {
- LVAL result, name, old_allocs, next;
- call_arg args[MAXARGS], *pargs;
- int nargs;
- int (*routine)();
- char *get_caddress();
- char *pattern;
-
- xlstkcheck(3);
- xlsave(old_allocs);
- xlprotect(current_allocs);
- xlsave(result);
- old_allocs = current_allocs;
- current_allocs = NIL;
-
- /* get the routine pointer */
- name = xlgastring();
- pattern = (which == 'C') ? INTERNAL_CNAME_PATTERN : INTERNAL_FNAME_PATTERN;
- sprintf(lbuf, pattern, getstring(name));
- routine = (pfi_t) get_caddress(lbuf);
- if (! routine) xlerror("can't find internal symbol by this name", name);
-
- /* convert the arguments to allocated pointers */
- for (nargs = 0; moreargs(); nargs++) {
- if (nargs >= MAXARGS) xlfail("too many arguments");
- args[nargs] = lisp2arg(xlgetarg());
- }
-
- /* make the call -- there must be a better way to do this */
- switch (nargs) {
- case 0: routine(); break;
- case 1: routine(args[0].addr); break;
- case 2: routine(args[0].addr, args[1].addr); break;
- case 3: routine(args[0].addr, args[1].addr, args[2].addr); break;
- case 4:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr);
- break;
- case 5:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr);
- break;
- case 6:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr);
- break;
- case 7:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr);
- break;
- case 8:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr);
- break;
- case 9:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr);
- break;
- case 10:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr, args[9].addr);
- break;
- case 11:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr, args[9].addr, args[10].addr);
- break;
- case 12:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr, args[9].addr, args[10].addr, args[11].addr);
- break;
- case 13:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr, args[9].addr, args[10].addr, args[11].addr,
- args[12].addr);
- break;
- case 14:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr, args[9].addr, args[10].addr, args[11].addr,
- args[12].addr, args[13].addr);
- break;
- case 15:
- routine(args[0].addr, args[1].addr, args[2].addr, args[3].addr,
- args[4].addr, args[5].addr, args[6].addr, args[7].addr,
- args[8].addr, args[9].addr, args[10].addr, args[11].addr,
- args[12].addr, args[13].addr, args[14].addr);
- break;
- }
-
- /* convert the pointers back to lists, grouped in a list */
- result = (nargs > 0) ? mklist(nargs, NIL) : NIL;
- for (next = result, pargs = args; consp(next); next = cdr(next), pargs++)
- rplaca(next, arg2lisp(*pargs));
-
- current_allocs = old_allocs;
- xlpopn(3);
-
- return(result);
- }
-
- /* CALL-CFUN */
- LVAL xscall_cfun() { return(call_foreign('C')); }
-
- /* CALL-FSUB */
- LVAL xscall_fsub() { return(call_foreign('F')); }
-
- /* CALL-LFUN */
- LVAL xscall_lfun()
- {
- LVAL name, old_allocs, result;
- LVAL (*routine)();
- char *get_caddress();
-
- xlstkcheck(2);
- xlsave(old_allocs);
- xlprotect(current_allocs);
- old_allocs = current_allocs;
- current_allocs = NIL;
-
- name = xlgastring();
- sprintf(lbuf, INTERNAL_CNAME_PATTERN, getstring(name));
-
- routine = (pfl_t) get_caddress(lbuf);
- if (! routine) xlerror("can't find internal symbol by this name", name);
- result = routine();
- current_allocs = old_allocs;
- xlpopn(2);
-
- return(result);
- }
-
- /************************************************************************/
- /** **/
- /** Fake COFF ldfcn's for BSD **/
- /** **/
- /************************************************************************/
-
- #ifdef STDBSD
-
- #define LDFILE FILE
- #define SYMENT struct nlist
- #define SUCCESS TRUE
- #define FAILURE FALSE
- #define LDNAMELIMIT 100
- #define AOUTHDR struct exec
- #define SCNHDR AOUTHDR
- #define FREAD fread
-
- static struct exec header;
- static char ldnamebuf[LDNAMELIMIT];
-
- static LDFILE *ldopen(name, dummy)
- char *name, *dummy;
- {
- LDFILE *fp;
-
- if ((fp = fopen(name, "r")) == NULL) xlfail("cannot open ld file");
- if (fread((char *) &header, sizeof(header), 1, fp) != 1 ||
- feof(fp) || ferror(fp)) {
- fclose(fp);
- fp = NULL;
- }
- return(fp);
- }
-
- static ldtbread(fp, i, psym)
- LDFILE *fp;
- int i;
- SYMENT *psym;
- {
- if (i < 0 || i >= header.a_syms / sizeof(SYMENT)) return(FAILURE);
- if (fseek(fp, N_SYMOFF(header) + i * sizeof(SYMENT), 0) < 0) return(FAILURE);
- if (fread((char *) psym, sizeof(SYMENT), 1, fp) != 1 ||
- feof(fp) || ferror(fp)) return(FAILURE);
- return(SUCCESS);
- }
-
- static char *ldgetname(fp, psym)
- LDFILE *fp;
- SYMENT *psym;
- {
- char *bp = ldnamebuf;
- long which = psym->n_un.n_strx;
- int i = 0;
-
- *bp = '\0';
- if (which) {
- ok_fseek(fp, N_STROFF(header) + which, 0);
- while ((*bp++ = getc(fp)) != '\0')
- if (++i >= LDNAMELIMIT) xlfail("name too long for ld buffer");
- }
- return(ldnamebuf);
- }
-
- static ldohseek(fp)
- LDFILE *fp;
- {
- if (fseek(fp, 0, 0) < 0) return(FAILURE);
- else return(SUCCESS);
- }
-
- static ldclose(fp)
- LDFILE *fp;
- {
- fclose(fp);
- return(SUCCESS);
- }
-
- #endif STDBSD
-
- /************************************************************************/
- /** **/
- /** Dynamic Loading Functions **/
- /** **/
- /************************************************************************/
-
- extern char *calloc();
-
- #define round_up(a, d) ((long)(a)%(d) ? (d)*((long)(a)/(d) + 1) : (long)(a))
-
- #ifdef STDBSD
- #define SYMVALUE(sym) ((char *) ((sym).n_value))
- #ifndef SYM_IS_GLOBAL_FUNCTION
- #define SYM_IS_GLOBAL_FUNCTION(ldptr,symbol) \
- (((symbol).n_type & N_TYPE) == N_TEXT && ((symbol).n_type & N_EXT))
- #endif /* SYM_IS_GLOBAL_FUNCTION */
- #endif STDBSD
-
- /* DYN-LOAD function */
- LVAL xsdynload()
- {
- char *name, *libs;
- LVAL flag, arg;
- LVAL k_fortran = xlenter(":FORTRAN");
- LVAL k_libflags = xlenter(":LIBFLAGS");
- int fort;
-
- name = (char *) getstring(xlgastring());
- if (! xlgetkeyarg(k_verbose, &flag)) flag = (VERBDFLT) ? s_true : NIL;
- verbose = flag != NIL;
- if (! xlgetkeyarg(k_fortran, &flag)) flag = NIL;
- fort = flag != NIL;
- if (xlgetkeyarg(k_libflags, &arg) && stringp(arg))
- libs = (char *) getstring(arg);
- else libs = "";
-
- link_and_load(name, libs, fort);
-
- return(s_true);
- }
-
- static enter_csymbol(name, addr)
- char *name, *addr;
- {
- LVAL table, list, entry;
- int i;
-
- if (s_cfun_table == NIL) {
- s_cfun_table = xlenter("__cfun_table__");
- setvalue(s_cfun_table, newvector(HASHSIZE));
- }
-
- table = getvalue(s_cfun_table);
- if (arrayp(table)) {
- i = hash(name, getsize(table));
-
- /* see if name is already in the table; replace its value if it is */
- for (list = getelement(table, i); consp(list); list = cdr(list)) {
- entry = car(list);
- if (stringp(car(entry)) && strcmp(name, getstring(car(entry))) == 0) {
- rplacd(entry, cvfixnum((FIXTYPE) addr));
- return;
- }
- }
-
- /* otherwise (not returned yet) make a new entry */
- entry = cons(NIL, NIL);
- setelement(table, i, cons(entry, getelement(table, i)));
- rplaca(entry, make_string(name));
- rplacd(entry, cvfixnum((FIXTYPE) addr));
- }
- }
-
- static char *find_hash_entry(name)
- char *name;
- {
- LVAL table, entry, list;
- int i;
-
- if (! symbolp(s_cfun_table)) return(NULL);
-
- table = getvalue(s_cfun_table);
- if (arrayp(table)) {
- i = hash(name, getsize(table));
- for (list = getelement(table, i); consp(list); list = cdr(list)) {
- entry = car(list);
- if (stringp(car(entry)) && strcmp(name, getstring(car(entry))) == 0)
- return((fixp(cdr(entry))) ? (char *) getfixnum(cdr(entry)) : NULL);
- }
- }
- return (NULL);
- }
-
- static char *get_caddress(name)
- char *name;
- {
- struct nlist nl[2];
- char *addr;
-
- if ((addr = find_hash_entry(name)) != NULL) return(addr);
- else {
- #ifdef COFF_FORMAT
- nl[0].n_name = name;
- nl[1].n_name = "";
- #else
- nl[0].n_un.n_name = name;
- nl[1].n_un.n_name = "";
- #endif
- if (nlist(progname, nl) == -1)
- xlfail("file not found or invalid name list");
- if((addr = (char *) nl[0].n_value) != NULL) {
- enter_csymbol(name, addr);
- return(addr);
- }
- else return (NULL);
- }
- }
-
- #ifdef STATIC_LOAD_ONLY
- static link_and_load(fname, libs, fort)
- char *fname, *libs;
- int fort;
- {
- xlfail("dynamic loading not available on this system");
- }
- #else
- #ifndef HAS_OWN_DYNLOAD
- static link_and_load(fname, libs, fort)
- char *fname, *libs;
- int fort;
- {
- char tmpfname[TMPNAMESIZE];
- char *code_start, *addr, *syslibs;
- int size, size_guess;
-
- /* make the libstring, the tempfile name and the initial code space */
- syslibs = (fort) ? FLIBS : CLIBS;
- sprintf(tmpfname, TMPPATTERN, getpid());
- size_guess = MIN_ALLOC;
- addr = calloc(1, size_guess);
- if (addr == nil) xlfail("can't make initial code allocation");
- code_start = (char *) round_up(addr, PAGE_SIZE);
- size_guess -= (long) (code_start - addr);
-
- /* do an incremental load of the file and libs against xlisp */
- sprintf(buf, LDPATTERN,
- progname, (int) code_start, fname, libs, syslibs, tmpfname);
- if (verbose) printf("first ld pass\n%s\n", buf);
- if (system(buf) != 0) {
- free(addr);
- xlfail("link failed");
- }
-
- /* check the code size and redo the load if needed */
- size = code_size(tmpfname, code_start);
- if (size_guess < size) {
- free(addr);
- addr = calloc(1, size + PAGE_SIZE);
- if (addr == nil) xlfail("can't make code allocation");
- code_start = (char *) round_up(addr, PAGE_SIZE);
- sprintf(buf, LDPATTERN,
- progname, (int) code_start, fname, libs, syslibs, tmpfname);
- if (verbose) printf("second ld pass\n%s\n", buf);
- if (system(buf) != 0) {
- free(addr);
- xlfail("link failed");
- }
- if (size < code_size(tmpfname, code_start)) {
- free(addr);
- xlfail("can't figure out tempfile size");
- }
- }
-
- /* read in the object file */
- if (verbose) printf("reading in the code ..."); fflush(stdout);
- read_code(tmpfname, code_start);
- if (verbose) printf("done\n");
-
- /* enter the external symbols into the hash table */
- if (verbose) printf("entering symbols..."); fflush(stdout);
- enter_symbols(tmpfname);
- if (verbose) printf("done\n");
-
- /* unlink the tempfile */
- unlink(tmpfname);
- }
-
- static code_size(tmpfname, code_start)
- char *tmpfname, *code_start;
- {
- LDFILE *fp;
- AOUTHDR header;
- SCNHDR scnheader;
- int size;
-
- if ((fp = ldopen(tmpfname, NULL)) == NULL)
- xlfail("cannot open temporary ld file");
-
- if (ldohseek(fp) == FAILURE) xlfail("could not seek to a.out header");
- if (FREAD((char *) &header, sizeof(header), 1, fp) < 1)
- xlfail("could not read a.out header");
-
- #ifdef COFF_FORMAT
- /* read last section header and measure size from code start */
- /* section numbers begin with one! */
- if (ldshread(fp, (unsigned short) N_SECTIONS(fp), &scnheader)==FAILURE)
- xlfail("cannot read object file section");
- size = SCN_ADDR(fp, scnheader) + SCN_LENGTH(fp, scnheader)
- - (long) code_start;
- #else
- size = header.a_text + header.a_data + header.a_bss;
- #endif COFF_FORMAT
-
- if (ldclose(fp) == FAILURE) xlfail("cannot close tempfile");
- return(size);
- }
-
- static read_code(tmpfname, addr)
- char *tmpfname, *addr;
- {
- LDFILE *fp;
- AOUTHDR header;
- SCNHDR scnheader;
- int size, i;
-
- if ((fp = ldopen(tmpfname, NULL)) == NULL)
- xlfail("cannot open temporary ld file");
-
- if (ldohseek(fp) == FAILURE) xlfail("could not seek to a.out header");
- if (FREAD((char *) &header, sizeof(header), 1, fp) < 1)
- xlfail("could not read a.out header");
-
- #ifdef COFF_FORMAT
- /* read in code and data sections, zero out bss sections */
- /* zeroing should not be needed since space came from */
- /* calloc, but it can't hurt. */
- /* section numbers begin with one! */
- for (i = 1 ; i <= N_SECTIONS(fp) ; i++) {
- if (ldshread(fp, (unsigned short) i, &scnheader)==FAILURE)
- xlfail("cannot read object file section");
- if (SCN_IS_BSS(fp, scnheader))
- bzero((char *) SCN_ADDR(fp, scnheader),
- (int) SCN_LENGTH(fp, scnheader));
- else {
- if (FSEEK(fp, SCN_FILE_LOC(fp, scnheader), 0) == -1)
- xlfail("could not seek to object file section");
- if (FREAD((char *) SCN_ADDR(fp, scnheader), 1,
- (int) SCN_LENGTH(fp, scnheader), fp)
- < SCN_LENGTH(fp, scnheader))
- xlfail("could not read object file section");
- }
- }
- #else
- ok_fseek(fp, (long) N_TXTOFF(header), 0);
- size = header.a_text + header.a_data;
- ok_fread((char *) addr, 1, size, fp);
- #endif
-
- if (ldclose(fp) == FAILURE) xlfail("cannot close tempfile");
- }
-
- static enter_symbols(tmpfname)
- char *tmpfname;
- {
- LDFILE *input;
- SYMENT symbol;
- char *symname, *symaddr;
- int i;
-
- /* open the file */
- if ((input = ldopen(tmpfname, NULL)) == NULL)
- xlfail("cannot open tempfile for symbol reading");
-
- /* process symbols while they last */
- i = 0;
- while (ldtbread(input, i, &symbol) == SUCCESS) {
- i++;
- if (SYM_IS_GLOBAL_FUNCTION(input, symbol)) {
- symname = ldgetname(input, &symbol);
- symaddr = SYMVALUE(symbol);
- enter_csymbol(symname, symaddr);
- }
- }
- if (ldclose(input) == FAILURE) xlfail("cannot close tempfile");
- }
-
- /************************************************************************/
- /** **/
- /** Utility Functions **/
- /** **/
- /************************************************************************/
-
- static ok_fread(ptr, size, nitems, stream)
- char *ptr;
- int size, nitems;
- FILE *stream;
- {
- if (fread(ptr, size, nitems, stream) != nitems ||
- feof(stream) || ferror(stream))
- xlfail("error while reading disk file");
- }
-
- static ok_fseek(stream, offset, ptrname)
- FILE *stream;
- long offset;
- int ptrname;
- {
- if (fseek(stream, offset, ptrname) < 0)
- xlfail("error while seeking on disk file");
- }
- #endif HAS_OWN_DYNLOAD
- #endif STATIC_LOAD_ONLY
- #endif FOREIGNCALL
-