Lazy Loading of Dynamic Dependencies
When a dynamic object is loaded into memory, the object is examined for any additional dependencies. By default, any dependencies that exist are immediately loaded. This cycle continues until the full dependency tree is exhausted. Finally, all inter-object data references that are specified by relocations, are resolved. These operations are performed regardless of whether the code in these dependencies is 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 the lazy binding of a function call, the loading of a dependency is delayed until the function is first referenced. As a result, 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.
Lazy loading is used by the link-editors reference to a debugging library,
liblddbg
. As debugging is only called upon
infrequently, loading this library every time that the link-editor is
invoked is unnecessary and expensive. By indicating that this library can be
lazily loaded, the expense of processing the library is moved to those
invocations that ask for debugging output.
The alternate method of achieving a lazy loading model is to use
dlopen()
and dlsym()
to load
and bind to a dependency when needed. This model is ideal if the number of
dlsym()
references is small. This model also
works well if 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.
An object is designated as lazily or normally loaded through the
link-editor options -z lazyload
and
-z nolazyload
respectively. These options are
position-dependent on the link-edit command line. Any dependency that
follows the option takes on the loading attribute specified by the option.
By default, the -z nolazyload
option is in
effect.
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. -z lazyload -ldebug -z nolazyload -lelf -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 libelf.so.1 [3] NEEDED 0x13d libc.so.1 [4] RUNPATH 0x147 $ORIGIN ... $ elfdump -y prog Syminfo section: .SUNW_syminfo index flgs bound to symbol .... [52] [ DEPEND LAZY ] [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. As
libelf.so.1
has no preceding
LAZY
flag, this library is loaded at the initial
startup of the program.
Note:
libc.so.1
has special system requirements, that
require the file not be lazy loaded. If
-z lazyload
is in effect when
libc.so.1
is processed, the flag is
effectively ignored.
The use of lazy loading can require a precise declaration of dependencies and
runpaths through out the objects used by an
application. For example, suppose two objects, libA.so
and libB.so
, both make reference to symbols in
libX.so
. libA.so
declares
libX.so
as a dependency, but
libB.so
does not. Typically, when
libA.so
and libB.so
are
used together, libB.so
can reference
libX.so
because libA.so
made this dependency available. But, if libA.so
declares libX.so
to be lazy loaded, it is possible that
libX.so
might not be loaded when
libB.so
makes reference to this dependency. A
similar failure can occur if libB.so
declares
libX.so
as a dependency but fails to provide a
runpath necessary to locate the dependency.
Regardless of lazy loading, dynamic objects should declare all their dependencies and how to locate the dependencies. With lazy loading, this dependency information becomes even more important.
Note:
Lazy loading can be disabled at runtime by setting the environment variableLD_NOLAZYLOAD
to a non-null
value.