4    Shared Libraries

Shared libraries are the default system libraries. The default behavior of the C compiler is to use shared libraries when performing compile and link operations.

This chapter addresses the following topics:

4.1    Shared Library Overview

Shared libraries consist of executable code that can be located at any available address in memory. Only one copy of a shared library's instructions is loaded, and the system shares that one copy among multiple programs instead of loading a copy for each program using the library, as is the case with archive (static) libraries.

Programs that use shared libraries enjoy the following significant advantages over programs that use archive libraries:

From a user perspective, the use of shared libraries is transparent. In addition, you can build your own shared libraries and make them available to other users. Most object files and archive libraries can be made into shared libraries. See Section 4.5 for more information on which files can be made into shared libraries.

Shared libraries differ from archive libraries in the following ways:

Figure 4-1 shows the difference between the use of archive and shared libraries.

Figure 4-1:  Use of Archive and Shared Libraries

4.2    Resolving Symbols

Symbol resolution is the process of mapping an unresolved symbol imported by a program or shared library to the pathname of the shared library that exports that symbol. Symbols are resolved in much the same way for shared and archive libraries, except that the final resolution of symbols in shared objects does not occur until a program is invoked.

The following sections describe:

4.2.1    Search Path of the Linker

When the linker (ld) searches for files that have been specified by using the -l option on the command line, it searches each directory in the order shown in the following list, looking first in each directory for a shared library (.so) file:

  1. /usr/shlib

  2. /usr/ccs/lib

  3. /usr/lib/cmplrs/cc

  4. /usr/lib

  5. /usr/local/lib

  6. /var/shlib

If the linker does not find a shared library, it searches through the same directories again, looking for an archive (.a) library. You can prevent the search for archive libraries by using the -no_archive option on the ld command.

4.2.2    Search Path of the Run-time Loader

Unless otherwise directed, the run-time loader (/sbin/loader) follows the same search path as the linker. You can use one of the following methods to direct the run-time loader to look in directories other than those specified by the default search path:

If the loader cannot find the library it needs in the paths defined by any of the preceding steps, it looks through the directories specified in the default path described in Section 4.2.1. In addition, you can use the _RLD_ROOT environment variable to alter the search path of the run-time loader. For more information, see loader(5).

4.2.3    Name Resolution

The semantics of symbol name resolution are based on the order in which the object file or shared object containing a given symbol appears on the link command line. The linker normally takes the leftmost definition for any symbol that must be resolved.

The sequence in which names are resolved proceeds as if the link command line was stored in the executable program. When the program runs, all symbols that are accessed during execution must be resolved. The loader aborts execution of the program if an unresolved text symbol is accessed.

For information on how unresolved symbols are handled by the system, see Section 4.2.4. The following sequence resolves references to any symbol from the main program or from a library:

  1. If a symbol is defined in an object or in an archive library from which you build the main executable program file, that symbol is used by the main program file and all of the shared libraries that it uses.

  2. If the symbol is not defined by the preceding step and is defined by one or more of the shared objects linked with the executable program, then the leftmost library on the link command line containing a definition is used.

  3. If the libraries on the link command line were linked to be dependent on other libraries, then the dependencies of libraries are searched in a breadth-first fashion instead of being searched in a depth-first fashion. For example, as shown in the following diagram, executable program A is linked against shared library B and shared library D, and library B is linked against library C.

                                 A
                                / \
                               B   D
                              /
                             C
     
     
    

    The search order is A-B-D-C. In a breadth-first search, the grandchildren of a node are searched after all the children have been searched.

  4. If the symbol is not resolved in any of the previous steps, the symbol remains unresolved.

Note that because symbol resolution always prefers the main object, shared libraries can be set up to call back into a defined symbol in the main object. Likewise, the main object can define a symbol that will override (preempt or hook) a definition in a shared library.

4.2.4    Options to Determine Handling of Unresolved External Symbols

The default behavior of the linker when building executable programs differs from its default behavior when building shared libraries:

When building shared libraries and -error_unresolved is specified, an output file (.so file) is produced even if unresolved symbols are present, overwriting any pre-existing .so file with the same name. See the Programmer's Guide for information about how to control overwriting of linker output files.

