Linker and Libraries Guide

Specifying a Version Binding

When creating a dynamic object that is linked against a shared object containing version definitions, you can instruct the link-editor to limit the binding to specific version definitions. Effectively, the link-editor enables you to control an object's binding to specific interfaces.

An object's binding requirements can be controlled using a DEPEND_VERSIONS mapfile directive. This directive is supplied using the link-editor's -M option and an associated mapfile. The DEPEND_VERSIONS directive uses the following syntax.

        $mapfile_version 2
        DEPEND_VERSIONS objname {
                ALLOW   = version_name;
                REQUIRE = version_name;
                ...
        };

The control of version binding can be useful in the following scenarios.

The following example illustrates the use of the version control mechanism. This example uses the shared object libfoo.so.1 containing the following version interface definitions.


$ pvs -dsv libfoo.so.1
        libfoo.so.1:
                _end;
                _GLOBAL_OFFSET_TABLE_;
                _DYNAMIC;
                _edata;
                _PROCEDURE_LINKAGE_TABLE_;
                _etext;
        SUNW_1.1:
                foo1;
                foo2;
                SUNW_1.1;
        SUNW_1.2:           {SUNW_1.1}:
                bar;

The version definitions SUNW_1.1 and SUNW_1.2 represent interfaces within libfoo.so.1 that were made available in software Release X and Release X+1 respectively.

An application can be built to bind only to the interfaces available in Release X by using the following version control mapfile directive.


$ cat mapfile
$mapfile_version 2
DEPEND_VERSIONS libfoo.so {
        ALLOW = SUNW_1.1;
};

For example, suppose you develop an application, prog, and want to ensure that the application can run on Release X. The application must only use the interfaces available in Release X. If the application mistakenly references the symbol bar, then the application is not compliant with the required interface. This condition is signalled by the link-editor as an undefined symbol error.


$ cat prog.c
extern void foo1();
extern void bar();

main()
{
        foo1();
        bar();
}
$ cc -o prog prog.c -M mapfile -L. -R. -lfoo
Undefined           first referenced
 symbol                 in file
bar                     prog.o  (symbol belongs to unavailable \
                                version ./libfoo.so (SUNW_1.2))
ld: fatal: Symbol referencing errors. No output written to prog

To be compliant with the SUNW_1.1 interface, you must remove the reference to bar. You can either rework the application to remove the requirement on bar, or add an implementation of bar to the creation of the application.


Note –

By default, shared object dependencies encountered as part of a link-edit, are also verified against any file control directives. Use the environment variable LD_NOVERSION to suppress the version verification of any shared object dependencies.


Binding to Additional Version Definitions

To record more version dependencies than would be produced from the normal symbol binding of an object, use the REQUIRE attribute to the DEPEND_VERSIONS mapfiile directive. The following sections describe scenarios where this additional binding can be useful.

Redefining an Interface

One scenario is the consumption of an ISV specific interface into a public standard interface.

From the previous libfoo.so.1 example, assume that in Release X+2, the version definition SUNW_1.1 is subdivided into two standard releases, STAND_A and STAND_B. To preserve compatibility, the SUNW_1.1 version definition must be maintained. In this example, this version definition is expressed as inheriting the two standard definitions.


$ pvs -dsv libfoo.so.1
        libfoo.so.1:
                _end;
                _GLOBAL_OFFSET_TABLE_;
                _DYNAMIC;
                _edata;
                _PROCEDURE_LINKAGE_TABLE_;
                _etext;
        SUNW_1.1:           {STAND_A, STAND_B}:
                SUNW_1.1;
        SUNW_1.2:           {SUNW_1.1}:
                bar;
        STAND_A:
                foo1;
                STAND_A;
        STAND_B:
                foo2;
                STAND_B;

If the only requirement of application prog is the interface symbol foo1, the application will have a single dependency on the version definition STAND_A. This precludes running prog on a system where libfoo.so.1 is less than Release X+2. The version definition STAND_A did not exist in previous releases, even though the interface foo1 did.

The application prog can be built to align its requirement with previous releases by creating a dependency on SUNW_1.1.


$ cat mapfile
$mapfile_version 2
DEPEND_VERSIONS libfoo.so {
        ALLOW = SUNW_1.1;
        REQURE = SUNW_1.1;
};
$ cat prog
extern void foo1();

main()
{
        foo1();
}
$ cc -M mapfile -o prog prog.c -L. -R. -lfoo
$ pvs -r prog
        libfoo.so.1 (SUNW_1.1);

This explicit dependency is sufficient to encapsulate the true dependency requirements. This dependency satisfies compatibility with older releases.

Binding to a Weak Version

Creating a Weak Version Definition described how weak version definitions can be used to mark an internal implementation change. These version definitions are well suited to indicate bug fixes and performance improvements made to an object. If the existence of a weak version is required, an explicit dependency on this version definition can be generated. The creation of such a dependency can be important when a bug fix, or performance improvement, is critical for the object to function correctly.

From the previous libfoo.so.1 example, assume a bug fix is incorporated as the weak version definition SUNW_1.2.1 in software Release X+3:


$ pvs -dsv libfoo.so.1
        libfoo.so.1:
                _end;
                _GLOBAL_OFFSET_TABLE_;
                _DYNAMIC;
                _edata;
                _PROCEDURE_LINKAGE_TABLE_;
                _etext;
        SUNW_1.1:           {STAND_A, STAND_B}:
                SUNW_1.1;
        SUNW_1.2:           {SUNW_1.1}:
                bar;
        STAND_A:
                foo1;
                STAND_A;
        STAND_B:
                foo2;
                STAND_B;
        SUNW_1.2.1 [WEAK]:  {SUNW_1.2}:
                SUNW_1.2.1;

Normally, if an application is built against this libfoo.so.1, the application records a weak dependency on the version definition SUNW_1.2.1. This dependency is informational only. This dependency does not cause termination of the application should the version definition not exist in the implementation of libfoo.so.1 that is used at runtime.

The REQUIRE attribute to the DEPEND_VERSIONS mapfile directive can be used to generate an explicit dependency on a version definition. If this definition is weak, then this explicit reference also the version definition to be promoted to a strong dependency.

The application prog can be built to enforce the requirement that the SUNW_1.2.1 interface be available at runtime by using the following file control directive.


$ cat mapfile
$mapfile_version 2
DEPEND_VERSIONS libfoo.so {
        ALLOW = SUNW_1.1;
        REQUIRE = SUNW_1.2.1;
};
$ cat prog
extern void foo1();

main()
{
        foo1();
}
$ cc -M mapfile -o prog prog.c -L. -R. -lfoo
$ pvs -r prog
        libfoo.so.1 (SUNW_1.2.1);

prog has an explicit dependency on the interface STAND_A. Because the version definition SUNW_1.2.1 is promoted to a strong version, the version SUNW_1.2.1 is normalized with the dependency STAND_A. At runtime, if the version definition SUNW_1.2.1 cannot be found, a fatal error is generated.


Note –

When working with a small number of dependencies, you can use the link-editor's -u option to explicitly bind to a version definition. Use this option to reference the version definition symbol. However, a symbol reference is nonselective. When working with multiple dependencies, that contain similarly named version definitions, this technique might be insufficient to create explicit bindings.