search    
HP-UX Linker and Libraries User's Guide
Hewlett-Packard
Shared Library Management Routines

You can explicitly load and use shared libraries from your program. The linker toolset provides two families of load routines, dlopen and shl_load. The dlopen routines (primarily for IPF/PA-64 mode ) use Unix SVR4 compatible mechanism for library management. The shl_load routines support the shared library mechanisms provided in previous versions of HP-UX.


Note

Support for shl_load library management routines may be discontinued in a future IPF/PA-64 HP-UX release. You are encouraged to migrate to the dlopen family of routines for shared library management if you use the IPF/PA-64 mode linker toolset.

Do not mix use of the shl_load and dlopen APIs.


Shared Library Management Routine Summaries

The following sections introduce the shared library management routines.


The dlopen Routines Summary

The dlopen family of shared library management routines is available for the IPF and PA-64 linker. The following routines are also supported by the PA-32 linker:

  • dlopen

  • dlclose

  • dlsym

  • dlerror

  • dladdr

The dlopen family of routines use Unix SVR4 shared library mechanisms.

Use the following dl* routines for shared library management:

Routine Action

dlopen

Loads a shared library. This routine does breadth-first searching.

dlopene

Extension of dlopen allowing specific placement of text.

dlsetlibpath

Sets the dynamic search path to locate shared libraries.

dlerrno

Returns numeric error code corresponding to last error that occurred during dynamic linking processing.

dlgetfileinfo

Returns file information for a library prior to loading it.

dlerror

Prints the last error message recorded by dld.

dlsym

Gets the address of a symbol in a shared library.

dlget

Returns information on a loaded module.

dlmodinfo

Returns information about a loaded module.

dlgetname

Retrieves the name of a loaded module, given a load model descriptor.

dlclose

Unloads a shared library previously loaded by dlopen().

dladdr

Gets the symbolic information for an address.

dlmodadd

Registers information about dynamically generated functions.

dlmodremove

Removes information registered using dlmodadd.

dlgetmodinfo

Retrieves information about a loaded module (program or shared library).

All the dlopen routines are thread-safe.

These routines are described in the dl*(3C) manpages.


The shl_load Routine Summary

The shl_load family of shared library management routines are available for both the PA32 (compatibility mode) and IPF/PA64 linker.

Use the following shl_load routines for shared library management:

Routine Action

shl_load  

Explicitly load a shared library. It lets you load a compatibility or standard mode shared library. It does depth-first searching.

shl_findsym

Finds the address of a global symbol in a shared library.

shl_get and shl_get_r

Get information about currently loaded libraries. The shl_get_r routine is a thread-safe version of the shl_get routine with the same syntax.

shl_gethandle and shl_gethandle_r

Get descriptor information about a loaded shared library. The shl_gethandle_r routine is a thread-safe version of the shl_gethandle routine with the same syntax.

shl_definesym

Adds a new symbol to the global shared library symbol table.

shl_getsymbols

Returns a list of symbols in a shared library.

shl_unload and cxxshl_unload

Unload a shared library. 

Except for shl_get and shl_gethandle, all routines are thread safe.

These routines are described in the shl_load(3x) manpage.


Related Files and Commands

The following commands and files provide more information about using shared library management routines.

Command/File Action

a.out(4)

Executable file from assembler, compiler, and linker output.

cc(1)

Command to invoke the HP-UX C compiler.

exec(2)

System loader.

ld(1)

Command to invoke the linker.

Shared Library Header Files

The shl_load family of shared library management routines use some special data types (structures) and constants defined in the C-language header file /usr/include/dl.h. When using these functions from C programs, be sure to include dl.h:

        #include <dl.h>
        

Similarly, if you are using the dlopen family of routines, include /usr/include/dlfcn.h.

        #include <dlfcn.h>
        

If an error occurs when calling shared library management routines, the system error variable errno is set to an appropriate error value. Constants are defined for these error values in /usr/include/errno.h (see errno(2)). Thus, if a program checks for these error values, it must include errno.h:

        #include <errno.h>
        

Throughout this section, all examples are given in C. To learn how to call these routines from aC++ or Fortran, see the inter-language calling conventions described in the compiler documentation.

Using Shared Libraries with cc and ld Options

In IPF/PA-64 mode, you can access the shl_load and dlopen routines by specifying either -ldld or -ldl on the command line. In PA-32 mode, you can access the shl_load family of routines by specifying the -ldld option on the cc(1) or ld(1) command line.

The default behavior of the PA-64 and IPF linker is to export all symbols defined by a program. However, some PA-32 implementations do not, by default, export all symbols, instead exporting only those symbols imported by a shared library seen at link time. In PA-32 compatibility mode, use the -E option to ld to ensure that all symbols defined in the program are available to the loaded libraries.

To create shared libraries, compile source files with +z or +Z and link the resultant object files. In PA-32 mode, to create share libraries, compile source files with +Z or +Z and link the resultant object files with -b.

Initializers for Shared Libraries

A shared library can have an initialization routine - known as an initializer - that is called when the load module (a shared library or executable) is loaded (initializer) or explicitly unloaded (finalizer or terminator). Typically, an initializer is used to initialize a shared library's data when the library is loaded.

When a program begins execution, its initializers are called before any other user code is executed. This allows for setup at initialization and cleanup at termination. Also, when a shared library is explicitly loaded using shl_load or dlopen or unloaded using shl_unload or dlclose, its initializers and terminators are called at the appropriate time.

In IPF/PA-64 mode, you can specify initializers and terminators even for archive libraries or nonshared executables.


Styles of Initializers

The linker supports two different types of initializers and terminators:

  • Init/fini style. (Not supported in HP-UX 10.X)

  • HP-UX 10.X style.

Init/Fini Style Initializers

This style uses init and fini functions to handle initialization and finalization (terminator) operations.

Init

Initializers (inits) are called before the user's code starts or when a shared library is loaded. Functions specified with this option must not take arguments and return nothing (void functions).

The C compiler pragma "init" can be used to declare these functions. For example:

        #pragma init "my_init"
        void my_init() { ... do some initializations ... }
        

The ld command also supports the +init function option to specify the initializer. Use this option while building a shared library, an incomplete executable, or fully bound executable.

Use the +init option to specify the initializer functions, to be invoked in reverse order, the order the functions appear right to left on the command line. Initializers are called in depth-first order. For example, when a shared library is loaded, the initializers in all its dependent libraries are called first.

Do not use +init with the -r option. (The linker ignores the +init option.)

You can specify more than one initializer function on the command line with multiple option-symbol pairs, that is, each function you specify must be preceded by the +init option.

Fini

Finalizers (finis) are called after the user's code terminates by either calling the libc exit function, returning from the main or _start functions, or when the shared library which contains the fini is unloaded from memory. Like init functions, functions specified with this option must not take arguments and return nothing (void functions).

The C compiler pragma "fini" can be used to create them. For example:

        #pragma fini "my_fini"
        void my_fini() { ... do some clean up ... }
        

The ld command also supports the +fini function option to specify the terminator. Use this option while building a shared library, an incomplete executable, or fully bound executable.

Use the +fini option to specify the terminator (finalizer) functions, to be invoked in forward order, the order the functions appear left to right on the command line. The terminator functions are called in reverse of the depth-first order of initializers.

Do not use +fini with the -r option. (The linker ignores the +fini option.)

You can specify more than one terminator function on the command line with multiple option-symbol pairs, that is, each function you specify must be preceded by the +fini option.

HP-UX-10.X Style Initializers

HP-UX 10.X style initializers are the same type supported in all HP-UX 10.X releases. These are called both, before the user's code is started or a shared library is loaded (using shl_load or dlopen), and when the shared library is unloaded (using shl_unload or dlclose). The linker option +I is used to declare this type of initializer. The function returns nothing but takes two arguments. The first is a handle to the shared library being initialized. This handle can be used in calling shl_load routines. The second is set to non-zero at startup and zero at program termination or library unload.

$ ld -b foo.o +I my_10x_init -o libfoo.so
#include <dl.h>
         
