Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ dso(5) — IRIX 6.5.3f

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

abi(5)

rld(1)

ld(1)

elf(5)

elfdump(1)

dlopen(3)

sgidladd(3)

sgidlopen_version(3)

sgidladd(3)

sgigetdsoversion(3)

gp_overflow(5)



DSO(5)                                                                  DSO(5)



NAME
     DSO - Dynamic Shared Object

TOPIC
     This man page is intended to be both a quick reference and a source of
     detailed information on Dynamic Shared Objects.  It is divided into 4
     sections:

     General Information and Overview

     Linking/Building Suggestions

     Performance Considerations

     Frequently Asked Questions and Detailed Discussions

GENERAL INFORMATION AND OVERVIEW
   Format
     A DSO, or Dynamic Shared Object, is an ELF format object file, very
     similar in structure to an executable program but with no "main".  It has
     a shared component, consisting of shared text and read-only data; a
     private component, consisting of data and the GOT (Global Offset Table);
     several sections that hold information necessary to load and link the
     object; and a liblist, the list of other shared objects referenced by
     this object. Most of the libraries supplied by SGI are available as
     dynamic shared objects.

   PIC -- Position Independent Code
     A DSO is relocatable at runtime; it can be loaded at any virtual address.
     A consequence of this is that all references to external symbols must be
     resolved at runtime.  References from the private region (.e.g. from
     private data) are resolved once at load-time; references from the shared
     region (e.g. from shared text) must go through an indirection table (GOT)
     and hence have a small performance penalty associated with them.

     Code compiled for use in a shared object is referred to as PIC whereas
     non-PIC is usually referred to as non-shared. Non-shared code and PIC
     cannot be mixed in the same object.

   What Happens at Runtime?
     Exec loads the main program and then loads the interpreter specified in
     the main program, generally /usr/lib/libc.so.1 for old 32-bit programs,
     /usr/lib32/libc.so.1 for new 32-bit programs, and /usr/lib64/libc.so.1
     for 64-bit programs, and the interpreter in turns loads rld(1), the
     runtime linking loader, which finishes the exec operation.  Starting with
     main's liblist, rld loads each shared object on the list that is not
     marked as delay-load, reads that object's liblist, and repeats the
     operation until all shared objects have been loaded, in a breadth-first
     manner.  This loading process (breadth first, ignoring delay-loaded
     objects) results in defining a sequence of objects.  Next, rld allocates
     storage for COMMON symbols and fixes up symbolic references in each
     loaded object. (This is necessary because we don't know until runtime



                                                                        Page 1





DSO(5)                                                                  DSO(5)



     where the object will be loaded.)  To do symbol lookup of a given symbol
     (in the process of of fixing up symbolic references), rld looks in each
     object's dynamic symbol table in turn (according to the sequence
     mentioned above).  If any strong symbol is found that satisfies the
     reference (ie, has the name of the given symbol and is an external
     definition) the lookup stops with that symbol.  If no strong definition
     is found with that name then the first weak  symbol found is accepted as
     the definition.  Next, each object's init code is executed. Finally,
     control is transferred to "__start" in the main program.

     The sequence at which the -init code is run is important to apps and DSOs
     having -init code.  The default order is that the objects are taken in
     the reverse order of the sequence defined in loading.  If -init code in
     one DSO calls a DSO whose -init code has not yet run, then the -init code
     in the called DSO is run before the called DSO routine is actually called
     (thus the default order is not followed in such a case).  It is an error
     for DSOs to mutually call one another (even indirectly) from within init
     sections since neither init section can complete first in such a case
     (rld catches this and invokes a fatal-error routine).  The init code in
     delay-loaded DSOs is not run till the DSO is actually loaded (when some
     routine in it is called it will be loaded).

     dlopen()ed and sgidlopen_version()ed DSOs are ignored in the symbol
     lookup mentioned above.  As are symbols in any DSO which is loaded at run
     time by being on the library list of a dlopen()ed or
     sgidlopen_version()ed DSO.  However, if a DSO has its symbols visible for
     any reason (say it was in the main program liblist) that DSO is not then
     hidden just because it is *also* on the library list of a dlopen()ed or
     sgidlopen_version()ed DSO.

     When execution terminates, the -fini code of each DSO in the base a.out
     is run in the reverse of the order in which the -init code was run.

     Be sure to read about quickstart, delayed loads, sgidladd(3),
     sgidlopenversion(3), and dlopen(3) as each can affect this general
     process.

   -init Code Runtime Ordering
     The general order in which the base executable DSOs -init code is run is
     specified by the MIPS abi (see URLs at the end of this document).

     Conceptually, prepare a dependency graph and run the -init sections such
     that before an -init in object A is run, that all -init sections in DSOs
     A depends on have already been run.  However, if a -init calls a DSO
     whose -init has not yet run, the -init of the called DSO is run before

     Physically, starting with the last DSO in rld's list and working toward
     the executable, we do a depth-first postorder call of -init code.  This
     is a particular choice of ordering within the conceptual framework.  The
     physical ordering is not specified by the MIPS ABI.





                                                                        Page 2





DSO(5)                                                                  DSO(5)



     Delay-loaded or dlopened()ed DSOs also have their -init run order
     recorded so that -fini will happen the reverse order.

   -fini Code Runtime Ordering
     The -fini code of the DSOs in the base executable is run choosing DSOs in
     the opposite order the actual -init code was actually run.

     Any DSO delay-loaded during the execution of -init code will change (in a
     difficult to predict manner) the order in which -init sections are run:
     At the time of the delay-load the -init code scan is restarted anew at
     the (new due to the new DSO) end of rld's list.

   Limitations on -init and -fini Code
     As of this writing, -init and -fini code should not perform libdl
     operations (including dlopen(), sgidladd(), dlsym() and dlclose()).
     Doing so can cause a variety of problems ranging from incorrect symbol
     resolution to program hangs.  In multithreaded programs, threads
     executing -init or -fini code with libdl operations will definitely hang.
     Be particularly aware of calls to setlocale() in init code, as it is
     implemented using dlopen() in some versions of IRIX. This limitation of
     rld is considered a bug.

     In multithreaded programs, -init and -fini code should avoid attempts to
     acquire (with pthread_mutex_lock(), for example) resources owned by other
     threads, unless it can be guaranteed that the other thread can release
     the resource without performing a libdl operation or lazy text
     resolution.  Should the thread owning the resource make a call into rld,
     both threads will deadlock.  This limitation of rld is considered a bug.

     Note that it is difficult to predict whether execution of a given section
     of code will require lazy text resolution, since a DSO's GOT may be reset
     to point at function stubs when it fails to quickstart or after libdl
     operations which might affect resolution of its symbols.  See the section
     on Quickstart below.

     The C++ runtime system uses the -init and -fini mechanism to construct
     and destroy static objects.  Therefore, constructors and destructors for
     such objects should avoid blocking calls if DSOs using them are to be
     dlopened(), dlclosed() or delay-loaded within a multithreaded program.

