Linker and Libraries Guide

Lazy Loading of Dynamic Dependencies

The default model for loading a dynamic dependency is to map it into memory and then 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 then resolved.

This model results in all dependencies of an application being loaded into memory, and all data relocations being 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 will not be loaded until explicitly referenced (by the applications execution). By taking advantage of a procedure calls lazy binding (see "Procedure Linkage Table (Processor-Specific)"), we can delay the loading of any dependency until it is first referenced. In fact, objects that are never referenced will never be loaded.

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

The alternate method of achieving this lazy loading model is to dlopen()/dlsym() 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. But for more complex interactions with known dependencies it is simpler to code to normal symbol references and designate the dependency to be lazily loaded.

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

Here a simple program with a dependency on libdebug.so.1 is to be lazily loaded. Inspection of the dynamic section (.dynamic) indicates that libdebug.so.1 is marked for lazy loading, and inspection of the symbol information section (.SUNW_syminfo) indicates the symbol reference that will trigger its loading:


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

Notice that the POSFLAG_1 above with the value of LAZY designates that the following NEEDED entry, libdebug.so.1, should be lazily loaded. libc.so.1 however, has no preceeding LAZY flag and thus will be loaded at the initial startup of prog.

The environment variable LD_FLAGS can be set to nolazyload to disable the lazy loading of dependencies at runtime.