Static linking
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.
Example
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() { help(); 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 help.so or help.o:
# ldd myprogram linux-vdso.so.1 => (0x00007fff34dff000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f784ce16000) libm.so.6 => /lib64/libm.so.6 (0x00007f784cbbf000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f784c9a9000) libc.so.6 => /lib64/libc.so.6 (0x00007f784c649000) /lib64/ld-linux-x86-64.so.2 (0x00007f784d120000) #
Verify it really contains code to output hello world in the executable file:
# strings myprogram /lib64/ld-linux-x86-64.so.2 SuSESuSE libstdc++.so.6 __gmon_start__ _Jv_RegisterClasses _ZNSt8ios_base4InitD1Ev _ZSt4cout _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c _ZNSt8ios_base4InitC1Ev libm.so.6 libgcc_s.so.1 libc.so.6 __cxa_atexit __libc_start_main GLIBC_2.2.5 GLIBCXX_3.4 %z %r %j %b %Z fff. l$ L t$(L |$0H 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.
gcc
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.