LINKING / BUILDING DYNAMIC SHARED OBJECTS
   Example
     Suppose your library is in an archive libfoo.a of object files all of
     which have been compiled -shared; and it references symbols found in
     libc.so.1, libgl.so, libX11.so and libnetls.so though most programs will
     never use the path that requires libnetls.so. SGI recommends building
     your DSO, libfoo.so, in the following way:

          ld                           \
               -elf                    \
               -shared                 \
               -no_unresolved          \



                                                                        Page 3





DSO(5)                                                                  DSO(5)



               -rdata_shared           \
               -soname libfoo.so            \
               -o libfoo.so            \
               -all libfoo.a           \
               -lX11                   \
               -delay_load -lnetls     \
               -lc                     \
               -lgl

     This builds a DSO called libfoo.so that will request rld to load
     libc.so.1, libX11.so, and libgl.so  whenever libfoo.so is loaded and will
     load libnetls.so if it is ever referenced.

     NOTE:
          If you have any C++ object files among the objects making up your
          DSO, you must replace ld in the above command with CC.  I.e. your
          command becomes:

               CC -elf -shared ... -o libfoo.so -all libfoo.a ...

          However, you do not have to do anything special at all to use such
          C++ DSOs when linking other programs against these DSOs. You can
          link C++ DSOs into C, C++, or Fortran programs using your usual link
          commands or link other DSOs against these C++ DSOs without taking
          any special action.

          For example, the following link works, with the preceding C++ DSO
          libfoo.so being properly initialized by rld at program startup:

               f77 fortran_prog.o -lfoo

   Controlling The Symbols Exported By A Dynamic Shared Object
     One of the benefits of using dynamic shared objects is the ability to
     release new versions of that object and still have objects that were
     linked against the old version, work with the new version. This is
     impossible to guarantee, if the set of symbols exported by an object
     cannot easily be understood by the object's creator.  ld provides several
     options that help the developer control which symbols are exported by a
     dynamic shared object.

     By default ld does not export symbols that are supplied by a linked-in
     archive or dynamic shared object. The developer is probably only a
     consumer of the linked-in object, not an exporter. In a subsequent
     release, the developer may not require the linked-in object and if the
     symbols provided by the linked-in object had been exported by the
     developer's object, the new object would not longer be upwardly
     compatible with the original version. This behavior can be overridden
     using the -exports option.  This default-symbol-hiding behavior, with
     respect to archives, is also overridden when building a dynamic shared
     object from an archive using the -all option.





                                                                        Page 4





DSO(5)                                                                  DSO(5)



     The developer has greater control over the list of symbols that are
     exported using options -exportedsymbol, -exportsfile, -hiddensymbol,
     or -hidesfile.  The first two options let the developer specifically
     list the symbols that are to be exported by the dynamic shared object.
     The -exportedsymbol option is followed by a comma separated list of
     names.  The -exportsfile option gives a filename that contains a space
     separated (including newlines) list of names. If any symbols are
     specifically exported, then only those symbols will be exported. All
     other symbols are automatically hidden.  The last two options let the
     developer specify a list of symbols that are to be not exported by the
     dynamic shared object.

     There are two consequences of hiding symbols. First, those symbols will
     not provide resolution to any undefined symbols in an object that links
     in the dynamic shared object. Second , any references to that symbol
     within the dynamic shared object will be resolved internally to the
     hidden symbol.

   Rules Of Thumb
     Use -nounresolved to find unresolved symbols. While it is not always
     possible to supply all the shared objects that will be referenced by
     libfoo.so on the link line, in general libraries should be self-
     contained. This is especially true for subsequent releases of a dynamic
     shared object. If a dynamic shared object has any unresolved references,
     they must be resolved by some other loaded object. Having unresolved
     symbols invites disaster since there is no guarantee that the symbols
     will be resolved and thus the application may not run.

     Link against the minimum set of .so's needed. Loading a shared object
     does carry a cost. Linking against unneeded dynamic shared objects causes
     them to be loaded even if they are never referenced.  ld warns when you
     have linked against a dynamic shared object that resolves no symbols.

     When building a C++ dynamic shared object, specify -exports for any
     dynamic shared object that provides the definitions of classes that
     classes in the object being created derive from. Specifying -exports in
     this case ensures that consumers of the object being created can create
     subclasses of classes provided by that object, without having to know the
     complete set of dynamic shared objects that will need to be loaded. Using
     the -exports flag in this case may bring in unwanted symbols. Use the
     -exportedsymbol, -exportsfile, -hiddensymbol, or -hidesfile options
     where appropriate.

     Use -rdatashared to move all read-only data into the shared segment.
     Unfortunately, there are many programs that write to supposedly read-only
     data; for this reason, -rdatashared is off by default. The -
     usereadonlyconst compiler option is on by default.

     If you reference the gl, have it last in the link line. Often libgl.so
     cannot be quickstarted (see below); putting it last allows all the
     "upstream" objects to still be quickstarted. You can also choose to
     delay-load libgl.so. This still allows your application to quickstart.



                                                                        Page 5





DSO(5)                                                                  DSO(5)



     Anytime a "downstream" shared object (a referenced object) changes, you
     should relink in order to quickstart, or run the requickstart tool rqs(1)
     on the object.

     Try to minimize inter-DSO data references.

     Try to minimize the use of global data. In DSOs, it is generally more
     efficient to malloc space when needed rather than use a large static data
     structure.

     Try to pack data together that is likely to be unmodified. This allows
     the kernel to make more of the data pages shared, copy-on-write.

     Use the -delayload option on any shared object on the link line that is
     not often used. This adds a small performance penalty for references to
     it, but can save time and memory for those programs that don't use it.

     Do not call sproc(2) sprocsp(2) or nsproc(2) from any code that may be
     executed  at init or fini time (meaning: from any code in or called by
     the functions named in the ld(1) -init and -fini options).

     Do not call dlopen(3), sgidlopenversion(3), sgidladd(3), dlclose(3) or
     dlerror(3) from any code that may be executed at init or fini time.

