Scott Schwartz <schwartz@galapagos.cse.psu.edu> suggests: One way to tidy up the dynamic loading stuff would be to grab the code from perl5.
George Carrette (gjc@mitech.com) outlines how to dynamically link on
VMS. There is already some code in `dynl.c' to do this, but
someone with a VMS system needs to finish and debug it.
main.c' program:
main() {init_lisp(); lisp_repl();}
repl.c', `gc.c',
eval.c and there are some toplevel non-static variables in use
called the_heap, the_environment, and some read-only
toplevel structures, such as the_subr_table.
$ LINK/SHARE=LISPRTL.EXE/DEBUG REPL.OBJ,GC.OBJ,EVAL.OBJ,LISPRTL.OPT/OPT
LISPRTL.OPT' must contain at least this:
SYS$LIBRARY:VAXCRTL/SHARE UNIVERSAL=init_lisp UNIVERSAL=lisp_repl PSECT_ATTR=the_subr_table,SHR,NOWRT,LCL PSECT_ATTR=the_heap,NOSHR,LCL PSECT_ATTR=the_environment,NOSHR,LCL
Notice: The psect (Program Section) attributes.
LCL
SHR,NOWRT
NOSHR,LCL
Note: If you do not have a handy list of all these toplevel variables, do not dispair. Just do your link with the /MAP=LISPRTL.MAP/FULL and then search the map file,
$SEARCH/OUT=LISPRTL.LOSERS LISPRTL.MAP ", SHR,NOEXE, RD, WRT"
And use an emacs keyboard macro to muck the result into the proper form. Of course only the programmer can tell if things can be made read-only. I have a DCL command procedure to do this if you want it.
$ DEFINE LISPRTL USER$DISK:[JAFFER]LISPRTL.EXE $LINK MAIN.OBJ,SYS$INPUT:/OPT SYS$LIBRARY:VAXCRTL/SHARE LISPRTL/SHARE
Note the definition of the LISPRTL logical name. Without such a
definition you will need to copy `LISPRTL.EXE' over to
`SYS$SHARE:' (aka `SYS$LIBRARY:') in order to invoke the main
program once it is linked.
MYSUBRS.C'. And there
is a routine INIT_MYSUBRS that must be called before using it.
$ CC MYSUBRS.C $ LINK/SHARE=MYSUBRS.EXE MYSUBRS.OBJ,SYS$INPUT:/OPT SYS$LIBRARY:VAXCRTL/SHARE LISPRTL/SHARE UNIVERSAL=INIT_MYSUBRS
Ok. Another hint is that you can avoid having to add the PSECT
declaration of NOSHR,LCL by declaring variables status in
the C language source. That works great for most things.
{void (*init_fcn)(); long retval; retval = lib$find_image_symbol("MYSUBRS","INIT_MYSUBRS",&init_fcn, "SYS$DISK:[].EXE"); if (retval != SS$_NORMAL) error(...); (*init_fcn)();}
But of course all string arguments must be (struct dsc$descriptor *) and the last argument is optional if MYSUBRS is defined as a
logical name or if `MYSUBRS.EXE' has been copied over to
`SYS$SHARE'. The other consideration is that you will want to turn
off C-C or other interrupt handling while you are inside most
lib$ calls.
As far as the generation of all the UNIVERSAL=...
declarations. Well, you could do well to have that automatically
generated from the public `LISPRTL.H' file, of course.
VMS has a good manual called the Guide to Writing Modular Procedures or something like that, which covers this whole area rather
well, and also talks about advanced techniques, such as a way to declare
a program section with a pointer to a procedure that will be
automatically invoked whenever any shared image is dynamically
activated. Also, how to set up a handler for normal or abnormal program
exit so that you can clean up side effects (such as opening a database).
But for use with LISPRTL you probably don't need that hair.
One fancier option that is useful under VMS for `LISPLIB.EXE' is to
define all your exported procedures through an call vector instead
of having them just be pointers into random places in the image, which
is what you get by using UNIVERSAL.
If you set up the call vector thing correctly it will allow you to
modify and relink `LISPLIB.EXE' without having to relink programs
that have been linked against it.
George Carrette (gjc@mitech.com) outlines how to dynamically link on Windows NT:
LISPLIB.exp: LISPLIB.lib: LISPLIB.def $(implib) -machine:$(CPU) -def:LISPLIB.def -out:LISPLIB.lib LISPLIB.DLL : $(LISPLIB_OBJS) LISPLIB.EXP $(link) $(linkdebug) \ -dll \ -out:LISPLIB.DLL \ LISPLIB.EXP $(LISPLIB_OBJS) $(conlibsdll)
LISPDEF.DEF' file has this:
LIBRARY lisplib EXPORT init_lisp init_repl
MAIN.EXE' using:
CLINK = $(link) $(ldebug) $(conflags) -out:$*.exe $** $(conlibsdll) MAIN.EXE : MAIN.OBJ LISPLIB.LIB $(CLINK)
MYSUBRS.DLL' is produced using:
mysubrs.exp: mysubrs.lib: mysubrs.def $(implib) -machine:$(CPU) -def:MYSUBRS.def -out:MYSUBRS.lib mysubrs.dll : mysubrs.obj mysubrs.exp mysubrs.lib $(link) $(linkdebug) \ -dll \ -out:mysubrs.dll \ MYSUBRS.OBJ MYSUBRS.EXP LISPLIB.LIB $(conlibsdll)
MYSUBRS.DEF' has
LIBRARY mysubrs EXPORT INIT_MYSUBRS
LoadLibrary and GetProcAddress.
LISP share_image_load(LISP fname) {long iflag; LISP retval,(*fcn)(void); HANDLE hLib; DWORD err; char *libname,fcnname[64]; iflag = nointerrupt(1); libname = c_string(fname); _snprintf(fcnname,sizeof(fcnname),"INIT_%s",libname); if (!(hLib = LoadLibrary(libname))) {err = GetLastError(); retval = list2(fname,LSPNUM(err)); serror1("library failed to load",retval);} if (!(fcn = (LISP (*)(void)) GetProcAddress(hLib,fcnname))) {err = GetLastError(); retval = list2(fname,LSPNUM(err)); serror1("could not find library init procedure",retval);} retval = (*fcn)(); nointerrupt(iflag); return(retval);}