An Introduction to Shared Library Creating, Linking and Versioning
In UNIX-like systems, shared libraries are what are called dynamic linked libraries in MS windows. They are usually named as "libXXX.so" where "XXX" is library name. The extension ".so" stands for Shared Object.
First, the ".o" files should be Position-Independent Code (PIC). This is achieved by compiling them with option "-fpic":
[/home/lungangfang/tmp]gcc -c -fpic shared1-1.c
Then, combine all ".o" files we want into one SO file. In fact, it should be something done by ld. But we needn't call ld directly. gcc will do it for us. So the command would be some like:
[/home/lungangfang/tmp]gcc -shared -o libmyshared.so shared1-1.o
Compiling Executables with SO
There are two ways to compile executables with SO.
One is to compile just as if the shared libraries are normal object files.
[/home/lungangfang/tmp]gcc caller.o libmyshared.so
However, a more common way is to use "-l" option to specify libraries to be linked.
gcc caller.o -L. -lmyshared
In above example, ld will search for libmyshared.so in its path list. Since I put libmyshared.so to a non-standard location, I have to use "-L." to add path to libmyshared.so (current path in this example) to path list of ld.
If libmyshared.so is not found, it will search for libmyshared.a. If there are both libmyshared.so and libmyshared.a, we may use option "-static" to enforce static linking.
Now that we have an executable a.out which need libmyshared.so at run time, the problem becomes: where to find SOs at run time? Generally speaking, a linker would search for SOs among following paths:
- Default/trusted directories such as /lib and /usr/lib
ld --verbose | grep -i search
- paths specified in /etc/ld.so.conf (if it exists,
man ldconfigfor more information)
- paths specified by environment variable LD_LIBRARY_PATH
[/home/lungangfang/tmp]echo $LD_LIBRARY_PATH [/home/lungangfang/tmp]./a.out ./a.out: error while loading shared libraries: libmyshared.so: cannot open shared object file: No such file or directory [/home/lungangfang/tmp]export LD_LIBRARY_PATH=/home/lungangfang/tmp/ [/home/lungangfang/tmp]./a.out From SO 1-1 [/home/lungangfang/tmp]
Avoid Version Conflict
So far, we've learn basic usage of SO. But, what if we released several versions of same shared library and they are all being used by some programs?
That involves veresioning of SOs. In general, the procedure is some like:
- When building SOs, Set version (
DT_SONAME) for them;
- Build executables as usual. However, the output executables will be a
little different since the SOs are different: the executables will contain
DT_SONAMEof SOs they linked to when building.
- At run time, executables will be linked to SOs according to major release
version indicated by
I've said shared libraries are usually named as "libXXX.so" for simplicity. But in fact, they can be named as "libXXX.so.maj.min", where "XXX" is library name, "maj" is major release version number, "min" is the subversion number (minor version) of that major release. "maj" and "min" are optional. Following are some valid SO names.
libcap.so libcap.so.1 libcap.so.1.10 libcrypto.so.0.9.7a libattr.so.1.0.1
Building SO with Version Enabled
[/home/lungangfang/tmp]gcc -shared -Wl,-soname,libmyshared.so.2 \ -o libmyshared.so.2.2 shared2-2.o
-Wl means following options should be passed to ld. Please be noted the
options are delimited by comma (",") to distinguish them from other fields.
Put it all in together, the command would do following for us:
Generate an SO file named "libmyshared.so.2.2" and set its major release
number to "libmyshared.so.2" (set
DT_SONAME of libmyshared.so.2.2 to
Organizing SO Files
We have to organize SO files of same library in following way:
- There are SO files named as "libXXX.so.maj.min". Each of them is a release of the library.
- There are SO files named as "libXXX.so.maj". They are usually symbolic links to latest subversion of each major release. Thus, when linking to "libXXX.so.maj", we always get the latest subversion of that major release.
- A SO named as "libXXX.so". That file is usually a symbolic link to latest "libXXX.so.maj" so that we always get latest version of "libXXX" when building executables with "-lXXX" or run executables without version information.
Following is an example:
[/home/lungangfang/tmp]ls -l lib* | cut -b60- libmyshared.so -> libmyshared.so.2 libmyshared.so.1 -> libmyshared.so.1.2 libmyshared.so.1.1 libmyshared.so.1.2 libmyshared.so.2 -> libmyshared.so.2.2 libmyshared.so.2.1 libmyshared.so.2.2
Building Executables with Version Enabled
There is nothing special. You just make sure the SOs you are using contains
version info (
DT_SONAME). Then, the output executables are SO version
/lib64 $ readelf -d libc.so.6 | grep SONAME 0x000000000000000e (SONAME) Library soname: [libc.so.6]
Running Executables with Version Enabled
When running, the executables will be to linked to SOs as usual except that
"libXXX.so.maj" instead of "libXXX.so" is looked for. For example, if you
build a.out with libmyshared.so.1.1, SO file libmyshared.so.1 will be needed
to run a.out because we set
DT_SONAME of libmyshared.so.1.1 to
"libmyshared.so.1" when we building it. Once we upgraded myshared to
libmyshared.so.1.2, we just re-link libmyshared.so.1 to libmyshared.so.1.2.
From then on, a.out will be linked to the new version of myshared at run
- ld manual
- If you meets '.la' and '.lo' files, you may want to have a look at libtool.
Quoted from http://www.gnu.org/software/libtool/ :
GNU libtool is a generic library support script. Libtool hides the complexity of using shared libraries behind a consistent, portable interface.
- There is a more flexible way to use shared libraries. man dlopen, dlerror, dlsym, dlclose etc.
blog comments powered by Disqus