6.2 Libraries

In Chapter 4 we discussed how to build, install, and use the GNU C library and its alternatives for application development. Here, we will discuss how to install those same libraries on the target's root filesystem so that they can be used at runtime by the applications we develop. We will not discuss diet libc, because it is mainly used as a static library.

6.2.1 glibc

As I said earlier, the glibc package contains a number of libraries. You can see the entire list of libraries installed during the package's build process by looking at your ${TARGET_PREFIX}/lib directory. This directory contains mainly four types of files:

Actual shared libraries

These files' names are formatted as libLIBRARY_NAME-GLIBC_VERSION.so, where LIBRARY_NAME is the name of the library and GLIBC_VERSION is the version of the glibc package you are using. The name of the math library for glibc 2.2.3 is libm-2.2.3.so.

Major revision version symbolic links

Major revision versions do not follow the same numbering as the actual glibc version. The major revision version for the actual shared C library in glibc 2.2.3, libc-2.2.3.so, is 6. In contrast, the major revision version for libdl-2.2.3.so is 2. The names of the symbolic links for the major revision version are formatted as libLIBRARY_NAME.so.MAJOR_REVISION_VERSION, where MAJOR_REVISION_VERSION is the major revision version of the library. For the actual C library, for instance, the symbolic link is libc.so.6. For libdl, it is libdl.so.2. Once a program has been linked to a library, it will refer to this symbolic link. At startup, the loader will therefore look for this file before loading the program.

Version-independent symbolic links to the major revision version symbolic links

The role of these links is to provide a universal entry for all the programs that need to link with a particular library, regardless of the actual major revision or the version of glibc involved. These symbolic links are typically formatted as libLIBRARY_NAME.so. For example, libm.so points to libm.so.6, which itself points to the actual shared library, libm-2.2.3.so. The only exception to this is libc.so, which, as I said in Chapter 4, is a link script. The version-independent symbolic link is the one used when linking programs.

Static library archives

These archives are used by applications that choose to link statically with a library. The names of these archives are formatted as libLIBRARY_NAME.a. The static archive for libdl, for instance, is libdl.a.

You will also find some other types of files in ${TARGET_PREFIX}/lib, such as crti.o and crt1.o, but you will not need to copy these to your target's root filesystem.

Out of the four types of files described above, we will need only two for each library: the actual shared libraries and the major revision version symbolic links. The two other file types are needed only when linking executables and are not required for the runtime operation of our applications.

In addition to the library files, we will need to copy the dynamic linker and its symbolic link. The dynamic linker itself follows the naming convention of the various glibc libraries, and is usually called ld-GLIBC_VERSION.so. In what is probably one of the most bizarre aspects of the GNU toolchain, however, the name of the symbolic link to the dynamic linker depends on the architecture for which the toolchain has been built. If the toolchain is built for the i386, the ARM, the SuperH, or the m68k, the symbolic link to the dynamic linker is usually called ld-linux.so.MAJOR_REVISION_VERSION. If the toolchain is built for the MIPS or the PowerPC, the symbolic link to the dynamic linker is usually called ld.so.MAJOR_REVISION_VERSION.

Before we actually copy any glibc component to the target's root filesystem, however, we need to select the glibc components required for our applications. Table 6-2 provides the description of all the components in glibc[1] and provides inclusion guidelines for each component. In addition to my guidelines, you will need to evaluate which components your programs need, depending on their linking.

[1] See the glibc manual for a complete description of the facilities provided.

Table 6-2. Library components in glibc and root filesystem inclusion guidelines

Library component

Content

Inclusion guidelines

ld

Dynamic linker.[2]

Compulsory.

libBrokenLocale

Fixup routines to get applications with broken locale features to run. Overrides application defaults through preloading. (Need to use LD_PRELOAD).

Rarely used.

libSegFault

Routines for catching segmentation faults and doing backtraces.

Rarely used.

libanl

Asynchronous name lookup routines.

Rarely used.

libc

Main C library routines.

Compulsory.

libcrypt

Cryptography routines.

Required for most applications involved in authentication.

libdl

Routines for loading shared objects dynamically.

Required for applications that use functions such as dlopen( ).

libm

Math routines.

Required for math functions.

libmemusage

Routines for heap and stack memory profiling.

Rarely used.

libnsl

NIS network services library routines.

Rarely used.

libnss_compat

Name Switch Service (NSS) compatibility routines for NIS.

Loaded automatically by the glibc NSS.[3]

libnss_dns

NSS routines for DNS.

Loaded automatically by the glibc NSS.

libnss_files

NSS routines for file lookups.

Loaded automatically by the glibc NSS.

libnss_hesiod

NSS routines for Hesiod name service.

Loaded automatically by the glibc NSS.

libnss_nis

NSS routines for NIS.

Loaded automatically by the glibc NSS.

libnss_nisplus

NSS routines for NIS plus.

Loaded automatically by the glibc NSS.

libpcprofile

Program counter profiling routines.

Rarely used.

libpthread

Posix 1003.1c threads routines for Linux.

Required for threads programming.

libresolv

Name resolver routines.

Required for name resolution.

librt

Asynchronous I/O routines.

Rarely used.

libthread_db

Thread debugging routines.

Loaded automatically by gdb when debugging threaded applications. Never actually linked to by any application.

libutil

Login routines, part of user accounting database.

Required for terminal connection management.

[2] This library component is actually not a library itself. Instead, ld.so is an executable invoked by the ELF binary format loader to load the dynamically linked libraries into an application's memory space.

[3] See Chapter 4 for details.

