Linker and Libraries Guide

Shared Objects as Filters

A filter is a special form of shared object used to provide indirection to an alternative shared object. Two forms of shared object filter exist: a standard filter and an auxiliary filter.

A standard filter, in essence, consists solely of a symbol table, and provides a mechanism of abstracting the compilation environment from the runtime environment. A link-edit using the filter will reference the symbols provided by the filter itself; however, the implementation of the symbol reference is provided from an alternative source at runtime.

Standard filters are identified using the link-editor's -F flag. This flag takes an associated file name indicating the shared object that supplies symbol references at runtime. This shared object is referred to as the filtee. Multiple use of the -F flag enables multiple filtees to be recorded.

If the filtee cannot be processed at runtime, or any symbol defined by the filter cannot be located within the filtees, a fatal error condition results.

An auxiliary filter has a similar mechanism, except that the filter itself contains an implementation corresponding to its symbols. A link-edit using the filter references the symbols provided by the filter itself. The implementation of the symbol reference can be provided from an alternative source at runtime.

Auxiliary filters are identified using the link-editor's -f flag. This flag takes an associated file name indicating the shared object that can be used to supply symbols at runtime. This shared object is referred to as the filtee. Multiple use of the -f flag allows multiple filtees to be recorded.

If the filtee cannot be processed at runtime, or any symbol defined by the filter cannot be located within the filtee, the implementation of the symbol within the filter will be used.

Generating a Standard Filter

To generate a standard filter, you first define a filtee, libbar.so.1, on which this filter technology is applied. This filtee might be built from several relocatable objects. In the following example, one of these objects originates from the file bar.c, and supplies the symbols foo and bar.


$ cat bar.c
char * bar = "bar";

char * foo()
{
        return("defined in bar.c");
}
$ cc -o libbar.so.1 -G -K pic .... bar.c ....

In the following example a standard filter, libfoo.so.1, is generated for the symbols foo and bar, and indicates the association to the filtee libbar.so.1. The environment variable LD_OPTIONS is used to circumvent the compiler driver from interpreting the -F option as one of its own.


$ cat foo.c
char * bar = 0;

char * foo(){}

$ LD_OPTIONS='-F libbar.so.1' \
cc -o libfoo.so.1 -G -K pic -h libfoo.so.1 -R. foo.c
$ ln -s libfoo.so.1 libfoo.so
$ dump -Lv libfoo.so.1 | egrep "SONAME|FILTER"
[1]     SONAME   libfoo.so.1
[2]     FILTER   libbar.so.1

If the link-editor references the standard filter libfoo.so.1 to create a dynamic executable or shared object, it will use the information from the filter's symbol table during symbol resolution. See “Symbol Resolution” for more details.

At runtime, any reference to the symbols of the filter result in the additional loading of the filtee libbar.so.1. The runtime linker uses this filtee to resolve any symbols defined by libfoo.so.1.

For example, the following dynamic executable, prog, references the symbols foo and bar, which are resolved during link-edit from the filter libfoo.so.1.


$ cat main.c
extern char * bar, * foo();

main()
{
        (void) printf("foo() is %s: bar=%s\n", foo(), bar);
}
$ cc -o prog main.c -R. -L. -lfoo
$ prog
foo() is defined in bar.c: bar=bar

The execution of the dynamic executable prog results in the function foo(), and the data item bar, being obtained from the filtee libbar.so.1, not from the filter libfoo.so.1.

In this example, the filtee libbar.so.1 is uniquely associated to the filter libfoo.so.1 and is not available to satisfy symbol lookup from any other objects that might be loaded as a consequence of executing prog.

Standard filters provide a mechanism for defining a subset interface of an existing shared object, or an interface group spanning a number of existing shared objects. Several filters are used in the Solaris operating environment.

The /usr/lib/libsys.so.1 filter provides a subset of the standard C library /usr/lib/libc.so.1. This subset represents the ABI-conforming functions and data items that reside in the C library that must be imported by a conforming application.

The /usr/lib/libdl.so.1 filter defines the user interface to the runtime linker itself. This interface provides an abstraction between the symbols referenced in a compilation environment from libdl.so.1 and the actual implementation binding produced within the runtime environment from ld.so.1.

