Library

From LQWiki
Jump to navigation Jump to search

Overview

If you are a programmer, you will want to re-use code written by others and design your program to be modular. You build up libraries containing re-usable code. Here is a library that contains functions for the KDE graphical user interface:

$ file /usr/lib64/libkdeui.so.5.0.0
/usr/lib64/libkdeui.so.5.0.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped

There are two types of linking to libraries:

  • static linking takes the code from the library and combines it with the main program's code into an executable
  • dynamic linking forms an executable that loads the library code when it is executed.

Dynamic linking has the advantages:

  • if you link against a software with a bug in the library, this bug can be fixed without the need to touch your executable.
  • if more than one executable uses the same shared library, the overall memory consumption is lower
  • the executable needs less disc space

Statically linked executables have the advantages:

  • they do not depend on other libraries in the system that may not yet be installed. (important if you set up Linux from scratch).
  • you can use them in another distribution that might have different libraries installed
  • you can be sure how they will behave - less testing effort

"dynamically linked libraries" are also called "shared libraries" or "shared objects" on Linux. Their extension typically is .so (usually plus some version numbers). On MS Windows the extension is .dll, on Mac OS X, .dylib.

Shared Libraries

Find out dependencies

To find out which shared objects are required by an application, use ldd:

bash$ ldd /usr/X11R6/bin/glxgears
        libGL.so.1 => /usr/X11R6/lib/libGL.so.1 (0x4001a000)
        libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x40080000)
        libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x4008e000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x40168000)
        libm.so.6 => /lib/libm.so.6 (0x4017c000)
        libc.so.6 => /lib/libc.so.6 (0x4019d000)
        libdl.so.2 => /lib/libdl.so.2 (0x402ba000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

ldd first asks the object file what libs it will need to run, then for each of those asks the operating system where it plans on finding them.

soname

Now, a properly built shared object (in ELF format, which is the format GNU/Linux uses) will have a soname built into it. This is a name that is specified at the library's build-time, to GCC's link editor (ld), with the -soname option. You can inspect any sensibly-built library's soname like so:

bash$ objdump -p /usr/X11R6/lib/libGL.so.1.2 | grep SONAME   
 SONAME      libGL.so.1

In this case, libGL.so.1.2's soname is "libGL.so.1". The point of the soname is for the creator of the library to be able to provide version compatibility information to the system. In this case, libGL.so.1.2's soname says that libGL.so.1.2 is compatible with any program that says it needs libGL.so.1.

Programs know what libs they need to link to at runtime -- they know the soname's they want. When an OpenGL program starts up and asks the system for libGL.so.1, the system delivers libGL.so.1.2.

link editing

Note, when you link a program that requires libFoo.so, you specify -lFoo as an option to the link editor on the gcc or g++ command line. What's going on at build-time is:

  • you tell gcc/g++ you want to set things up so your app links to libFoo.so at runtime.
  • after compiling, at link-time, gcc/g++ passes this information to its link editor ld
  • ld pokes around in /lib, /usr/lib, and anywhere else you specified for it to look using the -L gcc/g++ option; and then comes up with, say, libFoo.so.1.22.
    • If you have multiple incompatible libFoo.so's on your system (say, for example libFoo.so.1.22 and libFoo.so.2.0.1), either be sure to use the -L option, or else specify the lib's location explicitly (as in /usr/local/lib/libFoo.so.2.0.1 (note, no -l option is needed here)).
  • Your app links to this specific version of libFoo.so, and has libFoo.so's soname string embedded into itself.

A final note: if you are writing your own shared library, remember to properly use the -soname option (for example, -soname=libFoo.so.2).

ldconfig

Shared libs have symlinks with names differing only by version number:

libGL.so --> libGL.so.1.2
libGL.so.1 --> libGL.so.1.2
libGL.so.1.2 --> ../X11R6/lib/libGL.so.1.2

The user doesn't make these links manually. Rather, the program /sbin/ldconfig takes care of it for us. When you run ldconfig it does for us the following:

  • It searches through /lib, /usr/lib, and all the directories listed in /etc/ld.so.conf looking for shared objects.
  • For all the libs in the directories listed in /etc/ld.so.conf, it takes note of their sonames.
  • It then updates /etc/ld.so.cache with this information.
  • Using ld.so.cache, it creates symlinks: pointing from the soname named symlink to the actual lib. ldconfig only creates one symbolic link that is based on SONAME of a shared library. This is verified with glibc-2.4-31.2 on SUSE Linux Enterprise Desktop 10 (i586) VERSION = 10 and glibc-2.2.4-31.7 on Red Hat Enterprise Linux WS release 2.1 (Tampa).
  • As far as base name symbolic link creation is concerned, that is created by the library creater if the library is meant for program development. The base name shared library symbolic link is only required at the time of program compilation. One can verify this fact. As all of us know that to access sin() and cos() math functions we need to include the math library (passing -lm to gcc/g++). The required math library's shared symbolic link is /usr/lib/libm.so this points to /lib/libm.so.6(this is the SONAME symbolic link of the original math library /lib/libm-2.4.so). Here /lib/libm.so.6 file is created by ldconfig. Now give the following command rpm -qf /usr/lib/libm.so. The out put will be something like this glibc-devel-2.4-31.2 (this is on my machine. You may have different version of glibc-devel name.). The glibc-devel is required for program development.

Loading shared libs at runtime

When you run a program, a series of events happen culminating in the app being loaded into memory as well as any shared objects the program needs which weren't already loaded. Here's a walkthrough of those events:

  • You execute the program.
  • The OS's dynamic linker, ld.so (yes, it's a funny name for an executable), checks the executable to see which shared libs it needs, thus discovering the sonames of the needed libs.
  • With sonames in-hand, ld.so consults ld.so.cache to find out the locations of the shared lib files it needs to load into memory.
  • ld.so loads the required libs, and then the program is allowed to execute.

