wiki:Documentation/Tutorial/UsingLibraries

Tutorial 3: Using libraries

This tutorial will guide you how to link programs with libraries in OpenInkpot environment.

Sample program

This tutorial will use the following simple program to demonstrate linking against zlib library:

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%s\n", zlibVersion());
    return 0;
}

Dynamic libraries 101

Symbols from libraries are not automatically resolved in application:

$ gcc -o testzlib testzlib.c
/tmp/ccex2BFJ.o: In function `main':
testzlib.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: ld returned 1 exit status
$

In order to use library application should explicitly link against it. For GNU toolchain it is done by specifying -l<name> during link time:

$ gcc -o testzlib testzlib.c -lz
$ ./testlib
1.2.3.3
$

How does it work?

Linker searches for lib<name>.so (and lib<name>.a which are less interesting) files in search path, and then uses resolves symbols from application to the symbols from library. All libraries mentioned in command line are mareked as dependencies of binary:

$ readelf -a testzlib | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libz.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
$

As we can see, testzlib links libz.so.1 and libc.so.6. libc.so.6 is a C library which is automatically added to all applications unless compiler is instructed to avoid it.

Where does libz.so.1 come from? From the actual library:

$ ls -l /usr/lib/libz.so* 
   0 lrwxrwxrwx 1 root root  15 Aug  9 20:11 /usr/lib/libz.so -> libz.so.1.2.3.3
100K -rw-r--r-- 1 root root 94K Aug  5 01:39 /usr/lib/libz.so.1.2.3.3
$

File libz.so (found by linker) is a symlink to libz.so.1.2.3.3 which declares libz.so.1 SONAME which is then hardwired to destination binary:

$ readelf -a /usr/lib/libz.so.1.2.3.3 | grep SONAME
 0x000000000000000e (SONAME)             Library soname: [libz.so.1]
$

Note that lib<name>.so symlinks are contained in development packages (as they are useless run-time) and lib<name>.so.x.y.z are contained in runtime library packages.

Dynamic libraries during cross-compilation

/lib, /usr/lib and other similar directories contain libraries for the system you're building packages on, and (obviously) not for the device you want to run binaries on. In order to avoid mixing those, cross-compiler uses /usr/$architecture prefix for searching for compilation headers and libraries:

$ ls -l /usr/mipsel-ip-linux-gnu/lib/libz.so* 
lrwxrwxrwx 1 root root    15 Oct 16 21:10 /usr/mipsel-ip-linux-gnu/lib/libz.so -> libz.so.1.2.3.3
-rw-r--r-- 1 root root 89568 Mar  7  2009 /usr/mipsel-ip-linux-gnu/lib/libz.so.1.2.3.3
$ mipsel-ip-linux-gnu-gcc -o testzlib testzlib.c -lz
$ readelf -a testzlib | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libz.so.1]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
$ readelf -a /usr/mipsel-ip-linux-gnu/lib/libz.so.1.2.3.3 | grep SONAME
 0x0000000e (SONAME)                     Library soname: [libz.so.1]
$

Cross-compilation in OpenInkpot

Where do those files in /usr/$architecture come from? From the cross-libraries packages, such as libz1-dev-mipsel-cross and libz1-mipsel-cross, corresponding to libz1-dev and libz1 packages built for target:

$ sudo apt-get install libz1-dev-mipsel-cross libz1-mipsel-cross
...
$ # show the contents
$ dpkg -L libz1-dev-mipsel-cross
/.
/usr
/usr/mipsel-ip-linux-gnu
/usr/mipsel-ip-linux-gnu/include
/usr/mipsel-ip-linux-gnu/include/zconf.h
/usr/mipsel-ip-linux-gnu/include/zlib.h
/usr/mipsel-ip-linux-gnu/include/zlibdefs.h
/usr/mipsel-ip-linux-gnu/lib
/usr/mipsel-ip-linux-gnu/lib/libz.a
/usr/mipsel-ip-linux-gnu/lib/libz.so
/usr/share
/usr/share/doc
/usr/share/doc/libz1-dev-mipsel-cross
/usr/share/doc/libz1-dev-mipsel-cross/README
$ dpkg -L libz1-mipsel-cross
/.
/usr
/usr/mipsel-ip-linux-gnu
/usr/mipsel-ip-linux-gnu/lib
/usr/mipsel-ip-linux-gnu/lib/libz.so.1
/usr/mipsel-ip-linux-gnu/lib/libz.so.1.2.3.3
/usr/share
/usr/share/doc
/usr/share/doc/libz1-mipsel-cross
/usr/share/doc/libz1-mipsel-cross/README
$

Those are built during package uploading in OpenInkpot buildserver. You may easily make cross-libraries from native ones yourself using dpkg-cross tool:

$ dpkg-cross -a mipsel -b libelf0_0.8.13-1_mipsel.deb
Building libelf0-mipsel-cross_0.8.13-1_all.deb
$ dpkg-deb -c libelf0-mipsel-cross_0.8.13-1_all.deb   
drwxr-xr-x root/root         0 2009-11-13 08:42 ./
drwxr-xr-x root/root         0 2009-11-13 08:42 ./usr/
drwxr-xr-x root/root         0 2009-11-13 08:42 ./usr/share/
drwxr-xr-x root/root         0 2009-11-13 08:42 ./usr/share/doc/
drwxr-xr-x root/root         0 2009-11-13 08:42 ./usr/share/doc/libelf0-mipsel-cross/
-rw-r--r-- root/root       264 2009-11-13 08:42 ./usr/share/doc/libelf0-mipsel-cross/README
drwxr-xr-x root/root         0 2009-11-13 08:42 ./usr/mipsel-ip-linux-gnu/
drwxr-xr-x root/root         0 2009-11-13 08:42 ./usr/mipsel-ip-linux-gnu/lib/
-rw-r--r-- root/root     97816 2009-11-12 21:59 ./usr/mipsel-ip-linux-gnu/lib/libelf.so.0.8.13
lrwxrwxrwx root/root         0 2009-11-13 08:42 ./usr/mipsel-ip-linux-gnu/lib/libelf.so.0 -> libelf.so.0.8.13
$

pkg-config

Isn't it burdensome to add all those -lfoo -lbar to command line? Someone already thought about it and invented pkg-config, which provides the uniform way to query library (in broad sense) for the compilation and link flags:

$ pkg-config --cflags --libs glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include  -lglib-2.0  
$

All metadata for native compilation is stored in .pc files in /usr/share/pkgconfig and /usr/lib/pkgconfig. For cross-compilation pkg-config uses /usr/$architecture/lib/pkgconfig directory.

Unfortunately there is no cross-version of pkg-config in OpenInkpot (yet), so you'll need to pass environment variable explicitly:

$ PKG_CONFIG_PATH=/usr/mipsel-ip-linux-gnu/lib/pkgconfig pkg-config --libs xcb
-L/usr/mipsel-ip-linux-gnu/lib -lxcb  
$

dpkg-buildpackage utility does it for you.

Specifying dependencies

During package building in OpenInkpot the only packages available are the ones specified in *-Build-Depends fields in debian/control. As cross-libraries' packages names change from one target architecture to another, special XCS-Cross-Build-Depends field was invented:

XCS-Cross-Build-Depends: libz1-dev

will install libz1-dev-armel-cross when building for armel, libz1-dev-mipsel-cross when building for mipsel etc.

Rule of thumb for specifying dependencies is simple: all build tools go to XCS-Cross-Host-Build-Depends, all target libraries go to XCS-Cross-Build-Depends. Dont forget to add pkg-config to XCS-Cross-Host-Build-Depends` if your package uses it.

Further reading

  • Tutorial 4 will guide you how to build libraries