void my_10x_init(shl_t handle, int loading)
{ /* handle is the shl_load API handle for */ 
  /* the shared library being initialized. */
  /* loading is non-zero at startup and zero at termination. */
         
        if (loading) {
                ... do some initializations ...
 
        else {
               ... do some clean up ... 
        }
}


Note

Unlike PA-32 mode, the PA-64 and IPF HP-UX 10.X style initializers are called when unloading implicitly loaded shared libraries.


See Using HP-UX 10.X Style Initializers for more information on using these initializers.


Using Init/Fini Initializers

This section describes use of init/fini initializer and provides examples:

Init and Fini Usage Example

This example consists of three shared libraries lib1.so, lib2.so, and lib3.so. The lib1.so depends on lib3.so. The main program (a.out) depends on lib1.so and lib2.so. Each shared library has an init style initializer and a fini style terminator. The lib1.so and lib2.so use linker options (+init and +fini) to specify the initializers and terminators, and lib3.so uses compiler pragmas.

C source for lib1.so (file lib1.c):
        lib1()
        {
            printf("lib1\n");
        }

        
        void
        lib1_init()
        {
            printf("lib1_init\n");
        }
        
        void
        lib1_fini()
        {
            printf("lib1_fini\n");
        }

        
C source for lib2.so (file lib2.c):
        lib2()
        {
            printf("lib2\n");
        }
        
        void
        lib2_init()
        {
            printf("lib2_init\n");
        }
        
        void
        lib2_fini()
        {
            printf("lib2_fini\n");
        }
        
C source for lib3.so (file lib3.c):
        lib3()
        {
            printf("lib3\n");
        }
        
        #pragma init "lib3_init"
        
        void
        lib3_init()
        {
            printf("lib3_init\n");
        }
        
        #pragma fini "lib3_fini"
        
        void
        lib3_fini()
        {
            printf("lib3_fini\n");
        }
        
Commands used to build these libraries:
        $ cc lib1.c lib2.c lib3.c main.c -c
        $ ld -b lib3.o -o lib3.so
        $ ld -b +init lib2_init +fini lib2_fini lib2.o -o lib2.so
        $ ld -b +init lib1_init +fini lib1_fini lib1.o ./lib3.so -o \
        lib1.so
        $ cc -L. main.o -l1 -l2 -lc
        
Output from running a.out:
        lib2_init
        lib3_init
        lib1_init
        lib1
        lib2
        lib3
        lib1_fini
        lib3_fini
        lib2_fini
        
Ordering Within an Executable or Shared Library

Multiple initializers/terminators within the same load module (an executable or shared library) are called in an order following these rules:

  • Inits in .o (object) files or .a (archive) files are called in the reverse order of the link line.

  • Finis in .o or .a files are called in forward order of the link line.

  • HP-UX 10.X style initializers are called in forward order of the +I options specified on the link line when loading a shared library. They are then called in reverse order when unloading the library.

  • HP-UX 10.X style initializers are called after inits and before finis.

  • Any inits or finis in archive (.a) files are called only if the .o which contains it is used during the link. Use the linker -v option to determine which .o files within an archive file were used.

  • Shared libraries on the link line (dependent libraries) follow the ordering described in Ordering Among Executables and Shared Libraries.

For example, the linker command:

$ ld -b first_64bit.o -l:libfoo.so second_64bit.o
  my_64bit.a +I first_10x_init +I \ second_10x_init -o libbar.so

results in the following order when library is loaded:

  1. inits from any .o files used in my_64bit.a

  2. inits in second_64bit.o

  3. inits in first_64bit.o

  4. first_10x_init

  5. second_10x_init

and the following order when library is unloaded:

  1. second_10x_init

  2. first_10x_init

  3. finis in first_64bit.o

  4. finis in second_64bit.o

  5. finis from any .o files used in my_64bit.a


Note

The libfoo.so object file is ignored in this example. It follows the rules described in Ordering Among Executables and Shared Libraries


Link Time Support for Ordering Initializers Based on Compile Time Defined Priority

The linker also provides support for ordering of initializers within the same load module. The ordering of the initializers is based on the priorities that are specified in the source code, or at compile time. If no priority is specified, the default priority settings are applied.

The following example illustrates the ordering of initializers based on the priority specified at compile-time:

         $ cat main.C
         #include <stdio.h>

         struct A {
             A() {printf("Constructing A\n");}
         };
         A a;

         // This will be 1st initializer entry in a.out
         // Since init routines are execute in reverse order
         // it will be executed after A()
         #pragma priority -10
         struct B {
             B() {printf("Constructing B\n");}
         };
         B b;

         int main() {
             printf("In main\n");
         }
                                 
         $ aCC  main.C
         $ a.out
         Constructing A
         Constructing B
         In main
                                 

Ordering Among Executables and Shared Libraries

If multiple load modules have initializers/terminators, the following rules apply to ordering:

  • While loading, the inits and HP-UX 10.X style initializers of any dependent libraries are called before the ones in the current library.

  • While unloading, the finis and HP-UX 10.X style initializers of any dependent libraries are called after the finis of the current library.

  • If a shared library is itself a dependent of one of its dependents (a "circular" dependency), no ordering between them is guaranteed.

For example, given three libraries: libA.so, libB.so, libC.so. If libA.so were linked as (libB.so and libC.so are "dependent" libraries of libA.so):

        $ ld -b foo.o -lB -lC -o libA.so
        

One possible ordering while loading is:

  • inits in C

  • inits in B

  • inits in A

and while unloading is:

  • finis in A

  • finis in B

  • finis in C


Using HP-UX 10.X Style Initializers

The initializer is called for libraries that are loaded implicitly at program startup, or explicitly with shl_load or dlopen.

When calling initializers for implicitly loaded libraries, the dynamic loader waits until all libraries have been loaded before calling the initializers. It calls the initializers in depth-first order - that is, the initializers are called in the reverse order in which the libraries are searched for symbols. All initializers are called before the main program begins execution.

When calling the initializer for explicitly loaded libraries, the dynamic loader waits until any dependent libraries are loaded before calling the initializers. As with implicitly loaded libraries, initializers are called in depth-first order.

Note that initializers can be disabled for explicitly loaded libraries with the BIND_NOSTART flag to shl_load. For more information, see The shl_load Summary .

This section discusses the following topics:

Declaring the Initializer with the +I Option

To declare the name of the initializer, use the +I linker option when creating the shared library. The syntax of the +I option is:

        +I initializer
        

where initializer is the initializer's name.

Multiple initializers may be called by repeating the +I initializer option.

For example, to create a shared library named libfoo.so that uses an initializer named init_foo, use this linker command line:

        $ ld -b -o libfoo.so libfoo.o +I init_foo
        
Order of Execution of Multiple Initializers

Multiple initializers are executed in the same order that they appear on the command line; they are unloaded in reverse order. (This applies only to the calling order within a shared library, not across multiple shared libraries.)


Note

In PA-32 compatibility mode, initializers are not executed when unloading shared libraries which were implicitly loaded because the program exits without re-entering the dynamic loader to unload them. Initializers are called only during the explicit unloading of a shared library.


Initializers behave the same as other symbols; once they are bound they cannot be overridden with a new symbol through the use of shl_definesym() or by loading a more visible occurrence of the initializer symbol with the BIND_FIRST flag. What this means is that once the initializer is executed upon a load, it is guaranteed to be the same initializer that is called on an explicit unload.

Initializer Syntax
        void initializer( shl_t handle,
                          int loading )
        
initializer

The name of the initializer as specified with the +I linker option.

handle

The initializer is called with this parameter set to the handle of the shared library for which it was invoked.

loading

The initializer is called with this parameter set to 1 (true) when the shared library is loaded and 0 (false) when the library is unloaded.

The initializers cannot be defined as local definitions. Initializers cannot be hidden through the use of the -h option when building a shared library.

HP recommends that initializers be defined with names which do not cause name collisions with other user-defined names in order to avoid overriding behavior of shared library symbol binding.

Accessing Initializers' Addresses

Prior to the HP-UX 10.0 release, initializer's addresses could be accessed through the initializer field of the shared library descriptor which is returned from a call to shl_get(). To support multiple initializers, the shl_getsymbols() routine is enhanced to support the return of the initializer's address.

If only one initializer is specified for a given library, its address is still available through the initializer field of a shared library descriptor. If more than one initializer is specified, the initializer field is set to NO_INITIALIZER. Access to multiple initializers can then be accomplished through the use of shl_getsymbols(). (The shl_getsymbols() routine can also access a single initializer.)


Note

The shl_getsymbols() routine may not return the initializer which was invoked for a given library if a more visible initializer symbol is defined after the library being queried has been loaded. This can occur through the use of shl_definesym() and by explicitly loading a more visible symbol using the BIND_FIRST flag upon loading.


To access initializers, use the new flag, INITIALIZERS, in the shl_getsymbols() routine. This flag can be ORed with the NO_VALUES and GLOBAL_VALUES flags. For example,

        shl_getsymbols(handle,
                       TYPE_PROCEDURE,
                       INITIALIZERS | GLOBAL_VALUES,
                       malloc,
                       &symbol_array);

If the GLOBAL_VALUES modifier is not used and the initializer is defined in another shared library or in the program file, shl_getsymbols() does not find the initializer for the requested library because it is not defined within the library.

For more information on the usage of shl_getsymbols(), see The shl_getsymbols Routine.

Example: An Initializer for Each Library

One way to use initializers is to define a unique initializer for each library. For instance, the following example shows the source code for a library named libfoo.so that contains an initializer named init_foo:

C Source for libfoo.c
        #include <stdio.h>
        #include <dl.h>
        /*
         * This is the local initializer that is called when the libfoo.so
         * is loaded and unloaded:
         */
        void init_foo(shl_t hndl, int loading)
        {
         if (loading)
            printf("libfoo loaded\n");
          else
            printf("libfoo unloaded\n");
        }
        
        float in_to_cm(float in)              /* convert inches to centimeters */
        {
          return (in * 2.54);
        }
         
        float gal_to_l(float gal)             /* convert gallons to litres */
        {
          return (gal * 3.79);
        }
         
        float oz_to_g(float oz)               /* convert ounces to grams */
        {
          return (oz * 28.35);
        }
        

You can use the +I linker option to register a routine as an initializer. Following are the commands to create libfoo.so and to register init_foo as the initializer:

        $ cc -Aa -c libfoo.c
        $ ld -b -o libfoo.so +I init_foo libfoo.o
        

To use this technique with multiple libraries, each library must have a unique initializer name. The following example program loads and unloads libfoo.so.

C Source for testlib.c
        #include <stdio.h>
        #include <dl.h>
        main()
        {
          float (*in_to_cm)(float), (*gal_to_l)(float), (*oz_to_g)(float);
          shl_t hndl_foo;
          /*
           * Load libfoo.so and find the required symbols:
           */
           if ((hndl_foo = shl_load("libfoo.so",
              BIND_IMMEDIATE, 0)) == NULL)
              perror("shl_load: error loading libfoo.so"), exit(1);
           
           if (shl_findsym(hndl_foo, "in_to_cm", TYPE_PROCEDURE,
              (void *) &in_to_cm))
              perror("shl_findsym: error finding in_to_cm"), exit(1);
           
           if (shl_findsym(hndl_foo, "gal_to_l", TYPE_PROCEDURE,
              (void *) &gal_to_l))
              perror("shl_findsym: error finding gal_to_l"), exit(1);
           
           if (shl_findsym(hndl_foo, "oz_to_g", TYPE_PROCEDURE,
              (void *) &oz_to_g))
              perror("shl_findsym: error finding oz_to_g"), exit(1);
            /*
             * Call routines from libfoo.so:
             */
             printf("1.0in  = %5.2fcm\n", (*in_to_cm)(1.0));
             printf("1.0gal = %5.2fl\n", (*gal_to_l)(1.0));
             printf("1.0oz  = %5.2fg\n", (*oz_to_g)(1.0));
            /*
             * Unload the library:
             */
            shl_unload(hndl_foo);
          }
        

The following is the output of running the testlib program:

Output of testlib
        $ cc -Aa testlib.c -o testlib -ldld
        $ testlib
         
        libfoo loaded
        1.0in  =  2.54cm
        1.0gal =  3.79l
        1.0oz  = 28.35g
        libfoo unloaded
        
Example: A Common Initializer for Multiple Libraries

Rather than have a unique initializer for each library, libraries can have one initializer that calls the actual initialization code for each library. To use this technique, each library declares and references the same initializer (for example, _INITIALIZER), which calls the appropriate initialization code for each library.

This is easily done by defining load and unload functions in each library. When _INITIALIZER is called, it uses shl_findsym to find and call the load or unload function (depending on the value of the loading flag).

The following example shows the source for an _INITIALIZER function:

C Source for _INITIALIZER (file init.c)
        #include <dl.h>
        /*
         * Global initializer used by shared libraries that have
         * registered it:
         */
        void _INITIALIZER(shl_t hand, int loading)
        {
             void (*load_unload)();
         
             if (loading)
                shl_findsym(&hand, "load", TYPE_PROCEDURE, (void *) &load_unload);
             else
                shl_findsym(&hand, "unload", TYPE_PROCEDURE, (void *) &load_unload);
         
             (*load_unload) ();           /* call the function */
        }

The following two source files show shared libraries that have registered _INITIALIZER.

C Source for libunits.c
        #include <stdio.h>
        #include <dl.h>
        
        void load()                   /* called after libunits.so loaded */
        {
           printf("libunits.so loaded\n");
        }
         
        void unload()                 /* called after libunits.so unloaded */
        {
           printf("libunits.so unloaded\n");
        }
         
        extern void _INITIALIZER();
         
        float in_to_cm(float in)      /* convert inches to centimeters */
        {
           return (in * 2.54);
        }
         
        float gal_to_l(float gal)     /* convert gallons to litres */
        {
           return (gal * 3.79);
        }
         
        float oz_to_g(float oz)       /* convert ounces to grams */
        {
           return (oz * 28.35);
        }
        
C Source for libtwo.c
        #include <stdio.h>
        void load()                     /* called after libtwo.so loaded */
        {
           printf("libtwo.so loaded\n");
        }
        void unload()                   /* called after libtwo.so unloaded */
        {
           printf("libtwo.so unloaded\n");
        }
         
        extern void _INITIALIZER();
        void (*init_ptr)() = _INITIALIZER;
         
        void foo()
        {
           printf("foo called\n");
        }
        void bar()
        {
           printf("bar called\n");
        }

Following are the commands used to build these libraries:

        $ cc -Aa -c libunits.c
        $ ld -b -o libunits.so +I _INITIALIZER libunits.o
        $ cc -Aa -c  libtwo.c
        $ ld -b -o libtwo.so +I _INITIALIZER libtwo.o
        

The following is an example program that loads these two libraries:

C Source for testlib2.c
        #include <stdio.h>
        #include <dl.h>
        main()
        {
          float (*in_to_cm)(float), (*gal_to_l)(float), (*oz_to_g)(float);
          void (*foo)(), (*bar)();
          shl_t hndl_units, hndl_two;
         
          /*
           * Load libunits.so and find the required symbols:
           */
          if ((hndl_units = shl_load("libunits.so", BIND_IMMEDIATE, 0)) == NULL)
             perror("shl_load: error loading libunits.so"), exit(1);
          if (shl_findsym(hndl_units, "in_to_cm",
             TYPE_PROCEDURE, (void *) in_to_cm))
             perror("shl_findsym: error finding in_to_cm"), exit(1);
           
          if (shl_findsym(hndl_units, "gal_to_l",
             TYPE_PROCEDURE, (void *) gal_to_l))
             perror("shl_findsym: error finding gal_to_l"), exit(1);
             
          if (shl_findsym(hndl_units, "oz_to_g",
             TYPE_PROCEDURE, (void *) oz_to_g))
             perror("shl_findsym: error finding oz_to_g"), exit(1);
             
          /*
           * Load libtwo.so and find the required symbols:
           */
          if ((hndl_two = shl_load("libtwo.so", BIND_IMMEDIATE, 0)) == NULL)
             perror("shl_load: error loading libtwo.so"), exit(1);
          if (shl_findsym(hndl_two, "foo", TYPE_PROCEDURE, (void *) foo))
             perror("shl_findsym: error finding foo"), exit(1);
          if (shl_findsym(hndl_two, "bar", TYPE_PROCEDURE, (void *) bar))
             perror("shl_findsym: error finding bar"), exit(1);
          /*
           * Call routines from libunits.so:
           */
          printf("1.0in  = %5.2fcm\n", (*in_to_cm)(1.0));
          printf("1.0gal = %5.2fl\n", (*gal_to_l)(1.0));
          printf("1.0oz  = %5.2fg\n", (*oz_to_g)(1.0));
          /*
           * Call routines from libtwo.so:
           */
          (*foo)();
          (*bar)();
          /*
           * Unload the libraries so we can see messages displayed by initializer:
           */
          shl_unload(hndl_units);
          shl_unload(hndl_two);
        }

Following is the command to create the executable testlib2:

        $ cc -Aa -Wl,-E -o testlib2 testlib2.c init.c -ldld
        

Note that the -Wl,-E option is required to cause the linker to export all symbols from the main program. This allows the shared libraries to find the _INITIALIZER function in the main executable.

The output from running testlib2 is given below:

Output of testlib2

        $ cc -Aa testlib2.c -o testlib2 -ldld
        $ testlib2
        
        libunits.so loaded
        libtwo.so loaded
        1.0in  =  2.54cm
        1.0gal =  3.79l
        1.0oz  = 28.35g
        foo called
        bar called
        libunits.so unloaded
        libtwo.so unloaded
The dlopen Shared Library Management Routines

This section describes the dl* family of shared library management routines. All these routines are available in IPF/PA-64 mode. Support for the following routines is available in PA-32 mode:

  • dlopen

  • dlclose

  • dlsym

  • dlerror


The dlopen Routine

The dlopen routine opens a shared library.

Syntax
        void *dlopen(const char *file, int mode);
        void *dlopene(const char *file, int mode, struct dlopen_opts *opts);
        
Parameters

Parameter Definition  

file

Used to construct a pathname to the shared library file.

If files contain a slash character (/), dlopen uses the file argument itself as the pathname. If not, dlopen searches a series of directories for file.

  • Any directories specified by the dynamic search path, which is set by calling dlsetlibpath
  • Any directories specified by the environment variable LD_LIBRARY_PATH.

  • Any directories specified by the variable SHLIB_PATH.

  • Any directories specified by the RPATH of the calling load module.

  • The default library search path.

  • The current working directory.

 

flags

Mode Definition
 

RTLD_LAZY

Under this mode, only references to data symbols are relocated when the library is loaded. References to functions are not relocated until a given function is invoked for the first time. This mode results in better performance, because a process may not reference all of the functions in any given shared object.

 

RTLD_NOW

Under this mode, all necessary relocations are performed when the library is first loaded. This can cause some wasted effort if relocations are performed for functions that are never referenced. But it is useful for applications that need to know as soon as an object is loaded that all symbols referenced during execution are available.

 

RTLD_GLOBAL

The shared library's symbols are made available for the relocation processing of any other object. In addition, symbol lookup using dlopen(0, mode) and an associated dlsym() allows objects loaded with RTLD_GLOBAL to be searched.

 

RTLD_LOCAL

The shared library's symbols are made available for relocation processing only to objects loaded in the same dlopen invocation.

If neither RTLD_GLOBAL nor RTLD_LOCAL are specified, the default is RTLD_LOCAL.

 

RTLD_VERBOSE

Displays verbose messages when binding symbols.

Return Values

A successful dlopen call returns to the process a handle which the process can use on subsequent calls to dlsym and dlclose. This value must not be interpreted in any way by the process.

The dlopen routine returns NULL under the following conditions:

  • file cannot be found.

  • file cannot be opened for reading.

  • file is not a shared object.

  • An error occurs during the process of loading file or relocating its symbolic references.

More detailed diagnostic information is available through dlerror.

Description

The dlopen routine is one of a family of routines that give the user direct access to the dynamic linking facilities. The dlopen routine makes a shared library specified by a file available to a running process. A shared library may specify other objects that it "needs" in order to execute properly. These dependencies are specified by DT_NEEDED entries in the .dynamic section of the original shared library. Each needed shared library may, in turn, specify other needed shared libraries. All such shared libraries are loaded along with the original shared library as a result of the call to dlopen.

If the value of file is 0, dlopen provides a handle on a "global symbol shared library." This shared library provides access to the symbols from an ordered set of shared libraries consisting of the original a.out, all of the shared libraries that were loaded at program startup along with the a.out, and all shared libraries loaded using a dlopen operation along with the RTLD_GLOBAL flag. As the latter set of shared libraries can change during execution, the set identified by handle can also change dynamically.

Only a single copy of a shared library file is brought into the address space, even if dlopen is invoked multiple times in reference to the file, and even if different pathnames are used to reference the file.

When a shared library is brought into the address space of a process, it can contain references to symbols whose addresses are not known until the shared library is loaded. These references must be relocated before the symbols can be accessed. The mode parameter governs the relocations and may have the following values (defined in Parameters): RTLD_LAZY and RTLD_NOW.

Any shared library loaded by dlopen that requires relocations against global symbols can reference the following:

  • Symbols in the original a.out.

  • Any shared libraries loaded at program startup, from the shared library itself.

  • Any shared library included in the same dlopen invocation.

  • Any shared libraries that were loaded in any dlopen invocation that specified the RTLD_GLOBAL flag.

To determine the scope of symbols that are made available for relocation processing of objects loaded in a dlopen invocation, the mode parameter can be bitwise alternated with one of the following values:

RTLD_GROUP: Under this mode, the specified object and its dependencies behave as if they were built with -B group (see ld(1)). Only symbols from objects loaded in the same dlopen invocation are made available for relocation. This ensures that all relocations are satisfied using symbol definitions from the same dlopen invocation.

RTLD_WORLD: Under this mode, only symbols from global objects and from the object itself are available for relocation processing. It does not use symbol definitions from other objects loaded as part of the dlopen invocation. This flag has no effect on objects built with -B group (see ld(1)).

RTLD_PARENT: Under this mode, symbols from the object that invoked dlopen are also made available for relocation.

The default modes for dlopen are RTLD_WORLD|RTLD_GROUP. These flags are alternated together when the same object is loaded with different modes.

The values RTLD_GROUP, RTLD_WORLD, and RTLD_PARENT are not available for PA32 systems.

The following flags do not affect relocation processing, but provide the following features:

RTLD_NODELETE: Under this mode, the specified object and its dependencies behave as if they were built with -B nodelete . An explicit unload using dlclose or shl_load returns success silently without detaching the shared library for the process. Subsequently, the shared library handle is valid only for shl_findsym. It stays invalid for dlsym, dlclose, and shl_unload until the next explicit load using shl_load or dlclose.

RTLD_NOLOAD: Under this mode, the specified object is not loaded into the process's address space, but a valid handle is returned if the object already exists in the process address space. If the specified object does not already exist, then an error is returned. RTLD_NOLOAD can be used to query the presence, or for overriding the modes of an existing object.

If a file is specified in multiple dlopen invocations, mode is interpreted at each invocation. Note, however, that once RTLD_NOW has been specified, the linker operation completes all relocations, rendering any further RTLD_NOW operations redundant and any further RTLD_LAZY operations irrelevant. Similarly note that once you specify RTLD_GLOBAL, the shared library maintains the RTLD_GLOBAL status regardless of any previous or future specification of RTLD_LOCAL, as long as the shared library remains in the address space [see dlclose(3C)].

RTLD_VERBOSE: Under this mode, the dynamic loader displays verbose messages when binding symbols on PA32 systems. If RTLD_VERBOSE is ORed with the flags parameter, the dynamic loader displays verbose messages for all the unresolved symbols. You can use this option to view the symbols, which are not bound. Typically, RTLD_VERBOSE is used with RTLD_NOW to debug unresolved symbols.

For example:

...
void *dl =dlopen("unsat.sl", RTLD_NOW | RTLD_VERBOSE);
...


Note
The RTLD_VERBOSE flag for dlopen routine, and the BIND_VERBOSE flag for shl_load routine can also be enabled by using the _HP_DLDOPTS environment variable (instead of specifying the flags in the dlopen, and shl_load routines). This is particularly useful for existing programs to enable RTLD_VERBOSE, and BIND_VERBOSE without any change in code. To set the RTLD_VERBOSE and the BIND_VERBOSE flags by using _HP_DLDOPTS, enter the following command at the HP-UX prompt:

$ export _HP_DLOPTS=-load_verbose

Symbols introduced into a program through calls to dlopen may be used in relocation activities. Symbols so introduced may duplicate symbols already defined by the program or previous dlopen operations. To resolve the ambiguities such a situation may present, the resolution of a symbol reference to a symbol definition is based on a symbol resolution order. Two such resolution orders are defined: load and dependency ordering.

  • Load order establishes an ordering among symbol definitions using the temporal order in which the shared libraries containing the definitions were loaded, such that the definition first loaded has priority over definitions added later. Load ordering is used in relocation processing.

  • Dependency ordering uses a "breadth-first" order starting with a given shared library, then all of its dependencies, then any dependents of those, iterating until all dependencies are satisfied.

The dlsym function uses dependency ordering, except when the global symbol shared library is obtained via a dlopen operation on file with a value 0. The dlsym function uses load ordering on the global symbol shared library.

When a dlopen operation first makes it accessible, a shared library and its dependent shared libraries are added in dependency order. Once all shared libraries are added, relocations are performed using load order. Note that if a shared library and its dependencies have been loaded by a previous dlopen invocation or on startup, the load and dependency order may yield different resolutions.

The symbols introduced by dlopen operations and available through dlsym are those which are "exported" as symbols of global scope by the shared library. For shared libraries, such symbols are typically those that were specified in (for example) C source code as having extern linkage. In PA-32 mode, for a.out files, only a subset of externally visible symbols are typically exported: specifically those referenced by the shared libraries with which the a.out is linked. The exact set of exported symbols for any shared library or the a.out can be controlled using the linker [see ld(1)].

The dlopene routine is an extension to dlopen, which allows the caller to specify explicitly the placement of a shared library's text and data segment when the library is dynamically loaded.

The dlopen_opts structure has the following members:

        struct dlopen_opts {
           long flags;
           char* text_addr;
           char* data_addr;
        };
        
Flags contain the load option, defined by logical OR of the following values:

RTLD_EXT_TEXT_ADDR indicates that an explicit base address for the shared library text segment is provided.

RTLD_EXT_DATA_ADDR indicates that an explicit base address for the shared library private data segment is provided.

If the RTLD_EXT_DATA_NO_ZERO_FILL flag is set, dlopene does not zero fill the bss part of the data segment. This may improve load time for libraries with large bss sections. This flag is only valid with RTLD_EXT_DATA_ADDR.

The dynamic load accesses only the address fields that are specified by the "Flags" fields.

The text_addr contains the explicit base address for the shared library's text segment.

The data_addr contains the explicit base address for the shared library's data segment.

Both the text_addr and data_addr must be aligned at a 16-byte boundary.

The caller can invoke dlgetfileinfo to obtain the information needed to allocate memory for the load segments.

The caller of dlopene is responsible for allocating memory with the following permission:

  • read, write and execute (RWX) permission for text_addr

  • read and write (RW) permission for data_addr


Note

The environment variable LD_LIBRARY_PATH must contain a colon-separated list of directories, in the same format as the PATH variable [see sh(1)]. In PA-64 and IPF mode LD_LIBRARY_PATH is ignored if the process' real user id is different from its effective user id or its real group id is different from its effective group id [see exec(2)] or if the process has acquired any privileges [see tfadmin(1M)].

In IPF/PA-64 mode, with the +compat option specified, LD_LIBRARY_PATH and the +b embedded path are ignored when searching for dependent libraries.


Example

The following example shows how to use dlopen to load a shared library. The RTLD_GLOBAL flag enables global visibility to symbols in lib1.so. The RTLD_LAZY flag indicates that only references to data symbols are to be relocated and all function symbol references are to be delayed until their first invocation.

        #include <stdio.h>
        #include <dlfcn.h>
        
        int main(int argc, char **argv)
        {
            void* handle;    handle = dlopen("./lib1.so", RTLD_GLOBAL | RTLD_LAZY);
        
        if (handle == NULL) {
               printf("Cannot load library\n");
            }
        }
        

The following example illustrates the use of dlopene to load a shared library with an explicit data segment address. For simplicity, error checking has been omitted:

        #include <dlfcn.h>
        #include <sys/mman.h>
        
        int main() {
           struct dlfileinfo info;
           void *handle;
           struct dlopen_opts opts;
           int status;
           memset(&info, 0, sizeof(info));
           memset(&opts, 0, sizeof(opts));
              /* Get file info */
           status = dlgetfileinfo("libfoo.so", sizeof(info), &info);
           opts.flags = RTLD_EXT_DATA_ADDR;
           /* allocate memory for the data segment */
           opts.data_addr = (char*) mmap(0, info.data_size,
                              PROT_READ|PROT_WRITE,
                              MAP_SHARED|MAP_ANONYMOUS,
                              -1, 0);
           /* call dlopene */
           handle = dlopene("libfoo.so", RTLD_NOW|RTLD_GLOBAL, &opts);
           /* Insert user code to use library */
           /* close library */
           status = dlclose(handle);
           /* free memory */
           munmap(opts.data_addr, info.data_size);
        }
        

The dlsetlibpath Routine

The dlsetlibpath routine sets the dynamic search path to locate shared libraries.

Synopsis
        cc [flag ... ] file ... -ldl [library] ...
        #include <dlfcn.h>
        int dlsetlibpath(const char *libpath, int flags)
        
Description

The dlsetlibpath routine is one of a family of routines that give the user direct access to the dynamic linking facilities (using the -ldl option on the compiler of ld command line). The dlsetlibpath routine sets the dynamic search path used by dlopen, dlopene, and dlgetfileinfo to locate shared libraries.

The libpath is the dynamic search path. It is a list of one or more path names separated by colons (:).

When searching for a library, the dynamic loader uses search paths in the following default order:

  1. The dynamic search path specified in a call to dlsetlibpath.
  2. The LD_LIBRARY_PATH environment variable.
  3. The SHLIB_PATH environment variable.
  4. The embedded path of the calling module (executable program or shared library) for libraries names in calls to dlopen, dlopene, or dlgetfileinfo. For dependent libraries, the embedded path of the library that named it as a dependent is used.
  5. The standard library path.
  6. The current working directory. (This is only for libraries named in calls to dlopen, dlopene, and dlgetfileinfo, not their dependent libraries.)

See dld.so(5) for additional information on search paths and options that can change the order described above.

Any combination of these paths may be disabled by setting flags to one or more of the following values alternated together:

RTLD_FLAG_DISABLE_DYNAMIC_PATH - If this flag is set, the dynamic loader does not search the directories specified in the dynamic search path.

RTLD_FLAG_DISABLE_LD_LIBRARY_PATH - If this flag is set, the dynamic loader does not search the directories specified in the LD_LIBRARY_PATH environment variable.

RTLD_FLAG_DISABLE_SHLIB_PATH - If this flag is set, the dynamic loader does not search the directories specified in the SHLIB_PATH environment variable.

RTLD_FLAG_DISABLE_EMBEDDED_PATH - If this flag is set, the dynamic loader does not search the directories specified in the embedded path.

RTLD_FLAG_DISABLE_STD_PATH - If this flag is set, the dynamic loader does not search the standard library directory.

RTLD_FLAG_DISABLE_CWD_PATH - If this flag is set, the dynamic loader does not search the current.

Mulitple search paths can be disabled by alternating individual flags:
flags = RTLD_FLAG_DISABLE_STD_PATH | RTLD_FLAG_DISABLE_CWD_PATH

A single search path can be enabled by setting flags to the complement of the flag value that disables that search path:
flags = ~RTLD_FLAG_DISABLE_DYNAMIC_PATH

Examples

The following example illustrates the use of dlsetlibpath to set the dynamic search path and disable other search paths. For simplicity, error checking has been omitted.

        #include <dlfcn.h>
        int main() {
            void *handle;
            int   status;
            int   flags;
            /* Set dynamic search path and disable the embedded
             * path and the standard library directory.
             */
            flags = RTLD_FLAG_DISABLE_EMBEDDED_PATH |
                  
                     RTLD_FLAG_DISABLE_STD_PATH;
            status = dlsetlibpath("/opt/lib:/opt/usr/lib", flags);
            /* Call dlopen to load a library using the dynamic
             * search path.
             */
            handle = dlopen("mylib.so"), RTLD_LAZY);
            /* Remove the dynamic search path and reenable all
             * disabled search paths.
             */
            status = dlsetlibpath(NULL, 0);
        }
        