Apart from keeping track of which libraries you link your applications with, you can usually use the ldd command to find out the list of dynamic libraries that an application depends on. In a cross-platform development environment, however, your host's ldd command will fail when provided with target binaries. You could still use the cross-platform readelf command we installed in Chapter 4 to identify the dynamic libraries that your application depends on. Here is an example showing how the BusyBox utility's dependencies can be retrieved using readelf:

$ powerpc-linux-readelf -a ${PRJROOT}/rootfs/bin/busybox | \
> grep "Shared library"
 0x00000001 (NEEDED)                     Shared library: [libc.so.0]

Ideally, however, if you installed uClibc, you should use the cross-platform capable ldd-like command installed by uClibc. For our control module target, which is based on a PowerPC board, the command's name is powerpc-uclibc-ldd. This way, you can build the list of libraries your target binaries depend on. Here are the dependencies of the BusyBox utility, for example (one line has been wrapped to fit the page):

$ powerpc-uclibc-ldd ${PRJROOT}/rootfs/bin/busybox
        libc.so.0 => /home/karim/control-project/control-module/tools/uclibc/lib/
            libc.so.0
/lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0

Having determined the library components we need, we can copy them and the relevant symbolic links to the /lib directory of the target's root filesystem. Here is a set of commands that copy the essential glibc components:

$ cd ${TARGET_PREFIX}/lib
$ for file in libc libcrypt libdl libm \
> libpthread libresolv libutil
> do
> cp $file-*.so ${PRJROOT}/rootfs/lib
> cp -d $file.so.[*0-9] ${PRJROOT}/rootfs/lib
> done
$ cp -d ld*.so* ${PRJROOT}/rootfs/lib

The first cp command copies the actual shared libraries, the second one copies the major revision version symbolic links, and the third one copies the dynamic linker and its symbolic link. All three commands are based on the rules outlined earlier in this section regarding the naming conventions of the different files in ${TARGET_PREFIX}/lib. The -d option is used with the second and third cp commands to preserve the symbolic links as-is. Otherwise, the files pointed to by the symbolic links are copied in their entirety.

Of course, you can remove the libraries that are not used by your applications from the list in the set of commands above. If you would rather have the complete set of libraries included in glibc on your root filesystem, use the following commands:

$ cd ${TARGET_PREFIX}/lib
$ cp *-*.so ${PRJROOT}/rootfs/lib
$ cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib
$ cp libSegFault.so libmemusage.so libpcprocfile.so \
> ${PRJROOT}/rootfs/lib

If you have applications that use the glibc NSS, don't forget to copy the libnss_SERVICE libraries you need to your target's root filesystem. libnss_files and libnss_dns are the ones most often used. You will also need to copy the sample nsswitch.conf provided with glibc to your target's /etc directory and customize it to your setup:[4]

[4] Have a look at Linux Network Administrator's Guide (O'Reilly) for details about the customization of the nsswitch.conf file.

$ cp ${PRJROOT}/build-tools/glibc-2.2.1/nss/nsswitch.conf \
> ${PRJROOT}/rootfs/etc

Whether you copy all or part of the glibc libraries, you will notice that some of these libraries are large. To reduce the size of the libraries installed, we can use the cross-platform strip utility we built earlier. Be careful not to strip the original libraries, since you would have to install them all over again. Strip the libraries only after you copy them to the root filesystem:

$ powerpc-linux-strip ${PRJROOT}/rootfs/lib/*.so

On my control module, the ${PRJROOT}/rootfs/lib directory with all the glibc libraries weighs around 10 MB before stripping. By stripping all the libraries, the directory is reduced to 2.5 MB.

The glibc components have now been installed on the target's root filesystem and are ready to be used at runtime by our applications.

6.2.2 uClibc

As with glibc, uClibc contains a number of libraries. You can see the entire list by looking at your ${PREFIX}/uclibc/lib directory. This directory contains the same four different types of files as the glibc directory.

Because uClibc is meant to be a glibc replacement, the names of the uClibc components and their use is identical to the glibc components. Hence, you can use Table 6-2 for uClibc components. Note, however, that not all glibc components are implemented by uClibc. uClibc implements only ld, libc, libcrypt, libdl, libm, libpthread, libresolv, and libutil. Use the same method as described for glibc to identify the uClibc components you will need on your target.

Having determined the list of components we need, we can now copy them and their relevant symbolic links to the /lib directory of our target's root filesystem. The following set of commands copies the essential uClibc components:

$ cd ${PREFIX}/uclibc/lib
$ for file in libuClibc ld-uClibc libc libdl \
> libcrypt libm libresolv libutil
> do
> cp $file-*.so ${PRJROOT}/rootfs/lib
> cp -d $file.so.[*0-9] ${PRJROOT}/rootfs/lib
> done 

The commands are likely to report that two files haven't been found:

cp: libuClibc.so.[*0-9]: No such file or directory
cp: libc-*.so: No such file or directory

This is not a problem, since these files don't exist. The set of commands above is meant to be easy to type in, but you could add conditional statements around the cp commands if you prefer not to see any errors.

As with glibc, you can modify the list of libraries you copy according to your requirements. Note that, in contrast to glibc, you will not save much space by copying only a select few uClibc components. For my control module, for instance, the root filesystem's /lib directory weighs around 300 KB when all the uClibc components are copied. The following commands copy all uClibc's components to your target's root filesystem:

$ cd ${PREFIX}/uclibc/lib
$ cp *-*.so ${PRJROOT}/rootfs/lib
$ cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib

There is no need to strip uClibc components, since they were already stripped by uClibc's own build scripts. You can verify this using the file command.