You can control how the linker handles unresolved symbols by using the following options to the ld command:

See ld(1) for details about the effects of these options.

4.3    Linking with Shared Libraries

When compiling and linking a program, using shared libraries is the same as using static libraries. For example, the following command compiles program hello.c and links it against the default system C shared library libc.so:

% cc -o hello hello.c

You can pass certain ld command options to the cc command to allow flexibility in determining the search path for a shared library. For example, you can use the -Ldir option with the cc command to change the search path by adding dir before the default directories, as shown in the following example:

% cc -o hello hello.c -L/usr/person -lmylib

To exclude the default directories from the search and limit the search to specific directories and specific libraries, specify the -L option first with no arguments. Then, specify it again with the directory to search, followed by the -l option with the name of the library to search for. For example, to limit the search path to /usr/person for use with the private library libmylib.so, enter the following command:

% cc -o hello hello.c -L -L/usr/person -lmylib

Note that because the cc command always implicitly links in the C library, the preceding example requires that a copy of libc.so or libc.a must be in the /usr/person directory.

4.4    Turning Off Shared Libraries

In application linking, the default behavior is to use shared libraries. To link an application that does not use shared libraries, you must use the -non_shared option to the cc or ld commands when you link that application.

For example:

% cc -non_shared -o hello hello.c

Although shared libraries are the default for most programming applications, some applications cannot use shared libraries:

4.5    Creating Shared Libraries

You create shared libraries by using the ld command with the -shared option. You can create shared libraries from object files or from existing archive libraries.

4.5.1    Creating Shared Libraries from Object Files

To create the shared library libbig.so from the object files bigmod1.o and bigmod2.o, enter the following command:

% ld -shared -no_archive -o libbig.so bigmod1.o bigmod2.o -lc

The -no_archive option tells the linker to resolve symbols using only shared libraries. The -lc option tells the linker to look in the system C shared library for unresolved symbols.

To make a shared library available on a system level by copying it into the /usr/shlib directory, you must have root privileges. System shared libraries should be located in the /usr/shlib directory or in one of the default directories so that the run-time loader (/sbin/loader) can locate them without requiring every user to set the LD_LIBRARY_PATH variable to directories other than those in the default path.

4.5.2    Creating Shared Libraries from Archive Libraries

You can also create a shared library from an existing archive library by using the ld command. The following example shows how to convert the static library old.a into the shared library libold.so:

% ld -shared -no_archive -o libold.so -all old.a -none -lc

In this example, the -all option tells the linker to link all the objects from the archive library old.a. The -none option tells the linker to turn off the -all option. Note that the -no_archive option applies to the resolution of the -lc option but not to old.a (because old.a is explicitly mentioned).

4.6    Working with Private Shared Libraries

In addition to system shared libraries, any user can create and use private shared libraries. For example, you have three applications that share some common code. These applications are named user, db, and admin. You decide to build a common shared library, libcommon.so, containing all the symbols defined in the shared files io_util.c, defines.c, and network.c. To do this, follow these steps:

  1. Compile each C file that will be part of the library:

    % cc -c io_util.c
    % cc -c defines.c
    % cc -c network.c
    

  2. Create the shared library libcommon.so by using the ld command:

    % ld -shared -no_archive \
    -o libcommon.so io_util.o defines.o network.o -lc
    

  3. Compile each C file that will be part of the application:

    % cc -c user.c
    % cc -o user user.o -L. -lcommon
    

    Note that the second command in this step tells the linker to look in the current directory and use the library libcommon.so. Compile db.c and admin.c in the same manner:

    % cc -c db.c
    % cc -o db db.o -L. -lcommon
     
    % cc -c admin.c
    % cc -o admin admin.o -L. -lcommon
    

  4. Copy libcommon.so into a directory pointed to by LD_LIBRARY_PATH, if it is not already in that directory.

  5. Run each compiled program (user, db, and admin).

4.7    Using Quickstart

One advantage of using shared libraries is the ability to change a library after all executable images have been linked and to fix bugs in the library. This ability is very useful during the development phase of an application.

