Oracle® Solaris 11.2 Linkers and Libraries Guide

Exit Print View

Updated: July 2014
 
 

Using Stub Objects to Hide Obsolete Interfaces

Libraries evolve, and sometimes the original functionality proves to be undesirable. It is common for new abilities to be added, and for older ones to be considered obsolete. When backward compatibility is a concern, it is necessary to maintain such older functionality in the library for the benefit of existing objects. However, you may wish to prevent new use of these features. Stub objects can be used to enforce this policy. The mapfile STUB_ELIMINATE flag can be used to mark functions or data from an object that should be eliminated from the stub object, while remaining in the real object. This prevents new code, which links to the stub object, from using these obsolete items, and encourages code to be rewritten to use the preferred interfaces. Since the real objects still contain these items, existing objects are able to use them.

The libidx5 example from the previous section illustrates this. That library demonstrates how to export global data from an object. However, exported global data introduces complexity to dynamic linking, and is best avoided. It is usually a better design to provide a function to access such data, such as the idx5_func()function provided by libidx5. Continuing that example, STUB_ELIMINATE can be used to make the global data unavailable to new code that links to the stub, while providing those old interfaces in the real object for the benefit of existing programs.

The mapfile is rewritten to apply STUB_ELIMINATE to the two global data symbols. A benefit of applying STUB_ELIMINATE to global data is that it is no longer necessary to provide an ASSERT directive to provide the data size. In this example, the ASSERT is commented out. A real mapfile might omit it entirely.

$ cat better_mapfile
$mapfile_version 2
STUB_OBJECT;
SYMBOL_SCOPE {
        _idx5 {
                FLAGS=STUB_ELIMINATE;
                #ASSERT { TYPE=data; SIZE=4[5] };
        };
        idx5 {
                FLAGS=STUB_ELIMINATE;
                #ASSERT { BINDING=weak; ALIAS=_idx5 };
        };
        idx5_func;
    local:
        *;
};

A new version of the test program only uses the functional interface.

$ cat better_main.c
#include    <stdio.h>

extern int idx5_func(int);

int
main(int argc, char **argv)
{
        int i;
        for (i = 0; i < 5; i++)
                (void) printf("[%d] %d\n", i, idx5_func(i));
        return (0);
}

The old test program is saved, the stub object is rebuilt using the new mapfile, and the test program is rebuilt, linking against the new stub object that employs STUB_ELIMINATE:

$ cp a.out original_a.out
$ cc -Kpic -G -M better_mapfile -h libidx5.so.1 idx5.c -o stublib/libidx5.so.1 -zstub
$ cc better_main.c -o better_a.out -L stublib -R '$ORIGIN/lib' -lidx5 -lc
$ ./better_a.out
[0] 0
[1] 1
[2] 2
[3] 3
[4] 4

The original test program can no longer be built, because the stub library lacks the necessary global data symbols. However, the preexisting binary that used them continues to function because the real library still provides the global data symbols.

$ cc main.c -L stublib -R '$ORIGIN/lib' -lidx5 -lc
Undefined                       first referenced
 symbol                             in file
idx5                                main.o
_idx5                               main.o
ld: fatal: symbol referencing errors
$ ./original_a.out
[0] 0 0 0
[1] 1 1 1
[2] 2 2 2
[3] 3 3 3
[4] 4 4 4