Static linking

From LQWiki
Jump to navigation Jump to search

Static linking is the process of linking in parts of a static library when generating an object file or executable output. It differs from dynamic linking in that the parts of the library being used are incorporated directly in the executable rather than just a reference to the library, and all references to those parts are resolved during the linking process rather than each time the executable is run.

The main advantage of static linking over dynamic linking is that since the parts are included directly within the executable itself, the computer running the executable doesn't need to have the library installed which can help to reduce dependency hell. However, this ease of use comes at a cost. Firstly, the executable takes up more (often a lot more) space both on the hard drive and in RAM. The program also uses more RAM than a normal one would as the kernel won't allow other processes to share code already loaded into memory. Secondly, if the library needs a security fix, every program that uses it must be rebuilt, as opposed to just updating the library. Under a dynamic linking scheme, if two programs are run which both use the same library, the kernel can, to some extent, allow both processes to access the same copy of the library (so rather than loading a copy of the library for each process, it simply loads one and lets both of them access it). This isn't possible with a statically linked binary (executable) however, since the library is part of the program being run.


Let's say you want a library that defines a function help() that outputs "hello world". First create help.cpp

cat >help.cpp
#include <iostream>

extern "C" void help() 
  std::cout << "hello world" << '\n';

Then create main.cpp which will be the program calling the function help():

cat >main.cpp
#include <iostream>
#include <dlfcn.h> 

extern "C" void help(); 

int main() 
  return 0;

Now compile help.cpp, but do not make it an executable. An attempt to make it an executable will fail because it does not contain a main routine.

# gcc -c help.cpp

Verify your library help.o has been created:

# ls -ltr
total 12
-rw-r--r-- 1 root root   87 Dec 31 11:32 help.cpp
-rw-r--r-- 1 root root  105 Dec 31 11:57 main.cpp
-rw-r--r-- 1 root root 2712 Dec 31 11:57 help.o

Create an archive out of help.o. In a real case you would use more than one .o file:

# ar -r help.a help.o
# ls -ltr
total 16
-rw-r--r-- 1 root root   87 Dec 31 11:32 help.cpp
-rw-r--r-- 1 root root  105 Dec 31 11:57 main.cpp
-rw-r--r-- 1 root root 2712 Dec 31 11:57 help.o
-rw-r--r-- 1 root root 2854 Dec 31 12:00 help.a

Verify main.cpp has an unresolved dependency to a function help:

# g++ main.cpp 
/tmp/ccvIeJnW.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `help'
collect2: ld returned 1 exit status

Put the archive in a place so gcc can find it:

# cp help.a /usr/lib/libhelp.a

Compile and link your program:

# g++ -o myprogram main.cpp -lhelp

Test it:

# ./myprogram 
hello world

Verify it does NOT have a dependency on or help.o:

# ldd myprogram =>  (0x00007fff34dff000) => /usr/lib64/ (0x00007f784ce16000) => /lib64/ (0x00007f784cbbf000) => /lib64/ (0x00007f784c9a9000) => /lib64/ (0x00007f784c649000)
        /lib64/ (0x00007f784d120000)

Verify it really contains code to output hello world in the executable file:

# strings myprogram 
l$ L
hello world

Look at its size so you can compare it with the same example that has been linked dynamically:

# ll myprogram 
-rwxr-xr-x 1 root root 12541 Dec 31 12:03 myprogram

You see it has a size of 12k.


With gcc, libraries are linked with "-l". (Note that the library could be either dynamic, or static.) A static library specified with "-lfoo" will be looked up as "libfoo.a".

The library is searched for in standard directories, plus whatever you specify with "-L". The standard directories are:

  • /usr/local/lib
  • /usr/lib

The order that -l's appear in matters; Symbols are only pulled from -l's that have been requested by an object file before the -l appeared.

example: If foo.o has a symbol that is defined in libbar.a, then:

 g++ foo.o -lbar  # this works
 g++ -lbar foo.o  # this doesn't work

In the one that doesn't work, nothing is pulled from bar, because no symbols have been requested that are found in it, yet.

See also