During the production cycle, however, the shared libraries and applications that you develop are often fixed and will not change until the next release. If this is the case, you can take advantage of quickstart, a method of using predetermined addresses for all symbols in your program and libraries.

No special link options are required to prepare an application for quickstarting; however, a certain set of conditions must be satisfied. If an object cannot be quickstarted, it still runs but startup time is slower.

When the linker creates a shared object (a shared library or a main executable program that uses shared libraries), it assigns addresses to the text and data portions of the object. These addresses are what might be called quickstarted addresses. The linker performs all dynamic relocations in advance, as if the object will be loaded at its quickstarted address.

Any object depended upon is assumed to be at its quickstarted address. References to that object from the original object have the address of the depended-upon object set accordingly.

To use quickstart, an object must meet the following conditions:

The operating system detects these conditions by using checksums and timestamps.

When you build libraries, they are given a quickstart address. Unless each library used by an application chooses a unique quickstart address, the quickstart constraints cannot be satisfied. Rather than worry about addresses on an application basis, give a unique quickstart address to each shared library that you build to ensure that all of your objects can be loaded at their quickstart addresses.

The linker maintains the so_locations database to register each quickstart address when you build a library. The linker avoids addresses already in the file when choosing a quickstart address for a new library.

By default, ld runs as though the -update_registry ./so_locations option has been selected, so the so_locations file in the directory of the build is updated (or created) as necessary.

To ensure that your libraries do not collide with shared libraries on your system, enter the following commands:

% cd <directory_of_build>
% cp /usr/shlib/so_locations .
% chmod +w so_locations

You can now build your libraries. If your library builds occur in multiple directories, use the -update_registry option to the ld command to explicitly specify the location of a common so_locations file. For example:

% ld -shared -update_registry /common/directory/so_locations ...

If you install your shared libraries globally for all users of your system, update the systemwide so_locations file. Enter the following commands as root, with shared_library.so being the name of your actual shared library:

# cp shared_library.so /usr/shlib
# mv /usr/shlib/so_locations /usr/shlib/so_locations.old
# cp so_locations /usr/shlib

If several people are building shared libraries, the common so_locations file must be administered as any shared database would be. Each shared library used by any given process must be given a unique quickstart address in the file. The range of default starting addresses that the linker assigns to main executable files does not conflict with the quickstarted addresses it creates for shared objects. Because only one main executable file is loaded into a process, an address conflict never occurs between a main file and its shared objects.

If you are building only against existing shared libraries (and not building your own libraries), you do not need to do anything special. As long as the libraries meet the previously described conditions, your program will be quickstarted unless the libraries themselves are not quickstarted. Most shared libraries shipped with the operating system are quickstarted.

If you are building shared libraries, you must first copy the so_locations file as previously described. Next, you must build all shared libraries in bottom-up dependency order, using the so_locations file. Specify all depended-upon libraries on the link line. After all the libraries are built, you can then build your applications.

4.7.1    Verifying That an Object Is Quickstarting

To test whether an application's executable program is quickstarting, set the _RLD_ARGS environment variable to -quickstart_only and run the program. For example:

% setenv _RLD_ARGS -quickstart_only
% foo
(non-quickstart output)
21887:foo: /sbin/loader: Fatal Error: NON-QUICKSTART detected \
  -- QUICKSTART must be enforced

If the program runs successfully, it is quickstarting. If a load error message is produced, the program is not quickstarting.

4.7.2    Manually Tracking Down Quickstart Problems

