[comp.sys.apollo] Dynamic loading summarized

beihl@cadillac.CAD.MCC.COM (Gary Beihl) (01/03/89)

Thanks to those who responded to my recent inquiry regarding
dynamic loading.  What follows is a summary of what I've
learned.  It is indeed possible to dynamically load .o files
under the Apollo version of BSD Unix (SR10.0).  There are a
few gotchas however, which keep us from using it in our
application.

First, the .o file to be loaded has to be compiled with the
"-pic" (position-independent code) option.  This can be done
under BSD by specifying -W0,-pic to /bin/cc.  I believe some
performance penalty is paid when using this option due to 
an extra level of indirection.  Second, use the loader_$load()
call to access the system loader.  With the proper setting
of options to this function, one can lookup symbols and execute
functions in the new code.

The rub comes when the new object tries to reference symbols 
in the original executable.  You can't (by default).  It was
suggested to me that I redefine main() to be a stub which 
dynamically loads the entire program, thus making all symbols
known.  Unfortunately, this means everything must be compiled
with -W0,-pic. The loader kept telling me there was some part
of my main program requiring linking (I assume this meant not
compiled with -pic).  The `file' command does not distinguish
-pic from normal code.  Perhaps some of the system libraries
were being complained about.  I *did* compile all the application
code with -pic.

It seems to me that a better solution to this problem is to
have an incremental option on the linker.  The BSD interface
does not provide one.  Perhaps it's possible under AEGIS. I don't
think so.  Incremental linking eliminates the need for position
independent code, since the code can be custom-relocated to
the virtual address specified by the loading application. 




-- 
Gary Beihl  (beihl@mcc.com)

oj@apollo.COM (Ellis Oliver Jones) (01/08/89)

In article <685@cadillac.CAD.MCC.COM> beihl@cadillac.CAD.MCC.COM (Gary Beihl) writes:
>[To be dynamically loaded...] the .o file to be 
>loaded has to be compiled with the
>"-pic" (position-independent code) option.  This can be done
>under BSD by specifying -W0,-pic to /bin/cc.  I believe some
>performance penalty is paid when using this option due to 
>an extra level of indirection. 

There's no big performance penalty.  All the library
code is compiled this way, so the generated code when you
specify -pic is in no way second class.

>The rub comes when the new object tries to reference symbols 
>in the original executable.  You can't (by default).

We often solve this kind of problem by formalizing the
interface between separate executables in the form of an
entry point vector (epv).  An epv is an array of 
procedure pointers.  If module A dynamically loads module B,
we then arrange to pass the entry point vector (or a pointer to it)
from module A to module B.  Then when B calls back to A, it calls through
the epv.

Obviously, you have to alter the structure of your application to use
this technique, so it may not help you.  

>It seems to me that a better solution to this problem is to
>have an incremental option on the linker.  The BSD interface
>does not provide one.

Object modules and executables are indistinguishable.  It is possible
to link any pair of modules together via either /bin/ld or /com/bind .
It's also unnecessary to use /bin/ld or /com/bind at all on self-contained 
programs;  the C runtime library and most other "system subroutines" 
live in global libraries, and therefore are present in your address space
already.  Just run the .o or .bin file!

You might exploit the uniformity of objects and executables by
using a shell script to link together your "main"
module and one of several optional modules, then execute the result,
then delete it when you're done executing it.  This could give
you what you're looking for.

Beware: you CAN'T bind together two cmpexe files to produce a third.

You may also be able to use custom libraries to advantage.   A library
is any object code compiled -pic .  You can use the /com/inlib command to
install a library into a process.  You can also create a global
library, and have it loaded at node-boot time by calling it
/lib/userlib.global .  (In global libraries, beware;  all read-write
static data is initialized to zero, regardless of what kind of initialization
you ask for.)   Global libraries save much space and time when the same
code is running in many different processes.  They're quite painful
to debug, though, so be careful.

If you're interested in dynamic code loading, you should take a look
at the "Extensible Streams Toolkit" manual as well.  You should also
look at that manual if you're curious about the purpose of all the stuff
in /sys/mgrs .

On another subject, I understand that the next release of the PRISM 
(DN10000) operating system will have a maximum process count of
at least 150, and will use substantially less than 1/2 megabyte of
backing store (disk space) per process.  The current numbers are
56 (user processes) and approx. 5 megabytes, so this is a big improvement.

/Ollie Jones (speaking for myself, not necessarily for Apollo Computer, Inc.)
     (get the details right!  check the manuals before trying any of this!)

oj@apollo.COM (Ellis Oliver Jones) (01/09/89)

In my previous article <40b9f17f.d5b2@apollo.COM> oj@canyon.UUCP 
I made a lot of mistakes.  Everyone would be better off disregarding
the article entirely.  So sorry.  I'll try to do my homework better
next time.
/oj