Search code examples
c++shared-librariesstatic-libraries

Linking to shared and static libraries with c++ on a Linux system


I am messing around with a test project, lets call it mytest, it has a .cpp and a .h file, the contents are not really important - imagine it contains a few simple hello_world() type functions...

So, I was making a generic makefile to compile this into the various library outputs where an ls -l on my output folder gives:

libmytest.a
libmytest.so -> libmytest.so.1.0
libmytest.so.1 -> libmytest.so.1.0
libmytest.so.1.0

All good so far, my shared / static libraries are created.

Now I have a make install target in my make file, which basically copies the header to /usr/local/include and all of these library files to /usr/local/lib

Then I made another test cpp file called usertest.cpp (sorry for the not-very-imaginative/descriptive names), which links to the library files.

I compiled in various ways:

  1. g++ -Wall -Werror -I. -lmytest
  2. g++ -Wall -Werror -I. -lmytest -static

Then I deleted the libmytest.so* files so I only had the libmytest.a library file in /usr/local/lib Then I did the same test:

  1. g++ -Wall -Werror -I. -lmytest
  2. g++ -Wall -Werror -I. -lmytest -static

Finally I deleted the libmytest.a file and copied back the .so files so I only had the libmytest.so* library files in /usr/local/lib Then I did the same test:

  1. g++ -Wall -Werror -I. -lmytest
  2. g++ -Wall -Werror -I. -lmytest -static

The file size results(in bytes) are:

1. 7736        - Makes sense, all libs dynamically linked
2. 19674488    - Makes sense, all libs statically linked
3. 64908       - hmm... not really sure why
4. 19674488    - Makes sense, same as 2.
5. 7736        - Makes sense, same as 1.
6. failed      - Makes sense, no .so files!

I have three files sizes, the small (7736) is fully dynamically linked. The large is statically linked.... what is this medium one (64908)? So I have questions:

  • for 1. I assume the system looks for .so libraries first and .a libraries second?
  • For 3. What happened here? - is it dynamically linking the system libs but when it sees my .a lib it dynamically links it?

Note all outputs run fine and call functions from the library.


Solution

  • For 1. I assume the system looks for .so libraries first and .a libraries second?

    That's roughly right, but read on.

    For 3. What happened here? - is it dynamically linking the system libs but when it sees my .a lib it dynamically links it?

    A static library cannot be dynamically linked: it is statically linked. The shared ( = dynamic) system libraries are linked, assuming that the system libraries that the linker finds and prefers are in fact shared libraries.

    By default, the linkage option -lmytest directs the linker to search for an input file called libmytest.so (shared library) or libmytest.a (static library), first in the search directories you have specified in the commandline with the -Ldirname option, in the order specified, and then in its default search directories, in the configured order. It stops searching when it finds either of those files in one of those directories. If it finds both of them in the same directory then it selects the shared library, libmytest.so. The selected file, if any, is input to the linkage. If the search is unsuccessful the linker gives an error: cannot find -lmytest.

    This default behaviour can be changed by the option -static. If it appears anywhere in the commandline, the linker ignores all shared libraries: then -lmytest can only be satisfied by finding libmytest.a, and static system libraries must also be found.

    /usr/local/lib is one of the linker's default search directories. So when you execute:

    g++ -Wall -Werror -I. -lmytest
    

    in the scenario (3) where, /usr/local/lib/libmytest.a is found by the linker and /usr/local/lib/libmytest.so is not, libmytest.a satisfies -lmytest and is input to the linkage. The linker's default preference for shared libraries is unaffected.

    The contribution that the linkage of libmytest.a makes to the size of the executable is not obvious.

    A static library - quite unlike a shared library - is not an ELF binary that the linker has produced. It is ar archive of object files, produced by ar: it is a bag of files just that happen to be object files.

    By default, when an ar archive is input to the linker, it looks in the bag to find any object files that provide definitions for any undefined symbol references that have accrued from object files already linked into the output file (program or shared library) when the archive was inspected. If it finds any such object files, it extracts them from the archive and links them into the output file, exactly as if they had been individually listed in the commandline and the archive not mentioned at all. Except as a bag from which object files may be selected, the archive contributes nothing to the linkage.

    Thus, if there are N object files in libmytest.a, inputting that archive to a linkage might contribute between 0 and N object files to the output file, depending on what undefined references into members of that set of object files accrue earlier in the linkage, and which object files provide definitions for those references.

    And even if you know exactly which object files in libmytest.a will be required in your linkage, you cannot conclude that the sum of their sizes will be added to the size of the output file. An object file is partitioned into sections by the compiler, a section being the smallest unit of input and output that the linker recognizes. By default the linker will retain an input section for output only if that section provides the linker's selected definition of some symbol that the linkage must define. If an input section is of no such use, the linker will just discard it. So, even if an object file is linked, the linker might omit redundant sections within it from the output file.

    The behaviour of the -l | --library linker option is documented in 2.1 Command Line Options of the GNU ld manual