Multithread Usage

This routine is thread-safe. Calls to dlsetlibpath affect all subsequent calls to dlopen, dlopene, and dlgetfileinfo on any thread.

Return Value

If successful, dlsetlibpath returns 0, otherwise a non-0 value is returned. More detailed diagnostic information is available through dlerror or dlerrno.

Errors

If dlsetlibpath fails, a subsequent call to dlerrno returns one of the following values:

RTLD_ERR_NO_MEMORY - Cannot allocate dynamic memory.
RTLD_ERR_CANT_APPLY_RELOC
- Failed to apply relocation while resolving call to dlsetlibpath.
RTLD_ERR_SIGINHIBIT_FAILED
- The siginhibit call failed on entry to dlsetlibpath .
RTLD_ERR_SIGENABLE_FAILED
- The sigenable call failed on exit from dlsetlibpath.
RTLD_ERR_SETCANCELSTATE_FAILED
- __thread_setcancelstate failed on entry to or exit from dlsetlibpath.
RTLD_ERR_INV_DLSETLIBPATH_ARGUMENT
- Invalid argument in call to dlsetlibpath.

See also dlopen(3C), dlopene(3C), dlgetfileinfo(3C), dlerror(3C), dlerrno(3C), dld.so(5).


The dlerrno Routine

