Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
HP-UX Linker and Libraries User's Guide: HP 9000 Computers > Chapter 5 Creating and Using Libraries

Version Control with Shared Libraries

» 

Technical documentation

Complete book in PDF
» Feedback
Content starts here

 » Table of Contents

 » Glossary

 » Index

HP-UX provides two ways to support incompatible versions of shared library routines. “Library-Level Versioning ” describes how you create multiple versions of a shared library. “Intra-Library Versioning ” describes how a single shared library can have multiple versions of an object module. Library-level versioning is recommended over intra-library versioning.

NOTE: Beginning with HP-UX Release 11.00, the 64-bit linker toolset supports only library-level versioning.

When to Use Shared Library Versioning

For the most part, updates to a shared library should be completely upward-compatible; that is, updating a shared library won't usually cause problems for programs that use the library. But sometimes — for example, if you add a new parameter to a routine — updates cause undesirable side-effects in programs that call the old version of the routine. In such cases, it is desirable to retain the old version as well as the new. This way, old programs will continue to run and new programs can use the new version of the routine.

Here are some guidelines to keep in mind when making changes to a library:

  • When creating the first version of a shared library, carefully consider whether or not you will need versioning. It is easier to use library-level versioning from the start.

    When creating the first version of a shared library using intra-library versioning, version control is not an issue: The default version number is satisfactory.

  • When creating future revisions of a library, you must determine when a change represents an incompatible change, and thus deserves a new version. Some examples of incompatible changes are as follows:

    • As a general rule, when an exported function is changed such that calls to the function from previously compiled object files should not resolve to the new version, the change is incompatible. If the new version can be used as a wholesale replacement for the old version, the change is compatible.

    • For exported data, any change in either the initial value or the size represents an incompatible change.

    • Any function that is changed to take advantage of an incompatible change in another module should be considered incompatible.

Maintaining Old Versions of Library Modules

When using shared library versioning, you need to save the old versions of modules in the library:

  • With library-level versioning, when an incompatible change is made to a module, the entire old version of the library must be retained along with the new version.

  • With intra-library versioning, when an incompatible change is made to a module, all the old versions of the module should be retained along with the new version. The new version number should correspond to the date the change was made. If several modules are changed incompatibly in a library, it is a good idea to give all modules the same version date.

Library-Level Versioning

HP-UX 10.0 adds a new library-level versioning scheme that allows you to maintain multiple versions of shared libraries when you make incompatible changes to the library. By maintaining multiple versions, applications linked with the older versions continue to run with the older libraries, while new applications link and run with the newest version of the library. Library-level versioning is very similar to the library versioning on UNIX System V Release 4.

How to Use Library-Level Versioning

