22 March 2007

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.

Creating SO

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.

Running Executables

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 ldconfig for 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:

  1. When building SOs, Set version (DT_SONAME) for them;
  2. Build executables as usual. However, the output executables will be a little different since the SOs are different: the executables will contain the DT_SONAME of SOs they linked to when building.
  3. At run time, executables will be linked to SOs according to major release version indicated by DT_SONAME.

Naming Rule

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 "libmyshared.so.2").

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

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

Further Readings

  • ld manual
  • LD_PRELOAD, _init=(), =_fini()
  • 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