[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


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 discusses the following topics:


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


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 illustrates the difference between the use of archive and shared libraries.

Figure 4-1: Use of Archive and Shared Libraries


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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:


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 to the ld command.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


4.2.2    Search Path of the Loader

Unless otherwise directed, the run-time loader (/sbin/loader) follows the same search path as the linker (ld). 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 the previous section. In addition, you can use the _RLD_ROOT environment variable to alter the search path of the run-time loader. For more information, see the loader(5) reference page.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 (ld) 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 were 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 to determine the behavior of the system regarding unresolved symbols, see Section 4.2.4. The following sequence is followed to resolve 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, executable program A is linked against shared library B and shared library D. Library B is linked against library C as shown in the following diagram:

                                 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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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:

You can control the behavior of the linker by using the following flags to the ld command:

-expect_unresolved pattern
This flag specifies that any unresolved symbols matching pattern are neither displayed nor treated as warnings or errors. This flag can occur multiple times on a link command line. The patterns use shell wildcards (?, *, [, ]) and must be quoted properly to prevent expansion by the shell. See sh(1), csh(1), and ksh(1) for more information.

-warning_unresolved
This flag specifies that all unresolved symbols except those matching the -expect_unresolved pattern produce warning messages. This mode is the default for linking shared libraries.

-error_unresolved
This flag causes the linker to print an error message and return a nonzero error status when a link is completed with unresolved symbols other than those matching the -expect_unresolved pattern. This mode is the default for linking executable images.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flags to the cc command to allow flexibility in determining the search path for a shared library. For example, you can use the -Ldir flag with the cc command to change the search path by adding dir before the default directories, as 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 flag first with no arguments. Then, specify it again with the directory to search, followed by the -l flag 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 be in the /usr/person directory.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flag 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:


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


4.5    Creating Shared Libraries

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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flag tells the linker to resolve symbols using only shared libraries. The -lc flag 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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flag tells the linker to link all the objects from the archive library old.a. The -none flag tells the linker to turn off the -all flag. Note that the -no_archive flag applies to the resolution of the -lc flag but not to old.a (because old.a is explicitly mentioned).


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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, take the following 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).


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 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.

In order 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, you should give each shared library you build a unique quickstart address 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 flag 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 these 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 flag 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 system-wide 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

Of course, 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. You should mention all libraries that are depended upon on the link line. After all libraries are built, you can then build your applications.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


4.7.2    Tracking Down Quickstart Problems Manually

To determine why an executable program is not quickstarting, you can use the fixso utility as 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 quickstartable.

    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 quickstartable.

    Get a list of an executable program's dependencies:

    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 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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flag 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 fixso can be used 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 flag 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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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. Refer to Chapter 5 for more information about using dbx.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 were 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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flags to put the data section in the same 8-megabyte 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 avoid any unauthorized sharing, enter the following in the configuration file:

segmentation 0


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 binarily 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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


4.11.2    Shared Library Versions

You can maintain the backward compatibility of a shared library affected by binarily 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 a binarily 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 "osf.1". 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 version "osf.1" 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 binarily 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 flags used to effect version control of shared libraries.

Table 4-1: Linker Flags that Control Shared Library Versioning

Flag 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 flag, 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 flag is only useful when building a shared library (with -shared).

-exact_version Sets a flag 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 flag 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 flag 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 flag, 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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


4.11.3    Major and Minor Versions Identifiers

Digital UNIX does not distinguish between major and minor versions of shared libraries.

Major versions are used to distinguish incompatible versions of shared libraries. Minor versions typically distinguish different but compatible versions of a library. Minor versions are often used to provide revision-specific identification or to restrict the use of backward-compatible shared libraries.

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

The sequence of library revisions that follows illustrates 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 sequence of library revisions that follows illustrates 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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 a binarily 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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 flag 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 flag 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 illustrated 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 -- --

Presumably libA.so has been linked against a previous version of libcommon.so. At that time the rightmost version identifier of libcommon.so was "v1". libB.so has been linked against the libcommon.so shown here. Because libcommon.so 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 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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 ld flag -exact_match, 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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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 illustrate 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 compatibile libraries, one of which depends on the other.

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


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


4.13    Shared Library Restrictions

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