Initialization and Termination Sections
Dynamic objects can supply code that provides for runtime initialization and termination processing. The initialization code of a dynamic object is executed once each time the dynamic object is loaded in a process. The termination code of a dynamic object is executed once each time the dynamic object is unloaded from a process or at process termination. This code can be encapsulated in one of two section types, either an array of function pointers or a single code block. Each of these section types is built from a concatenation of like sections from the input relocatable objects.
The sections .pre_initarray
,
.init_array
and
.fini_array
provide arrays of runtime
pre-initialization, initialization, and
termination functions, respectively. When creating a dynamic object,
the link-editor identifies these arrays with the
.dynamic
tag pairs
DT_PREINIT_[ARRAY/ARRAYSZ]
,
DT_INIT_[ARRAY/ARRAYSZ]
, and
DT_FINI_[ARRAY/ARRAYSZ]
accordingly.
These tags identify the associated sections so that the sections can
be called by the runtime linker. A
pre-initialization array is applicable to
executables only.
Note:
Functions that are assigned to these arrays must be provided from the object that is being built.The sections .init
and
.fini
provide a runtime initialization
and termination code block, respectively. The compiler drivers
typically supply .init
and
.fini
sections with files they add to the
beginning and end of your input file list. These compiler provided
files have the effect of encapsulating the .init
and .fini
code from your relocatable objects into
individual functions. These functions are identified by the reserved
symbol names _init
and _fini
respectively. When creating a dynamic object, the link-editor
identifies these symbols with the .dynamic
tags
DT_INIT
and DT_FINI
accordingly. These tags identify the associated sections so they can
be called by the runtime linker.
For more information about the execution of initialization and termination code at runtime see Initialization and Termination Routines.
The registration of initialization and termination
functions can be carried out directly by the link-editor by using
the -z initarray
and
-z finiarray
options. For example, the
following command places the address of foo
() in
an .init_array
element, and the address of
bar
() in a .fini_array
element.
$ cat main.c #include <stdio.h> void foo() { (void) printf("initializing: foo()\n"); } void bar() { (void) printf("finalizing: bar()\n"); } void main() { (void) printf("main()\n"); } $ cc -o main -z initarray=foo -z finiarray=bar main.c $ main initializing: foo() main() finalizing: bar()
The creation of initialization and termination sections can be carried
out directly using an assembler. However, most compilers offer
special primitives to simplify their declaration. For example, the
previous code example can be rewritten using the following
#pragma
definitions. These definitions
result in a call to foo
() being placed in an
.init
section, and a call to
bar
() being placed in a
.fini
section.
$ cat main.c #include <stdio.h> #pragma init (foo) #pragma fini (bar) .... $ cc -o main main.c $ main initializing: foo() main() finalizing: bar()
Initialization and termination code, spread throughout several relocatable objects, can result in different behavior when included in an archive library or shared object. The link-edit of an application that uses this archive might extract only a fraction of the objects contained in the archive. These objects might provide only a portion of the initialization and termination code spread throughout the members of the archive. At runtime, only this portion of code is executed. The same application built against the shared object will have all the accumulated initialization and termination code executed when the dependency is loaded at runtime.
To determine the order of executing initialization and termination code within a process at runtime is a complex issue that involves dependency analysis. Limit the content of initialization and termination code to simplify this analysis. Simplified, self contained, initialization and termination code provides predictable runtime behavior. See Initialization and Termination Order for more details.
Data initialization should be independent if the
initialization code is involved with a dynamic object whose memory
can be dumped using
dldump
(3C).