Article 114251 of comp.os.vms:
In article <59l08m$jgm@charm.magnus.acs.ohio-state.edu>, JONESD@er6.eng.ohio-state.edu (David Jones) writes:
>In message <59kpj2$3s0@franklin.its.utas.edu.au>,
>  John.Humble@phys.utas.edu.au (John Humble) writes:
>>We've developed several large shareable images during the years our department
>>has used VAXs.  They are constructed using the MACRO Assembler, using Macro
>>routines developed by one of my predecessors.  The components of the images
>>are various Fortran subroutines and Pascal procedures, together with functions
>>written in both languages.
>>

Well, I just learned more than I wanted to about these beasts while porting
Xforms.  I wouldn't wish that particular learning path on anyone, so here
is a summary of my hard earned knowledge.

The key, as always, is a working example, something you won't find in the
linker manual.  I don't know the origin of this piece of code, it may have 
come from DSNLINK (so I hope this doesn't step on anybody's copyright) but
it helped me understand what is going on immensely.  The key point here is
that in DEC C you have to use /external=common to get the global stuff into
the right format to share.  It says this in the linker manual, but you have
to stumble over it to find it. 

$! make_share_example.com
$ SET VERIFY
$ SET DEFAULT SYS$LOGIN
$!
$!      MYSHR includes routines SUB1 and SUB2, which accesses the
$!      common data area called MYSTR.  Executable image CALLSHR
$!      also accesses MYSTR.
$!
$ CREATE MYSHR.C
#include <string.h>
#include <stdio.h>
extern char mystr[10];
sub1 ()
{
        printf ("In SUB1\n");
        strcpy (mystr, "bbbbbbbbb");
}
sub2 ()
{
        printf ("In SUB2\n");
        strcpy (mystr, "ccccccccc");
}
$!
$!      Executable image CALLSHR calls routines in shareable image
$!      MYSHR, to modify the contents of MYSTR.
$!
$ CREATE CALLSHR.C
#include <string.h>
#include <stdio.h>
extern char mystr[10];
void sub1();
void sub2();
main ()
{
        strcpy (mystr, "aaaaaaaaa");
        printf ("String MYSTR is set to %s\n\n", mystr);
        printf ("Calling SUB1\n");
        sub1 ();
        printf ("String MYSTR is now set to %s\n\n", mystr);
        printf ("Calling SUB2\n");
        sub2 ();
        printf ("String MYSTR is now set to %s\n", mystr);
}
$!
$!      Compile the C source.  Use the qualifier /EXTERN=COMMON_BLOCK
$!      to ensure that DEC C creates a program section (PSECT)
$!      reference for the symbol MYSTR.
$!
$ CC/EXTERN=COMMON_BLOCK/standard=ansi/prefix=all MYSHR
$ CC/EXTERN=COMMON_BLOCK/standard=ansi/prefix=all CALLSHR
$! this works too
$! CC/EXTERN=COMMON_BLOCK MYSHR,CALLSHR
$!
$!      Use the Linker option SYMBOL_VECTOR to define the universal
$!      symbols SUB1 and SUB2 as procedures, and MYSTR as a PSECT.
$!
$ LINK /SHARE /MAP /FULL MYSHR,SYS$INPUT/OPT
GSMATCH=lequal,1,1000
SYMBOL_VECTOR=(sub1=PROCEDURE)
SYMBOL_VECTOR=(sub2=PROCEDURE)
SYMBOL_VECTOR=(mystr=PSECT)
$!SYMBOL_VECTOR=(sub1=PROCEDURE,-
$!               sub2=PROCEDURE,-
$!               mystr=PSECT)
$!
$!      Create the executable image CALLSHR.
$!
$ LINK /MAP /FULL CALLSHR, SYS$INPUT/OPT
SYS$LOGIN:MYSHR/SHARE
$!
$!      The image activator looks for all shareable images in
$!      SYS$SHARE, unless it finds logical names (same as image file
$!      names) to direct it to the location of the shareable image.
$!
$ DEFINE MYSHR SYS$LOGIN:MYSHR.EXE
$ RUN CALLSHR
$!
$ DEASSIGN MYSHR
$ EXIT