The dlerrno routine returns a numeric error code that corresponds to the last error that occurred during dynamic linking processing.

Synopsis
        cc [flag ... ] file ... -ldl [library] ...
        #include <dlfcn.h>
        int *dlerrno(void);
        
Description

The dlerrno routine is one of a family of routines that give the user direct access to the dynamic linking facilities (using the -ldl option on the compiler or ld command line). The dlerrno routine returns a numeric error code that corresponds to the last error that occurred during dynamic linking processing. If no dynamic linking errors have occurred since the last invocation of dlerrno, dlerrno returns RTLD_ERR_NO_ERR (-1). Thus, invoking dlerrno a second time, immediately following a prior invocation, returns RTLD_ERR_NO_ERR (-1).

For a complete list of the mnemonic error codes, see the dlerrno(3C) manpage.


The dlgetfileinfo Routine

The dlgetfileinfo routine returns file information for a library prior to loading it.

Synopsis
        cc [flag ... ] file ... -ldl [library] ...
        #include <dlfcn.h>
        uint64_t dlgetfileinfo(const char *file,
                               size_t info_size,
                               struct dlfileinfo *info);
        
Description

The dlgetfileinfo routine is one of a family of routines that give the user direct access to the dynamic linking facilities (using the -ldl option on the compiler or ld command line). dlgetfileinfo returns file information for a library prior to loading it. This information can be used to allocate load segments before calling dlopene.

The file is used to construct a pathname to the library. The dynamic loader searches for the library using the standard search rules used by dlopen and dlopene. If the library is found and is a valid shared library, dlgetfileinfo returns information about the library through the info parameter.

The info_size is the size in bytes of the info buffer.

The info is a pointer to a buffer allocated by the user program. The dynamic loader fills this buffer with file information.

A dlfileinfo structure has the following members:

        struct dlfileinfo {
           size_t text_size;
           size_t data_size;
           char *filename;
        }
        

The text_size is the size in bytes of a shared library's text segment.

The data_size is the size in bytes of a shared library's data segment.

The filename is the path to the shared library. This path may be passed to a subsequent dlopen or dlopene call to avoid searching for the library a second time. The caller of dlgetfileinfo must copy the value of filename to insure that it is not corrupted.

Examples

The following example illustrates the use of dlgetfileinfo to allocate memory for mapping a shared library's data segment. For simplicity, error checking has been omitted.

        #include <dlfcn.h>
        #include <string.h>
        /* allocate_data is a user-supplied routine that allocates a
         * memory buffer of at least "data_size" bytes and returns
         * a pointer to the buffer.
         */
        extern char *allocate_data(size_t data_size);
        int main() {
            void   *handle;
            int    status;
            char   *pathname;
            struct dlfileinfo info;
            struct dlopen_opts opts;
            /* Locate library and get file information */
            status = dlgetfileinfo("mylib.so", sizeof(info), &info);
            if (status == 0) {
               
               /* Make a copy of the library pathname returned by
                * dlgetfileinfo().
                */
               pathname = strdup(info.filename);
               /* Allocate data segment */
               opts.data_addr = allocate_data(info.data_size);
               /* Not preallocating text segment */
               opts.text_addr = 0;
               /* Set dlopen() flags to indicate the data segment
                * has been preallocated.
                */
               opts.flags = RTLD_EXT_DATA_ADDR:
               /* Call dlopene() to load the library using the path
                * where dlgetfileinfo found the library and the
                * preallocated memory buffer for mapping the library
                * data segment.
                */
               handle = dlopene(pathname, RTLD_LAZY, &opts);
               /* Remove copy of library pathname */
               free(pathname);
               /* Insert code here to use library */
               /* Close library */ 
               status = dlclose(handle);
               /* Insert code here to free storage allocated by
                * allocate_data().
                */
           }
        }
        
Multithread Usage

The dlgetfileinfo routine is thread-safe.

Return Value

If successful, dlgetfileinfo returns 0, otherwise a non-0 value is returned. More detailed diagnostic information is available through dlerror or dlerrno.

Errors

If dlgetfileinfo fails, a subsequent call to dlerrno returns an error. For a complete listing of those values, see the dlgetfileinfo(3C) manpage.


The dlerror Routine

The dlerror routine gets diagnostic information.

  Syntax
        char *dlerror(void);
        
  Description

The dlerror routine returns a null-terminated character string (with no trailing newline character) that describes the last error that occurred during dynamic linking processing. If no dynamic linking errors have occurred since the last invocation of dlerror, it returns NULL. Thus, invoking dlerror a second time, immediately following a prior invocation, results in NULL being returned.


Note

The messages returned by dlerror may reside in a static buffer that is overwritten on each call to dlerror. Application code must not write to this buffer. Programs taht seek to preserve an error message must make their own copies of that message.


Example

The following code sequence shows how to use dlerror to get diagnostic information:

        void*   handle;
         
        /* Try to load a non-existing library */
         
        handle = dlopen("invalid.so", RTLD_GLOBAL | RTLD_LAZY);
         
        if (handle == NULL) {
         
            printf("%s\n", dlerror());
         
        }
        


The dlsym Routine

The dlsym gets the address of a symbol in shared library.

Syntax
        void *dlsym(void *handle, const char *name);
        
Parameters

Parameter Definition

handle

Either the value returned by a call to dlopen or one of the special flags RTLD_NEXT, RTLD_SELF, and RTLD_DEFAULT. In the former case, the corresponding shared library must not have been closed using dlclose.

name

The symbol's name as a character string.

Return Values

If handle does not refer to a valid shared library opened by dlopen, or if the named symbol cannot be found within any of the shared libraries associated with handle, dlsym returns NULL. The dlerror routine provides more detailed diagnostic information.

Description

The dlsym routine allows a process to obtain the address of a symbol defined within a shared library previously opened by dlopen.

The dlsym routine searches for the named symbol in all shared libraries loaded automatically as a result of loading the shared library referenced by handle [see dlopen(3C)]. If handle is RTLD_NEXT, the search begins with the "next" shared library after the shared library from which dlsym was invoked. Shared libraries are searched using a load order symbol resolution algorithm [see dlopen(3C)]. The "next" shared library, and all other shared libraries searched, are either of global scope (because they were loaded at startup or as part of a dlopen operation with the RTLD_GLOBAL flag) or are shared libraries loaded by the same dlopen operation that loaded the caller of dlsym.

If handle is RTLD_SELF, the search begins with the object from which dlsym was invoked. Objects are searched using the load order symbol resolution algorithm.

If handle is RTLD_DEFAULT, then the symbol search is done in the scope of the object that invoked dlsym. For example, if the caller object was loaded as a result of dlopen with RTLD_GROUP, it searches symbols in objects that were loaded in the same dlopen invocation as the caller object.

Usage

RTLD_NEXT can be used to navigate an intentionally created hierarchy of multiply defined symbols created through interposition. For example, if a program wished to create an implementation of malloc that embedded some statistics gathering about memory allocations, such an implementation could define its own malloc which gathers the necessary information, and use dlsym with RTLD_NEXT to find the "real" malloc, which performs the actual memory allocation. Of course, this "real" malloc can be another user-defined interface that added its own value and then used RTLD_NEXT to find the system malloc.

Examples

The following example shows how to use dlopen and dlsym to access either function or data objects. (For simplicity, error checking has been omitted.)

        void *handle;
        int i, *iptr;
        int (*fptr)(int);
        /* open the needed object */
        handle = dlopen("/usr/mydir/mylib.so", RTLD_LAZY);
        /* find address of function and data objects */
        fptr = (int (*)(int))dlsym(handle, "some_function");
        iptr = (int *)dlsym(handle, "int_object");
        /* invoke function, passing value of integer as a parameter */
        i = (*fptr)(*iptr);
        

The following example shows how to use dlsym with RTLD_NEXT to add functionality to an existing interface. (Error checking has been omitted.)

        extern void record_malloc(void *, size_t);
                void *
                malloc(size_t sz)
                {
                        void *ptr;
                        void *(*real_malloc)(size_t);
                
                        real_malloc = (void * (*) (size_t))
                                        dlsym(RTLD_NEXT, "malloc");
                        ptr = (*real_malloc)(sz);
                        record_malloc(ptr, sz);
                        return ptr;
                }
        


The dlget Routine

The dlget routine retrieves information about a loaded module (program or shared library).

Syntax
        void *dlget(int index, 
                    struct load_module_desc *desc, 
                    size_t desc_size);
        
Parameters

Parameter Definition

index

Specifies the requested shared library by its placement on the dynamic loader's search list. An index of -1 requests information about the dynamic loader. An index of -2 requests information about the program file itself.

desc

Must be preallocated by the user. The structure members are filled in by the dynamic loader with information about the requested shared library.

desc_size

Specifies the size in bytes of the load_module_desc structure sent in by the user.

Return Values

If successful, dlget returns a handle for the shared library as defined by the return value from dlopen(). If a call to dlget is unsuccessful, a NULL pointer is returned and desc remains unchanged.

Description

The dlget routine is one of a family of routines that give the user direct access to the dynamic linking facilities. The dlget routine retrieves information about a load module from an index specifying the placement of a load module in the dynamic loader's search list.

A load_module_desc structure has the following members:

        struct load_module_desc {
           unsigned long text_base;
           unsigned long text_size;
           unsigned long data_base;
           unsigned long data_size;
           unsigned long unwind_base;
           unsigned long linkage_ptr;
           unsigned long phdr_base;
           unsigned long tls_size;
           unsigned long tls_start_addr;
           }
        
Example

The following code sequence shows how to use dlget to retrieve information about loaded modules. The code sequence prints the text base of all loaded modules:

        void*          handle;
        int              index;
        struct        load_module_desc desc;
        
        for (index = -2; ; i++) {   handle = dlget(i, &desc, sizeof(struct load_module_desc));
        
        if (handle = NULL) {
                printf("%s\n", dlerror());
                break;
            }
            else {
                printf("library %d text base = %lx\n", index,
                        desc.text_base);
            }
        }
        


The dlmodinfo Routine

The dlmodinfo routine retrieves information about a loaded module (program or shared library).

Syntax
        cc [flag...] file...   -ldl  [library]... 
        
        #include    <dlfcn.h> unsigned long dlmodinfo(unsigned long ip_value,
                             struct load_module_desc *desc,
                             size_t desc_size,
                             void *(*read_tgt_mem)(void* buffer,
                                                      unsigned long ptr,
                                                      size_t bufsiz,
                                                      int ident),
                             int ident_parm,
                             uint64_t load_map_parm);
        
Parameters

Parameter Description

