Specifying a Version Binding
When creating a dynamic object 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 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:
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.
$ADDVERS - Allows for additional version definitions to be recorded.
This binding control can be useful:
If a shared object has been versioned to define unique and independent versions, possibly defining different standards interfaces. The application can then ensure 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 example illustrates the user 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 libfoo.so - SUNW_1.1; |
For example, suppose you develop an application, prog, and want to ensure that the application will run on Release X. The application can then 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. You can either rework the application to remove the requirement on bar, or add an implementation of bar to the creation of the application.
Binding to Additional Version Definitions
To record more version dependencies than would be produced from the normal symbol binding of an object, use the $ADDVERS file control directive. This section describes a couple of scenarios where this additional binding might be useful.
Continuing with the 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 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.
"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 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 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; |