Linker and Libraries Guide

Lazy Loading of Dynamic Dependencies

The default model for loading a dynamic dependency is to load it into memory and examine it for any additional dependencies. If any exist they in turn are immediately loaded. This cycle continues until the full dependency tree is exhausted, at which point all inter-object references (relocations) are resolved.

Under this default model, all dependencies of an application are loaded into memory, and all data relocations are performed, regardless of whether the code in these dependencies will actually be referenced by the application during its execution.

Under a lazy loading model, any dependencies that are labeled for lazy loading are loaded only when explicitly referenced. By taking advantage of a function call's lazy binding the loading of a dependency is delayed until it is first referenced. In fact, objects that are never referenced are never loaded.

A relocation reference can be immediate or lazy. Because immediate references must be resolved when an object is initialized, any dependency that satisfies this reference must be immediately loaded. Therefore, identifying such a dependency as lazy loadable has little effect. See “When Relocations Are Performed”. Immediate references between dynamic objects are generally discouraged.

An example of lazy loading is the link-editor itself, which references a debugging library, liblddbg.so.4. Because debugging is only called upon infrequently, loading this library every time the link-editor is invoked is unnecessary and expensive. By indicating that this library can be lazily loaded, the expense of processing can be moved to those invocations that ask for debugging output.

The alternate method of achieving this lazy loading model is to use dlopen() and dlsym() to load and bind to a dependency when needed. This is ideal if the number of references (through dlsym()) is small, or the dependency name or location is not known at link-edit time. For more complex interactions with known dependencies, coding to normal symbol references and designating the dependency to be lazily loaded is simpler.

Designating an object to be lazily or normally loaded is done through the link-editor options -z lazyload and -z nolazyload respectfully. These options are position-dependent on the link-edit command line. Any dependency found following the option takes on the loading attribute specified by the option.

The following simple program has a dependency on libdebug.so.1. The dynamic section (.dynamic), shows libdebug.so.1 is marked for lazy loading. The symbol information section (.SUNW_syminfo), shows the symbol reference that triggers libdebug.so.1 loading.


$ cc -o prog prog.c -L. -zlazyload -ldebug -znolazyload -R'$ORIGIN'
$ elfdump -d prog
 
Dynamic Section:  .dynamic
     index  tag           value
       [0]  POSFLAG_1     0x1           [ LAZY ]
       [1]  NEEDED        0x123         libdebug.so.1
       [2]  NEEDED        0x131         libc.so.1
       [3]  RUNPATH       0x13b         $ORIGIN
       ...
$ elfdump -y prog
 
Syminfo section: .SUNW_syminfo
     index flgs  boundto                symbol
	      ...
      [52] DL       [1] libdebug.so.1   debug

The POSFLAG_1 with the value of LAZY designates that the following NEEDED entry, libdebug.so.1, should be lazily loaded. Because libc.so.1 has no preceding LAZY flag it is loaded at the initial startup of the program.


Note -

Lazy loading can be disabled at runtime by setting the environment variable LD_NOLAZYLOAD to a non-null value.