ip_value

An address. The instruction pointer value of the requested library.

desc

A buffer of memory allocated by the user program. The dynamic loader fills this in with module information.

desc_size

Size in bytes of the desc buffer.

read_tgm_mem

A pointer to a function used by dlmodinfo to retrieve needed information.

If the value is NULL, the dynamic loader uses its own internal data structures to find the correct load module and ignore the ident_parm and load_map_parm parameters.

buffer

A buffer supplied by dlmodinfo to read into.

ptr

The virtual memory address to read from.

bufsiz

The size of buffer in bytes.

ident

The value of the ident_parm parameter to dlmodinfo.

ident_parm

Only used to pass the fourth parameter to read_tgt_mem.

load_map_parm

Only used when calling through read_tgt_mem. Contains the starting address of the load map.

Return Values

If successful, dlmodinfo returns a handle for the shared library as defined by the return value from dlopen(). NULL is returned otherwise. The return values are type-converted to unsigned long

Description

The dlmodinfo routine is one of a family of routines that give the user direct access to the dynamic linking facilities. The dlmodinfo routine retrieves information about a load module from a given address value. The dlmodinfo routine searches all currently loaded load modules looking for a load module whose address range (address range of all loaded segments) holds the given address value. The dlmodinfo routine fills the load_module_desc with information from the matching load module.

The read_tgm_mem parameter allows the dlmodinfo routine to find a load module in one process on behalf of another. The calling process passes a callback via read_tgt_mem in order to read memory in a different process address space from the one in which dlmodinfo resides. The ip_value, load_map_parm, and ptr from read_tgt_mem parameters can be pointers to shared libraries in another process.

If the calling process calls dlmodinfo with a callback registered via read_tgt_mem, it must supply the starting address of the target process' load map in the load_map_parm parameter to dlmodinfo. This can be retrieved from the DT_HP_LOAD_MAP entry in the .dynamic section in the target executable file.

Example

The following code sequence shows how to use dlmodinfo to retrieve information about a load module. In this example the dlmodinfo is provided with the address of a function foo. The address of foo is matched with the address range (the address range of all loaded segments) of all load modules. The dlmodinfo fills in the load_module_desc with information form the matching load module.

        void foo()
        {
            printf("foo\n");
        }
        
        int retrieve_info()
        {
            unsigned      long        handle;    struct                       load_module_desc desc;
        
        handle = dlmodinfo((unsigned long) &foo,
                                &desc,
                                 sizeof(struct load_module_desc),
                                 NULL,
                                 0,
                                  0);
        
        if (handle != 0) {
            printf("text base = %lx\n", desc.text_base);
            }
        }
        


The dlgetname Routine

The dlgetname routine retrieves the name of a load module given a load module descriptor.

Syntax
        char *dlgetname(struct load_module_desc *desc,
                        size_t desc_size,
                        void *(*read_tgt_mem)(void* buffer,
                                              unsigned long long ptr,
                                              size_t bufsiz,
                                              int ident),
                        int ident_parm,
                        unsigned long long load_map_parm);
        
Parameters

Parameter Description

desc

A buffer of memory allocated by the user program. The dynamic loader fills this in with module information.

desc_size

Size in bytes of the desc buffer.

read_tgm_mem

A pointer to a function used by dlmodinfo to retrieve needed information.

If the value is NULL, the dynamic loader uses its own internal data structures to find the correct load module and ignore the ident_parm and load_map_parm parameters.

buffer

A buffer supplied by dlmodinfo to read into.

ptr

The virtual memory address to read from.

bufsiz

The size of buffer in bytes.

ident

The value of the ident_parm parameter to dlmodinfo.

ident_parm

Only used to pass the fourth parameter to read_tgt_mem.

load_map_parm

Only used when calling through read_tgt_mem. Contains the starting address of the load map.

Return Values

dlgetname returns the pathname of a load module represented by desc. If desc does not describe a loaded module, dlgetname returns NULL.

Description

The dlgetname routine is one of a family of routines that give the user direct access to the dynamic linking facilities.

The read_tgt_mem, ident_parm, and load_map_parm parameters are identical to those for the dlmodinfo routine.

The caller of dlgetname must copy the return value to insure that it is not corrupted.

Example

The following code sequence shows how to use dlgetname to retrieve the pathname of a load module. This example uses dlget to get a load_module_desc of the required load module and passes that load_module_desc to dlgetname to retrieve the pathname.

        void*   handle;
        struct        load_module_desc desc;
        char*         dll_name;
        
        /* Get load module of the index'th shared library */
        handle = dlget(1, &desc, sizeof(struct load_module_desc));
        
        /* Retrieve pathname of the shared library */
        dll_name = dlgetname(&desc,
                             sizeof(struct load_module_desc),
                              NULL,
                              0,
                              NULL);
        printf("pathname of 1st shared library : %s\n", dll_name);
        


The dlclose Routine

The dlclose routine closes a shared library.

Syntax
        int dlclose(void *handle);
        
Parameters

Parameter Definition

handle

Value returned by a previous invocation of dlopen.

Return Values

If the referenced shared library was successfully closed, dlclose returns 0. If the shared library cannot be closed or if the handle does not refer to an open shared library, the dlclose routine returns a non-0 value. For more detailed diagnostic information you can use the dlerror routine.

Description

The dlclose routine disassociates a shared library previously opened by the dlopen routine from the current process. Once a shared library has been closed using the dlclose routine, the dlsym routine no longer has access to its symbols. All shared libraries loaded automatically as a result of invoking dlopen on the referenced shared library [see dlopen(3C)] are also closed.

A successful invocation of dlclose does not guarantee that the shared libraries associated with handle have actually been removed from the address space of the process. shared libraries loaded by one invocation of dlopen may also be loaded by another invocation of dlopen. The same shared library may also be opened multiple times. A shared library is not removed from the address space until all references to that shared library through an explicit dlopen invocation have been closed and all other shared libraries implicitly referencing that shared library have also been closed. Once a shared library is closed by dlclose, referencing symbols contained in that shared library can cause undefined behavior.

Example

The following example shows how to use dlclose to unload a shared library:

        void*       handle;
        int             ret_value;
        
        handle = dlopen("./lib1.so", RTLD_GLOBAL | RTLD_LAZY);if (handle == NULL) {
            printf("%s\n", dlerror());
        }
        
        ret_value = dlclose(handle);
        
        if (ret_value != 0) {
            printf("%s\n", dlerror());
        }
        


The dladdr Routine

The dladdr routine gets the symbolic information for an address.

Syntax
        int dladdr(void *address, D1_info *dlip);
        
Parameters

Parameter Description

address

dlip

A pointer to a D1_info structure. The structure must be allocated by the user.

Return Values

If the specified address does not fall within one of the load modules, 0 is returned; the contents of the D1_info structure are not modified. Otherwise, a non-zero value is returned and the fields of the D1_info structure are set.

Diagnostics

If no symbol is found within the load module containing address whose value is less than or equal to address, the dli_sname, dli_saddr, and dli_size fields are set to 0; the dli_bind field is set to STB_LOCAL, and the dli_type field is set to STT_NOTYPE.

For a.out's, only a subset of externally visible symbols are typically exported: specifically those referenced by the load modules with which the a.out is linked. The exact set of exported symbols for any shared library or the a.out can be controlled using the linker (see ld(1)).

Description

The dladdr routine is one of a family of routines that give the user direct access to the dynamic linking facilities (using the -ldl option on the compiler or ld line). The dladdr routine allows a process to obtain information about the symbol that most closely defines a given address. The dladdr routine determines whether the specified address is located within one of the load modules (executable or shared libraries) that make up the process' address space. An address is deemed to fall within a load module when it is between the base address at which the load module was mapped and the highest virtual address mapped for that load module, inclusive. If a load module fits this criteria, its dynamic symbol table is searched to locate the nearest symbol to the specified address. The nearest symbol is the one whose value is equal to, or closest to but not less than, the specified address.

The dlip parameter is a pointer to a D1_info structure. The structure must be allocated by the user. The structure members are set by dladdr if the specified address falls within one of the load modules. The D1_info structure contains the following members:

             struct {
                  const char *dli_fname;
                  void *dli_fbase;
                  const char *dli_sname;
                  void *dli_saddr;
                  size_t dli_size;
                  int dli_bind;
                  int dli_type;
             };
        

The dli_fname field contains a pointer to the filename of the load module containing the address. The contents of this memory location can change between calls to dladdr.

The dli_fbase field contains a handle to the load module. This can be used as the first argument to dlsym.

The dli_sname field contains a pointer to the name of the nearest symbol to the specified address. This symbol either has the same address, or is the nearest symbol with a lower address. The contents of this memory location can change between calls to dladdr.

The dli_saddr field contains the actual address of the nearest symbol. For code symbols, it contains the address of the Official Plabel Descriptor (OPD) for the nearest code symbol.

The dli_size field contains the size of the nearest symbol as defined in the dynamic symbol table.

The dli_bind field contains the binding attribute of the nearest symbol as defined in the dynamic symbol table. The values for this are those used for a symbol's binding in the ELF symbol table (see elf.h).

The dli_type field contains the type of the nearest symbol as defined in the dynamic symbol table. The values for this are those used for a symbol's type in the ELF symbol table (see elf.h).

Example

The following example shows how to use dladdr to :

        #include <dlfcn.h>
        typedef int (*fptr_t) ();
        int func1() {return 11;}
        int main()
        {
            D1_info di;
            char* symname = (char*)strdup("func1");
            if (dladdr(&func1, &di) == 0) {
                printf("dladdr() failed for %s\n", symname);
                return 1;
            }
            if (di.dli_sname) {
        fptr_t tmp = (fptr_t)dlsym(di.dli_fbase, symname);
                if (di.dli_saddr !=void *)&func1)
                   puts("approx match");
                if (tmp != (fptr_t)di.dli_saddr) {
                   printf("dlsym(%s): %s\n", symname, dlerror());
                }
            }
            printf("%s: %s(%d %d %d)\n", di.dli_sname? di.dli_sname:"nil",
           di.dli_fname, di.dli_type, di.dli_bind, di.dli_size);
            return 0;
        }
        


The dlmodadd Routine

The dlmodadd routine registers information about dynamically-generated functions.

Syntax
        void* dlmodadd(void *associate handle,
                            void *func_start,
                            size_t func_size,
                            void *linkage_ptr,
                            void *unwind_info);
        
Parameters

Parameter Description

associate handle

The module handle for an existing module with which the new function is associated. When the dynamic loader is called from the new function, it behaves as if it had been called from the associated module. This handle must be a handle returned by the dlopen() routine or the dlget routine.

func_start

The starting address of the generated machine code for the function. It is NOT the address of a function descriptor.

func_size

The size, in bytes, of the generated machine code.

linkage_ptr

The global pointer (gp) value to be used for the function. It could be the gp of the associated module. It must be set to a valid value, even if the generated code doesn't actually use gp, as it is required at a minimum for following the personality routine pointer in the unwind information.

unwind_info

A pointer to the beginning of the unwind info block for the function. The unwind info block contains the header word, unwind descriptors, personality routine pointer, and language-specific data, as described in the Itanium-based system Software Conventions and Runtime Architecture document.

Return Values

If successful, dlmodadd returns a handle as identification of the newly-added function. NULL is returned otherwise.

Description

The dlmodadd routine registers information about a dynamically-generated function, which can be retrieved through the dlmodinfo routine. The dlmodremove interface can be used to remove the registered information.

When dlmodadd is invoked to register information about a dynamically-generated function, the dynamic loader creates an unwind header and a single-entry unwind table for the function. The unwind header is associated with the address range occupied by the dynamically-generated function. The dlmodadd() routine returns a handle as identification of the newly-added function. Handles returned by dlmodadd() share the same namespace as handles returned by dlopen() and dlget(), but they may not be used in calls to dlclose(). A handle returned by dlmodadd() is treated by dlsym() as if the associated handle had been used instead. If dlmodinfo() is called with an ip_value that belongs to a function that has been registered with dlmodadd(), it returns the handle of the dynamically-generated function, as originally returned by dlmodadd(), along with a load_module_desc structure describing the function. A load_module_desc for a dynamically-generated function always has a 0 value in the phdr_base field.

The text_base, text_size, data_base, and data_size fields may be used for converting segment-relative offsets to virtual addresses, but they may not be relied on to identify a range of memory belonging exclusively to the function. The unwind_base field will contain the address of the unwind header built for the function. If a library is unloaded by dlclose(), the unwind info for all dynamically-generated functions associated with the library being unloaded will also be removed.


The dlmodremove Routine

The dlmodremove removes information registered using dlmodadd.

Syntax
        int dlmodremove(void* handle);
        
Parameters

Parameter Definition

handle

Value returned by a previous invocation of dlmodremove.

Return Values

The dlmodremove routine returns 0 on success. If handle does not refer to a valid handle, dlmodremove returns a non-0 value.

Description

The dlmodremove routine removes the registered information of individual dynamically-generated functions. It removes the registration of the indicated function and frees the space used by the unwind header and unwind table created by dlmodadd(). It does not free the space used by the unwind info block or by the generated code, which was allocated by the user and must be freed by the user.


The dlgetmodinfo Routine

