How To Name A Solaris Shared Object |
Ali Bahrami Friday October 15, 2010
In order to add a new shared object to Solaris, you need to know how to name it. As obvious as this sounds, there is a lot of confusion surrounding this subject.
Solaris follows a standard set of rules for shared object naming, and largely serves as a example of how we intend things to work. Unfortunately, some poor examples have also crept into the system over the years, no doubt adding to the confusion.
The Linker and Libraries Guide contains the basic information, but we seem to be missing a concise description of how Solaris shared objects are supposed to be named. Without that, people will end up trying to intuit what they should be doing by looking about and guessing. The occasional misstep is almost inevitable.
I hope this discussion will fill that gap. I will describe the rules we follow under Solaris, and explain the reasoning behind them.
Each shared object is therefore accessible by its fully versioned name, or via a symbolic link that makes it available via a generic non-versioned name. Although libc does not show this, there can be more than one version of a given object. This happens when a shared object is changed in a backward incompatible manner, something that we try very hard to prevent under Solaris. The generic symbolic link always points at the most current version of the object. This is the version that newly built code should use.% ls -alF /lib/libc.* lrwxrwxrwx 1 root root 9 Mar 22 2010 /lib/libc.so -> libc.so.1* -rwxr-xr-x 1 root bin 1721888 Oct 4 10:08 /lib/libc.so.1* % elfdump -d /lib/libc.so.1 | grep SONAME [4] SONAME 0xb8bc libc.so.1
The non-versioned and versioned names serve the differing needs of the link-editor (ld), and runtime linker (ld.so.1):
Having arranged for ld to find the object via its generic name, it is now necessary to ensure that the runtime linker will look for it via its fully versioned name. This does not happen by default:
For example, we saw above that the SONAME for the C runtime library is "libc.so.1". /bin/ls is linked against libc using the ld -lc command line option. Without the SONAME in libc, we'd expect the NEEDED entry to contain "libc.so", but instead we see the desired result:
Despite being linked via the generic name, the runtime linker searches for libc.so.1, and not libc.so, when the program is actually run, as shown by ldd:% elfdump -d /bin/ls | grep libc.so [8] NEEDED 0x60f libc.so.1
To summarize:% ldd /bin/ls | grep libc.so libc.so.1 => /lib/libc.so.1
There are objects in the Solaris system that exist as implementation details, to provide support for the parts of the system that are publically documented and committed. Such objects are subject to unannounced change, or even removal, so the lack of a compilation symlink saves a great deal of trouble. For example, Solaris ships with the following object:
Without a compilation symlink named libavl.so, this object will be ignored by the link-editor when it builds new objects. Your programs will not accidentally find it even if you specify -lavl to ld, because ld will not be able to locate a file named libavl.so. If libavl becomes public and committed someday, a compilation symlink will be added for it.% ls -alF /lib/libavl.so* -rwxr-xr-x 1 root bin 14K Oct 4 10:07 /lib/libavl.so.1*
I mentioned before that Solaris contains some shared objects that do not faithfully follow the rules we are discussing. By far, the most common error is to deliver a compilation symlink with a private object. The mere presence of a compilation symlink should not be taken as evidence that the object is public and safe to use. A public object will have manpages documenting the library and the functions it contains, and those manpages will include an ATTRIBUTES section that details the commitment level of the interfaces it provides.
Although Solaris uses a single version number, our history includes a time when we used more. Those of you who remember SunOS 4.x may recall that those systems had major and minor numbers, as evidenced by the BCP objects still delivered with sparc systems:
Note that there is no compilation symlink we supply these old objects so that customers can continue to run their ancient (now approaching 20 years) SunOS 4.x executables, but we don't want anyone linking new code to them!% ls -alF /usr/4lib/libc.so* -rwxr-xr-x 1 root bin 411820 Jan 22 2005 /usr/4lib/libc.so.1.9* -rwxr-xr-x 1 root bin 411080 Jan 22 2005 /usr/4lib/libc.so.2.9*
Shared objects were first added to SunOS in version 4.0. As with today's system, a change in the major number reflected an incompatible interface change, and when that happened, the older objects would continue to be supplied for the benefit of old executables, and a separate new object would be delivered with the new code. A change in minor number reflected a compatible change, but the runtime linker would print warning messages when you ran an old executable against a newer minor version. This quickly proved to be a bad idea, as it needlessly annoyed users.
We learned two lessons from SunOS 4.x shared objects:
Other bodies of code do utilize additional (minor, micro, etc) version numbers, and you will see them under Solaris in software that originates elsewhere. This is done in order to match name used by the community that produces the software in question, and not necessarily for object versioning. When building such software for Solaris, you must follow a slightly more general version of our rules:
For example:
This GNU object, delivered with Solaris, retains its original version number (5.7). However, it still follows our basic rules. The object has the fully versioned named, the SONAME contains only the major number, and a compilation symlink is supplied.% ls -alF /usr/gnu/lib/libncurses.so* lrwxrwxrwx 1 root root 15 Mar 22 2010 /usr/gnu/lib/libncurses.so -> libncurses.so.5* lrwxrwxrwx 1 root root 17 Mar 22 2010 /usr/gnu/lib/libncurses.so.5 -> libncurses.so.5.7* -rwxr-xr-x 1 root bin 351K Jun 10 11:53 /usr/gnu/lib/libncurses.so.5.7* % elfdump -d /usr/gnu/lib/libncurses.so.5.7 | grep SONAME [2] SONAME 0x27a1 libncurses.so.5
The use of an SONAME that contains only the major version number is done in order to preserve the principle that you should be able to replace an object with a newer version of the same object as long as the major number does not change, and that it will not be necessary to recompile objects linked against such an object in order to run them. To put this in more concrete terms, we expect that libncurses.so.5.7 can be replaced by libncurses.so.5.8, and that any program linked against libncurses.so.5.7 can use libncurses.5.8, without the need to rebuild.
Related to this point, it is worth noting that we now have two symbolic links rather than the single link we use for native Solaris objects, and the purpose of these two links is different, and unrelated:
Many objects are used both via ld, and dlopen(). For such objects, you must follow the rules described above that allow the object to work properly with ld. The advice given in this section is only for objects that will never be linked to via ld.
An object that is not linked to via ld does not have to follow any of the rules we've discussed so far:
For example, the elfedit utility is delivered with a set of runtime loadable support modules:
% ls -alFR /usr/lib/elfedit/ /usr/lib/elfedit/: total 1185 drwxr-xr-x 3 root bin 13 Sep 2 15:09 ./ drwxr-xr-x 169 root bin 2023 Oct 4 10:09 ../ lrwxrwxrwx 1 root root 1 Jun 19 13:52 32 -> ./ lrwxrwxrwx 1 root root 5 Jun 19 13:52 64 -> amd64/ drwxr-xr-x 2 root bin 10 Sep 2 15:09 amd64/ -rwxr-xr-x 1 root bin 62868 Sep 2 15:09 cap.so* -rwxr-xr-x 1 root bin 97948 Sep 2 15:09 dyn.so* -rwxr-xr-x 1 root bin 92412 Sep 2 15:09 ehdr.so* -rwxr-xr-x 1 root bin 56248 Sep 2 15:09 phdr.so* -rwxr-xr-x 1 root bin 65428 Sep 2 15:09 shdr.so* -rwxr-xr-x 1 root bin 41760 Sep 2 15:09 str.so* -rwxr-xr-x 1 root bin 71268 Sep 2 15:09 sym.so* -rwxr-xr-x 1 root bin 47688 Sep 2 15:09 syminfo.so* /usr/lib/elfedit/amd64: total 1447 drwxr-xr-x 2 root bin 10 Sep 2 15:09 ./ drwxr-xr-x 3 root bin 13 Sep 2 15:09 ../ -rwxr-xr-x 1 root bin 90928 Sep 2 15:09 cap.so* -rwxr-xr-x 1 root bin 130992 Sep 2 15:09 dyn.so* -rwxr-xr-x 1 root bin 125064 Sep 2 15:09 ehdr.so* -rwxr-xr-x 1 root bin 78368 Sep 2 15:09 phdr.so* -rwxr-xr-x 1 root bin 83968 Sep 2 15:09 shdr.so* -rwxr-xr-x 1 root bin 54240 Sep 2 15:09 str.so* -rwxr-xr-x 1 root bin 98592 Sep 2 15:09 sym.so* -rwxr-xr-x 1 root bin 70056 Sep 2 15:09 syminfo.so*
Installing them under /usr/lib/elfedit makes it obvious what application they support, while the use of the .so extension shows that they are sharable objects.
These elfedit objects are private modules delivered together with the elfedit utility, and not used by anything else. In addition, elfedit includes a module version in the handshake it completes with each module as the module is loaded. Therefore, adding version numbers to the file names would add no value, and is not done.
[15] New Mapfile Syntax | [17] Solaris 11 |