PERFORMANCE CONSIDERATIONS
   Quickstart
     When building a shared object or an executable, ld assigns addresses to
     the object and attempts to resolve all references.  At runtime, if rld
     verifies that the same set of objects are loaded at the original
     addresses, then rld can skip all the runtime relocation work and let the
     program run. This saves time by skipping doing the relocations and saves
     memory since rld does not have to read in the sections that hold the
     relocation information.

     At static link time, ld resolves each unresolved function call to a stub
     routine which references an rld function called lazytextresolve(). When
     invoked at runtime, it performs the relocation needed for all future
     calls to the original function. In this way, more programs can quickstart
     even if some of the function references are not resolved at static link
     time.

     Quickstart fails whenever the dynamic shared objects on a system do not
     match the objects used when linking and application or the shared objects
     that it depends on.  This will occur whenever a new version of a dynamic
     shared object is released.  When quickstart fails rld has to do a
     significant amount of work.  The rqs(1) command can be used  to
     recalculate the quickstart information associated with an application or
     a dynamic shared object.  rqs must be called in proper order so that
     dynamic shared objects on an objects liblist are requickstarted before
     the object ir requickstarted. rqs will rewrite the object it is
     requickstarting back in place.  It is possible to mark an object as non-
     requickstartable by using the -norqs flag to ld.



                                                                        Page 6





DSO(5)                                                                  DSO(5)



   Avoiding Gratuitous Shared Object Loads
     Since for each shared object that is loaded rld does a considerable
     amount of work and can use up large amounts of real memory it is
     advantageous to not link against shared objects that are not needed.


   Reducing the number of Conflicts
     Whenever more than one shared object (including the main program) needed
     by an executable define and use the same name for a symbol, this is
     called a conflict and the name for which multiple definitions exist is
     recorded in your program in the section named ".conflict"  The names of
     all conflicting symbols pertaining to a program can be obtained via the
     -Dc flag to elfdump. One example of a conflict is the routine malloc
     which is defined both in libc.so.1 and in libmalloc.so.

     Conflicts represent extra work to be done at startup, because the
     presence of a conflict means that the objects in the link may not have
     chosen a consistent instance of the symbol in question.  This extra work
     is memory-intensive, since even the presence of one conflict may mean
     that many pages of memory must be examined by rld which would otherwise
     have not been needed for a quickstarting program. The -quickstartinfo
     flag makes ld print out a warning about every conflict it finds and the
     names of two of the objects in which it is defined. Of course, sometimes
     conflicts are a necessary part of the design of certain applications.


   dlopen(3), sgidlopenversion(3), sgidladd(3), and delayed loads
     The overhead associated with objects that are referenced but seldom
     actually used can be mitigated by using dlopen(3), sgidlopenversion(3),
     sgidladd(3), or delayed loads. The use of any of these, delays the
     loading of a shared object (and the objects on it's liblist) until it is
     actually referenced.  The most convenient is the -delayload option to
     ld. All three require that there be no references from any other objects'
     data section to the delay loaded shared object.


FREQUENTLY ASKED QUESTIONS
List of Questions:

   1)  What is DSO?
   2)  How do dynamic shared objects compare with shared libraries?
   3)  How do I maintain binary compatibility between versions of
     DSOs?"

   4)  Under which versions of the OS can I use DSO?
   5)  What object-file format does DSO use?
   6)  How do I install the tools so I can use DSO on my system?
   7)  How do I build an executable that uses a shared object?
   8)  How do I build an executable that doesn't use shared
     linking?"





                                                                        Page 7





DSO(5)                                                                  DSO(5)



   9)  How do I tell if an executable will use dynamic linking?
   10) How do I build a shared object?
   11) Where does the system look for shared objects at runtime?
   12) What is Quickstart?
   13) What is the solocations file?
   14) What directives can be put in a solocations file?
   15) What is /usr/lib/solocations?
   16) If I don't have a valid solocations, can I generate one from
     all the .so's in, say, /usr/lib?"

   17) How expensive is it (at runtime) to NOT use the
     -update_registry option?"

   18) How and when will Quickstart be used?
   19) What about run-time loading under user control?
   20) What benefits will I get from DSO?
   21) What costs are associated with DSO?
   22) What is the -KPIC option?
   23) Must main programs which want to use DSOs use -KPIC for
     compilation?"

   24) How do I change my assembly language sources to use -KPIC?
   25) Can I mix IRIX 4 static shared libraries with DSOs?
   26) What options do I have when building a shared object?
   27) What pitfalls should I know about which are associated with
     DSO?"

   28) What should I do about a GOT overflow?
   29) How are multiple versions of DSOs supported?
   30) Why are the global objects in my C++ DSO not being initialized?
   31) Where can I find more documentation on DSO?
   1)  What is DSO?
     DSO stands for Dynamic Shared Object.  DSO provides a capability similar
     to static shared libraries under IRIX4 and earlier, e.g., it gives
     applications the ability to share the text of heavily used libraries,
     which need not be included in the executable file.  However DSO has two
     important distinctions from static shared libraries.

   2)  How do dynamic shared objects compare with static shared libraries?
     First, a dynamic shared object contains only position-independent code,
     so that it may be mapped into the virtual address space of different
     processes at different addresses and still be shared.  Second, dynamic
     shared objects, and indeed the executable itself are mapped in by a
     runtime loader, rld, which resides in memory in the same address space as
     the executable.  This gives the system the ability to change the binding
     of symbols during executions, at the request of the executing program.

   3)  How do I maintain binary compatibility between versions of DSOs?
     As long as the shared objects maintain the same exported symbols, or
     perhaps add new symbols without removing any or changing semantics, and
     don't change exported structures, they will be binary compatible.
     Ordering of symbols, routines and global data are irrelevant.



                                                                        Page 8





