In this tutorial, we will explain what the static linking is, how this affect the size of final binary, and why statically linking with g++ sometimes is pain. By definition, a statically compiled binary is a group of programmer ‘s routines, external functions, and variables which are packed into the final binary executable. The compiler or the linker produces the final object and embeds all the functions and variables and the linking phase.
There are two reasons of using dynamic linking and shared libraries: 1) Avoid creating a huge binary, if all the programs use a standard set of libraries why not having the operating system providing to them 2) Compatibility on operating system and machine dependant characteristics: sometimes the libraries must be implemented based on the architecture or the operating system and using dynamic linking is an easy way to avoid this catch. On the other hand, static linking is the ideal way of distributing one software product, paying of course the cost of larger file size compared with the dynamic linking. A programmer can create a binary that has no dependencies in different library editions. New linker options for g++ 4.5 make this information applicable also in g++.
We will use three other different tools except gcc/g++: the nm that list symbols from object files, the ldd that print shared library dependencies and the size that list section sizes and total size.
For this tutorial we will start by using a simple c test file:
:~/toys/c-tests$ cat test3.c
#include <stdio.h>
#include <stdlib.h>
#define MAX 43
long long unsigned int array[MAX];
int main(int argc, char **argv){
int i;
for (i=0;i<MAX;i++)
array[i]=(rand()%128)*5;
for (i=0;i<MAX;i++)
printf(" %4llu",array[i]);
return 0;
}
The file includes one call to rand and one call to printf function. Let's compile the file without any flag:
:~/toys/c-tests$ gcc test3.c
Now using the nm we can find the undefined functions.
:~/toys/c-tests$ nm -g a.out
00000000004006e8 R _IO_stdin_used
w _Jv_RegisterClasses
0000000000600e30 D __DTOR_END__
0000000000601028 A __bss_start
0000000000601018 D __data_start
0000000000601020 D __dso_handle
w __gmon_start__
0000000000400600 T __libc_csu_fini
0000000000400610 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000601028 A _edata
00000000006011b8 A _end
00000000004006d8 T _fini
0000000000400428 T _init
0000000000400480 T _start
0000000000601060 B array
0000000000601018 W data_start
0000000000400564 T main
U printf@@GLIBC_2.2.5
U rand@@GLIBC_2.2.5
:~/toys/c-tests$
We can see that the functions __libc_start_main (entry point of the program), printf, and rand are undefined. Next we use the ldd to see the shared files dependencies:
:~/toys/c-tests$ ldd a.out
linux-vdso.so.1 => (0x00007fffec5ff000)
libc.so.6 => /lib/libc.so.6 (0x00007f528d2ec000)
/lib64/ld-linux-x86-64.so.2 (0x00007f528d65c000)
Finally using the size tool we see that the size of the executable is actually something more that 2KBytes.
:~/toys/c-tests$ size a.out
text data bss dec hex filename
1363 528 376 2267 8db a.out
:~/toys/c-tests$
Next step is to create a static linking binary. We use information from the GCC linker manual.
:~/toys/c-tests$ gcc -static test3.c
:~/toys/c-tests$ ldd a.out
not a dynamic executable
The printout of the nm is much bigger because of libc liking. Unfortunately no the binary is much bigger around 624KBytes:
:~/toys/c-tests$ size a.out
text data bss dec hex filename
608112 3648 12824 624584 987c8 a.out
To decrease the file size of final binary you can use some linking flags such as -Wl,-dead_strip,-gc-sections after using -ffunction-sections -fdata-sections compiler flags. However, gc-sections is gcc and architecture depentant so use it carefully. Sometimes it does not even run! For example:
:~/toys/c-tests$ gcc -static -ffunction-sections -fdata-sections -Wl,-dead_strip,-gc-sections test3.c
/usr/bin/ld: warning: cannot find entry symbol ad_strip; defaulting to 00000000004001d0
A warning... who cares?
:~/toys/c-tests$ size a.out
text data bss dec hex filename
577481 3392 11016 591889 90811 a.out
Good! We decrease the file size to 577 KBytes!
:~/toys/c-tests$ ./a.out
Segmentation fault
:~/toys/c-tests$
Oups, it seems that we must be more careful with the warnings! Tip: remove the '-dead_strip' flag!
The story of g++ is a bit different. g++ uses also the libstdc++ except of libc, so a simple -static-libgcc or -static is not enough. Fortunately a new option introduced in gcc-4.5, the -static-libstdc++ that solves this problem. You can find more information about this issue in this blog, dated from 2005. Let's make the same experiment with the g++ 4.6.
:~/toys/c-tests$ cat test4.cpp #include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
int main(){
vector<int> coll; // vector container for int
// append elements
for (int i=1; i<=6; ++i) {
coll.push_back(i);
}
// print all elements
for (int i=0; i<coll.size(); ++i) {
cout << coll[i] << ' ';
}
cout << endl;
printf("Test!\n");
}
Let's try again to see if we are missing any symbol:
:~/toys/c-tests$ nm a.out | grep " U "
U _Unwind_Resume
U _ZNSolsEPFRSoS_E
U _ZNSolsEi
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt17__throw_bad_allocv
U _ZSt20__throw_length_errorPKc
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
U _ZdlPv
U _Znwm
U __cxa_atexit
U __cxa_begin_catch
U __cxa_end_catch
U __cxa_rethrow
U __gxx_personality_v0
U __libc_start_main
U memmove
U puts
Many, let's size the size:
:~/toys/c-tests$ size a.out
text data bss dec hex filename
8711 712 304 9727 25ff a.out
8,7 KB is a nice number, but what happens if we statically link the binary? In latest versions of g++ usually only "--static" flag is necessary:
:~/toys/c-tests$ g++-4.6 test4.cpp --static
:~/toys/c-tests$ size a.out
text data bss dec hex filename
1313651 10672 94441 1418764 15a60c a.out
Huge! Lets see if we decrease a bit the size. We are going to use the '-gc-sections' of the linker.
2585
We removed 2585 symbols not bad. Lets calculate the size of the binary:
text data bss dec hex filename
1094767 10392 85673 1190832 122bb0 a.out
We saved 219 KB. It is not munch, but it is the best that we can do.