Shared Objects
Shared objects are one form of output created by the link-editor and are generated by specifying the -G option. In the following example, the shared object libfoo.so.1 is generated from the input file foo.c.
$ cc -o libfoo.so.1 -G -K pic foo.c |
A shared object is an indivisible unit generated from one or more relocatable objects. Shared objects can be bound with dynamic executables to form a runable process. As their name implies, shared objects can be shared by more than one application. Because of this potentially far-reaching effect, this chapter describes this form of link-editor output in greater depth than has been covered in previous chapters.
For a shared object to be bound to a dynamic executable or another shared object, it must first be available to the link-edit of the required output file. During this link-edit, any input shared objects are interpreted as if they had been added to the logical address space of the output file being produced. All the functionality of the shared object is made available to the output file.
These shared objects become dependencies of this output file. A small amount of bookkeeping information is maintained within the output file to describe these dependencies. The runtime linker interprets this information and completes the processing of these shared objects as part of creating a runable process.
The following sections expand upon the use of shared objects within the compilation and runtime environments. These environments are introduced in "Runtime Linking".
Naming Conventions
Neither the link-editor nor the runtime linker interprets any file by virtue of its file name. All files are inspected to determine their ELF type (see "ELF Header"). This information enables the link-editor to deduce the processing requirements of the file. However, shared objects usually follow one of two naming conventions, depending on whether they are being used as part of the compilation environment or the runtime environment.
When used as part of the compilation environment, shared objects are read and processed by the link-editor. Although these shared objects can be specified by explicit file names as part of the command passed to the link-editor, the -l option is usually used to take advantage of the link-editor's library search capabilities. See "Shared Object Processing".
A shared object applicable to this link-editor processing should be designated with the prefix lib and the suffix .so. For example, /usr/lib/libc.so is the shared object representation of the standard C library made available to the compilation environment. By convention, 64-bit shared objects are placed in a subdirectory of the lib directory called 64. For example, the 64-bit counterpart of /usr/lib/libc.so.1, is /usr/lib/64/libc.so.1.
When used as part of the runtime environment, shared objects are read and processed by the runtime linker. To allow for change in the exported interface of the shared object over a series of software releases, provide the shared object as a versioned file name.
A versioned file name commonly takes the form of a .so suffix followed by a version number. For example, /usr/lib/libc.so.1 is the shared object representation of version one of the standard C library made available to the runtime environment.
If a shared object is never intended for use within a compilation environment, its name might drop the conventional lib prefix. Examples of shared objects that fall into this category are those used solely with dlopen(3DL). A suffix of .so is still recommended to indicate the actual file type, and a version number is strongly recommended to provide for the correct binding of the shared object across a series of software releases. Chapter 5, Application Binary Interfaces and Versioning describes versioning in more detail.
Note - The shared object name used in a dlopen(3DL) is usually represented as a simple file name, those with no `/' in the name. The runtime linker can then use a set of rules to locate the actual file. See "Loading Additional Objects" for more details.
Recording a Shared Object Name
The recording of a dependency in a dynamic executable or shared object will, by default, be the file name of the associated shared object as it is referenced by the link-editor. For example, the following dynamic executables, built against the same shared object libfoo.so, result in different interpretations of the same dependency:
$ cc -o ../tmp/libfoo.so -G foo.o $ cc -o prog main.o -L../tmp -lfoo $ dump -Lv prog | grep NEEDED [1] NEEDED libfoo.so $ cc -o prog main.o ../tmp/libfoo.so $ dump -Lv prog | grep NEEDED [1] NEEDED ../tmp/libfoo.so $ cc -o prog main.o /usr/tmp/libfoo.so $ dump -Lv prog | grep NEEDED [1] NEEDED /usr/tmp/libfoo.so |
As these examples show, this mechanism of recording dependencies can result in inconsistencies due to different compilation techniques. Also, the location of a shared object as referenced during the link-edit might differ from the eventual location of the shared object on an installed system. To provide a more consistent means of specifying dependencies, shared objects can record within themselves the file name by which they should be referenced at runtime.
During the link-edit of a shared object, its runtime name can be recorded within the shared object itself by using the -h option. In the following example, the shared object's runtime name libfoo.so.1, is recorded within the file itself. This identification is known as an soname.
$ cc -o ../tmp/libfoo.so -G -K pic -h libfoo.so.1 foo.c |
The following example shows how the soname recording can be displayed using dump(1) and referring to the entry that has the SONAME tag.
$ dump -Lvp ../tmp/libfoo.so ../tmp/libfoo.so: [INDEX] Tag Value [1] SONAME libfoo.so.1 ......... |
When the link-editor processes a shared object that contains an soname, this is the name that is recorded as a dependency within the output file being generated.
If this new version of libfoo.so is used during the creation of the dynamic executable prog from the previous example, all three methods of creating the executable result in the same dependency recording.
$ cc -o prog main.o -L../tmp -lfoo $ dump -Lv prog | grep NEEDED [1] NEEDED libfoo.so.1 $ cc -o prog main.o ../tmp/libfoo.so $ dump -Lv prog | grep NEEDED [1] NEEDED libfoo.so.1 $ cc -o prog main.o /usr/tmp/libfoo.so $ dump -Lv prog | grep NEEDED [1] NEEDED libfoo.so.1 |
In the previous examples, the -h option is used to specify a simple file name, one that has no `/' in the name. This convention enables the runtime linker to use a set of rules to locate the actual file. See "Locating Shared Object Dependencies" for more details.
Inclusion of Shared Objects in Archives
The mechanism of recording an soname within a shared object is essential if the shared object is ever processed from an archive library.
An archive can be built from one or more shared objects and then used to generate a dynamic executable or shared object. Shared objects can be extracted from the archive to satisfy the requirements of the link-edit. Unlike the processing of relocatable objects, which are concatenated to the output file being created, any shared objects extracted from the archive will be recorded as dependencies. See "Archive Processing" for more details on the criteria for archive extraction.
The name of an archive member is constructed by the link-editor and is a concatenation of the archive name and the object within the archive. For example:
$ cc -o libfoo.so.1 -G -K pic foo.c $ ar -r libfoo.a libfoo.so.1 $ cc -o main main.o libfoo.a $ dump -Lv main | grep NEEDED [1] NEEDED libfoo.a(libfoo.so.1) |
Because a file with this concatenated name is unlikely to exist at runtime, providing an soname within the shared object is the only means of generating a meaningful runtime file name for the dependency.
Note - The runtime linker does not extract objects from archives. Therefore, in the above example the required shared object dependencies must be extracted from the archive and made available to the runtime environment.
Recorded Name Conflicts
When shared objects are used to create a dynamic executable or another shared object, the link-editor performs several consistency checks to ensure that any dependency names that will be recorded in the output file are unique.
Conflicts in dependency names can occur if two shared objects used as input files to a link-edit both contain the same soname. For example:
$ cc -o libfoo.so -G -K pic -h libsame.so.1 foo.c $ cc -o libbar.so -G -K pic -h libsame.so.1 bar.c $ cc -o prog main.o -L. -lfoo -lbar ld: fatal: recording name conflict: file `./libfoo.so' and \ file `./libbar.so' provide identical dependency names: libsame.so.1 ld: fatal: File processing errors. No output written to prog |
A similar error condition will occur if the file name of a shared object that does not have a recorded soname matches the soname of another shared object used during the same link-edit.
If the runtime name of a shared object being generated matches one of its dependencies, the link-editor will also report a name conflict. For example:
$ cc -o libbar.so -G -K pic -h libsame.so.1 bar.c -L. -lfoo ld: fatal: recording name conflict: file `./libfoo.so' and \ -h option provide identical dependency names: libsame.so.1 ld: fatal: File processing errors. No output written to libbar.so |
Shared Objects With Dependencies
Shared objects can have their own dependencies. The search rules used by the runtime linker to locate shared object dependencies are covered in "Directories Searched by the Runtime Linker". If a shared object does not reside in the default directory /usr/lib (for 32-bit objects), or /usr/lib/64 (for 64-bit objects), then the runtime linker must explicitly be told where to look. The preferred mechanism of indicating any requirement of this kind is to record a runpath in the object that has the dependencies by using the link-editor's -R option.
In the following example, the shared object libfoo.so has a dependency on libbar.so, which is expected to reside in the directory /home/me/lib at runtime or, failing that, in the default location.
$ cc -o libbar.so -G -K pic bar.c $ cc -o libfoo.so -G -K pic foo.c -R/home/me/lib -L. -lbar $ dump -Lv libfoo.so libfoo.so: **** DYNAMIC SECTION INFORMATION **** .dynamic: [INDEX] Tag Value [1] NEEDED libbar.so [2] RUNPATH /home/me/lib ......... |
The shared object is responsible for specifying any runpath required to locate its dependencies. Any runpath specified in the dynamic executable will only be used to locate the dependencies of the dynamic executable. These runpaths will not be used to locate any dependencies of the shared objects.
The environment variable LD_LIBRARY_PATH has a more global scope. Any path names specified using this variable will be used by the runtime linker to search for any shared object dependencies. Although useful as a temporary mechanism that influences the runtime linker's search path, the use of this environment variable is strongly discouraged in production software. See "Directories Searched by the Runtime Linker" for a more extensive discussion.