Of course, if you had 2 incompatible versions of libFoo.so on your system -- say, libFoo.so.1.4 and libFoo.so.2.0.1 -- each would have a different soname: "libFoo.so.1" and "libFoo.so.2". As such, ldconfig and ld.so would be able to tell the difference between the two, and you could have them both installed on your system simultaneously. That is, applications that depend on "libFoo.so" being installed would always get the right one at runtime.

Creating your own libraries

Shared libraries

This is an example how you get your own dynamic linked library and a program that uses it. The only thing you will need to is to install gcc:

cat > main.cpp << EOF
#include <iostream>
#include <dlfcn.h> 

extern "C" void help(); 

int main() 
{
  help();
  return 0;
}
EOF

We have now created your main program. It contains a declaration of the function help(), but no implementation. The program does nothing more than to call the function help(), notably, without knowing about its implementation.

cat > help.cpp << EOF
#include <iostream>

extern "C" void help() 
{
  std::cout << "hello world" << '\n';
}
EOF

We have now created your library. It implements the function help().

gcc help.cpp -o libhelp.so -ldl -shared -fPIC

We have now built your library, help.so

g++ main.cpp -lhelp

Now we have built an executable a.out that uses a library libhelp.so. This looks for me like this:

tweedleburg:~/test # ldd a.out
        linux-vdso.so.1 =>  (0x00007fff6dffe000)
        libhelp.so => not found
        libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fec65a02000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fec657ac000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fec65595000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fec6523c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fec65d0e000)

Of course we cannot get it running:

tweedleburg:~/test # ./a.out
./a.out: error while loading shared libraries: libhelp.so: cannot open shared object file: No such file or directory

Without the correct path to search the libraries:

tweedleburg:~/test # export LD_LIBRARY_PATH=.
tweedleburg:~/test # ./a.out
hello world

Static libraries

For a static library (or "archive"):

ar -r libFooBar.a foo.o bar.o
gcc -o your_app main.o libFooBar.a

See also