Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
5.  Application Binary Interfaces and Versioning Internal Versioning Specifying a Version Binding Binding to Additional Version Definitions  Previous   Contents   Next 
   
 

Normally, if an application is built against this shared object, the application will record a weak dependency on the version definition SUNW_1.2.1. This dependency is informational only, in that it will not cause termination of the application should the version definition not be found in the libfoo.so.1 used at runtime.

The file control directive $ADDVERS can be used to generate an explicit dependency on a version definition. If this definition is weak, then this explicit reference also causes 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
libfoo.so - SUNW_1.1 $ADDVERS=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 been built with an explicit dependency on the interface STAND_A. Because the version definition SUNW_1.2.1 is promoted to a strong version, it is also normalized with the dependency STAND_A. At runtime, if the version definition SUNW_1.2.1 cannot be found, a fatal error will be generated.


Note - When working with one or two dependencies, you can use the link-editor's -u option to explicitly bind to a version definition by referencing the version definition symbol. However, a symbol reference is nonselective. When working with multiple dependencies, that might contain similarly named version definitions, this technique is insufficient to create explicit bindings.


Version Stability

The various models for binding to versions within an object only remain intact if the individual version definitions remain constant over the life time of the object.

Once a version definition for an object has been created and made public, it must exist in subsequent releases of that object unchanged. Both the version name and the symbols associated with it must remain constant. For this reason, wildcard expansion of the symbol names defined within a version definition is not supported. The number of symbols matching the wildcard might differ over the course of an objects evolution.

Relocatable Objects

Version information can be recorded and used within dynamic objects. Relocatable objects can maintain versioning information in a similar manner. However, there are one or two subtle differences in how this information is used.

Any version definitions supplied to the link-edit of a relocatable object are recorded in the same format as they are when building dynamic executables or shared objects. However, by default, symbol reduction is not carried out on the object being created. Instead, when the relocatable object is finally used as input to the generation of a dynamic object, the version recording itself will be used to determine the symbol reductions to apply.

In addition, any version definitions found in relocatable objects will be propagated to the dynamic object. For an example of version processing in relocatable objects, see "Reducing Symbol Scope".

External Versioning

Runtime references to a shared object should always refer to the file's version file name. This is usually expressed as a file name with a version number suffix. When a shared object's interface changes in an incompatible manner, such that it will break old applications, a new shared object should be distributed with a new versioned file name. In addition, the original versioned file name must still be distributed to provide the interfaces required by the old applications.

You should provide shared objects as separate versioned file names within the runtime environment when building applications over a series of software releases. You can then guarantee that the interface against which the applications were built is available for them to bind during their execution.

The following section describes how to coordinate the binding of an interface between the compilation and runtime environments.

Coordination of Versioned Filenames

During a link-edit, the most common method to input shared objects is to use the -l option. This option uses the link-editor's library search mechanism to locate shared objects that are prefixed with lib and suffixed with .so.

However, at runtime, any shared object dependencies should exist in their versioned name form. Instead of maintaining two distinct shared objects that follow these naming conventions, create file system links between the two file names.

To make the runtime shared object libfoo.so.1 available to the compilation environment, provide a symbolic link from the compilation file name to the runtime file name. For example:

$ cc -o libfoo.so.1 -G -K pic foo.c
$ ln -s libfoo.so.1 libfoo.so
$ ls -l libfoo*
lrwxrwxrwx  1 usr grp          11 1991 libfoo.so -> libfoo.so.1
-rwxrwxr-x  1 usr grp        3136 1991 libfoo.so.1

Either a symbolic or hard link can be used. However, as a documentation and diagnostic aid, symbolic links are more useful.

The shared object libfoo.so.1 has been generated for the runtime environment. Generating a symbolic link libfoo.so, has also enabled this file's use in a compilation environment. For example:

$ cc -o prog main.o -L. -lfoo

The link-editor will process the relocatable object main.o with the interface described by the shared object libfoo.so.1, which it will find by following the symbolic link libfoo.so.

If over a series of software releases, new versions of this shared object are distributed with changed interfaces, the compilation environment can be constructed to use the interface that is applicable by changing the symbolic link. For example:

$ ls -l libfoo*
lrwxrwxrwx  1 usr grp          11 1993 libfoo.so -> libfoo.so.3
-rwxrwxr-x  1 usr grp        3136 1991 libfoo.so.1
-rwxrwxr-x  1 usr grp        3237 1992 libfoo.so.2
-rwxrwxr-x  1 usr grp        3554 1993 libfoo.so.3

Three major versions of the shared object are available. Two of these shared objects, libfoo.so.1 and libfoo.so.2, provide the dependencies for existing applications. libfoo.so.3 offers the latest major release for creating and running new applications.

Using this symbolic link mechanism itself is insufficient to coordinate the correct binding of a shared object from its use in the compilation environment to its requirement in the runtime environment. As the example presently stands, the link-editor will record in the dynamic executable prog the file name of the shared object it has processed. In this case, that file name will be the compilation environment file name.

$ dump -Lv prog

prog:
 **** DYNAMIC SECTION INFORMATION ****
.dynamic:
[INDEX] Tag      Value
[1]     NEEDED   libfoo.so
.........

When the application prog is executed, the runtime linker will search for the dependency libfoo.so. prog will bind to the file to which this symbolic link is pointing.

To provide the correct runtime name to be recorded as a dependency, the shared object libfoo.so.1 should be built with an soname definition. This definition identifies the shared object's runtime name. This name is used as the dependency name by any object that links against this shared object. This definition can be provided using the -h option during the link-edit of the shared object itself. For example:

$ cc -o libfoo.so.1 -G -K pic -h libfoo.so.1 foo.c
$ ln -s libfoo.so.1 libfoo.so
$ cc -o prog main.o -L. -lfoo
$ dump -Lv prog

prog:
 **** DYNAMIC SECTION INFORMATION ****
.dynamic:
[INDEX] Tag      Value
[1]     NEEDED   libfoo.so.1
.........

This symbolic link and the soname mechanism have established a robust coordination between the shared-object naming conventions of the compilation and runtime environment. The interface processed during the link-edit is accurately recorded in the output file generated. This recording ensures that the intended interface will be furnished at runtime.


Caution - Creating a new externally versioned shared object is a major change. Be sure you understand the complete dependencies of any processes that use this shared object.

For example, an application might have dependencies on libfoo.so.1 and an externally delivered object libISV.so.1. This latter object might also have a dependency on libfoo.so.1. If the application is redesigned to use the new interfaces in libfoo.so.2 without any change to its use of the external object libISV.so.1, then both major versions of libfoo.so will be brought into the running process. Because the only reason to change the version of libfoo.so is to mark an incompatible change, having both versions of the object within a process can lead to incorrect symbol binding and hence undesirable interactions.


 
 
 
  Previous   Contents   Next