To determine why an executable program is not quickstarting, you can use the fixso utility, described in Section 4.7.3, or you can manually test for the conditions described in the following list of requirements. Using fixso is easier, but it is helpful to understand the process involved.

  1. The executable program must be able to be quickstarted.

    Test the quickstart flag in the dynamic header. The value of the quickstart flag is 0x00000001. For example:

    % odump -D foo | grep FLAGS
    

    (non-quickstart output)

      FLAGS: 0x00000000
    

    (quickstart output)

      FLAGS: 0x00000001
    

    If the quickstart flag is not set, one or more of the following conditions exists:

  2. The executable program's dependencies must be able to be quickstarted. Get a list of an executable program's dependencies. For example:

    % odump -Dl foo
    

    (quickstart output)

                     ***LIBRARY LIST SECTION***
      Name             Time-Stamp        CheckSum   Flags Version
    foo:
      libX11.so    Sep 17 00:51:19 1993 0x78c81c78  NONE
      libc.so      Sep 16 22:29:50 1993 0xba22309c  NONE osf.1
      libdnet_stub.so Sep 16 22:56:51 1993 0x1d568a0c  NONE osf.1
    

    Test the quickstart flag in the dynamic header of each of the dependencies:

    % cd /usr/shlib
    % odump -D libX11.so libc.so libdnet_stub.so | grep FLAGS
    

    (quickstart output)

      FLAGS: 0x00000001
      FLAGS: 0x00000001
      FLAGS: 0x00000001
    

    If any of these dependencies cannot be quickstarted, the same measures suggested in step 1 can be applied here, provided that the shared library can be rebuilt by the user.

  3. The timestamp and checksum information must match for all dependencies.

    The dependencies list in step 2 shows the expected values of the timestamp and checksum fields for each of foo's dependencies. Match these values against the current values for each of the libraries:

    % cd /usr/shlib
    % odump -D libX11.so libc.so libdnet_stub.so | \
    grep TIME_STAMP
    

    (quickstart output)

      TIME_STAMP: (0x2c994247) Fri Sep 17 00:51:19 1993
      TIME_STAMP: (0x2c99211e) Thu Sep 16 22:29:50 1993
      TIME_STAMP: (0x2c992773) Thu Sep 16 22:56:51 1993
     
    % odump -D libX11.so libc.so libdnet_stub.so | grep CHECKSUM
    

    (quickstart output)

      ICHECKSUM: 0x78c81c78
      ICHECKSUM: 0xba22309c
      ICHECKSUM: 0x1d568a0c
    

    If any of the tests in these examples shows a timestamp or checksum mismatch, relinking the program should fix the problem.

    You can use the version field to verify that you have identified the correct libraries to be loaded at run time. To test the dependency versions, use the odump command as shown in the following example:

    % odump -D libX11.so | grep IVERSION
    % odump -D libc.so | grep IVERSION
      IVERSION: osf.1
    % odump -D libdnet_stub.so | grep IVERSION
      IVERSION: osf.1
    

    The lack of an IVERSION entry is equivalent to a blank entry in the dependency information. It is also equivalent to the special version _null.

    If any version mismatches are identified, you can normally find the correct matching version of the shared library by appending the version identifier from the dependency list or _null to the path /usr/shlib.

  4. Each of the executable program's dependencies must also contain dependency lists with matching timestamp and checksum information.

    Repeat step 3 for each of the shared libraries in the executable program's list of dependencies:

    % odump -Dl libX11.so
    

    (quickstart output)

                      ***LIBRARY LIST SECTION***
      Name             Time-Stamp        CheckSum   Flags Version
    libX11.so:
      libdnet_stub.so Sep 16 22:56:51 1993 0x1d568a0c  NONE osf.1
      libc.so      Sep 16 22:29:50 1993 0xba22309c  NONE osf.1
    % odump -D libdnet_stub.so libc.so | grep TIME_STAMP
      TIME_STAMP: (0x2c992773) Thu Sep 16 22:56:51 1993
      TIME_STAMP: (0x2c99211e) Thu Sep 16 22:29:50 1993
    % odump -D libdnet_stub.so libc.so | grep CHECKSUM
      ICHECKSUM: 0x1d568a0c
      ICHECKSUM: 0xba22309c
    

    If the timestamp or checksum information does not match, the shared library must be rebuilt to correct the problem. Rebuilding a shared library will change its timestamp and, sometimes, its checksum. Rebuild dependencies in bottom-up order so that an executable program or shared library is rebuilt after its dependencies have been rebuilt.

4.7.3    Tracking Down Quickstart Problems with the fixso Utility

The fixso utility can identify and repair quickstart problems caused by timestamp and checksum discrepancies. It can repair programs as well as the shared libraries they depend on, but it might not be able to repair certain programs, depending on the degree of symbolic changes required.

The fixso utility cannot repair a program or shared library if any of the following restrictions apply:

