Shared libraries are a technique for placing library functions into a single unit that can be shared by multiple processes at run time. This technique can save both disk space and RAM. This chapter covers the fundamentals of shared libraries. The next chapter covers a number of advanced features of shared libraries.
One way of building a program is simply to compile each of its source files to produce corresponding object files, and then link all of these object files together to produce the executable program, like so:
$cc -g -c prog.c mod1.c mod2.c mod3.c
$cc -g -o prog_nolib prog.o mod1.o mod2.o mod3.o
Linking is actually performed by the separate linker program, ld. When we link a program using the cc (or gcc) command, the compiler invokes ld behind the scenes. On Linux, the linker should always be invoked indirectly via gcc, since gcc ensures that ld is invoked with the correct options and links the program against the correct library files.
In many cases, however, we may have source files that are used by several programs. As a first step toward saving ourselves some work, we could compile these source files just once, and then link them into different executables as required. Although this technique saves us compilation time, it still suffers from the disadvantage that we must name all of the object files during the link phase. Furthermore, our directories may be inconveniently cluttered with a large number of object files.
To get around these problems, we can group a set of object files into a single unit, known as an object library. Object libraries are of two types: static and shared. Shared libraries are the more modern type of object library, and provide several advantages over static libraries, as we describe in Section 41.3.
In the cc command shown above, we used the -g option to include debugging information in the compiled program. In general, it is a good idea to always create programs and libraries that allow debugging. (In earlier times, debugging information was sometimes omitted so that the resulting executable used less disk and RAM, but nowadays disk and RAM are cheap.)
In addition, on some architectures, such as x86-32, the -fomit-frame-pointer option should not be specified because this makes debugging impossible. (On some architectures, such as x86-64, this option is enabled by default since it doesn’t prevent debugging.) For the same reason, executables and libraries should not be stripped of debugging information using strip(1).