Linux Shared Libraries
LinuxA shared library is a library that is loaded when a program starts. It can be shared by many different applications. When installed correctly, all new programs will start using it.
Naming shared libraries
Shared libraries have three different names, the soname
, the real name
and the linker name
. The following describes each names purpose:
Soname | Has the prefix 'lib', the name of the library, the extension '.so' and finally the major version number, an example would be: /usr/lib/libmylibrary.so.1 |
---|---|
Real Name | The real name is the full name of the library, it's the same as the soname, but also contains the minor version number, example: /usr/lib/libmylibrary.so.1.0 |
Linker Name | The Linker name is used by the GCC linker to find the library, it's format is the same as the soname\real name, but no version info: /usr/lib/libmylibrary.so |
Where libraries are located
A modern Linux distribution will keep start-up libraries in /lib
, libraries that the distribution and applications depend on will live in /usr/lib
, and all other libraries (self built, unknown etc.) live in /usr/local/lib
.
These directories are referenced in the file /etc/ld.so.conf
, which tells the system where to search for libraries. On larger distributions, this can be quite a lot of libraries, so the library loader creates /etc/ld.so.cache which contains the most widely used. You can add extra libraries to this directory by invoking ldconfig
.
Creating a shared library
To create a shared library, either compile the source into object code, then link – or do it in one complete step:
a) Separate compile\link:
g++ -fPIC -g -c -Wall mylibrary.cpp
g++ -shared -Wl,-soname,libmylibrary.so.1 -o libmylibrary.so.1.0 mylibrary.o
b) All in one: g++ -shared -fPIC -o libmylibrary.so.1.0 -Wl,-soname,libmylibrary.so.1 object.cpp
The -Wl
option tells the compiler that the option -soname is to be passed to the linker, and not the compiler, it’s exactly the same as running the following command:
ld -shared -soname libmylibrary.so.1 -o libmylibrary.so.1.0 mylibrary.o
To verify if the library file has a valid soname, use the following:
objdump -p libmylibrary.so.1.0 | grep SONAME
You should see a SONAME line containing the valid soname. The end result is a library file with the real name of the library, and the SONAME embedded inside (as verified by the previous command).
Testing the library WITHOUT installing
You can make use of the library straight away without actually installing it into the system. This is ideal if you don’t have root privileges to directories such as /usr/lib
and /usr/local/lib
.
Your application needs access to the library at compile time and runtime, the big difference is that at compile time - the linker looks for the LINKER NAME and at runtime, ld (internal library magic thing) looks for the SONAME. Your application also needs access to the header file for the library. Where you keep these is up to you, however I prefer to create /lib
and /include
directories within your applications source tree, and store them in each directory respectively.
Let’s assume the following directory structure:
Application: ~/project/program.cpp
Library: ~/project/lib/libobject.so.1.0
Header: ~/project/include/object.h
Based on the information above, in order to compile program.cpp, we’ll need to ensure the library file is accessible, since the linker is only looking for the LINKER NAME. Within the /lib directory, we would create a symbolic link to our library, with a LINKER NAME:
ln -sf libobject.so.1.0 libobject.so
When the application runs, the system will look for the SONAME, so we best create this:
ln -sf libobject.so.1.0 libobject.so.1
Note
There are far more elegant ways to perform these steps, using ldconfig – however for simplicity and clarity, I've opted to show the manual steps, illustrating what each symbolic link is for.You could also create the soname (using the Wl,-soname flag above) as the LINKER NAME, so the step above would be unnecessary, however I believe it’s the preferred method to keep the names separate.
Now we can return to the application directory and compile, given the library name, and it’s location:
g++ -o program -lobject -L lib program.cpp
The -l
flag tells the linker that we intend to use the ‘liboject’ library (remember ‘lib’ is pre-appended to each library name), and the -L
flag tells it that it will be found in /lib
(without this, the linker would have searched the directories specified in /etc/ld.so.conf
and not found our precious little library).
There’s just one more problem – the system is looking for the libraries SONAME (which we created above), but can’t find it in any of the systems standard locations, we can confirm this by running ldd program
(this ‘SONAME’ above is whatever you specified during the libraries compilation on the Wl,-soname flag).
To fix this, we need to tell our shell to temporarily use the lib directory as a source for lib files:
export LD_LIBRARY_PATH=`pwd`
Now your application will run.
Testing a library in this way is a lot more involved then installing the library. The reason for this is that most tools are configured to search standard directories, non of which we used above. Using the same small ‘program’ example, the following is an overview of installing a library.
Installing the shared library
To install copy the shared library created (libmylibrary.so.1.0) into the lib directory /usr/local/lib
, then run ldconfig to update the cache: (run in the directory /usr/local/lib
)
sudo ldconfig -v
This creates the soname link. You might be wondering why we specified the ‘soname’ in the linking stage above, this is because the ‘soname’ is compiled into the library internally, which is then used as version information for executables. Programs know the ‘soname’ of the library they want to load, and the system uses this information to deliver the correct ‘real name’. Also, ldconfig uses the internal soname to create the symlink (you can confirm this by creating the library without the -soname
option, ldconfig will fail to create the link).