Statically Linking C and C++ Programs on Linux with gcc

Before statically linking you C and C++ programs, you should be aware of the drawbacks of the static linking especially with glibc. There are some good discussions already: with glibc you’re linking static programs which are not really static and some others here and here.

That said, you can choose to statically link C and C++ programs on Linux, only when you know what you are doing and why. And you have ways to avoid some problems like setting GCONV_PATH, enabling --enable-static-nss for glibc, using other DNS libraries rather than NSS and never use dlopen. You can even use other libc implementation like uClibc or musl libc which are more friendly for static linking. A comparison of C/POSIX standard library implementations for Linux can be found here.

Linux binaries are not portable if they are built in normal way even with full static linking. However, there are many companies that supply portable Linux binaries that run on a wide range of Linux systems of various versions. Static linking is one of the techniques to build portable binaries. For a detailed explanation of common techniques for building portable binaries, I suggest Creating portable Linux binaries which discusses the “secret sauce”.

One more note: this is not problems from the Linux kernel. Linux kernel keeps very good compatibility. Read Linus’ “WE DO NOT BREAK USERSPACE!” arguments here.

You may wonder why I am talking about static linking. My main motivation is to have memory mapping statically determined and controlled by the program. See the /proc/PID/maps of one statically linked program as follows:

00400000-0060c000 r-xp 00000000 fd:00 920962                             /home/kvplus/think/tmp/runtime/vpc
0080b000-00814000 rwxp 0020b000 fd:00 920962                             /home/kvplus/think/tmp/runtime/vpc
00814000-408a5000 rwxp 00000000 00:00 0 
41652000-41675000 rwxp 00000000 00:00 0                                  [heap]
100000000-800000000 rwxp 00000000 00:00 0 
240000000000-250000000000 rwxs 00000000 ca:11 36                         /mnt/kvplus1-data/pmem-kvplus-0-0
250000000000-260000000000 rwxs 00000000 ca:11 37                         /mnt/kvplus1-data/pmem-kvplus-0-1
260000000000-270000000000 rwxs 00000000 ca:11 38                         /mnt/kvplus1-data/pmem-kvplus-0-2
270000000000-280000000000 rwxs 00000000 ca:11 39                         /mnt/kvplus1-data/pmem-kvplus-0-3
280000000000-290000000000 rwxs 00000000 ca:11 40                         /mnt/kvplus1-data/pmem-kvplus-0-4
290000000000-2a0000000000 rwxs 00000000 ca:11 41                         /mnt/kvplus1-data/pmem-kvplus-0-5
2a0000000000-2b0000000000 rwxs 00000000 ca:11 42                         /mnt/kvplus1-data/pmem-kvplus-0-6
2b0000000000-2c0000000000 rwxs 00000000 ca:11 43                         /mnt/kvplus1-data/pmem-kvplus-0-7
2c0000000000-2d0000000000 rwxs 00000000 ca:11 44                         /mnt/kvplus1-data/pmem-kvplus-0-8
2d0000000000-2e0000000000 rwxs 00000000 ca:11 45                         /mnt/kvplus1-data/pmem-kvplus-0-9
2e0000000000-2f0000000000 rwxs 00000000 ca:11 46                         /mnt/kvplus1-data/pmem-kvplus-0-10
2f0000000000-300000000000 rwxs 00000000 ca:11 47                         /mnt/kvplus1-data/pmem-kvplus-0-11
340000000000-350000000000 rwxs 00000000 ca:11 48                         /mnt/kvplus1-data/pmem-kvplus-0-12
350000000000-360000000000 rwxs 00000000 ca:11 49                         /mnt/kvplus1-data/pmem-kvplus-0-13
360000000000-370000000000 rwxs 00000000 ca:11 50                         /mnt/kvplus1-data/pmem-kvplus-0-14
370000000000-380000000000 rwxs 00000000 ca:11 51                         /mnt/kvplus1-data/pmem-kvplus-0-15
380000000000-390000000000 rwxs 00000000 ca:11 52                         /mnt/kvplus1-data/pmem-kvplus-0-16
390000000000-3a0000000000 rwxs 00000000 ca:11 53                         /mnt/kvplus1-data/pmem-kvplus-0-17
3a0000000000-3b0000000000 rwxs 00000000 ca:11 54                         /mnt/kvplus1-data/pmem-kvplus-0-18
3b0000000000-3c0000000000 rwxs 00000000 ca:11 55                         /mnt/kvplus1-data/pmem-kvplus-0-19
3c0000000000-3d0000000000 rwxs 00000000 ca:11 56                         /mnt/kvplus1-data/pmem-kvplus-0-20
3d0000000000-3e0000000000 rwxs 00000000 ca:11 57                         /mnt/kvplus1-data/pmem-kvplus-0-21
3e0000000000-3f0000000000 rwxs 00000000 ca:11 58                         /mnt/kvplus1-data/pmem-kvplus-0-22
3f0000000000-400000000000 rwxs 00000000 ca:11 59                         /mnt/kvplus1-data/pmem-kvplus-0-23
7fd664000000-7fd664021000 rwxp 00000000 00:00 0 
7fd664021000-7fd668000000 ---p 00000000 00:00 0 
7fd66abb1000-7fd66abb2000 ---p 00000000 00:00 0 
7fd66abb2000-7fd66b5b2000 rwxp 00000000 00:00 0 
7fd66b5b2000-7fd66b5b3000 ---p 00000000 00:00 0 
7fd66b5b3000-7fd66efb8000 rwxp 00000000 00:00 0 
7fd66efb8000-7fd66efb9000 ---p 00000000 00:00 0 
7fd66efb9000-7fd66f9b9000 rwxp 00000000 00:00 0 
7fff054ba000-7fff054cf000 rwxp 00000000 00:00 0                          [stack]
7fff055ff000-7fff05600000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

