Oracle® Solaris 11.2 Linkers and Libraries Guide

Exit Print View

Updated: July 2014
 
 

Providing an Alternative to dlopen()

Lazy loading can provide an alternative to dlopen(3C) and dlsym(3C) use. See Runtime Linking Programming Interface. For example, the following code from libfoo.so.1 verifies an object is loaded, and then calls interfaces provided by that object.

void foo()
{
        void *handle;

        if ((handle = dlopen("libbar.so.1", RTLD_LAZY)) != NULL) {
                int (*fptr)();

                if ((fptr = (int (*)())dlsym(handle, "bar1")) != NULL)
                        (*fptr)(arg1);
                if ((fptr = (int (*)())dlsym(handle, "bar2")) != NULL)
                        (*fptr)(arg2);
                ....
        }
}

Although very flexible, this model of using dlopen() and dlsym() is an unnatural coding style, and has some drawbacks.

  • The object in which the symbols are expected to exit must be known.

  • The calls through function pointers provide no means of verification by either the compiler, or lint(1).

This code can be simplified if the object that supplies the required interfaces satisfies the following conditions.

  • The object can be established as a dependency at link-edit time.

  • The object is always available.

By exploiting that a function reference can trigger lazy loading, the same deferred loading of libbar.so.1 can be achieved. In this case, the reference to the function bar1() results in lazy loading the associated dependency. This coding is far more natural, and the use of standard function calls provides for compiler, or lint(1) validation.

void foo()
{
        bar1(arg1);
        bar2(arg2);
        ....
}
$ cc -G -o libfoo.so.1 foo.c -L. -zdefs -zlazyload -lbar -R'$ORIGIN'

However, this model fails if the object that provides the required interfaces is not always available. In this case, the ability to test for the existence of the dependency, without having to know the dependency name, is desirable. A means of testing for the availability of a dependency that satisfies a function reference is required.

A robust model for testing for the existence of a function can be achieved with explicitly defined deferred dependencies, and use of dlsym(3C) with the RTLD_PROBE handle.

An explicitly defined deferred dependency is an extension to a lazy loadable dependency. A symbol reference that is associated to a deferred dependency is referred to as a deferred symbol. A relocation against this symbol is only processed when the symbol is first referenced. These relocations are not processed as part of LD_BIND_NOW processing, or through dlsym(3C) with the RTLD_NOW flag.

Deferred dependencies are established at link-edit time using the link-editors –z deferred option.

$ cc -G -o libfoo.so.1 foo.c -L. -zdefs -zdeferred -lbar -R'$ORIGIN'

Having established libbar.so.1 as a deferred dependency, a reference to bar1() can verify that the dependency is available. This test can be used to control the reference to functions provided by the dependency in the same manner as dlsym(3C) had been used. This code can then make natural calls to bar1() and bar2(). These calls are much more legible and easier to write, and allow the compiler to catch errors in their calling sequences.

void foo()
{
        if (dlsym(RTLD_PROBE, "bar1")) {
                bar1(arg1);
                bar2(arg2);
                ....
        }
}

Deferred dependencies offer an additional level of flexibility. Provided the dependency has not already been loaded, the dependency can be changed at runtime. This mechanism offers a level of flexibility similar to dlopen(3C), where different objects can be loaded and bound to by the caller.

If the original dependency name is known, then the original dependency can be exchanged for a new dependency using dlinfo(3C) with the RTLD_DI_DEFERRED argument. Alternatively, a deferred symbol that is associated with the dependency can be used to identify the deferred dependency using dlinfo(3C) with the RTLD_DI_DEFERRED_SYM argument.