The /usr/lib/libxnet.so.1 filter uses multiple filtees. This library provides socket and XTI interfaces from /usr/lib/libsocket.so.1, /usr/lib/libnsl.so.1, and /usr/lib/libc.so.1.

Because the code in a standard filter is never referenced at runtime, there is no point in adding content to any functions defined within the filter. Filter code might require relocation, which would result in an unnecessary overhead when processing the filter at runtime. Functions are best defined as empty routines, or directly from a mapfile. See “Defining Additional Symbols”.

When generating data symbols within a filter, you should always initialize the data items to ensure that they result in references from dynamic executables.

Some of the more complex symbol resolutions carried out by the link-editor require knowledge of a symbol's attributes, including the symbol's size. See “Symbol Resolution” for more details. Therefore, you should generate the symbols in the filter so that their attributes match those of the symbols in the filtee. This ensures that the link-editing process analyzes the filter in a manner compatible with the symbol definitions used at runtime.


Note -

The link-editor uses the ELF class of the first input relocatable file it sees to govern the class of object it creates. Use the link-editor's -64 option to create a 64–bit filter solely from a mapfile.


Generating an Auxiliary Filter

The creation of an auxiliary filter is essentially the same as creating a standard filter (see “Generating a Standard Filter” for more details). First define a filtee, libbar.so.1, on which this filter technology is applied. This filtee might be built from several relocatable objects. One of these objects originates from the file bar.c, and supplies the symbol foo:


$ cat bar.c
char * foo()
{
        return("defined in bar.c");
}
$ cc -o libbar.so.1 -G -K pic .... bar.c ....

In the following example, an auxiliary filter, libfoo.so.1, is generated for the symbols foo and bar, and indicates the association to the filtee libbar.so.1. The environment variable LD_OPTIONS is used to circumvent the compiler driver from interpreting the -f option as one of its own.


$ cat foo.c
char * bar = "foo";

char * foo()
{
        return ("defined in foo.c");
}
$ LD_OPTIONS='-f libbar.so.1' \
cc -o libfoo.so.1 -G -K pic -h libfoo.so.1 -R. foo.c
$ ln -s libfoo.so.1 libfoo.so
$ dump -Lv libfoo.so.1 | egrep "SONAME|AUXILIARY"
[1]     SONAME    libfoo.so.1
[2]     AUXILIARY libbar.so.1

If the link-editor references the auxiliary filter libfoo.so.1 to create a dynamic executable or shared object, it will use the information from the filter's symbol table during symbol resolution. See “Symbol Resolution” for more details.

At runtime, any reference to the symbols of the filter result in a search for the filtee libbar.so.1. If this filtee is found, the runtime linker uses the filtee to resolve any symbols defined by libfoo.so.1. If the filtee is not found, or a symbol from the filter is not found in the filtee, then the original value of the symbol within the filter is used.

For example, the following dynamic executable, prog, references the symbols foo and bar, which are resolved during link-edit from the filter libfoo.so.1.


$ cat main.c
extern char * bar, * foo();

main()
{
        (void) printf("foo() is %s: bar=%s\n", foo(), bar);
}
$ cc -o prog main.c -R. -L. -lfoo
$ prog
foo() is defined in bar.c: bar=foo

When the dynamic executable prog is executed, the function foo() is obtained from the filtee libbar.so.1, not from the filter libfoo.so.1. However, the data item bar is obtained from the filter libfoo.so.1, as this symbol has no alternative definition in the filtee libbar.so.1.

Auxiliary filters provide a mechanism for defining an alternative interface of an existing shared object. This mechanism is used in the Solaris operating environment to provide optimized functionality within platform specific shared objects. See “Instruction Set Specific Shared Objects” and “System Specific Shared Objects” for examples.

Filtee Processing

The runtime linker's processing of a filter defers the loading of a filtee until a reference to a symbol within the filter has occurred. This implementation is analogous to the filter performing a dlopen(3DL) on each of its filtees as they are required. This implementation accounts for differences in dependency reporting that can be produced by tools such as ldd(1).

The link-editor's -z loadfltr option can be used when creating a filter to cause the immediate processing of its filtees at runtime. In addition, the immediate processing of any filtees within a process can be triggered by setting the LD_LOADFLTR environment variable to any value.