The fixso utility can identify quickstart problems as shown in the following example:

% fixso -n hello.so
fixso: Warning: found '/usr/shlib/libc.so' (0x2d93b353) which does
       not match timestamp 0x2d6ae076 in liblist of hello.so, will fix
fixso: Warning: found '/usr/shlib/libc.so' (0xc777ff16) which does
       not match checksum 0x70e62eeb in liblist of hello.so, will fix

The -n option suppresses the generation of an output file. Discrepancies are reported, but fixso does not attempt to repair the problems it finds. The following example shows how you can use fixso to repair quickstart problems:

% fixso -o ./fixed/main main
fixso: Warning: found '/usr/shlib/libc.so' (0x2d93b353) which does
       not match timestamp 0x2d7149c9 in liblist of main, will fix
% chmod +x fixed/main

The -o option specifies an output file. If no output file is specified, fixso uses a.out. Note that fixso does not create the output file with execute permission. The chmod command allows the output file to be executed. This change is necessary only for executable programs and can be bypassed when using fixso to repair shared libraries.

If a program or shared library does not require any modifications to repair quickstart, fixso indicates this as shown in the following example:

% fixso -n /bin/ls
no fixup needed for /bin/ls

4.8    Debugging Programs Linked with Shared Libraries

Debugging a program that uses shared libraries is essentially the same as debugging a program that uses archive libraries.

The dbx debugger's listobj command displays the names of the executable programs and all of the shared libraries that are known to the debugger. See Chapter 5 for more information about using dbx.

4.9    Loading a Shared Library at Run Time

In some situations, you might want to load a shared library from within a program. This section includes two short C program examples and a makefile to demonstrate how to load a shared library at run time.

The following example (pr.c) shows a C source file that prints out a simple message:

printmsg()
    {
        printf("Hello world from printmsg!\n");
    }

The next example (used1.c) defines symbols and demonstrates how to use the dlopen function:

#include <stdio.h>
#include <dlfcn.h>
 
/* All errors from dl* routines are returned as NULL */
#define BAD(x)          ((x) == NULL)
 
main(int argc, char *argv[])
{
    void *handle;
    void (*fp)();
 
    /* Using "./" prefix forces dlopen to look only in the current
     * current directory for pr.so.  Otherwise, if pr.so was not
     * found in the current directory, dlopen would use rpath,
     * LD_LIBRARY_PATH and default directories for locating pr.so.
     */
    handle = dlopen("./pr.so", RTLD_LAZY);
    if (!BAD(handle)) {
        fp = dlsym(handle, "printmsg");
        if (!BAD(fp)) {
            /*
             * Here is where the function
             * we just looked up is called.
             */
            (*fp)();
        }
        else {
            perror("dlsym");
            fprintf(stderr, "%s\n", dlerror());
        }
    }
    else {
        perror("dlopen");
        fprintf(stderr, "%s\n", dlerror());
    }
    dlclose(handle);
}

The following example shows the makefile that makes pr.o, pr.so, so_locations, and usedl.o:

# this is the makefile to test the examples
 
all:    runit
 
runit:  usedl pr.so
        ./usedl
 
usedl:  usedl.c
        $(CC) -o usedl usedl.c
 
pr.so:  pr.o
        $(LD) -o pr.so -shared pr.o -lc

4.10    Protecting Shared Library Files

Because of the sharing mechanism used for shared libraries, normal file system protections do not protect libraries against unauthorized reading. For example, when a shared library is used in a program, the text part of that library can be read by other processes even when the following conditions exist:

Only the text part of the library, not the data segment, is shared in this manner.

To prevent unwanted sharing, link any shared libraries that need to be protected by using the linker's -T and -D options to put the data section in the same 8-MB segment as the text section. For example, enter a command similar to the following:

% ld -shared -o libfoo.so -T 30000000000  \
-D 30000400000 object_files

In addition, segment sharing can occur with any file that uses the mmap system call without the PROT_WRITE flag as long as the mapped address falls in the same memory segment as other files using mmap.