The dlgetmodinfo retrieves information about a loaded module (program or shared library).

Syntax
        uint64_t dlgetmodinfo(int index,
                                   struct load_module_desc *desc,
                                   size_t desc_size,
                                   void *(*read_tgt_mem) (void* buffer,
                                                          uint64_t ptr,
                                                          size_t bufsiz,
                                                          int ident),
                                   int ident_parm,
                                   uint64_t load_map_parm);
        
Parameters

Parameter Definition

desc

A buffer of memory allocated by the user program. The dynamic loader fills this in with module information.

desc_size

Size in bytes of the desc buffer.

read_tgm_mem

A pointer to a function used by dlmodinfo to retrieve needed information.

If the value is NULL, the dynamic loader uses its own internal data structures to find the correct load module and ignore the ident_parm and load_map_parm parameters.

buffer

A buffer supplied by dlmodinfo to read into.

ptr

The virtual memory address to read from.

bufsiz

The size of buffer in bytes.

ident

The value of the ident_parm parameter to dlmodinfo.

ident_parm

Only used to pass the fourth parameter to read_tgt_mem.

load_map_parm

Only used when calling through read_tgt_mem. Contains the starting address of the load map.

Return Values

If successful, dlgetmodinfo returns a handle for the shared library as defined by the return value from dlopen(). NULL is returned otherwise. The return values are type-converted to uint64_t.

Description

The dlgetmodinfo routine is one of a family of routines that give the user direct access to the dynamic linking facilities. The dlgetmodinfo routine retrieves information about a load module from an index specifying the placement of the load module in the dynamic loader's search list. Unlike dlget, dlgetmodinfo can retrieve information about a load module in another process.

An index of -1 requests information about the dynamic loader; an index of -2 requests information about the program file itself.

The dlgetmodinfo routine fills the load_module_desc with information from the matching load module.

Example

The following example shows how to use dlgetmodinfo:

        #include <dlfcn.h>
        #include <stdio.h>
        int main()
        {
           void *handle;
           struct load_module_desc desc;
           handle = dlgetmodinfo((void*) -2,
              &desc,
              sizeof(struct load_module_desc),
              NULL,
              0,
              0);
           if (handle == NULL) {
              printf("Error: dlgetmodinfo returns NULL\n");
              exit(1);
           } else {
              printf("text base = 0x%x\n", desc.text_base);
           }
        }
        

The shl_load Shared Library Management Routines

This section describes the shl_load family of shared library management routines.


Note

You can use these routines in both PA-32 and IPF/PA-64 mode. Support for these routines may be discontinued in a future Itanium or PA-64 HP-UX release. If you use these routines in Itanium or PA-64  mode, consider converting your programs to the dl* family of shared library management routines.



The shl_load Routine

The shl_load routine explicitly loads a library.

Syntax
        shl_t shl_load( const char * path,
                        int flags,
                        long address )
        
Parameters
path

A null-terminated character string containing the path name of the shared library to load.

flags

Specifies when the symbols in the library must be bound to addresses. It must be one of these values, defined in <dl.h>:

BIND_IMMEDIATE

Binds the addresses of all symbols immediately upon loading the library.

BIND_DEFERRED

Binds the addresses when they are first referenced.

Be aware that BIND_IMMEDIATE causes the binding of all symbols, and the resolution of all imports, even from older versioned modules in the shared library. If symbols are not accessible because they come from old modules, they are unresolved and shl_load may fail.

In addition to the above values, the flags parameter can be ORed with the following values:

BIND_NONFATAL

Allows binding of unresolved symbols.

BIND_VERBOSE

Makes dynamic loader display verbose messages when binding symbols.

BIND_FIRST

Inserts the loaded library before all others in the current link order.

DYNAMIC_PATH

Causes the dynamic loader to perform dynamic library searching when loading the library. The +s and +b options to the ld command determine the directories the linker searches. This is the default mode if +compat linker option is not specified.

BIND_NOSTART

Causes the dynamic loader to not call the initializer, even if one is declared for the library, when the library is loaded or on a future call to shl_load or dlopen. This also inhibits a call to the initializer when the library is unloaded.

BIND_RESTRICTED

Causes the search for a symbol definition to be restricted to those symbols that were visible when the library was loaded.

BIND_TOGETHER

Causes the library being loaded and all its dependent libraries to be bound together rather than each independently. Use this when you have interdependent libraries and you are using BIND_FIRST.

BIND_BREADTH_FIRST

Causes the dependent libraries to be loaded breadth first. By default, shl_load loads dependent libraries depth-first.

These flags are discussed in detail in the shl_load Example .

address

Specifies the virtual address at which to attach the library. Set this parameter to 0 (zero) to tell the system to choose the best location. This argument is currently ignored; mapping a library at a user-defined address is not currently supported.

Return Value

If successful, shl_load returns a shared library handle of type shl_t. This address can be used in subsequent calls to shl_close, shl_findsym, shl_gethandle, and shl_gethandle_r. Otherwise, shl_load returns a shared library handle of NULL and sets errno to one of these error codes (from <errno.h>):

 
ENOEXEC

The specified path is not a shared library, or a format error was detected in this or another library.

ENOSYM

A symbol needed by this library or another library which this library depends on could not be found.

ENOMEM

There is insufficient room in the address space to load the shared library.

EINVAL

The requested shared library address was invalid.

ENOENT

The specified path does not exist.

EACCESS

Read or execute permission is denied for the specified path.

Description

A program needs to explicitly load a library only if the library was not linked with the program. This typically occurs only when the library cannot be known at link time - for example, when writing programs that must support future graphics devices.

However, programs are not restricted to using shared libraries only in that situation. For example, rather than linking with any required libraries, a program can explicitly load libraries as they are needed. One possible reason for doing this is to minimize virtual memory overhead. To keep virtual memory resource usage to a minimum, a program can load libraries with shl_load and unload with shl_unload when the library is no longer needed. However, it is normally not necessary to incur the programming overhead of loading and unloading libraries yourself for the sole reason of managing system resources.

Note that if shared library initializers have been declared for an explicitly loaded library, they are called after the library is loaded. For details, see Initializers for Shared Libraries.

To explicitly load a shared library, use the shl_load routine. This ensures that constructors of nonlocal static objects are executed when the library is loaded.

The shl_load routine lets you load a compatibility or standard mode shared libraries. The BIND_BREADTH_FIRST flag overrides the default depth-first loading mechanism.

shl_load Usage

Since the library was not specified at link time, the program must get the library name at run time. Here are some practical ways to do this:

  • Hard-code the library name into the program (the easiest method).

  • Get the library name from an environment variable using the getenv library routine (see getenv(3C)).

  • Get the library path name from the command line through argv.

  • Read the library name from a configuration file.

  • Prompt for the library path name at run time.

If successful, shl_load returns a shared library handle (of type shl_t), which uniquely identifies the library. This handle can then be passed to the shl_findsym or shl_unload routine.

Once a library is explicitly loaded, use the shl_findsym routine to get pointers to functions or data contained in the library; then call or reference them through the pointers. This is described in detail in The shl_findsym Routine .

Use caution when building shared libraries with external library dependencies. Any library that contains static references to Thread Local Storage (TLS) can only be loaded implicitly at program startup. To enable dynamic loading of libraries that contain TLS, compile the library source files with the compiler option +tls=dynamic.

shl_load Example

The following example shows the source for a function named load_lib that explicitly loads a library specified by the user. The user can specify the library in the environment variable SHLPATH or as the only argument on the command line. If the user chooses neither of these methods, the function prompts for the library path name.

The function then attempts to load the specified library. If successful, it returns the shared library handle, of type shl_t. If an error occurs, it displays an error message and exits. This function is used later in The shl_findsym Routine .

load_lib - Function to Load a Shared Library
        #include        <stdio.h>      /* contains standard I/O defs           */
        #include        <stdlib.h>     /* contains getenv definition           */
        #include        <dl.h>         /* contains shared library type defs    */
         
        shl_t load_lib(int argc,
                       char * argv[])   /* pass argc and argv from main */
        {
          shl_t   lib_handle;             /* temporarily holds library handle  */
          char    lib_path[MAXPATHLEN];   /* holds library path name           */
          char    *env_ptr;               /* points to SHLPATH variable value  */
          /*
           * Get the shared library path name:
           */
          if (argc > 1)               /* library path given on command line */
            strcpy(lib_path, argv[1]);
          else                        /* get lib_path from SHLPATH variable */
            {
              env_ptr = getenv("SHLPATH");
              if (env_ptr != NULL)
                strcpy(lib_path, env_ptr);
              else                    /* prompt user for shared library path */
                {
                  printf("Shared library to use >> ");
                  scanf("%s", lib_path);
                }
            }
          /*
           * Dynamically load the shared library using BIND_IMMEDIATE binding:
           */
          lib_handle = shl_load( lib_path, BIND_IMMEDIATE, 0);
          if (lib_handle == NULL)
            perror("shl_load: error loading library"), exit(1);
          return lib_handle;
        }
        
BIND_NONFATAL Modifier

If you load a shared library with the BIND_IMMEDIATE flag and the library contains unresolved symbols, the load fails and sets errno to ENOSYM. ORing BIND_NONFATAL with BIND_IMMEDIATE causes shl_load to allow the binding of unresolved symbols to be deferred if their later use can be detected - for example:

        shl_t libH;
           . . .
        libH = shl_load("libxyz.so", BIND_IMMEDIATE | BIND_NONFATAL, 0);
        

However, data symbol binding cannot be deferred, so using the BIND_NONFATAL modifier does not allow the binding of unresolved data symbols.

BIND_VERBOSE Modifier

If BIND_VERBOSE is ORed with the flags parameter, the dynamic loader displays messages for all unresolved symbols. This option is useful to see exactly which symbols cannot be bound. Typically, you may use this with BIND_IMMEDIATE to debug unresolved symbols.

For example,

        shl_t libH;
           . . .
        libH = shl_load("libxyz.so", BIND_IMMEDIATE | BIND_VERBOSE, 0);
        
BIND_FIRST Modifier

If BIND_FIRST is ORed with the flags parameter, the loaded library is inserted before all other loaded shared libraries in the symbol resolution search order. This has the same effect as placing the library first in the link order - that is, the library is searched before other libraries when resolving symbols. This is used with either BIND_IMMEDIATE or BIND_DEFERRED.

For example,

        shl_t libH;
          . . . 
        libH = shl_load("libpdq.so", BIND_DEFERRED | BIND_FIRST, 0);
        

BIND_FIRST is typically used when you want to make the symbols in a particular library more visible than the symbols of the same name in other libraries. Compare this with the default behavior, which is to append loaded libraries to the link order.

DYNAMIC_PATH Modifier

The flag DYNAMIC_PATH can also be alternated with the flags parameter, causing the dynamic loader to search for the library using a path list specified by the +b option at link time or the SHLIB_PATH environment variable at run time.

BIND_NOSTART Modifier

The flag BIND_NOSTART inhibits execution of initializers for the library.

BIND_RESTRICTED Modifier

This flag is most useful with the BIND_DEFERRED flag; it has no effect with BIND_IMMEDIATE. It is also useful with the BIND_NONFATAL flag.

When used with only the BIND_DEFERRED flag, it exhibits the following behavior: When a symbol is referenced and needs to be bound, this flag causes the search for the symbol definition to be restricted to those symbols that were visible when the library was loaded. If a symbol definition cannot be found within this restricted set, it results in a run-time symbol-binding error.

When used with BIND_DEFERRED and the BIND_NONFATAL modifier, it has the same behavior, except that when a symbol definition cannot be found, the dynamic loader looks in the global symbol set. If a definition still cannot be found within the global set, a run-time symbol-binding error occurs.

BIND_TOGETHER Modifier

BIND_TOGETHER modifies the behavior of BIND_FIRST. When the library being loaded has dependencies, BIND_FIRST causes each dependent library to be loaded and bound separately. If the libraries have interdependencies, the load may fail because the needed symbols are not available when needed.

BIND_FIRST | BIND_TOGETHER causes the library being loaded and its dependent libraries to be bound all at the same time, thereby resolving interdependencies. If you are not using BIND_FIRST, libraries are bound together by default so this option has no effect.

BIND_BREADTH_FIRST Modifier

This flag causes the dependent libraries to be loaded breadth first. By default, the PA-64 and IPF mode shl_load loads dependent libraries depth-first. This modifier overrides the default load order.

Binding Flags Examples

Suppose you have the libraries libE.so, libF.so, and libG.so. The libE library depends on libF and libF depends on libG. In addition, libG depends on libF-libF and libG are interdependent. Your program loads libE.so with shl_load().

When using BIND_DEFERRED or BIND_IMMEDIATE without BIND_FIRST, these libraries are loaded such that all symbols are visible and the interdependencies are resolved:

        shl_t libE;
        libE = shl_load("libE.so", BIND_IMMEDIATE, 0);  
        shl_load succeeds.
        

When using BIND_IMMEDIATE | BIND_FIRST, however, libG is loaded and bound first and because it depends on libF, an error results because the needed symbols in libF are not yet available:

        libE = shl_load("libE.so", BIND_IMMEDIATE | BIND_FIRST, 0); 
        shl_load fails.
        