DSO(5)                                                                  DSO(5)



   4)  Under which versions of the OS can I use DSO?
     DSO is available under IRIX versions 5.0 and later.  Programs built with
     DSO will not work on earlier version of IRIX.

   5)  Which object-file format does DSO use?
     DSO uses the ELF object file format, as defined in the SVR4 ABI.  ELF
     objects cannot be run under IRIX 4.0.5 or earlier.

   6)  How do I install the tools so I can use DSO on my system?
     IRIX 5.0 and later releases all support and use DSOs. In order to compile
     and build shared objects you will need to have the Developer's Option
     installed.

   7)  How do I build an executable that uses a shared object?
              cc myfile.c -lmine

     This will link you with libmine.so and also with libc.so.1, if either are
     available. If no libmine.so is available, but there is a libmine.a, the
     libmine.a will be used along with libc.so.1, and you will still get
     dynamic linking. To be explicit, add the -callshared flag to the cc
     line:

              cc -call_shared myfile.c -lmine

   8)  How do I build an executable that doesn't use shared linking?
     Use the -nonshared flag:

              cc -non_shared myfile.c -lmine

          Some libraries are not and will not be available non-shared.  The
          ones that are available are not installed by default, so one must
          request their installation.  In general, the user of -non_shared is
          deprecated.

   9)  How do I tell if an executable will use dynamic linking?
     elfdump -o shows you the ELF program header. This contains all the
     information necessary for exec and rld to run the program/shared object.
     Only a.outs which use dynamic linking will have a PHDR, INTERP, or
     DYNAMIC entry.

     An example and a more detailed description follows:

     % elfdump -o /bin/cat

                  ***PROGRAM HEADER***
Type     Offset      Vaddr      Paddr     Filesz      Memsz      Align RWX
   PHDR 0x00000034 0x00400034 0x00400034 0x000000c0 0x00000000 0x00000004 r--
 INTERP 0x00000100 0x00400100 0x00400100 0x00000009 0x00000009 0x00000004 r--
REGINFO 0x00000110 0x00400110 0x00400110 0x00000018 0x00000018 0x00000004 r--
DYNAMIC 0x00000150 0x00400150 0x00400150 0x00000a70 0x00000a70 0x00000010 r--
   LOAD 0x00000000 0x00400000 0x00400000 0x00003000 0x00003000 0x00001000 r-x
   LOAD 0x00003000 0x10000000 0x10000000 0x00001000 0x00001290 0x00010000 rwx



                                                                        Page 9





DSO(5)                                                                  DSO(5)



     Each line is an entry in the program header, and refers to a "segment" of
     the file.

     PHDR points to the program header itself within the file. Only
          executables which use dynamic linking will have this field.

     INTERP
          points to a place in the file where the name of the interpreter
          required for this program is to be found. For any ABI-conforming
          object, this name will be "/usr/lib/libc.so.1".

     REGINFO
          points to a place in the file where information about register setup
          can be found. Currently this mostly consists of the correct gp value
          for this object.

     DYNAMIC
          points to the information in the file which is needed by rld to
          execute it correctly. This information includes the liblist, a
          symbol table, and other information.

     LOAD points to segments that are to be mapped into the memory image.

     The columns give various information about each segment.

     Offset
          is the offset in the file to the beginning of the segment.

     Vaddr
          is the virtual address of the beginning of the segment in the memory
          image of the file, ASSUMING that it was mapped as described in the
          LOAD entries

     Paddr
          is the same as Vaddr in our implementation.

     Filesz
          is the size of the segment in the file.

     Memsz
          is the size of the segment in the memory image.  When this is larger
          it is assumed to be zero-filled.

     Align
          is the alignment required by this section. If an segment is to be
          mapped somewhere into memory other than at Vaddr, the new address
          must be congruent to Vaddr modulo the alignment. In the example
          above, the first segment must always be loaded at a page boundary,
          and the second must always be loaded at a 64K boundary.






                                                                       Page 10





DSO(5)                                                                  DSO(5)



     RWX  specifies the protections r(ead), w(rite), or x(ecute) for the
          segment.

     Programs which are linked -nonshared do not have a PHDR, INTERP, or
     DYNAMIC section.  Thus elfdump -o is a convenient method to determine if
     a program is linked -nonshared or not.


   10) How do I build a shared object?
     To begin with, build a .o or .a which contains all the routines you want
     to have in your .so (shared object).  This can be done with cc -c and ar.
     Then invoke ld with the -shared flag. Normally the extension .so is used
     to designate shared objects.

     Here is an example:

         cc -c myobj.c
         ld -shared myobj.o -o myobj.so

         -or-

         <build libmine.a the usual way.>
         ld -shared -all libmine.a -o libmine.so

     The -all flag in the second example tells ld to include all the routines
     in the library. This is necessary since there are no undefined references
     (as in a main(), say) which is the usual way that ld knows to include
     files from an archive.

   11) Where does the system look for shared objects at runtime?
     The search path for shared objects is acquired in the following order for
     the old 32bit ABI:

     1) the path of the shared object if given in the liblist,

     2) in any directories specified via the
          -rpath flag when the executable was built

     3) in any directory specified by the LD_LIBRARY_PATH environment
          variable, if it is defined

     4) in the directories in the default path
          (/usr/lib:/lib:/lib/cc:/usr/lib/cc)

     If the _RLD_ROOT environment variable is defined, then its value is
     appended to the front of any path specified by -rpath and the default
     path.  _RLD_ROOT itself is also a colon(:) separated list.

     For the new 32bit ABI the rules are similar, but the following
     differences exist:  1)  the LD_LIBRARYN32_PATH is used if defined,
     otherwise LD_LIBRARY_PATH is used 2) _RLDN32_ROOT is used for the list of
     paths 3)The default path directory list is (/usr/lib32:/lib32).



                                                                       Page 11





