Linker and Libraries Guide

Initialization and Termination Routines

Before transferring control to the application, the runtime linker processes any initialization sections found in the application and its dependencies. The .preinit_array, .init_array, and .init sections, are created by the link-editor when a dynamic object is built. These sections are labeled with the .dynamic tags DT_PREINIT_ARRAY, DT_INIT_ARRAY and DT_INIT respectively. See “Initialization and Termination Sections”.

The functions whose addresses are contained in the arrays specified by DT_PREINIT_ARRAY and DT_INIT_ARRAY are executed by the runtime linker in the same order in which their addresses appear in the array. If an object contains both DT_INIT and DT_INIT_ARRAY entries, the function referenced by the DT_INIT entry is processed before functions referenced by the DT_INIT_ARRAY entry for that object.

A dynamic executable may provide pre-initialization functions in the .preinit_array section. These functions are executed after the runtime linker has built the process image and performed relocations but before any other initialization functions. Pre-initialization functions are not permitted in shared objects.


Note -

Any DT_INIT section within the dynamic executable is called from the application itself by the process startup mechanism supplied by the compiler driver. The dynamic executable's DT_INIT section is called last, after all its dependencies initialization sections are executed.


Prior to the Solaris 2.6 release, any initialization routines from dependencies were called in reverse load order, which is the reverse order of the dependencies displayed with ldd(1). Starting with the Solaris 2.6 release, the runtime linker constructs a dependency-ordered list of initialization routines from the dependencies that have been loaded. This list is built from the dependency relationship expressed by each object, in addition to any bindings that occur outside of the expressed dependencies.

The initialization sections are executed in the reverse topological order of the dependencies. If any cyclic dependencies are found, the objects that form the cycle cannot be topologically sorted. Thus, the initialization sections of those sections are executed in their reverse load order.

Use ldd(1) with the -i option to display the initialization order of an object's dependencies. For example, the following dynamic executable and its dependencies exhibit a cyclic dependency:


$ dump -Lv B.so.1 | grep NEEDED
[1]     NEEDED      C.so.1
$ dump -Lv C.so.1 | grep NEEDED
[1]     NEEDED      B.so.1
$ dump -Lv main | grep NEEDED
[1]     NEEDED      A.so.1
[2]     NEEDED      B.so.1
[3]     NEEDED      libc.so.1
$ ldd -i main
        A.so.1 =>        ./A.so.1
        B.so.1 =>        ./B.so.1
        libc.so.1 =>     /usr/lib/libc.so.1
        C.so.1 =>        ./C.so.1
        libdl.so.1 =>    /usr/lib/libdl.so.1

   cyclic dependencies detected, group[1]:
        ./libC.so.1
        ./libB.so.1

   init object=/usr/lib/libc.so.1
   init object=./A.so.1
   init object=./C.so.1 - cyclic group [1], referenced by:
        ./B.so.1
   init object=./B.so.1 - cyclic group [1], referenced by:
        ./C.so.1

Caution - Caution -

Prior to Solaris 8 10/00, the environment variable LD_BREADTH could be set to a non-null value to force the runtime linker to execute initialization sections in pre-Solaris 2.6 order. This functionality has since been disabled, as the initialization dependencies of many applications have become complex and mandate topological sorting. Any LD_BREADTH setting is now silently ignored.


Initialization processing is repeated for any objects added to the running process with dlopen(3DL).

Because cyclic dependencies often exist, the runtime linker also employs dynamic initialization invocation in an attempt to execute any initialization sections before the code in the object is called. During symbol binding, the runtime linker determines whether the initialization sections of the object being bound to have been called. If not, the runtime linker calls them before returning from the symbol binding procedure. The exact sequence of initialization calls can be observed by setting the LD_DEBUG environment variable to include basic. See “Debugging Library”.

Dynamic objects can also provide termination sections. These sections, .fini_array and .fini, are created by the link-editor when a dynamic object is built, and are labeled with the .dynamic tags DT_FINI_ARRAY and DT_FINI respectively. See “Initialization and Termination Sections”.

Any termination routines are organized such that they can be recorded by atexit(3C). These routines are called when the process calls exit(2), or when objects are removed from the running process with dlclose(3DL).

The functions whose addresses are contained in the array specified by DT_FINI_ARRAY are executed by the runtime linker in the reverse order in which their addresses appear in the array. If an object contains both DT_FINI and DT_FINI_ARRAY entries, the functions referenced by the DT_FINI_ARRAY entry are processed before the one referenced by the DT_FINI entry for that object.

Any .fini section within the dynamic executable is called from the application itself by the process termination mechanism supplied by the compiler driver. The dynamic executable's .fini section is called first, before its dependency's termination sections are executed.

Starting with the Solaris 2.6 release, termination routines are called in the topological order of dependencies. Prior to the Solaris 2.6 release, termination routines were called in load order.

Although this initialization and termination calling sequence seems quite straightforward, be careful about placing too much emphasis on this sequence. The ordering of objects can be affected by both shared object and application development. See “Dependency Ordering”.