Using BIND_IMMEDIATE | BIND_FIRST | BIND_TOGETHER loads libE, libF, and libG together and correctly resolves all symbols:

        libE = shl_load("libE.so", BIND_IMMEDIATE | BIND_FIRST | BIND_TOGETHER, 0); 
        shl_load succeeds.
        


The shl_findsym Routine

The shl_findsym routine obtains the address of an exported symbol from a shared library. To call a routine or access data in an explicitly loaded library, first get the address of the routine or data with shl_findsym.

Syntax
        int shl_findsym( shl_t * handle,
                         const char * sym,
                         short type,
                         void * value )
        
Parameters
handle

A pointer to a shared library handle of the library to search for the symbol name sym. This handle can be obtained from the shl_get routine (described in the shl_get and shl_get_r Routines). The handle parameter can also point to:

NULL

If a pointer to NULL is specified, shl_findsym searches all loaded libraries for sym. If sym is found, shl_findsym sets handle to a pointer to the handle of the shared library containing sym. This is useful for determining which library a symbol resides in. For example, the following code sets handle to a pointer to the handle of the library containing symbol _foo:

                        shl_t handle;
                        handle = NULL;
                        shl_findsym(&handle,"_foo",...);
                        
PROG_HANDLE

This constant, defined in dl.h, tells shl_findsym to search for the symbol in the program itself. This way, any symbols exported from the program can be accessed explicitly.

sym

A null-terminated character string containing the name of the symbol to search for.

type

The type of symbol to look for. It must be one of the following values, defined in <dl.h>:

TYPE_PROCEDURE

Looks for a function or procedure.

TYPE_DATA

Looks for a symbol in the data segment (for example, variables).

TYPE_UNDEFINED

Looks for any symbol.

TYPE_STORAGE

Same as TYPE_DATA

value

A pointer in which shl_findsym stores the address of sym, if found.

Return Value

If successful, shl_findsym returns an integer (int) value zero. If shl_findsym cannot find sym, it returns -1 and sets errno to zero. If any other errors occur, shl_findsym returns -1 and sets errno to one of these values (defined in <errno.h>):

ENOEXEC

A format error was detected in the specified library.

ENOSYM

A symbol on which sym depends cannot be found.

EINVAL

The specified handle is invalid.

Description

To call a routine or access data in an explicitly loaded library, first get the address of the routine or data with shl_findsym.

To call a routine in an explicitly loaded library:

  1. Declare a pointer to a function of the same type as the function in the shared library

  2. Using shl_findsym with the type parameter set to TYPE_PROCEDURE, find the symbol in the shared library and assign its address to the function pointer declared in Step 1

  3. Call the pointer to the function obtained in Step 2, with the correct number and type of arguments

To access data in an explicitly loaded library:

  1. Declare a pointer to a data structure of the same type as the data structure to access in the library

  2. Using shl_findsym with the type parameter set to TYPE_DATA, find the symbol in the shared library and assign its address to the pointer declared in Step 1

  3. Access the data through the pointer obtained in Step 2  

shl_findsym Example

Suppose you have a set of libraries that output to various graphics devices. Each graphics device has its own library. Although the actual code in each library varies, the routines in these shared libraries have the same name and parameters, and the global data is the same. For instance, they all have these routines and data:

gopen()

opens the graphics device for output

gclose()

closes the graphics device

move2d(x,y)

moves to pixel location x,y

draw2d(x,y)

draws to pixel location x,y from current x,y

maxX

contains the maximum X pixel location on the output device

maxY

contains the maximum Y pixel location on the output device

The following example shows a C program that can load any supported graphics library at run time, and call the routines and access data in the library. The program calls load_lib (see load_lib - Function to Load a Shared Library ) to load the library.

Load a Shared Library and Call its Routines and Access its Data
        #include <stdio.h>      /* contains standard I/O defs        */
        #include <stdlib.h>     /* contains getenv definition        */
        #include <dl.h>         /* contains shared library type defs */
        /*
         * Define linker symbols:
         */
         
        #define GOPEN   "gopen"
        #define GCLOSE  "gclose"
        #define MOVE2D  "move2d"
        #define DRAW2D  "draw2d"
        #define MAXX    "maxX"
        #define MAXY    "maxY"
         
        shl_t   load_lib(int argc, char * argv[]);
        main(int argc,
             char * argv[])
        {
          shl_t lib_handle;           /* handle of shared library       */
          int   (*gopen)(void);       /* opens the graphics device      */
          int   (*gclose)(void);      /* closes the graphics device     */
          int   (*move2d)(int, int);  /* moves to specified x,y location */
          int   (*draw2d)(int, int);  /* draw line to specified x,y location*/
          int   *maxX;                /* maximum X pixel on device      */
          int   *maxY;                /* maximum Y pixel on device      */
         
          lib_handle = load_lib(argc, argv);  /* load required shared library */
          /*
           * Get addresses of all functions and data that will be used:
           */
          if (shl_findsym(&lib_handle, GOPEN, TYPE_PROCEDURE, (void *) &gopen))
            perror("shl_findsym: error finding function gopen"), exit(1);
          if (shl_findsym(&lib_handle, GCLOSE, TYPE_PROCEDURE, (void *) &gclose))
            perror("shl_findsym: error finding function gclose"), exit(1);
          if (shl_findsym(&lib_handle, MOVE2D, TYPE_PROCEDURE, (void *) &move2d))
            perror("shl_findsym: error finding function move2d"), exit(1);
          if (shl_findsym(&lib_handle, DRAW2D, TYPE_PROCEDURE, (void *) &draw2d))
            perror("shl_findsym: error finding function draw2d"), exit(1);
          if (shl_findsym(&lib_handle, MAXX, TYPE_DATA, (void *) &maxX))
            perror("shl_findsym: error finding data maxX"), exit(1);
          if (shl_findsym(&lib_handle, MAXY, TYPE_DATA, (void *) &maxY))
            perror("shl_findsym: error finding data maxY"), exit(1);
          /*
           * Using the routines, draw a line from (0,0) to (maxX,maxY):
           */
          (*gopen)();                 /* open the graphics device       */
          (*move2d)(0,0);             /* move to pixel 0,0              */
          (*draw2d)(*maxX,*maxY);     /* draw line to maxX,maxY pixel   */
          (*gclose)();                /* close the graphics device      */
        }
        

Shown below is the compile line for this program, along with the commands to set SHLPATH appropriately before running the program. SHLPATH is declared and used by load_lib(), defined in The shl_load and cxxshl_load Routines example. Notice that load_lib() is compiled here along with this program. Finally, this example assumes you have created a graphics library, libgrphdd.so:

        $ cc -Aa -o drawline shl_findsym.c load_lib.c -ldld
        $ SHLPATH=/usr/lib/libgrphdd.so
        $ export SHLPATH
        $ drawline
        


The shl_get and shl_get_r Routines

The shl_get and shl_get_r routines obtain information on the currently loaded libraries.

Syntax
        int shl_get( int index,
                     struct shl_descriptor **desc )
        
Parameters
index

Specifies an ordinal number of the shared library in the process. For libraries loaded implicitly (at startup time), index is the ordinal number of the library as it appeared on the command line. For example, if libc was the first library specified on the ld command line, then libc has an index of 1. For explicitly loaded libraries, index corresponds to the order in which the libraries were loaded, starting after the ordinal number of the last implicitly loaded library. Two index values have special meaning:

-1

Refers to the dynamic loader (dld.so).

-2

Refers to the main program itself.

A shared library's index can be modified during program execution by either of the following events:

  • The program loads a shared library with the BIND_FIRST modifier to shl_load. This increments all the shared library indexes by one.

  • The program unloads a shared library with shl_unload. Any libraries following the unloaded library have their index decremented by one.

desc

Returns a pointer to a statically allocated buffer (struct shl_descriptor **) containing a shared library descriptor. The structure contains these important fields:

tstart

The start address (unsigned long) of the shared library text segment.

tend

The end address (unsigned long) of the shared library text segment.

dstart

The start address (unsigned long) of the shared library data segment.

dend

The end address (unsigned long) of the shared library bss segment. The data and bss segments together form a contiguous memory block starting at dstart and ending at dend.

handle

The shared library's handle (type shl_t).

filename

A character array containing the library's path name as specified at link time or at explicit load time.

initializer

A pointer to the shared library's initializer routine (see Initializers for Shared Libraries. It is NULL if there is no initializer. This field is useful for calling the initializer if it was disabled by the BIND_NOSTART flag to shl_load.

If the shared library has multiple initializers, this field is also set to NULL. Multiple initializers can be found with shl_getsymbols, described later in this chapter.

This buffer is statically allocated. Therefore, if a program intends to use any of the members of the structure, the program must make a copy of the structure before the next call to shl_get. Otherwise, shl_get overwrites the static buffer when called again.

Return Value

If successful, shl_get returns an integer value 0. If the index value exceeds the number of currently loaded libraries, shl_get returns -1 and sets errno to EINVAL.

Description

To obtain information on currently loaded libraries, use the shl_get function. If you are programming in a threaded environment, use the thread-safe version shl_get_r which is the same as shl_get in all other respects. (See Programming with Threads on HP-UX for more information about threads.)

Other than obtaining interesting information, this routine is of little use to most programmers. A typical use may be to display the names and starting/ending address of all shared libraries in a process's virtual memory address space.

Example

The function show_loaded_libs shown below displays the name and start and end address of the text and data/bss segments the library occupies in a process's virtual address space.

show_loaded_libs - Display Library Information
        #include        <stdio.h>      /* contains standard I/O defs           */
        #include        <dl.h>         /* contains shared library type defs    */
        void    show_loaded_libs(void)
        {
        int     idx;
        struct  shl_descriptor  *desc;
         
          printf("SUMMARY of currently loaded libraries:\n");
          printf("%-25s  %10s  %10s  %10s  %10s\n",
              "___library___", "_tstart_", "__tend__", "_dstart_", "__dend__");
         
          idx = 0;
          for (idx = 0; shl_get(idx, &desc) != -1; idx++)
            printf("%-25s  %#10lx  %#10lx  %#10lx  %#10lx\n",
             desc->filename, desc->tstart, desc->tend, desc->dstart, desc->dend);
        }
        

Calling this function from a C program compiled with shared libc and libdld produced the following output:

        SUMMARY of currently loaded libraries:
        ___library___         _tstart_    __tend__    _dstart_    __dend__
        ./a.out                 0x1000      0x1918  0x40000000  0x40000200
        /usr/lib/libdld.so  0x800ac800  0x800ad000  0x6df62800  0x6df63000
        /usr/lib/libc.so    0x80003800  0x80091000  0x6df63000  0x6df85000
        


The shl_gethandle and shl_gethandle_r Routines

The shl_gethandle and shl_gethandle_r routines return descriptor information about a loaded shared library.

Syntax
        int shl_gethandle( shl_t handle,
                           struct shl_descriptor **desc )
        
Parameters
handle

The handle of the shared library you want information about. This handle is the same as that returned by shl_load.

desc

Points to shared library descriptor information - the same information returned by the shl_get routine. The buffer used to store this desc information is static, meaning that subsequent calls to shl_gethandle overwrites the same area with new data. Therefore, if you need to save the desc information, copy it elsewhere before calling shl_gethandle again.

Return Value

If handle is not valid, the routine returns -1 and sets errno to EINVAL. Otherwise, shl_gethandle returns 0.

Description

The shl_gethandle routine returns descriptor information about a loaded shared library. If you are programming in a threaded environment, use the thread-safe version shl_gethandle_r which is the same as shl_gethandle in all other respects. (See Programming with Threads on HP-UX for more information about threads.)

Example

The following function named show_lib_info displays information about a shared library, given the library's handle.

show_lib_info - Display Information for a Shared Library
        #include <stdio.h>
        #include <dl.h>
         
        int show_lib_info(shl_t libH)
        {
        struct shl_descriptor *desc;
         
          if (shl_gethandle(libH, &desc) == -1)
          {
            fprintf(stderr, "Invalid library handle.\\n");
            return -1;
          }
          printf("library path:    %s\\n", desc->filename);
          printf("text start:      %#10lx\\n", desc->tstart);
          printf("text end:        %#10lx\\n", desc->tend);
          printf("data start:      %#10lx\\n", desc->dstart);
          printf("data end:        %#10lx\\n", desc->dend);
          return 0;
        }
        


The shl_definesym Routine

The shl_definesym routine adds new symbols to the global shared library symbol table.

Syntax
        int shl_definesym( const char *sym,
                           short type,
                           long value,
                           int flags )
        
Parameters
sym

A null-terminated string containing the name of the symbol to change or to add to the process's shared library symbol table.

type

The type of symbol - either TYPE_PROCEDURE or TYPE_DATA.

value

If value falls in the address range of a currently loaded library, an association is made and the symbol is undefined when the library is unloaded. (Note that memory dynamically allocated with malloc(3C) does not fall in the range of any library.) The defined symbol may be overridden by a subsequent call to this routine or by loading a more visible library that provides a definition for the symbol.

flags

Must be set to zero.

Return Value

If successful, shl_definesym returns 0. Otherwise, it returns -1 and sets errno accordingly. See shl_definesym(3X) for details.

Description