DSO(5)                                                                  DSO(5)



     For the 64bit ABI the rules are similar, but the following differences
     exist:  1) The LD_LIBRARY64_PATH is used if defined, otherwise
     LD_LIBRARY_PATH is used 2) _RLD64_ROOT is used for the list of paths 3)
     The default path directory list is (/usr/lib64:/lib64).

     See the rld(1) manpage for details.

   12) What is Quickstart?
     Quickstart is an optimization. Using an so_locations file, each shared
     object is pre-relocated by ld, as if it had been loaded at the address in
     the so_locations file.  That way, if nothing unusual happens when we
     start up the application, all the shared objects will map at their
     Quickstart addresses, and rld will not need to do a relocation pass over
     them.

     If for some reason more than one shared object wishes to map the same
     address, rld will move one of them to an unused address and perform a
     relocation pass to fix up the address references.

     If one or more of the shared objects linked against at static link time
     has changed by the time the program executes, rld will need to do extra
     work to ensure that symbols have been resolved to their proper value.

   13) What is the solocations file?
     In the directory in which you build a shared object, after you've
     actually built one, you will notice a file named so_locations.

     It is a registry of shared objects.  It maintains the default or
     Quickstart addresses of a group of shared objects which are to cooperate
     by not having their default location overlap with one another.  It is
     generated and updated by ld each time it builds a shared object.

     If you make substantial library changes between one build of the library
     and another you should remove the so_locations file before rebuilding the
     library, since the information derived from the older build  (and put in
     the so_locations files) may make the new library build unable to complete
     successfully.

     Since rqsall(1)/rqs(1) can rearrange a.outs and DSOs to restore
     quickstartability the so_locations file is less important than it was
     before rqs existed.

   14) What directives can be put in an solocations file?
     Comment line
          so_name [ :st = { .text | .data | $range } base_addr,padded_size : ] *

     where
          so_name         full path name (or trailing component) of a
                          shared object
          st              string identifying start of the segment description
          .text | .data   segment types: text or data
          $range           limit the range of address that can be used



                                                                       Page 12





DSO(5)                                                                  DSO(5)



          base_addr       address where the segment starts
          padded_size     padded size of the segment

     The following directives control the placement of new shared objects:

          $text_align_size=<align>  padding=<pad-size>
          $data_align_size=<align>  padding=<pad-size>
              These two directives specify the alignment and padding
           requirements for text and data segments respectively.
           The size value in so location is calculated based on:
              (section size + padding) aligned to the section align size
              The align values for text and data as well as the padding
              values must be aligned to a bucket size. If not, ld will
           generate a warning message and align these values to bucket
           size.

          $start_address=<addr>
              Specifies where to start looking for addresses to put shared
              objects.

          $data_after_text=[ 1 | 0 ]
              Instructs the linker to place data immediately after the text
              at specified text and data alignment requirements.
              We set the data_after_text to 0 if the argument of this directive
              is missing.
     WARNING: The format and use of the solocations  file is under review and
     may in the future move to a simpler format. It is suggested in the mean
     time that only $range be used and not .text or .data in the
     specification.

     Also, when building a DSO with the -checkregistry or -updateregistry
     flag, and if there is already an entry corresponding to this DSO in an
     so_location file, the linker will try to assign the same addresses for
     text and data.  However, if the size of the DSO changes and does not fit
     in the specified location any more, the linker will search for another
     spot that fits.  If the optional $range comment is given, the linker will
     only place the DSO in the specified range of addresses.  If there is not
     enough room, an error will be given.

   15) What is /usr/lib/solocations?
     /usr/lib/so_locations (for the old 32bit ABI), /usr/lib32/so_locations
     (for the new 32bit ABI), and /usr/lib64/so_locations (for the 64bit ABI)
     represent the default layout for the system shared objects in the
     respective ABIs.  Developers who build shared objects may find it
     interesting to consult this file, in order to avoid collisions between
     their shared objects and system shared objects.  This file is absolutely
     irrelevant to users who merely run programs which use shared objects.

     There are two options which are relevant, -updateregistry, and <file>
     looks at <file> and builds the current .so at a location which doesn't
     conflict with anything in the file (unless the current one is listed.  -
     checkregistry does not write to <file>.  -updateregistry <file> will



                                                                       Page 13





DSO(5)                                                                  DSO(5)



     consult <file> as with -checkregistry, but will attempt to write an
     entry for the .so being built into <file>.  If <file> is not writable, -
     updateregistry turns into If <file> is not readable -checkregistry and
     -updateregistry are ignored.

   16) If I don't have a valid solocations file, can I generate one from all
     the .so's in, say, /usr/lib?
     There is no convenient method to do so.  There is no guarantee that all
     the .so's in /usr/lib have been coordinated so that a consistent
     so_locations file can be made from them.  So it is better to get the one
     that a particular release was made with.

   17) How expensive is it (at runtime) NOT to use -updateregistry option?
     If one uses rqsall(1)/rqs(1) to requickstart an application and its DSOs
     then there need not be any cost. rqs(1) can make the DSOs quickstartable
     regardless how the DSO addresses were determined.

     If one does not use rqs then the lack of an updated registry can impose
     startup costs.  It is very difficult to say how much a particular
     executable will suffer since it depends on which shared objects the
     program uses and whether they have been Quickstarted for the same
     address.  When there is a conflict between two objects, one will be
     moved, which means that all addresses referring to names in that object
     need to be relocated.

   18) How and when will Quickstart be used?
     Normally, the linker will use Quickstart unless there are unresolved
     symbols at static link time.

     In every executable and every shared object is a list of objects which
     were looked at at static link time -- when the object was made.  This
     list also contains timestamps and checksums for each of the objects.
     Various levels of extra work are required if the timestamp or checksum
     has changed in the library at run-time.

   19) What about run-time loading under user control?
     We support an interface known as libdl, which allows users to dynamically
     load their own shared objects as needed. The calls are

     dlopen() -- open a new shared object and get a "handle" to it.

     dlsym()  -- find the value of a name defined in an object.

     dlclose()-- close a shared object.

     dlerror()-- report errors.

     sgidladd() -- functions much like dlopen however it exposes
          all symbols to the rest of the program.






                                                                       Page 14





DSO(5)                                                                  DSO(5)



     sgidlopen_version -- functions much like dlopen however
          it allows specifying a specific required version of the DSO.

     Consult the individual manpages for details.

   20) What benefits will I get from DSO?
     Executables linked with shared objects will be smaller since the shared
     objects are not part of the executable file image.

     Executables which use a shared object need not be relinked if a shared
     object is changed -- once the updated shared object is installed, the
     executable will pick it up automatically.

     Shared libraries are much easier to build, use, and debug than static
     shared libraries.

     DSO allows application designers to make more machine-independent
     software.  System-dependent routines can be given a uniform interface and
     a shared object which implements that interface can be built for each
     different platform.  Then an actual application can be shipped as-is
     ("shrink-wrapped" software) to various platforms and run on them all.

     DSO gives applications the ability to change the binding of symbols at
     run time, under user control.

   21) What costs are associated with DSO?
     A shared object incurs two costs, both against performance.

     At startup, there will be a startup cost while rld maps in the various
     objects, performs symbol resolution, etc.  We believe this cost is small
     compared to the time it takes to contact the X server, for example.

     A shared object's text must be PIC (position independent code).
     PICification is accomplished by the code generator/assembler when the -
     KPIC flag is specified. -KPIC is the default, so it is not necessary to
     supply the -KPIC flag.  PIC code is necessarily slower. Experiments have
     indicated that this speed reduction is usually less than 5 percent, but
     can be as much as 15 percent.  depending on the application. With full
     optimization the speed reduction can be near zero.  PIC code seems to be
     worst on very small leaf routines which access global data.

   22) What is the -KPIC option?
     This the default, so you need never use it.  This flags tells the code
     generator/assembler to generate PIC directly.  The result is an object
     file that can be put into a DSO without further modification

              cc -KPIC -c foo.c

     will give you a PIC object foo.o.  Other drivers (cc, pc, f77, and as)
     also accept the -KPIC option.  This is the default.





                                                                       Page 15





DSO(5)                                                                  DSO(5)



     Routines written in assembly language need to be modified before -KPIC
     can be used. See the question below.

     PIC objects generated by using -KPIC must be compiled -G 0.

   23) Must main programs which want to use DSOs use -KPIC for compilation?
     Yes.  DSOs use -KPIC so that position-independent code will be generated.
     Main programs are not generally position-independent, but must still use
     the DSO calling convention when calling a routine which is defined in a
     DSO. In particular, this means that a main program must have a GOT, and
     the code which is generated must use it.  Therefore, modules which will
     become part of main programs must be compiled -KPIC as well as modules
     which become part of DSOs.

   24) How do I change my assembly language sources to use -KPIC?
     The following refers to the older 32 bit abi using ucode compilers.  For
     n32 and 64 bit abi information, look at the information and pointers in
     the abi(5) manpage.

     Several new assembler directives are added to support generation of PIC.
     You should also get yourself familiar with the MIPS ABI Supplement and
     the PIC coding model it describes.  In addition, files which are to be
     assembled with -KPIC must also be -G 0.  This is normally turned on by
     the driver by default.

     Note that with the exception of (a) and (d), all other directives
     described below will be ignored when -KPIC is not explicitly specified.
     Also, item (d), ".gpword", will be turned into ".word". The result will
     be a NON-PIC version of the same routine.

     a) .option pic2

     This directive forces the assembler to mark the output object file "PIC"
     and activates the following directives.  It overrides the command line
     argument.  Normally, you don't need to specify this directive.  Instead,
     you should use the -KPIC or -nonshared flags to toggle between
     generating PIC or non-PIC.

     Note that even though -KPIC will be made the default for the high-
     language driver (cc/pc/f77) in future releases, it will *NOT* be the
     default for assembly sources.  You will always have to explicitly specify
     -KPIC for compiling .s files.

     b) .cpload reg

     This directive expands into three instructions that sets the gp register
     to the context pointer value for the current function.  The three
     instructions are:
          lui  gp,_gp_disp
          addui     gp,gp,_gp_disp
          addu gp,gp,reg




                                                                       Page 16





DSO(5)                                                                  DSO(5)



     _gp_disp is a reserved symbol defined by the linker to be the distance
     between the lui instruction and the context pointer.  This directive is
     required at the beginning of each subroutine that uses the gp register.

     You must add this directive at the beginning of every procedure, with the
     exception of leaf-procedures that do not access any global variables, and
     procedures that are static (i.e., not marked .globl or .extern).

     c) .cprestore offset

     This directive causes the assembler to issue
               sw   gp,offset(sp)
     at the point where it appears.  Additionally, it causes the assembler to
     emit
               lw   gp,offset(sp)
     after every jump-and-link (jal) or branch-and-link (bal) operation,
     thereby restoring the gp register after function calls.  The programmer
     is responsible for allocating the stack space for the gp.  This space
     should be in the saved register area of the stack frame to remain
     consistent with MIPS' calling and debugger conventions.

     d) .gpword local-sym

     This directive is similar to .word except that the relocation entry for
     local-sym has the R_MIPS_GPREL32 type.  After linkage, this results in a
     32-bit value that is the distance between local-sym and the context
     pointer (i.e. the gp).  local-sym must be local.  It is currently used
     for PIC switch tables.

     e) .cpadd reg

     This adds the value of the context pointer (gp) to reg.

     EXAMPLES:
          This is a simplified version of the "hello world" program:
          --------------------------------------------------------------
               .option   pic2
               .data
               .align    2
          $$5:
               .ascii    "hello world\X0A\X00"
               .text
               .align    2
          main:
               .set  noreorder
               .cpload   $25
               .set  reorder
               subu $sp, 40
               sw   $31, 36($sp)
               .cprestore     32
               la   $4, $$5
               jal  printf



                                                                       Page 17





DSO(5)                                                                  DSO(5)



              move  $2, $0
               lw   $31, 36($sp)
               addu $sp, 40
               j    $31
          ----------------------------------------------------------------
          The actual instructions generated by the assembler will be:

               lui  gp,0      #
               addiu     gp,gp,0        # generated by .cpload
               addu gp,gp,t9  #
               lw   a0,0(gp)  # gp-relative addressing used
               lw   t9,0(gp)  # t9 is used for func. call
               addiu     sp,sp,-40
               sw   ra,36(sp)
               sw   gp,32(sp) # from .cprestore
               jalr ra,t9          # jal is changed to jalr
               addiu     a0,a0,0
               lw   ra,36(sp)
               lw   gp,32(sp) # activated by .cprestore
               move v0,zero
               jr   ra
               addiu     sp,sp,40
               nop
          ----------------------------------------------------------------

     NOTE:

     The MIPS ABI requires that register t9 ($25) be used for indirect
     function calls, so .cpload should always use $25.  No reorder mode should
     also be used.  Also, programmers should make sure that t9 is dead before
     any function call because the register will be changed (and not restored)
     during the function call.

     If your program uses an indirect jump (jalr), you must also use t9 as the
     jump register.

     If you have an unconditional jump to an external label:
               j  _cerror
         you have to rewrite it into indirect jump via t9, i.e.:
               la t9,_cerror
               j  t9

     If you use branch-and-link (bal) instruction, and if the target procedure
     begins with a .cpload, you have to specify an alternate entry point:

          foo: .set noreorder # callee
               .cpload   $25
               .set reorder
          $$1: ...            # alternative entry point
               ...
               j    $31       # foo returns




                                                                       Page 18





