ELF initialization and termination sections and routines execute at a sensitive point in the life cycle of the object. During initialization, the object has been loaded into memory, but is not fully initialized. During finalization, the object is still loaded in memory, but is no longer safe to use, and may be partially removed from the process state. In either context, the process state is not fully consistent, and there are significant limits on what code can safely do. Common pitfalls include, but are not limited, to the following.
Cyclic dependencies resulting in deadlock, where the initialization code for one object triggers the loading of another object, which in turn calls back into the initial object.
Thread serialization failures when a shared object is used in a multithreaded application. Two threads may attempt to access a lazily loaded library at the same time. The thread that gets there first will cause the runtime linker to load the object and start to run the initialization code. Programmers are often under the mistaken impression that the runtime linker can prevent more than one thread from accessing a given object when ELF initialization and termination code is running, but this is not the case. The runtime linker cannot prevent other threads from attempting to access the library once the initialization code is running. It is therefore possible for a second thread to access the object in an inconsistent state. It is the responsibility of the object to serialize such access, either by providing the necessary locks, or my requiring the caller to do so.
ELF initialization and termination sections and routines allow for the execution of arbitrary code, giving the illusion that they are capable of doing anything that code running in a normal context might do. In this view, such code seems like nothing more than a convenient way to do initialization or cleanup without explicit function calls. This misconception leads to failures that can be difficult to diagnose.
Programmers should be cautious in their use of ELF initialization and termination code, and limit the scope and complexity of operations. The link-editor and runtime linker are not cognizant of the content or purpose of such code, and cannot diagnose or prevent unsafe code. Small self contained operations are safe. Operations involving access to other objects or process state may not be. Rather than attempt complex operations in initialization and termination code, libraries should provide explicit initialization and termination functions for their callers to run, and document the requirement to do so.
The following section considers these issues in detail.