Previous: Improvements To Make, Up: Improvements To Make


6.4.1 VMS Dynamic Linking

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.

  1. Say you have this main.c program:
    main()
         {init_lisp();
          lisp_repl();}
    
  2. and you have your lisp in files 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
    
  3. where 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
    means to keep the name local to the shared library. You almost always want to do that for a good clean library.
    SHR,NOWRT
    means shared-read-only. Which is the default for code, and is also good for efficiency of some data structures.
    NOSHR,LCL
    is what you want for everything else.

    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.

  4. Now MAIN.EXE would be linked thusly:
    $ 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.

  5. Now say you have a file of optional subrs, 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.

  6. Then the dynamic loader would have to do this:
    {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.