Any program using mmap to examine files that might be highly protected can ensure that no segment sharing takes place by introducing a writable page into the segment before or during the mmap. The easiest way to provide protection is to use the mmap system call on the file with PROT_WRITE enabled in the protection, and use the mprotect system call to make the mapped memory read-only. Alternatively, to disable all segmentation and to avoid any unauthorized sharing, enter the following line in the configuration file:

segmentation 0

4.11    Shared Library Versioning

One of the advantages of using shared libraries is that a program linked with a shared library does not need to be rebuilt when changes are made to that library. When a changed shared library is installed, applications should work as well with the newer library as they did with the older one.

Note

Because of the need for address fixing, it can take longer to load an existing application that uses an older version of a shared library when a new version of that shared library is installed. You can avoid this kind of problem by relinking the application with the new library.

4.11.1    Binary Incompatible Modifications

Infrequently, a shared library might be changed in a way that makes it incompatible with applications that were linked with it before the change. This type of change is referred to as a binary incompatibility. A binary incompatibility introduced in a new version of a shared library does not necessarily cause applications that rely on the old version to break (that is, violate the backward compatibility of the library). The system provides shared library versioning to allow you to take steps to maintain a shared library's backward compatibility when introducing a binary incompatibility in the library.

Among the types of incompatible changes that might occur in shared libraries are the following:

This is by no means an exhaustive list of the types of changes that result in binary incompatibilities. Shared library developers should exercise common sense to determine whether any change is likely to cause failures in applications linked with the library prior to the change.

4.11.2    Shared Library Versions

You can maintain the backward compatibility of a shared library affected by incompatible changes by providing multiple versions of the library. Each shared library is marked by a version identifier. You install the new version of the library in the library's default location, and the older, binary compatible version of the library in a subdirectory whose name matches that library's version identifier.

For example, if an incompatible change was made to libc.so, the new library (/usr/shlib/libc.so) must be accompanied by an instance of the library before the change (/usr/shlib/osf.1/libc.so). In this example, the older, binary compatible version of libc.so is the osf.1 version. After the change is applied, the new libc.so is built with a new version identifier. Because a shared library's version identifier is listed in the shared library dependency record of a program that uses the library, the loader can identify which version of a shared library is required by an application (see Section 4.11.6).

In the example, a program built with the older libc.so, before the binary incompatible change, requires the osf.1 version of the library. Because the version of /usr/shlib/libc.so does not match the one listed in the program's shared library dependency record, the loader will look for a matching version in /usr/shlib/osf.1.

Applications built after the incompatible change will use /usr/shlib/libc.so and will depend on the new version of the library. The loader will load these applications by using /usr/shlib/libc.so until some further binary incompatibility is introduced.

Table 4-1 describes the linker options used to effect version control of shared libraries.

Table 4-1:  Linker Options That Control Shared Library Versioning

Option Description
-set_version version-string Establishes the version identifiers associated with a shared library. The string version-string is either a single version identifier or a colon-separated list of version identifiers. No restrictions are placed on the names of version identifiers; however, it is highly recommended that UNIX directory naming conventions be followed. If a shared library is built with this option, any program built against it will record a dependency on the specified version or, if a list of version identifiers is specified, the rightmost version specified in the list. If a shared library is built with a list of version identifiers, the run-time loader will allow any program to run that has a shared library dependency on any of the listed versions. This option is only useful when building a shared library (with -shared).
-exact_version Sets an option in the dynamic object produced by the ld command that causes the run-time loader to ensure that the shared libraries the object uses at run time match the shared libraries used at link time. This option is used when building a dynamic executable file (with -call_shared) or a shared library (with -shared). Its use requires more rigorous testing of shared library dependencies. In addition to testing shared libraries for matching versions, timestamps and checksums must also match the timestamps and checksums recorded in shared library dependency records at link time.

You can use the odump command to examine a shared library's versions string, as set by using the -set_version version-string option of the ld command that created the library. For example:

% odump -D library-name

The value displayed for the IVERSION field is the version string specified when the library was built. If a shared library is built without the -set_version option, no IVERSION field will be displayed. These shared libraries are handled as if they had been built with the version identifier _null.

When ld links a shared object, it records the version of each shared library dependency. Only the rightmost version identifier in a colon-separated list is recorded. To examine these dependencies for any shared executable file or library, use the following command:

% odump -Dl shared-object-name

4.11.3    Major and Minor Versions Identifiers

Tru64 UNIX does not distinguish between major and minor versions of shared libraries:

Tru64 UNIX shared libraries use a colon-separated list of version identifiers to provide the versioning features normally attained through minor versions.

The following sequence of library revisions shows how revision-specific identification can be added to the version list of a shared library without affecting shared library compatibility:

Shared Library Version
libminor.so 3.0
libminor.so 3.1:3.0
libminor.so 3.2:3.1:3.0

Each new release of libminor.so adds a new identifier at the beginning of the version list. The new identifier distinguishes the latest revision from its predecessors. Any executable files linked against any revision of libminor.so will record 3.0 as the required version, so no distinction is made between the compatible libraries. The additional version identifiers are only informational.

The following sequence of library revisions shows how the use of backward-compatible shared libraries can be restricted:

Shared Library Version
libminor2.so 3.0
libminor2.so 3.0:3.1
libminor2.so 3.0:3.1:3.2

In this example, programs linked with old versions of libminor2.so can be executed with newer versions of the library, but programs linked with newer versions of libminor2.so cannot be executed with any of the previous versions.

4.11.4    Full and Partial Versions of Shared Libraries

You can implement a binary compatible version of a shared library as a complete, independent object or as a partial object that depends directly or indirectly on a complete, independent object. A fully duplicated shared library takes up more disk space than a partial one, but involves simpler dependency processing and uses less swap space. The reduced disk space requirements are the only advantage of a partial version of a shared library.

A partial shared library includes the minimum subset of modules required to provide backward compatibility for applications linked prior to a binary incompatible change in a newer version of the library. It is linked against one or more earlier versions of the same library that provide the full set of library modules. By this method, you can chain together multiple versions of shared libraries so that any instance of the shared library will indirectly provide the full complement of symbols normally exported by the library.

For example, version osf.1 of libxyz.so includes modules x.o, y.o, and z.o. It was built and installed using the following commands:

% ld -shared -o libxyz.so -set_version osf.1 \
    x.o y.o z.o -lc
% mv libxyz.so /usr/shlib/libxyz.so

If, at some future date, libxyz.so requires an incompatible change that affects only module z.o, a new version, called osf.2, and a partial version, still called osf.1, can be built as follows:

% ld -shared -o libxyz.so -set_version osf.2 x.o \
    y.o new_z.o -lc
% mv libxyz.so /usr/shlib/libxyz.so
% ld -shared -o libxyz.so -set_version osf.1 z.o \
    -lxyz -lc
% mv libxyz.so /usr/shlib/osf.1/libxyz.so

4.11.5    Linking with Multiple Versions of Shared Libraries

In general, applications are linked with the newest versions of shared libraries. Occasionally, you might need to link an application or shared library with an older, binary compatible version of a shared library. In such a case, use the ld command's -L option to identify older versions of the shared libraries used by the application.

The linker issues a warning when you link an application with more than one version of the same shared library. In some cases, the multiple version dependencies of an application or shared library will not be noticed until it is loaded for execution.

By default, the ld command tests for multiple version dependencies only for those libraries it is instructed to link against. To identify all possible multiple version dependencies, use the ld command's -transitive_link option to include indirect shared library dependencies in the link step.

When an application is linked with partial shared libraries, the linker must carefully distinguish dependencies on multiple versions resulting from partial shared library implementations. The linker reports multiple version warnings when it cannot differentiate between acceptable and unacceptable multiple version dependencies.

In some instances, multiple version dependencies might be reported at link time for applications that do not use multiple versions of shared libraries at run time. Consider the libraries and dependencies shown in Figure 4-2 and described in the following table.

Figure 4-2:  Linking with Multiple Versions of Shared Libraries

Library Version Dependency Dependent Version
libA.so v1 libcommon.so v1
libB.so v2 libcommon.so v2
libcommon.so v1:v2

