![]() |
![]() |
| |||
Initialization and Termination SectionsDynamic objects may supply code that provides for runtime initialization and termination processing. 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 .preinit_array, .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 they may be called by the runtime linker. A pre-initialization array is applicable to dynamic executables only. The sections .init and .fini provide a runtime initialization and termination code block, respectively. However, the compiler drivers typically supply .init and .fini sections with files they add to the beginning and end of your input file list. These files have the effect of encapsulating the .init and .fini code 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 may be called by the runtime linker. For more information regarding 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 using the -z initarray and -z finiarray options. For example, the following command places the address of foo() in an .initarray element, and the address of bar() in a .finiarray element.
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.
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 using 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 involving dependency analysis. Limiting the content of initialization and termination code can simplifying this analysis, while providing both flexible, and 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(3DL). Symbol ProcessingDuring input file processing, all local symbols from the input relocatable objects are passed through to the output file image. All global symbols are accumulated internally within the link-editor. Each global symbol supplied by a relocatable object is searched for within this internal symbol table. If a symbol with the same name has already been encountered from a previous input file, a symbol resolution process is called. This symbol resolution process determines which of the two entries is kept. On completing input file processing, and providing no fatal error conditions have been encountered during symbol resolution, the link-editor determines if any unresolved symbol references remain. Unresolved symbol references can cause the link-edit to terminate. Finally, the link-editor's internal symbol table is added to the symbol tables of the image being created. The following sections expand upon symbol resolution and undefined symbol processing. Symbol ResolutionSymbol resolution runs the entire spectrum, from simple and intuitive to complex and perplexing. Resolutions can be carried out silently by the link-editor, can be accompanied by warning diagnostics, or can result in a fatal error condition. The resolution of two symbols depends on their attributes, the type of file providing the symbol, and the type of file being generated. For a complete description of symbol attributes, see Symbol Table Section. For the following discussions, however, it is worth identifying three basic symbol types:
In its simplest form, symbol resolution involves the use of a precedence relationship that has defined symbols dominating tentative symbols, which in turn dominate undefined symbols. The following C code example shows how these symbol types can be generated. Undefined symbols are prefixed with u_, tentative symbols are prefixed with t_, and defined symbols are prefixed with d_.
| |||
| |||