The shl_definesym function allows you to add a new symbol to the global shared library symbol table. Use of this routine is unnecessary for most programmers.

There are two main reasons to add or change shared library symbol table entries:

  • to generate symbol definitions as the program runs - for example, aliasing one symbol with another

  • to override a current definition

Symbol definitions in the incomplete executable may also be redefined with certain restrictions:

  • The incomplete executable always uses its own definition for any data (storage) symbol, even if a more visible one is provided.

  • The incomplete executable only uses a more visible code symbol if the main program itself does not provide a definition.


The shl_getsymbols Routine

The shl_getsymbols routine retrieves symbols that are imported (referenced) or exported (defined) by a shared library. This information is returned in an allocated array of records, one for each symbol. Most programmers do not need to use this routine.

Syntax
        int shl_getsymbols( shl_t handle,
                            short type,
                            int flags,
                            void * (*memfunc)(),
                            struct shl_symbol **symbols )
        
Parameters
handle

The handle of the shared library whose symbols you want to retrieve. If handle is NULL, shl_getsymbols returns symbols that were defined with the shl_definesym routine.

type

Defines the type of symbol to retrieve. It must be one of the following values, which are defined as constants in <dl.h>:

TYPE_PROCEDURE

Retrieve only function or procedure symbols.

TYPE_DATA

Retrieve only symbols from the data segment (for example, variables).

TYPE_UNDEFINED

Retrieve all symbols, regardless of type.

TYPE_STORAGE

same as TYPE_DATA

flags

Defines whether to retrieve import or export symbols from the library. An import symbol is an external reference made from a library. An export symbol is a symbol definition that is referenced outside the library. In addition, any symbol defined by shl_definesym is an export symbol. Set this argument to one of the following values (defined in <dl.h>):

IMPORT_SYMBOLS

To return import symbols.

EXPORT_SYMBOLS

To return export symbols.

INITIALIZERS

To return initializer symbols.

One of the following modifiers can be alternated with both the EXPORT_SYMBOLS and the INITIALIZERS flags:

NO_VALUES

Do not calculate the value field of the shl_symbol structure for symbols. The value field has an undefined value.

GLOBAL_VALUES

For symbols that are defined in multiple libraries, this flag causes shl_getsymbols to return the most-visible occurrence, and to set the value and handle fields of the shl_symbol structure (defined in the description of the symbols parameter).

memfunc

Points to a function that has the same interface (calling conventions and return value) as malloc(3C). The shl_getsymbols function uses this function to allocate memory to store the array of symbol records, symbols.

symbols

This points to an array of symbol records for all symbols that match the criteria determined by the type and value parameters. The type of these records is struct shl_symbol, defined in <dl.h> as:

                struct shl_symbol {
                  char * name;
                  short  type;
                  void * value;
                  shl_t  handle;
                };
                

The members of this structure are described in The shl_symbol Structure.

Return Value

If successful, shl_getsymbols returns the number of symbols found; otherwise, -1 is returned and shl_getsymbols sets errno to one of these values:

ENOEXEC

A format error was detected in the specified library.

ENOSYM

Some symbol required by the shared library cannot be found.

EINVAL

The specified handle is invalid.

ENOMEM

memfunc failed to allocate the requested memory.

The shl_symbol Structure

The members of the shl_symbol structure are defined as follows:

name

Contains the name of a symbol.

type

Contains the symbol's type: TYPE_PROCEDURE or TYPE_DATA is a data symbol used for C uninitialized global variables or Fortran common blocks.

value

Contains the symbol's address. It is valid only if EXPORT_SYMBOLS is specified without the NO_VALUES modifier.

handle

Contains the handle of the shared library in which the symbol is found, or NULL in the case of symbols defined by shl_definesym. It is valid only if EXPORT_SYMBOLS or INITIALIZERS are requested without the NO_VALUES modifier. It is especially useful when used with the GLOBAL_VALUES modifier, allowing you to determine the library in which the most-visible definition of a symbol occurs.

shl_getsymbols Example

show_symbols - Display Shared Library Symbols shows the source for a function named show_symbols that displays shared library symbols. The syntax of this routine is defined as:

        int show_symbols(shl_t  hndl,
                         short  type,
                         int    flags)
        
hndl

The handle of the shared library whose symbols you want to display.

type

The type of symbol you want to display. This is the same as the type parameter to shl_getsymbols and can have these values: TYPE_PROCEDURE, TYPE_DATA, or TYPE_UNDEFINED. If it is TYPE_UNDEFINED, show_symbols displays the type of each symbol.

flags

This is the same as the flags parameter. It can have the value EXPORT_SYMBOLS or IMPORT_SYMBOLS. In addition, it can be ORed with NO_VALUES or GLOBAL_VALUES. If EXPORT_SYMBOLS is specified without being ORed with NO_VALUES, show_symbols displays the address of each symbol.

show_symbols - Display Shared Library Symbols
        #include <dl.h>
        #include <stdio.h>
        #include <stdlib.h>
        int show_symbols(shl_t   hndl,
                         short   type,
                         int     flags)
        {
          int   num_symbols, sym_idx;
          struct shl_symbol *symbols, *orig_symbols;
         
          num_symbols = shl_getsymbols(hndl, type, flags, malloc, &symbols);
          if (num_symbols < 0) {
            printf("shl_getsymbols failed\n");
            exit(1);
          }
          orig_symbols = symbols;
          for (sym_idx = 0; sym_idx < num_symbols; sym_idx++)
          {
           printf("    %-30s", symbols->name); /* display symbol name    */
           if (type == TYPE_UNDEFINED) /* display type if TYPE_UNDEFINED */
              switch (symbols->type) {
                case TYPE_PROCEDURE:
                  printf("  PROCEDURE");
                  break;
                case TYPE_DATA:
                  printf("  DATA     ");
                  break;
                case TYPE_STORAGE:
                  printf("  STORAGE  ");
                }
            if ((flags & EXPORT_SYMBOLS)        /* export symbols requested    */
                && (flags & NO_VALUES)==0)      /* NO_VALUES was NOT specified */
             printf("  0x%8X", symbols->value); /* so display symbol's address */
            printf("\n");                       /* terminate output line       */
            symbols++;                          /* move to next symbol record  */
            }
         free(orig_symbols);                /* free memory allocated by malloc */
         return num_symbols;                /* return the number of symbols */
        }
        

The following example shows the source for a program named show_all.c that calls show_symbols to show all imported and exported symbols for every loaded shared library. It uses shl_get to get the library handles of all loaded libraries.

show_all - Use show_symbols to Show All Symbols
        #include <dl.h>
        #include <stdio.h>
        /* prototype for show_syms */
        int show_syms(shl_t hndl, short type, int flags); 
        main()
        {
         int   idx, num_syms;
         struct shl_descriptor * desc;
         
         for (idx=0; shl_get(idx, &desc) != -1; idx++) /* step through libs */
         {
          printf("[%s]\n", desc->filename); /* show imports & exports for each */
          printf("  Imports:\n");
          num_syms = show_symbols(desc->handle, TYPE_UNDEFINED, IMPORT_SYMBOLS);
          printf("      TOTAL SYMBOLS: %d\n", num_syms);
          printf("  Exports:\n");
          num_syms = show_symbols(desc->handle, TYPE_UNDEFINED, EXPORT_SYMBOLS);
          printf("      TOTAL SYMBOLS: %d\n", num_syms);
         }
        }
        

The show_all program shown above was compiled with the command:

        $ cc -Aa -o show_all show_all.c show_symbols.c -ldld
        


Note

The following output for the example differs in Itanium/PA64 mode. For example, STORAGE is not supported.


The output produced by running this program is shown below:

        [show_all]
          Imports:
            errno                           STORAGE
            _start                          PROCEDURE
            malloc                          PROCEDURE
            free                            PROCEDURE
            exit                            PROCEDURE
            printf                          PROCEDURE
            shl_get                         PROCEDURE
            shl_getsymbols                  PROCEDURE
            __d_trap                        PROCEDURE
              TOTAL SYMBOLS: 9
          Exports:
            environ                         DATA       0x40001018
            errno                           STORAGE    0x400011CC
            _SYSTEM_ID                      DATA       0x40001008
            __dld_loc                       STORAGE    0x400011C8
            _FPU_MODEL                      DATA       0x4000100C
            _end                            DATA       0x400011D0
            _environ                        DATA       0x40001018
            __d_trap                        PROCEDURE  0x7AFFF1A6
            main                            PROCEDURE  0x7AFFF1BE
              TOTAL SYMBOLS: 9
        [/usr/lib/libc.1]
          Imports:
            _res_rmutex                     STORAGE
            errno                           STORAGE
            _regrpc_rmutex                  STORAGE
            _yellowup_rmutex                STORAGE
            _FPU_MODEL                      STORAGE
            _environ_rmutex                 STORAGE
            _iop_rmutex                     STORAGE
            _rpcnls_rmutex                  STORAGE
            _switch_rmutex                  STORAGE
            _mem_rmutex                     STORAGE
            _dir_rmutex                     STORAGE
        


The shl_unload Routine

The shl_unload routine unloads or frees up space for a shared library.

Syntax
         int shl_unload(shl_t handle)
        
Parameters
handle

The handle of the shared library you wish to unload. The handle value is obtained from a previous call to shl_load, shl_findsym, or shl_get.

Return Value

If successful, shl_unload returns 0. Otherwise, shl_unload returns -1 and sets errno to an appropriate value:

EINVAL

Indicates the specified handle is invalid.

ETXTBSY

Indicates the specified shared library is currently in use and hence cannot be unloaded. Read more information.

ENOENT

Indicates the specified handle cannot be found.

Description

To unload a shared library, use the shl_unload function. One reason to do this is to free up the private copy of shared library data and swap space allocated when the library was loaded with shl_load. (This is done automatically when a process exits.)

Another reason for doing this occurs if a program needs to replace a shared library. For example, suppose you implement some sort of shell or interpreter, and you want to load and execute user "programs" which are actually shared libraries. So you load one program, look up its entry point, and call it. Now you want to run a different program. If you do not unload the old one, its symbol definitions may get in the way of the new library. So you must unload it before loading the new library.

Note that if shared library initializers are declared for a shared library, they are called when the shared library is explicitly unloaded. For details, see Initializers for Shared Libraries.

If unloading a C++ library, use the cxxshl_unload routine. This ensures that destructors of nonlocal static objects are executed when the library is unloaded. The syntax of cxxshl_unload is the same as that of shl_unload.

Usage

When a library is unloaded, existing linkages to symbols in an unloaded library are not invalidated. Therefore, the programmer must ensure that the program does not reference symbols in an unloaded library as undefined behavior can result. In general, this routine is recommended only for experienced programmers.

The shl_unload Routine Returning ETXTBSY

When shl_unload returns -1 and sets errno to ETXTBSY, it means the library cannot be unloaded because it is still being used by the program or other shared libraries. This error occurs when a shared library cannot be unloaded for the following reasons:

  • Another dll (dynamic loading library) loaded by the program has an explicit dependency on the library.
  • The program has bound a symbol reference to some symbol defined by the library.

Majority of the problems that lead to this error are application-specific. Most applications can ignore this error, unless the applications absolutely depend on a library being unmapped from the process' address space. To ascertain instances of the problem, you can do the following:

  • Examine the program's dependencies to see if any dll depends on the shared library you are attempting to unload.
  • Check the program and its shared libraries for symbol references that may have been resolved to a symbol definition in the shared library you are attempting to unload. You can use elfdump for this check.

The shl_unload routine is likely to return ETXTBSY in the non-C++ class of programs. Any non-C++ program that dynamically loads a C++ shared library, will probably get an ETXTBSY error when it attempts to unload that shared library. To correct this problem, build the program using the C++ driver.

Non-C++ class of programs are programs that are not linked with the C++ driver and C++ support libraries (shared library linked with the C++ driver or with C++ support libraries).

Dynamic Loader Compatibility Warnings

Starting with the HP-UX 10.20 release, the dynamic loader generates compatibility warnings. These warnings include linker toolset features that may change over time. To display run-time compatibility warnings, set the _HP_DLDOPTS environment variable as follows:

        export _HP_DLDOPTS=-warnings  Turn on compatibility warnings
        

The following sections provide information about the dynamic loader compatibility warnings.


Unsupported Shared Library Management Routines

The following shared library management shl_load(3X) routines may become unsupported in a future HP-UX release:

  • shl_definesym()

  • shl_get()

  • shl_get_r()

  • shl_gethandle()

  • shl_gethandle_r()

  • shl_getsymbols()

When these routines become unsupported, the SVR4 dlopen (3C) family of routines will be the only dynamic loading routines supported.


Unsupported Shared Library Management Flags

The following shared library management shl_load(3X) flags may become unsupported in a future HP-UX release:

  • BIND_FIRST

  • BIND_NOSTART

  • BIND_RESTRICTED

  • BIND_TOGETHER

  • BIND_NONFATAL

  • BIND_VERBOSE

  • DYNAMIC_PATH

The following shl_findsym() flags may become unsupported in a future release:

  • TYPE_PROCEDURE

  • TYPE_DATA

  • TYPE_STORAGE


Note

The IPF PA-64 mode linker does not support the TYPE_STORAGE flag