Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
HP-UX Linker and Libraries User's Guide: HP 9000 Computers > Chapter 6 Shared Library Management Routines

Initializers for Shared Libraries

» 

Technical documentation

Complete book in PDF
» Feedback
Content starts here

 » Table of Contents

 » Glossary

 » Index

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 (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 allow 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, it initializers and terminators are called at the appropriate time.

In 64-bit 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:

  • HP-UX 10.X style.

  • Init/fini style.

NOTE: The 32-bit mode linker supports only the HP-UX 10.X style initializers. See “32-bit Mode Initializers” for more information.

The 64-bit mode linker supports both of these styles.See “64-bit Mode Initializers” for more information.

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) as well as when the shared library is unloaded (using shl_unload or dlclose). The linker option +I is used to create 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.

$ ld -b foo.o +I my_10x_init -o libfoo.sl

#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 32-bit mode, the 64-bit HP-UX 10.X style initiators are called when unloading implicitly lordered shared libraries.

See “32-bit Mode Initializers” for more information on using these initiators.

Init/Fini Style Initializers

This style uses init and fini functions to handle initialization operations.

Init

Inits are called before the user's code starts or when a shared library is loaded. They are functions which take no arguments and return nothing. The C compiler pragma "init" is used to declare them. For example:

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

The ld command supports the +init option to specify the initializer.

Fini

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 inits, these also take no arguments and return nothing. The C compiler pragma "fini" is used to create them. For example:

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

The ld command supports the +fini option to specify theterminator.

32-bit Mode Initializers

The 32-bit mode linker supports HP-UX 10.X style initializers.

This section contains the following topics:

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.

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 and cxxshl_load Routines ”.

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.sl that uses an initializer named init_foo, use this linker command line:

$ ld -b -o libfoo.sl 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: Initializers are not executed when unloading shared libraries which were implicitly loaded since the program exits without re-entering the dynamic loader to unload them. Initializers are only called 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.

It is strongly recommended 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 has been 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 will be 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: shl_getsymbols() 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, a new flag, INITIALIZERS, has been defined for the shl_getsymbols() routine. It 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.sl that contains an initializer named init_foo:

Example 6-1 C Source for libfoo.sl

#include <stdio.h>
#include <dl.h>
/*
* This is the local initializer that is called when the libfoo.sl
* 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. Here are the commands to create libfoo.sl and to register init_foo as the initializer:

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

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

Example 6-2 C Source for testlib

#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.sl and find the required symbols:
*/
if ((hndl_foo = shl_load("libfoo.sl",
BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libfoo.sl"), 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: errror finding oz_to_g"), exit(1);
/*
* Call routines from libfoo.sl:
*/
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:

Example 6-3 Output of 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 could 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:

Example 6-4 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.

Example 6-5 C Source for libunits.sl

#include <stdio.h>
#include <dl.h>
void load() /* called after libunits.sl loaded */
{
printf("libunits.sl loaded\n");
}

void unload() /* called after libunits.sl unloaded */
{
printf("libunits.sl 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);
}

Example 6-6 C Source for libtwo.sl

#include <stdio.h>
void load() /* called after libtwo.sl loaded */
{
printf("libtwo.sl loaded\n");
}
void unload() /* called after libtwo.sl unloaded */
{
printf("libtwo.sl unloaded\n");
}

extern void _INITIALIZER();
void (*init_ptr)() = _INITIALIZER;

void foo()
{
printf("foo called\n");
}
void bar()
{
printf("bar called\n");
}

Here are the commands used to build these libraries:

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

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

Example 6-7 C Source for testlib2

#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.sl and find the required symbols:
*/
if ((hndl_units = shl_load("libunits.sl", BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libunits.sl"), 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: errror finding oz_to_g"), exit(1);

/*
* Load libtwo.sl and find the required symbols:
*/
if ((hndl_two = shl_load("libtwo.sl", BIND_IMMEDIATE, 0)) == NULL)
perror("shl_load: error loading libtwo.sl"), 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.sl:
*/
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.sl:
*/
(*foo)();
(*bar)();
/*
* Unload the libraries so we can see messages displayed by initializer:
*/
shl_unload(hndl_units);
shl_unload(hndl_two);
}

Here is the compiler command used 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.

Finally, the output from running testlib2 is shown:

Output of testlib2

libfoo loaded
1.0in = 2.54cm
1.0gal = 3.79l
1.0oz = 28.35g
libfoo unloaded

64-bit Mode Initializers

The 64-bit mode linker support both styles of initializers:

Init and Fini Usage Example

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

Example 6-8 C source for lib1.sl (file lib1.c):

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

Example 6-9 C source for lib2.sl (file lib2.c):

lib2()
{
printf("lib2\n");
}
void
lib2_init()
{
printf("lib2_init\n");
}
void
lib2_fini()
{
printf("lib2_fini\n");
}

Example 6-10 C source for lib3.sl (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");}

Example 6-11 Commands used to build these libraries:

$ cc +DD64 lib1.c lib2.c lib3.c main.c -c;
$ ld -b lib3.o -o lib3.sl;
$ ld -b +init lib2_init +fini lib2_fini lib2.o -o lib2.sl;
$ ld -b +init lib1_init +fini lib1_fini lib1.o ./lib3.sl -o \
lib1.sl;
$ cc -L. +DD64 main.o -l1 -l2 -lc;

Example 6-12 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.sl second_64bit.o my_64bit.a +I first_10x_init +I second_10x_init -o libbar.sl

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: libfoo.sl is ignored in this example. It follows the rules in “Ordering Among Executables and Shared Libraries”.

Ordering Among Executables and Shared Libraries

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

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

  • When unloading, the finis and HP-UX 10.X 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.sl, libB.sl, libC.sl. If libA.sl were linked as (libB.sl and libC.sl are "dependent" libraries of libA.sl):

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

One possible ordering while loading is:

  • inits in C

  • inits in B

  • inits in A

and while unloading is:

  • finis inA

  • finis in B

  • finis in C

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 1997 Hewlett-Packard Development Company, L.P.