Now then, the example file is great, but it doesn't really tell you how
to generate the necessary SYMBOL_VECTOR pieces.  I wrote a .com file to
do that from an .OLB.  All you have to do is to compile everything, stick
it into one or more .OLB files, and then run the following procedures to
pull out the PROCEDURE and DATA SYMBOL_VECTORS.  (We'll get to PSECT)
in a minute.  Standard disclaimers apply!!!!

$!make_opt_from_olb.com
$! 8-NOV-1996, David Mathog, Biology Division, Caltech
$!
$! program which analyzes all .OLB files specified, produces from it
$! a .OPT file, and then uses that to make a shareable image of that
$! object library.  The shareable images are produced in the directory
$! from which this procedure is run.
$!
$ if (P1 .eqs. "")
$ then
$   type sys$Input

This procedure converts a list of .OLB object libraries into a set
of option files for linking shareable images.

  Syntax:
      @make_opt_from_olb  P1 P2 P3
  
  P1 = [dir]lib1.olb [,[dir2]lib2.olb, etc.]

  P2 = [X11 ,Xt, Xmu, Motif, GL, GLu]
    optional shares to include (not case sensitive), these are:
      GLu      sys$share:decw$openglushr.exe/share
      Gl       sys$share:decw$openglshr.exe/share
      Motif    sys$share:decw$xmlibshr/share
      Xmu      sys$share:decw$xmulibshr/share
      Xt       sys$share:decw$xtshr/share
      X11      sys$share:decw$xlibshr/share

  P3 = comman separated list of names for DATA (rather than PROCEDURE names)
  P4 = name of a file to append to the .opt

The option files take their names from the object library names, and 
are created in the directory from which this program is run.

Note that this procedure cannot, and does not, produce the actual
shareable, if the library refers to other libraries, this routine could
not define the appropriate flags in the options file - that must be
done manually.  Also, you might want to remove some of the modules from the
object library, for instance, if the library has GL pieces, but you don't
have that on your system (obviously then programs requiring GL wouldn't
work...).  Here is the final command to make the shareable: 

  $ link/share=[]whatever.exe whatever.opt/opt

$ exit
$ endif
$ list  = P1
$ list2 = P2
$ list3 = F$EDIT(P3,"COLLAPSE,UPCASE")
$ if (list3 .nes. "")then list3 = "," + list3 + ","
$ lenlist3 = F$length(list3)
$ extra = P4
$!
$ i = 0
$ top:
$    target = f$element(i,",",list)
$    if(target .nes. ",")
$    then
$       target = f$edit(target,"TRIM,COLLAPSE")
$       filename = f$parse(target,,,"NAME")
$       write sys$output "Now processing object library ''target'"
$       define/user sys$Output []scratch1.tmp
$       library/list/names 'target'
$       open/write ofil: []'filename'.opt.
$       write ofil: "GSMATCH=lequal,1,1000"
$       open/read  ifil: []scratch1.tmp
$       scanning = 0
$       loop:
$          read/error=done/end=done ifil: string
$          string = f$edit(string,"COMPRESS,TRIM")
$          if(string .eqs. "")then goto loop
$          if(F$EXTRACT(0,7,string) .eqs. "Module ")
$          then
$             if(scanning .eq. 0)then scanning = 1
$             write sys$output string
$          else
$             if(scanning .eq. 0)then goto loop
$             if(f$locate(","+string+",",list3) .nes. lenlist3)
$             then
$                write ofil: "SYMBOL_VECTOR = (",string,"=DATA)"
$             else
$                write ofil: "SYMBOL_VECTOR = (",string,"=PROCEDURE)"
$             endif             
$          endif
$          goto loop
$       done:
$       close ifil:
$       write ofil: target,"/lib"
$!
$! now add any shareable libraries the user specified,
$! do them in a "top down" order
$!
$  list2 = f$edit(list2,"COLLAPSE,UPCASE")
$  list2 = list2 + ","
$  full = f$length(list2)
$  if(f$locate("GLU,",list2) .nes. full)then   write ofil: "sys$share:decw$openglushr.exe/share
$  if(f$locate("GL,",list2) .nes. full)then    write ofil: "sys$share:decw$openglshr.exe/share
$  if(f$locate("MOTIF,",list2) .nes. full)then write ofil: "sys$share:decw$xmlibshr/share"
$  if(f$locate("XMU,",list2) .nes. full)then   write ofil: "sys$share:decw$xmulibshr/share
$  if(f$locate("XT,",list2) .nes. full)then    write ofil: "sys$share:decw$xtshr/share
$  if(f$locate("X11,",list2) .nes. full)then   write ofil: "sys$share:decw$xlibshr/share
$!
$!
$       close ofil:
$!
$!  the following is a very odd way of doing an append that works no matter 
$!  what file types the two in question are, so long as they are both text
$!
$       if(extra .nes. "")
$           then
$             open/append ofil 'filename'.opt
$             search/out=ofil/match=nor 'extra' "this string is not in this file!!!!"
$             close ofil
$           endif
$!
$       delete scratch1.tmp.
$       i = i + 1
$       goto top
$    endif
$! done


Ok, so at this point you've compiled everything with /EXTERNAL=common
(or the equivalent for the language in question), stuck it all into an .OLB,
generated an .OPT file from the .OLB.  Are you done?  Maybe in Fortran, 
where the only global variables are in COMMON, in C and probably Pascal you
have more work to do. Global variables will not have been picked up
properly.  I've never found any tools to do this short of inspecting the
code and locating all of the globals (which should be in .h files for C,
but might not be :-( ). For each that you find, add line like this to the
.OPT file: 

SYMBOL_VECTOR = (FL_EVENT=PSECT)

When that's done, you use your .opt file something like this:

$ link/share=xforms_shr.exe xforms.opt/opt

While you *can* define logicals for these .exe files, I've found that it
is vastly easier to stick them into sys$share, assuming that you have the
privs to do so.

Hint, if you miss declaring one of these PSECTs your code will bomb at a 
point associated with the missing variable.  Finding all of the globals 
that need to be declared this way is the single most painful part of 
putting together the shareable image - there really should be some way to 
do it automatically, but I've not located one.

Oh yeah, key point, everything you link to this beast must ALSO be compiled
/external=COMMON (for DEC C).

This really is an area where the linker/compiler should do more of the
work.  For the life of me I can't see why we shouldn't be able to do: 

$ cc/external=common *.c
$ lib/create/object all.olb *.obj
$ link/share=all_shr.exe/automatic all.olb

After all, 98% of the work in putting together the .opt was accomplished by
the .com above, and the compiler/linker really should be able to
communicate the identities of variables that need PSECT as well. 

Good luck,

David Mathog
mathog@seqaxp.bio.caltech.edu
Manager, sequence analysis facility, biology division, Caltech 