DSO(5)                                                                  DSO(5)



         bar:  ...            # caller
               ...
               bal  $$1       # by-pass the .cpload
               ...

     This is very important because .cpload assumes register $25 contains the
     address of foo, but in this case $25 is not set up.  Note that since both
     foo and bar reside in the same file, they must have the same value for
     $gp.  So the .cpload instructions can be and must be bypassed.  However,
     since foo can still be called from outside, the .cpload is still
     required.

     Alternatively, if you don't want to have an alternate entry point, you
     can set up register $25 before the bal:
               la   t9,foo
               bal  foo

         but this will be less efficient.

     position-independent jump table (or any table of text addresses).
     Entries of the address table created by .gpword are converted into
     displacement from the context pointer.  To get the correct text address,
     .cpadd should be used to add the value of gp back to them.  Since the gp
     is updated by the run-time linker, the correct text address can be
     reconstructed regardless of the location of the DSO.

   25) Can I mix IRIX 4 static shared libraries with DSOs?
     No.

   26) What options do I have when building a shared object?
     If you specify the flag -Bdynamic while linking a shared object, symbols
     in the shared object will be resolved differently than the default
     linkage convention.  In particular, the runtime linker will always try to
     resolve any symbols referenced in that object to symbols defined in that
     object first, instead of looking for definitions in objects in the order
     specified on the link line.

     The effect of this is to make all symbols defined and used in such
     objects non-preemptable.  Ordinarily a such symbol definitions could be
     preempted by a definition in an earlier shared object, but when
     -Bsymbolic is specified, this is not the case.

   27) What pitfalls are associated with DSO?
     Behind most surprises is the fact that linking semantics are
     fundamentally different, but only in a subtle way.  Let us suppose that
     your program links with three libraries, libA, libB and libC, in that
     order.  Further suppose that both libA and libC define some symbol x, but
     don't use it.  Furthermore, let us suppose that libB contains a reference
     to x.  Archive linking (the old way) will resolve B's reference to x to
     the definition in C, whereas shared object linking will resolve B's
     reference to x to the definition in A.




                                                                       Page 19





DSO(5)                                                                  DSO(5)



     Why the difference?  With archive linking, when libA is examined, there
     is no outstanding reference to x, hence the definition of x is not
     extracted from the archive.  Later when libC is examined, there is a
     reference to x, so it is loaded.

     With shared objects, all the constituent object files have been joined
     into one object, so all symbol definitions are always present.  The
     resolution rule is simple, take the definition in the object listed
     first. Thus the definition in libA is used.

     Another sort of surprise is the "runtime dangling reference".  It is
     altogether possible to build and link an application with no errors or
     even warnings, only to get a message from rld stating that your program
     has unresolvable symbols.

     What's going on?  Well, if you build a shared object as part of your
     program, the linker will not normally complain about undefined symbols
     during a link of a shared object.  This is because undefined symbols are
     expected during such a build and are perfectly acceptable.  But if the
     main program does not use a symbol, it does not get flagged as undefined
     during static linking.  Thus the runtime "surprise".  You can use the -
     nounresolved flag to the linker to avoid such surprises.

     Now we turn to a nasty pitfall which can be avoided by some cleverness in
     building a shared object.  If a particular object in an archive has an
     external reference to a data symbol (which it expects to be defined in
     main, libl.a, for example) the linker would not try to resolve that
     external unless the object file in question was actually referenced by
     the main program.  If that archive is turned into a shared object
     naively, the external data reference must be resolved whenever ANY
     function in the shared object is used, even if no function in the object
     file in question is ever called and no use is made of the external data
     symbol in question.

     This can lead to a scenario where a user has a link that worked with the
     archives, but builds a program which gets terminated by the runtime
     linker (rld).  It is a very bad idea to convert libraries which contain
     external data symbols to shared objects naively.

     One thing that can be done is to split the archive into several shared
     objects which are placed on the liblist of a "master" shared object.
     Since rld will not by default try to resolve data symbols until the first
     call is made to a particular object we can create the situation where no
     attempt to resolve the offending external data symbol is made until a
     call is made to the object in which it is referenced.

     Here's an example of how that works: Let us suppose that
     has_extern_data.o is an object with an undefined external in it which
     resides in the archive libxyz.a  Here is how to isolate that external
     data reference:





                                                                       Page 20





DSO(5)                                                                  DSO(5)



     First make has_extern_data.o into a shared object all its own.

           % ar x libxyz.a has_extern_data.o
           % ld -shared has_extern_data.o -o has_extern_data.so

     Now, make libxyz.so, excluding has_extern_data.o from being included
     directly, but instead putting it in the liblist of libxyz.so

         % ld -shared -all -exclude has_extern_data.o libxyz.a has_extern_data.so      -o libxyz.so

     Another pitfall is attempting to have data references from one
     sgidladd()ed DSO to another. When the first is sgidladd()ed the data
     references are unsatisfied and not resolved. Any use of the data before
     doing the second sgidladd() will get the unresolved value.  And then
     after sgidladd()ing the value will change!

   28) What should I do about a GOT overflow?
     By default, addresses are loaded out of the Global Offset Table (GOT)
     using a 16 bit offset from a context pointer.  This means that the size
     of the GOT is limited (by default) to 64K bytes, or about 16 K symbols.
     When there are too many symbols referenced by a DSO (or a.out) the linker
     issues the message "GOT overflows"  and will specify an object file which
     references the symbol which is "out of reach".

     When developers encounter this problem, they sometimes attempt to split
     the DSO or a.out in question into several smaller DSOs, each of which can
     conform to the GOT size limit. Good performance can be achieved this way,
     and we have routinely recommended this approach.

     However it is usually more practical (easier and better performance) to
     use -multigot, and this is now the preferred solution.  Use the -Wl,-
     multigot flag on the link line of the program/DSO being constructed when
     using CC or cc or f77.  Use -multigot on the ld command line if using ld
     directly (as one would with a C DSO for example).

     As a last resort, developers may wish to use the -xgot compile-time flag
     to tell the compiler to issue a different (and slower) code sequence uses
     a 32-bit offset. This will allow the GOT to contain up to 1G entries. It
     is critical that every object linked into a final DSO or a.out be
     compiled with -xgot turned on, otherwise code may have been generated
     which will not work with an extended GOT. However, files compiled with
     -xgot may be linked into a DSO or a.out which has a GOT that does not
     exceed the 8K symbol limit for 64-bit objects (16K for 32-bit objects)
     and will work correctly, if somewhat slower.  The GOT size of any shared
     objects linked is irrelevant.

     The directory /usr/lib/xgot contains the extended-GOT versions of those
     objects which SGI has built both normally and -xgot. If a system or third
     party archive contains small GOT objects which are needed in an extended
     GOT link, a developer can take the following steps: 1) Look in
     /usr/lib/xgot to see if an extended GOT version exists.  2) Turn the
     archive into its own shared object, thus isolating it from the extended



                                                                       Page 21





DSO(5)                                                                  DSO(5)



     GOT binary. 3) contact the archive provider.  In a few cases (crt1.o,
     crtn.o, c++init.o, and fixade.o), where the performance issues were
     minimal,  the default objects in /usr/lib are in fact built large GOT.

     Now that -multigot exists there is no reason to accept the performance
     penalty of -xgot.

   29) How are multiple versions of DSOs supported?
     IRIX 5.0.1 (Compilers v3.16) and later supports the ability to tag shared
     objects and executables with a version number.  This is intended to
     support interface changes.  Details are below; items marked (SGI ONLY) do
     not apply to MSIG ABI binaries, but only to binaries generated on IRIX
     without the -abi flag turned on.

     Versioning of Shared Objects.

     QUICK OVERVIEW

     For a shared object to be versioned the following needs to be done:
     Version strings consist of 3 parts and a dot: The string "sgi", a decimal
     number (the major number), a dot, and a decimal number (the minor
     number).

     Add the command -set_version sgi1.0 to the command to build the shared
     object (cc -shared, ld -shared, etc.).

     Whenever you make a COMPATIBLE change update the minor version number
     (the one after the dot), and add the latest version string to colon-
     separated list of version strings, e.g., -set_version
     sgi1.0:sgi1.1:sgi1.3

     Whenever you make an INCOMPATIBLE change, update the major version
     number.  Pass this as the version list, e.g., -set_version sgi2.0.
     Change the filename of the OLD shared object by adding a dot followed by
     the previous major number to the filename of the shared object.  DO NOT
     CHANGE the soname of the object.  No change to the file contents are
     necessary or desirable.  Simply rename the file.

    HOW IT ALL WORKS

    Versioning is only available for NON-ABI executables.  The current ABI
    does not require objects to have versioning, nor does it require systems
    to pay attention to versioning. It does allow objects to contain version
    strings, but does not require systems to do anything with this
    information.

    NON-ABI compliant executables will have the RHF_SGI_ONLY bit turned on in
    the .dynamic section. This flag will be understood and reported by elfdump
    -L -long. Only executables with this flag on will get the versioning
    treatment described below. This RHF_SGI_ONLY will be on by default.

    When an executable is linked against a shared object, the last entry of



                                                                       Page 22





DSO(5)                                                                  DSO(5)



    the shared object's version string is recorded in the executable as part
    of the liblist.  This can be examined by elfdump -Dl.

    When an executable is linked, the user may specify -require_minor or
    -ignore_minor for each shared object linked against.  If -require_minor is
    specified, a bit will be set in the flags field of the liblist entry for
    the shared object in question.  The default is -ignore_minor.

    When an executable (ABI or RHF_SGI_ONLY) is run rld will look for the
    proper filename in its usual search routine.

    (SGI_ONLY) If a file with the correct name is found the version string in
    the liblist is compared to the list of version strings in the shared
    object.  If the LL_REQUIRE_MINOR bit is set in the liblist entry, and
    there is an exact match between the version string in the depender and one
    of the strings in the version list of the dependee, then that library is
    used.  If the LL_REQUIRE_MINOR bit is clear, and if there is a match of
    major versions, then that library is used.

    (SGI_ONLY) If no proper match is found, a new soname is built by taking
    the soname found in the executables liblist, and the major number found in
    the version string corresponding to that liblist entry, and putting them
    together as <soname>.<major> This is searched for in the same way as
    above. Version strings are matched in exactly the same way as described
    above.


   30) Why are the global objects in my C++ DSO not being initialized?
     Did you link your DSO using the CC command (instead of using ld
     directly)? See the NOTE: After the first example in this man page, that
     discusses how to create a C++ DSO.

   31) Why are some libraries only available as a DSO, where as other
     libraries are available as both a DSO and an archive?
     The ABI specifies which DSOs must be on every system.  The converse of
     that is that no one can assume that any other .so is on an ABI conforming
     system. Libraries explicitly called out in the MIPS ABI are considered
     part of the system interface; and the decision was made to only ship such
     system interfaces in DSO form. Libraries that are not specified in the
     MIPS ABI must also be supplied in archive form to generate MIPS ABI
     compliant binaries using these libraries.

     For example the libraries: libX11.so,and libc.so.1 are explicitly called
     out in the MIPS ABI making the DSO version of Xlib and libc a system
     interface. Other examples are libsocket.so and libdl.so which are also
     only supplied as DSOs.

     Archive versions of libXt.a, libXm.a,libm.a libmalloc.a, etc...  are
     supplied because shared library versions of these libraries are not
     specified in the MIPS ABI, therefore they are not guaranteed to exist on
     all ABI conforming systems.




                                                                       Page 23





DSO(5)                                                                  DSO(5)



   32) Where can I find more documentation on DSO?
     Besides the other manpages mentioned below, System V Application Binary
     Interface (publically available on the web pages for the MIPS abi, see
     http://www.mipsabi.org) and System V Application Binary Interface -- MIPS
     Processor Supplement (publically available on the web pages for the MIPS
     abi, see http://www.mipsabi.org) and MIPSpro N32 ABI Handbook (in IRIS
     Insight) are good sources of DSO implementation details.  The
     www.mipsabi.org web pages also contain full definitions of the MIPS abi.

SEE ALSO
     abi(5), rld(1), ld(1), elf(5), elfdump(1), dlopen(3), sgidladd(3),
     sgidlopen_version(3),sgidladd(3), sgigetdsoversion(3),gp_overflow(5)

UPDATES
     This man page is periodically updated;  the last update done on
     1998/March/5 for IRIX 6.5







































                                                                       Page 24



Typewritten Software • bear@typewritten.org • Edmonds, WA 98026