To use library-level versioning, follow these steps:

  1. Name the first version of your shared library with an extension of .0 (that's the number zero), for example libA.0. Use the +h option to designate the internal name of the library, for example, libA.0:

    ld -b *.o -o libA.0 +h libA.0      Creates the shared library libA.0. 
  2. Since the linker still looks for libraries ending in .sl with the -l option, create a symbolic link from the usual name of the library ending in .sl to the actual library. For example, libA.sl points to libA.0:

    ln -s libA.0 libA.sl                 libA.sl is a symbolic link to libA.0.
  3. Link applications as usual, using the -l option to specify libraries. The linker searches for libA.sl, as usual. However, if the library it finds has an internal name, the linker places the internal name of the library in the executable's shared library dependency list. When you run the application, the dynamic loader loads the library named by this internal name. For example:

    ld /opt/langtools/lib/crt0.o prog.o -lA -lc Binds a.out with libA.0.

Creating a New, Incompatible Version of the Library

When you create a new version of the library with incompatible changes, repeat the above steps except increment the number in the suffix of the shared library file name. That is, create libA.1 rather than libA.0 and set the symbolic link libA.sl to point to libA.1. Applications linked with libA.0 continue to run with that library while new applications link and run with libA.1. Continue to increment the suffix number for subsequent incompatible versions, for example libA.2, libA.3, and so forth.

Migrating an Existing Library to Use Library-Level Versioning

If you have an existing library you can start using library-level versioning. First rename the existing library to have the extension .0. Then create the new version of the library with the extension .1 and an internal name using the .1 extension. Create a symbolic link with the extension .sl to point to the .1 library.

When you run a program that uses shared libraries and was linked before HP-UX 10.0, the dynamic loader first attempts to open the shared library ending in .0. If it cannot find that library, it attempts to open the library ending in .sl.

The +h Option and Internal Names

The +h option gives a library an internal name for library-level versioning. Use +h to create versioned libraries:

+h internal_name

internal_name is typically the same name as the library file itself, for example libA.1 as in +h libA.1. When you link with a library that has an internal name, the linker puts the internal_name in the shared library dependency list of the executable or shared library being created. When running the resulting executable or shared library, it is the library named by this internal name that the dynamic loader loads.

You can include a relative or absolute path with the internal name, but you must ensure the dynamic loader can find the library from this name using its normal search process.

For 32-bit mode, if internal_name includes a relative path (that is, a path not starting with /), the internal name stored by the linker is the concatenation of the actual path where the library was found and internal_name, including the relative path. When the resulting program is run, if the dynamic loader cannot find the library at this location, the program will not run.

If internal_name includes an absolute path (that is, a path starting with /), the internal name stored by the linker is simply the internal_name, including the absolute path. When the resulting program is run, if the dynamic loader cannot find the library at this location, the program will not run.

For 64-bit mode, see “Internal Name Processing” for more information.

File System Links to Shared Libraries

This section discusses the situation where you have used the ln(1) command to create file system links to shared library files. For example:

$ ld -b -o /X/libapp.sl *.o          Create shared library. $ ln -s /X/libapp.sl /tmp/libmine.sl Make the link. 

Figure 5-5 Title not available (File System Links to Shared Libraries )

During a link, the linker records the file name of the opened library in the shared library list of the output file. However, if the shared library is a file system link to the actual library, the linker does not record the name of the library the file system link points to. Rather it records the name of the file system link.

For example, if /tmp/libmine.sl is a file system link to /X/libapp.sl, the following command records /tmp/libmine.sl in a.out, not /X/libapp.sl as might be expected:

$ ld /opt/langtools/lib/crt0.o main.o -L /tmp -lmine -lc

To use library-level versioning in this situation, you must set up corresponding file system links to make sure older applications linked with the older libraries run with these libraries. Otherwise, older applications could end up running with newer shared libraries. In addition, you must include the absolute path name in the internal name of the new library.

For example, in 32-bit mode, to make the above example work correctly with library-level versioning, first implement library-level versioning with the actual library /X/libapp.sl and include the absolute path in the internal name of the new library:

$ mv /X/libapp.sl /X/libapp.0               Rename old version. $ ld -b -o /X/libapp.1 +h /X/libapp.1 *.o   Create new version.
$ ln -s /X/libapp.1 /X/libapp.sl Set up symbolic link.

Then set up the corresponding file system links:

$ ln -s /X/libapp.0 /tmp/libmine.0    Link to old version. $ ln -s /X/libapp.1 /tmp/libmine.1    Link to new version. $ rm /tmp/libmine.sl                  Remove old link.
$ ln -s /X/libapp.sl /tmp/libmine.sl Link to the link.

Figure 5-6 Title not available (File System Links to Shared Libraries )

With these links in place, the loader will load /X/libapp.0 when running the a.out file created above. New applications will link and run with /X/libapp.1.

NOTE: Renaming the old version of the .0 version library only works for 32-bit mode.

For 64-bit mode programs, the dynamic loader only loads the library recorded in the dynamic load table. You should use library-lever versioning and create your 64-bit shared library with an internal name unless the library will not be versioned in the future.

Using shl_load(3X) with Library-Level Versioning

Once library level versioning is used, calls to shl_load(3X) should specify the actual version of the library to be loaded. For example, if libA.sl is now a symbolic link to libA.1, then calls to dynamically load this library should specify the latest version available when the application is compiled as shown below:

shl_load("libA.1", BIND_DEFERRED, 0);

This insures that when the application is migrated to a system that has a later version of libA available, the actual version desired is the one that is dynamically loaded.

Intra-Library Versioning

Intra-library versioning is a second method of maintaining multiple incompatible versions of shared library routines. Library-level versioning is recommended over intra-library versioning.

This section provides information on the following topics:

The Version Number Compiler Directive

With intra-library versioning, you assign a version number to any module in a shared library. The version number applies to all global symbols defined in the module's source file. The version number is a date, specified with a compiler directive in the source file. The syntax of the version number directive depends on the language:

C and C++:

#pragma HP_SHLIB_VERSION "date"

FORTRAN:

$SHLIB_VERSION 'date'

Pascal:

$SHLIB_VERSION 'date'$

The date argument in all three directives is of the form month/year. The month must be 1 through 12, corresponding to January through December. The year can be specified as either the last two digits of the year (94 for 1994) or a full year specification (1994). Two-digit year codes from 00 through 40 represent the years 2000 through 2040.

This directive should only be used if incompatible changes are made to a source file. If a version number directive is not present in a source file, the version number of all symbols defined in the object module defaults to 1/90.

Shared Library Dependencies and Version Control

A shared library as a whole can be thought of as having a version number itself. This version number is the most recent of the versioned symbols in the library and any dependent libraries.

When a shared library is built with a dependent shared library, the version number of the dependent library used during the link is recorded with the dependency.

When shl_load(3X) is called to load a shared library, the latest version of the library is loaded. If that library has dependencies, the dynamic loader (dld.sl(5)) will load the versions of the dependent libraries that were recorded in the dependency list. Note that these are not necessarily the most recent versions of the dependent libraries. When dld.sl loads a particular version of a shared library, it will load the same version of any dependent libraries.

If a shared library lists a second shared library as a dependency, dld.sl will generate an error if the second shared library has a version number which is older than the version number recorded with the dependency. This means that the first library was built using a more recent version of the second library than the version that dld.sl currently finds.

Adding New Versions to a Shared Library

To rebuild a shared library with new versions of object files, run ld again with the newly compiled object files. For example, suppose you want to add new functionality to the routines in length.c, making them incompatible with existing programs that call libunits.sl. Before making the changes, make a copy of the existing length.c and name it oldlength.c. Then change the routines in length.c with the version directive specifying the current month and date. The following shows the new length.c file:

#pragma HP_SHLIB_VERSION "11/93" /* date is November 1993 */
/*
* New version of "in_to_cm" also returns a character string
* "cmstr" with the converted value in ASCII form.
*/
float in_to_cm(float in, char *cmstr) /* convert in to cm */
{
. . . /* build "cmstr" */
return(in * 2.54);
}
. . . /* other length conversion routines */

To update libunits.sl to include the new length.c routines, copy the old version of length.o to oldlength.o; then compile length.c and rebuild the library with the new length.o and oldlength.o:

$ cp length.c oldlength.c              Save the old source.
$ mv length.o oldlength.o Save old length.o. . . . Make new length.c.
$ cc -Aa -c +z length.c Make new length.o.
$ ld -b -o libunits.sl oldlength.o \ Relink the library.
volume.o mass.o length.o

Thereafter, any programs linked with libunits.sl use the new versions of length-conversion routines defined in length.o. Programs linked with the old version of the library still use those routines from oldlength.o. For details on linking with shared libraries, see Chapter 3 “Linker Tasks”.

Specifying a Version Date

When adding modules to a library for a particular release of the library, it is best to give all modules the same version date. For example, if you complete file1.o on 04/93, file2.o on 05/93, and file3.o on 07/93, it would be best to give all the modules the same version date, say 07/93.

The reason for doing this is best illustrated with an example. Suppose in the previous example you gave each module a version date corresponding to the date it was completed: 04/93 for file1.o, 05/93 for file2.o, and 07/93 for file3.o. You then build the final library on 07/93 and link an application a.out with the library. Now suppose that you introduce an incompatible change to function foo found in file1.o, set the version date to 05/93, and rebuild the library. If you run a.out with the new version of the library, a.out will get the new, incompatible version of foo because its version date is still earlier than the date the application was linked with the original library!

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 1997 Hewlett-Packard Development Company, L.P.