And a non-statically-linked programs /proc/PID/maps is as follows:

00400000-00406000 r-xp 00000000 fd:00 4115                               /bin/sleep
00605000-00606000 rw-p 00005000 fd:00 4115                               /bin/sleep
0173c000-0175d000 rw-p 00000000 00:00 0                                  [heap]
7f53a7dba000-7f53adbeb000 r--p 00000000 fd:00 133506                     /usr/lib/locale/locale-archive
7f53adbeb000-7f53adc02000 r-xp 00000000 fd:00 1873                       /lib64/
7f53adc02000-7f53ade01000 ---p 00017000 fd:00 1873                       /lib64/
7f53ade01000-7f53ade02000 r--p 00016000 fd:00 1873                       /lib64/
7f53ade02000-7f53ade03000 rw-p 00017000 fd:00 1873                       /lib64/
7f53ade03000-7f53ade07000 rw-p 00000000 00:00 0 
7f53ade07000-7f53adf7a000 r-xp 00000000 fd:00 1849                       /lib64/
7f53adf7a000-7f53ae179000 ---p 00173000 fd:00 1849                       /lib64/
7f53ae179000-7f53ae17d000 r--p 00172000 fd:00 1849                       /lib64/
7f53ae17d000-7f53ae17e000 rw-p 00176000 fd:00 1849                       /lib64/
7f53ae17e000-7f53ae183000 rw-p 00000000 00:00 0 
7f53ae183000-7f53ae18a000 r-xp 00000000 fd:00 1877                       /lib64/
7f53ae18a000-7f53ae389000 ---p 00007000 fd:00 1877                       /lib64/
7f53ae389000-7f53ae38a000 r--p 00006000 fd:00 1877                       /lib64/
7f53ae38a000-7f53ae38b000 rw-p 00007000 fd:00 1877                       /lib64/
7f53ae38b000-7f53ae3a9000 r-xp 00000000 fd:00 1842                       /lib64/
7f53ae5a0000-7f53ae5a3000 rw-p 00000000 00:00 0 
7f53ae5a7000-7f53ae5a8000 rw-p 00000000 00:00 0 
7f53ae5a8000-7f53ae5a9000 r--p 0001d000 fd:00 1842                       /lib64/
7f53ae5a9000-7f53ae5aa000 rw-p 0001e000 fd:00 1842                       /lib64/
7f53ae5aa000-7f53ae5ab000 rw-p 00000000 00:00 0 
7fff7fede000-7fff7fef3000 rw-p 00000000 00:00 0                          [stack]
7fff7fffd000-7fff7fffe000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

The mmap from the .so are problems for certain programs.

The following part of this post will introduce how to “statically” link C and C++ programs with gcc on Linux.

Statically link C programs

You should first install the glibc-static package. On Fedora/RHEL/CentOS, you can

# yum install glibc-static

Add the -static as a compilation option to gcc:

$ gcc -static src.c obj.o -o prog

Check whether the compiled binary is statically linked:

$ ldd prog

It should print out:

not a dynamic executable

If you are using some libraries, such as pthread, you should specify these libraries according to their reference order (referrer appears first):

$ gcc -static prog.c -o prog -lpthread

More on the library order in the gcc manual

-l library
Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded.

Statically link C++ programs

First, install the package needed:

# yum install glibc-static libstdc++-static

Under GCC 4.4, you can use the -static option as for C programs:

$ g++ -static

After GCC 4.5, gcc supports the -static-libstdc++ option:

$ gcc -static -static-libstdc++ -static-libgcc

Also note the library order.

After building the program, check whether the program is statically linked as what we do for C programs:

$ ldd cpp-program
not a dynamic executable

Note for CMake: to specify a static library (libssl as an example) in CMake (find more here):

find_library(OPENSSL_LIBRARY libssl.a)

Eric Ma

Eric is a systems guy. Eric is interested in building high-performance and scalable distributed systems and related technologies. The views or opinions expressed here are solely Eric's own and do not necessarily represent those of any third parties.


  1. Great article, solves me a problem trying to compile zabbix agent static (fatal error: ac_nonexistent.h: No such file or directory). Thank you!

  2. With cmake, to add the `-static` link option, you may use this piece of code in the CMakeLists.txt file:


  3. I keep forgetting and remembering linking statically most of the time since I switch between vs and mingw/gnu, but thanks to your explanation finally I can keep tabs on these details. Thanks Eric for these very useful info!

Leave a Reply

Your email address will not be published. Required fields are marked *