Author: Mugabi Siro

Category: ELF Support

Summary:

A tutorial on generating and using static libraries on a GNU/Linux system. The C language is mainly assumed. Development platform used was Ubuntu 12.04 AMD64.

Tags: gnu/linux toolchain gnu linux s/w development elf support libraries

Preliminaries

Refer to ELF Object File Types for a background on ELF object file types supported on a GNU/Linux system.

Static libraries, Overview

A static library file is an archive of concatenated relocatable object files. It features a header that describes the size and location of each relocatable object file member. Each relocatable (.o) corresponds to a C source file (.c) module which, typically, defines one or a few related functions and variables. These archive files conventionally have the .a extension and are generated via the GNU Archiver, ar(1), utility.

In contrast, combining all the C modules into a single source file to generate a monolithic relocatable object file, and then directly linking programs against it - instead of using an archive of separate relocatables - has a number of disadvantages. Notably, any program linked against the monolithic relocatable will always include its entire bloat, regardless of whether the program only references symbols that are contained in a few of the C modules of the monolithic relocatable's single source file. On the other hand, using an archive will result in a final program that only includes the set of member relocatables that define the referenced symbols.

Generating a static library

This is a two step procedure:

  • Generate the relocatable object file members e.g:

    $ gcc -Wall -O2 -c foo1.c foo2.c foo3.c ...
    
  • Generate the archive file, e.g:

    $ ar rcs libfoo.a foo1.o foo2.o foo3.o ...
    

    where:

    • r insert the files listed into the archive, replacing any matching and pre-existing members.
    • c create the archive if it didn't exist
    • s add an, or update the, archive index

Consult ar(1) for more details on usage and available options.

Linking against static libraries

Dependencies

A general rule with gcc(1) (or ld(1)) command line when linking against libraries is: the object file or library which contains definitions of exported symbols must be specified after any other files which reference those symbols.

So, for example, if libfoo.a contains references to symbols defined in libbar.a, then the command line will resemble:

$ gcc -Wall -O2 prog.c libfoo.a libbar.a

Sometimes, circular references may exist between archives such that, say, libbar1.a contains references to symbols defined in libbar2.a, and libbar2.a also contains references to symbols defined in libbar1.a. In this case:

$ gcc -Wall -O2 prog.c libbar1.a libbar2.a libbar1.a

Alternatively:

$ gcc -Wall -O2 prog.c -Wl,--start-group libbar1.a libbar2.a -Wl,--end-group

However, note that this second approach can incur significant build time since the archives are searched repeatedly until all possible references are resolved. See ld(1).

The standard gcc(1) linker options, -L and -l, may also be used accordingly when linking against static libraries e.g.

$ gcc -Wall -O2 prog.c -Wl,--start-group -L . -lbar1 -lbar2 -Wl,--end-group

Building statically linked executables

Static libraries allow the generation of statically linked executables. Generally, shared libraries hold information suitable only for dynamically linked object files and (conventionally) cannot be used for building statically linked executables. Standard GNU/Linux toolchain installations1 include both shared and static library versions e.g:

$ locate libc.so.6
/lib/x86_64-linux-gnu/libc.so.6
/lib32/libc.so.6
/usr/arm-linux-gnueabi/lib/libc.so.6
/usr/arm-linux-gnueabihf/lib/libc.so.6

$ locate libc.a
/usr/arm-linux-gnueabi/lib/libc.a
/usr/arm-linux-gnueabihf/lib/libc.a
/usr/lib/x86_64-linux-gnu/libc.a
/usr/lib32/libc.a

By default, gcc(1) will generate a dynamically linked executable against the shared library versions. To build a statically linked executable, specify the -static switch e.g:

$ gcc -Wall -O2 -static prog.c

$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, ...

An attempt to explicitly link against a shared library version when building a statically linked executable will fail, for instance:

$ gcc -Wall -O2 -static prog.c libfoobar.so
/usr/bin/ld: attempted static link of dynamic object `libfoobar.so'
collect2: ld returned 1 exit status

Recall that shared objects contain code only suitable for use with dynamically linked object files.

As an exercise, perform the following static library and statically linked executable build and test with the libfoo.c, foo.h and fooprog.c sources in the Shared Library Tutorial entry:

$ gcc -Wall -O2 -c libfoo.c

$ ar rcs libfoo.a libfoo.o

$ gcc -Wall -O2 -static fooprog.c libfoo.a

$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked ...

$ ./a.out 
libfoo

Note that it is generally possible to mix static and shared libraries on the gcc(1) command line when building dynamically linked executables:

$ gcc -Wall -O2 fooprog.c libfoo.a  ## no "-static" so "gcc(1)" pulls in "libc.so" shared lib by default

$ file a.out 
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs) ...

$ ./a.out 
libfoo

Resources and Futher Reading

  • http://tldp.org/HOWTO/Program-Library-HOWTO/

Footnotes

1. See GNU/Linux Toolchain Intro [go back].