News, tips, partners, and perspectives for the Oracle Solaris operating system

Dependencies - perhaps they can be lazily loaded

In a previous posting I stated that you should only record those dependencies you need, and nothing else. There's another step you can take to reduce start-up processing overhead.

Dynamic objects need to resolve symbolic references from each other. Function calls are typically implemented through an indirection that allows the function binding to be deferred until the function call is first made. Because of this deferral, it is also possible to cause the defining dependency to be loaded when the function call is first made. This model is referred to as Lazy Loading.

To establish lazy loading, you must pass the -z lazyload option to ld(1) when you build your dynamic object. In addition, the association of a symbol reference to a dependency requires that the dependency is specified as part of the link-edit. It is recommended that you use the link-editors -z defs option to insure that all dependencies are specified when you build your dynamic object. The following example establishes lazy dependencies for the references foo() and bar().

    % cat wally.c
    extern void foo(), bar();
    void wally(int who)
        who ? foo() : bar();
    % cc -o wally.so wally.c -G -Kpic -zdefs -zlazyload -R'$ORIGIN' foo.so bar.so

The lazy loading attribute of these dependencies can be displayed with elfdump(1).

   % elfdump -d wally.so | egrep "NEEDED|POSFLAG"
        [0]  POSFLAG_1        0x1               [ LAZY ]
        [1]  NEEDED           0x66              foo.so
        [2]  POSFLAG_1        0x1               [ LAZY ]
        [3]  NEEDED           0x6d              bar.so

By default, ldd(1) displays all dependencies, in that it will force lazy loaded objects to be processed. To reveal lazy loading, use the -L option. For example, when a dynamic object is loaded into memory, all data relocations are performed before the object can gain control. Thus the following operation reveals that neither dependency is loaded.

    % ldd -Ld wally.so

Once function relocations are processed, both dependencies are loaded to resolve the function reference.

    % ldd -Lr wally.so
        foo.so =>       ./foo.so
        bar.so =>       ./bar.so

ldd(1) becomes a convenient tool for discovering whether lazy loading might be applicable. Suppose we rebuilt wally.so without the -z lazyload option. And recall that the -u option can be used to discover
unused dependencies.

    % cc -o wally.so wally.c -G -Kpic -zdefs -R'$ORIGIN' foo.so bar.so
    % ldd -Ldu wally.so
        foo.so =>        ./foo.so
        bar.so =>        ./bar.so
      unused object=./foo.so
      unused object=./bar.so

This has revealed that loading wally.so and relocating it as would occur at process startup, did not require the dependencies foo.so or bar.so to be loaded. This confirms that these two dependencies can be lazily loaded when reference to them is first made.

Lazy loading can be observed at runtime using the runtime linkers debugging capabilities (LD_DEBUG=files). For example, if wally() was called with a zero argument, we'd see bar.so lazily loaded.

   % LD_DEBUG=files main
   25670: 1: transferring control: ./main
   25608: 1: file=bar.so;  lazy loading from file=./wally.so: symbol=bar

Note, not only on does lazy loading have the potential of reducing the cost of start-up processing, but if lazy loading references are never called, the dependencies will never be loaded as part of the process.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.