In the preceding table, libA.so was linked against a version of libcommon.so that had a rightmost version identifier of v1. Unlike libA.so, libB.so was linked against a version of libcommon.so that had a rightmost version identifier of v2. Because the libcommon.so shown in the table includes both v1 and v2 in its version string, the dependencies of both libA.so and libB.so are satisfied by the one instance of libcommon.so.

When a.out is linked, only libA.so and libB.so are mentioned on the link command line. However, the linker examines the dependencies of libA.so and libB.so, recognizes the possible multiple version dependency on libcommon.so, and issues a warning. By linking a.out against libcommon.so as well, you can avoid this false warning.

4.11.6    Version Checking at Load Time

The loader performs version matching between the list of versions supported by a shared library and the versions recorded in shared library dependency records. If a shared object is linked with the -exact_match option on the link command line, the loader also compares the timestamp and checksum of a shared library against the timestamp and checksum values saved in the dependency record.

After mapping in a shared library that fails the version-matching test, the loader attempts to locate the correct version of the shared library by continuing to search other directories in RPATH, LD_LIBRARY_PATH, or the default search path.

If all of these directories are searched without finding a matching version, the loader attempts to locate a matching version by appending the version string recorded in the dependency to the directory path at which the first nonmatching version of the library was located.

For example, a shared library libfoo.so is loaded in directory /usr/local/lib with version osf.2, but a dependency on this library requires version osf.1. The loader attempts to locate the correct version of the library using a constructed path like the following:

/usr/local/lib/osf.1/libfoo.so
 

If this constructed path fails to locate the correct library or if no version of the library is located at any of the default or user-specified search directories, the loader makes one last attempt to locate the library by appending the required version string to the standard system shared library directory (/usr/shlib). This last attempt will therefore use a constructed path like the following:

/usr/shlib/osf.1/libfoo.so
 

If the loader fails to find a matching version of a shared library, it aborts the load and reports a detailed error message indicating the dependency and shared library version that could not be located.

You can disable version checking for programs that are not installed with the setuid function by setting the loader environment variable as shown in the following C shell example:

% setenv _RLD_ARGS -ignore_all_versions

You can also disable version checking for specific shared libraries as shown in the following example:

% setenv _RLD_ARGS -ignore_version libDXm.so

4.11.7    Multiple Version Checking at Load Time

Like the linker, the loader must distinguish between valid and invalid uses of multiple versions of shared libraries:

The following figures show shared object dependencies that will result in multiple dependency errors. Version identifiers are shown in parentheses.

In Figure 4-3, an application uses two layered products that are built with incompatible versions of the base system.

Figure 4-3:  Invalid Multiple Version Dependencies Among Shared Objects: Example 1

In Figure 4-4, an application is linked with a layered product that was built with an incompatible version of the base system.

Figure 4-4:  Invalid Multiple Version Dependencies Among Shared Objects: Example 2

In Figure 4-5, an application is linked with an incomplete set of backward-compatible libraries that are implemented as partial shared libraries.

Figure 4-5:  Invalid Multiple Version Dependencies Among Shared Objects: Example 3

The following figures show valid uses of multiple versions of shared libraries.

In Figure 4-6, an application uses a backward-compatibility library implemented as a partial shared library.

Figure 4-6:  Valid Uses of Multiple Versions of Shared Libraries: Example 1

In Figure 4-7, an application uses two backward-compatible libraries, one of which depends on the other.

Figure 4-7:  Valid Uses of Multiple Versions of Shared Libraries: Example 2

4.12    Symbol Binding

The loader can resolve symbols using either deferred or immediate binding. Immediate binding requires that all symbols be resolved when an executable program or shared library is loaded. Deferred (lazy) binding allows text symbols to be resolved at run time. A lazy text symbol is resolved the first time that a reference is made to it in a program.

By default, programs are loaded with deferred binding. Setting the LD_BIND_NOW environment variable to a non-null value selects immediate binding for subsequent program invocations.

Immediate binding can be useful to identify unresolvable symbols. With deferred binding in effect, unresolvable symbols might not be detected until a particular code path is executed.

Immediate binding can also reduce symbol-resolution overhead. Run-time symbol resolution is more expensive per symbol than load-time symbol resolution.

4.13    Shared Library Restrictions

The use of shared libraries is subject to the following restrictions: