When building a dynamic object against a shared object containing version definitions, it is possible to instruct the link-editor to limit the binding to specific version definitions. Effectively, the link-editor allows you to control an object's binding to specific interfaces.
An object's binding requirements can be controlled using a file control directive. This directive is supplied using the link-editor's -M option and an associated mapfile. The syntax for these file control mapfile directives is shown below:
name - version [ version ... ] [ $ADDVERS=version ]; |
name - represents the name of the shared object dependency. This name should match the shared object's compilation environment name as used by the link-editor (see "Library Naming Conventions").
version - represents the version definition name within the shared object that should be made available for binding. Multiple version definitions can be specified.
There are a couple of scenarios where this binding control can be useful:
If a shared object has been versioned to define unique and independent versions, possibly defining different standards interfaces, then the application can insure that its bindings meet the requirements of a specific interface.
If a shared object has been versioned over several software releases, application developers can restrict themselves to the interfaces that were available in a previous software release. Thus, an application can be built using the latest release of the shared object in the knowledge that the application's interface requirements can be met by a previous release of the shared object.
The following is an example of using 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 ReleaseX and ReleaseX+1 respectively.
An application can be built to bind only to the interfaces available in ReleaseX by using the following version control mapfile directive:
$ cat mapfile libfoo.so - SUNW_1.1; |
For example, if you develop an application, prog, and wish to insure that the application will run on ReleaseX, then the application can only use the interfaces available in that release. If the application mistakenly references the symbol bar, then the application's noncompliance to the required interface will be 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. This can be achieved either by reworking the application to remove the requirement on bar, or by adding an implementation of bar to the build of the application.
Sometimes it is desirable to record additional version dependencies than would be produced from the normal symbol binding of an object. This can be achieved using the $ADDVERS file control directive. This section describes a couple of scenarios where this additional binding may be useful.
Continuing with the libfoo.so.1 example, assume that in ReleaseX+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, but now it 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 we continued to build our application prog so that its only requirement is the interface symbol foo1 it 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 ReleaseX+2 as the version definition STAND_A did not exist in previous releases, even though the interface foo1 did.
Therefore, the application prog can be built to align its requirement with previous releases by creating a dependency on SUNW_1.1 by using the following file control directive:
$ cat mapfile libfoo.so - SUNW_1.1 $ADDVERS=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 and satisfy compatibility with older releases.
In "Creating a Weak Version Definition" it was 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 for the correct execution of an application, then an explicit dependency on this version definition can be generated.
Establishing such a dependency can be important when a bug fix, or performance improvement, is critical for the application to function correctly.
Continuing with the libfoo.so.1 example, assume a bug fix is incorporated as the weak version definition SUNW_1.2.1 in software ReleaseX+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 shared object, it 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.
Therefore, 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); |
Here, 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.
When working with one or two dependencies, explicit binding to a version definition can also be achieved by referencing the version definition symbol. This is most easily achieved by using the link-editors -u option. However, a symbol reference is nonselective, and when working with multiple dependencies, which may contain similarly named version definitions, this technique is insufficient to create explicit bindings.