GCC Compilation Quick Tour - static
Previously, we've taken a look at the basic compilation of C programs using GCC. It links dynamically by default.
In this article, we simply add a -static option to GCC to change its default link policy from dynamic to static.
We then go on to make a basic comparison of the processes of dynamic linking and static linking.
Let's start by exploring the installed gcc compiler toolchain. First, let's look at the host machine on which it resides.
$ uname -a
Linux rpi3b-ubuntu 5.15.0-1055-raspi #58-Ubuntu SMP PREEMPT Sat May 4 03:52:40 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
gcc version#
Explore gcc entity(real body):
$ which gcc
/usr/bin/gcc
$ readlink `which gcc`
gcc-11
$ which gcc-11
/usr/bin/gcc-11
$ readlink `which gcc-11`
aarch64-linux-gnu-gcc-11
Show gcc version:
# gcc version
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/aarch64-linux-gnu/11/lto-wrapper
Target: aarch64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.4.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=aarch64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=aarch64-linux-gnu --host=aarch64-linux-gnu --target=aarch64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
Show as/ld version:
$ which as
/usr/bin/as
$ readlink `which as`
aarch64-linux-gnu-as
$ as -v
GNU assembler version 2.38 (aarch64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.38
^C
$ as --version
GNU assembler (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `aarch64-linux-gnu'.
$ which ld
/usr/bin/ld
$ readlink `which ld`
aarch64-linux-gnu-ld
$ ld -v
GNU ld (GNU Binutils for Ubuntu) 2.38
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
gcc -static#
When compiling links with gcc, dynamic linking is used by default. There are two ways to specify static linking:
- use the
-staticoption to enable full static linking. - use the
-Wl,-Bstatic,-Wl,-Bdynamicoptions to switch some libraries to be linked statically.
refer to gcc 全静态链接,Q: linker "-static" flag usage。
GCC Link Options provide some switches:
- -static
- -static-libgcc
- -static-libstdc++
The demo program is as follows.
/* Code 07-01, file name: 0701.c */
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello, Linux!\n");
return 0;
}
Add option -static to gcc to create a static executable:
$ gcc -v 0701.c -static -o b.out
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/aarch64-linux-gnu/11/lto-wrapper
Target: aarch64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.4.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=aarch64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=aarch64-linux-gnu --host=aarch64-linux-gnu --target=aarch64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
COLLECT_GCC_OPTIONS='-v' '-static' '-o' 'b.out' '-mlittle-endian' '-mabi=lp64' '-dumpdir' 'b.out-'
/usr/lib/gcc/aarch64-linux-gnu/11/cc1 -quiet -v -imultiarch aarch64-linux-gnu 0701.c -quiet -dumpdir b.out- -dumpbase 0701.c -dumpbase-ext .c -mlittle-endian -mabi=lp64 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -o /tmp/cc27y9pj.s
GNU C17 (Ubuntu 11.4.0-1ubuntu1~22.04) version 11.4.0 (aarch64-linux-gnu)
compiled by GNU C version 11.4.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP
GGC heuristics: --param ggc-min-expand=91 --param ggc-min-heapsize=115878
ignoring nonexistent directory "/usr/local/include/aarch64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/aarch64-linux-gnu/11/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/aarch64-linux-gnu/11/../../../../aarch64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/aarch64-linux-gnu/11/include
/usr/local/include
/usr/include/aarch64-linux-gnu
/usr/include
End of search list.
GNU C17 (Ubuntu 11.4.0-1ubuntu1~22.04) version 11.4.0 (aarch64-linux-gnu)
compiled by GNU C version 11.4.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP
GGC heuristics: --param ggc-min-expand=91 --param ggc-min-heapsize=115878
Compiler executable checksum: 52ed857e9cd110e5efaa797811afcfbb
COLLECT_GCC_OPTIONS='-v' '-static' '-o' 'b.out' '-mlittle-endian' '-mabi=lp64' '-dumpdir' 'b.out-'
as -v -EL -mabi=lp64 -o /tmp/ccLYABaR.o /tmp/cc27y9pj.s
GNU assembler version 2.38 (aarch64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.38
COMPILER_PATH=/usr/lib/gcc/aarch64-linux-gnu/11/:/usr/lib/gcc/aarch64-linux-gnu/11/:/usr/lib/gcc/aarch64-linux-gnu/:/usr/lib/gcc/aarch64-linux-gnu/11/:/usr/lib/gcc/aarch64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/aarch64-linux-gnu/11/:/usr/lib/gcc/aarch64-linux-gnu/11/../../../aarch64-linux-gnu/:/usr/lib/gcc/aarch64-linux-gnu/11/../../../../lib/:/lib/aarch64-linux-gnu/:/lib/../lib/:/usr/lib/aarch64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/aarch64-linux-gnu/11/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-static' '-o' 'b.out' '-mlittle-endian' '-mabi=lp64' '-dumpdir' 'b.out.'
/usr/lib/gcc/aarch64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/aarch64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/aarch64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccCXXMyi.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --build-id --hash-style=gnu --as-needed -Bstatic -X -EL -maarch64linux --fix-cortex-a53-843419 -z relro -o b.out /usr/lib/gcc/aarch64-linux-gnu/11/../../../aarch64-linux-gnu/crt1.o /usr/lib/gcc/aarch64-linux-gnu/11/../../../aarch64-linux-gnu/crti.o /usr/lib/gcc/aarch64-linux-gnu/11/crtbeginT.o -L/usr/lib/gcc/aarch64-linux-gnu/11 -L/usr/lib/gcc/aarch64-linux-gnu/11/../../../aarch64-linux-gnu -L/usr/lib/gcc/aarch64-linux-gnu/11/../../../../lib -L/lib/aarch64-linux-gnu -L/lib/../lib -L/usr/lib/aarch64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/aarch64-linux-gnu/11/../../.. /tmp/ccLYABaR.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/aarch64-linux-gnu/11/crtend.o /usr/lib/gcc/aarch64-linux-gnu/11/../../../aarch64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-static' '-o' 'b.out' '-mlittle-endian' '-mabi=lp64' '-dumpdir' 'b.out.'
configure#
The configuration of autoconf is almost the same as default dynamic link.
OPTIONS#
Nothing more than the default except an additional manually formulated option -static.
collect2#
/usr/lib/gcc/aarch64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/aarch64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/aarch64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccCXXMyi.res -plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_eh
-plugin-opt=-pass-through=-lc
--build-id --hash-style=gnu --as-needed -Bstatic
-X -EL -maarch64linux --fix-cortex-a53-843419 -z relro -o b.out
/usr/lib/aarch64-linux-gnu/crt1.o
/usr/lib/aarch64-linux-gnu/crti.o
/usr/lib/gcc/aarch64-linux-gnu/11/crtbeginT.o
/tmp/ccLYABaR.o
--start-group -lgcc -lgcc_eh -lc --end-group
/usr/lib/gcc/aarch64-linux-gnu/11/crtend.o
/usr/lib/aarch64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-static' '-o' 'b.out' '-mlittle-endian' '-mabi=lp64' '-dumpdir' 'b.out.'
/usr/lib/gcc/aarch64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/aarch64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/aarch64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccyiqbzK.res -plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lc
-plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_s
--build-id --eh-frame-hdr --hash-style=gnu --as-needed
-dynamic-linker /lib/ld-linux-aarch64.so.1
-X -EL -maarch64linux --fix-cortex-a53-843419 -pie -z now -z relro
/usr/lib/aarch64-linux-gnu/Scrt1.o
/usr/lib/aarch64-linux-gnu/crti.o
/usr/lib/gcc/aarch64-linux-gnu/11/crtbeginS.o
/tmp/ccMWnd3L.o
-lgcc
--push-state --as-needed -lgcc_s --pop-state
-lc
-lgcc
--push-state --as-needed -lgcc_s --pop-state
/usr/lib/gcc/aarch64-linux-gnu/11/crtendS.o
/usr/lib/aarch64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-mlittle-endian' '-mabi=lp64' '-dumpdir' 'a.'
Here are a few of the notable points of difference:
- gcc:
-lgcc_sfor dynamic,-lgcc_ehfor static; - dynamic:
--eh-frame-hdr,-dynamic-linker; static:-Bstatic; - dynamic exclusive:
-pie -z now
CRT object#
Both gcc and gcc -static use the same pair of crti/crtn.
- /usr/lib/aarch64-linux-gnu/
crti.o|crtn.o: Defines the function prologs/epilogs for the.init/.finisections.
However, the version of crt1, crtbegin/crtend is different.
- /usr/lib/aarch64-linux-gnu/
crt1.o: This object is expected to contain the_startsymbol which takes care of bootstrapping the initial execution of the program. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtbeginT.o: Used in place ofcrtbegin.owhen generating static executables. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtend.o: GCC uses this to find the start of the destructors.
- /usr/lib/aarch64-linux-gnu/
Scrt1.o: Used in place ofcrt1.owhen generating PIEs. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtbeginS.o: Used in place ofcrtbegin.owhen generating shared objects/PIEs. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtendS.o: Used in place ofcrtend.owhen generating shared objects/PIEs.
It's obvious that the prefix/suffix capital "S" `stands for "Shared".
Previously, we've explored the CRT object files in GCC Compilation Quick Tour - dynamic.
$ find /usr/lib/ -not \( -path "*/gcc-cross" -prune \) -type f -regextype egrep -iregex ".*crt(begin|end|1|i|n)?(S|T)?\.o"
/usr/lib/gcc/aarch64-linux-gnu/11/crtbegin.o
/usr/lib/gcc/aarch64-linux-gnu/11/crtbeginS.o
/usr/lib/gcc/aarch64-linux-gnu/11/crtendS.o
/usr/lib/gcc/aarch64-linux-gnu/11/crtbeginT.o
/usr/lib/gcc/aarch64-linux-gnu/11/crtend.o
/usr/lib/aarch64-linux-gnu/Mcrt1.o
/usr/lib/aarch64-linux-gnu/Scrt1.o
/usr/lib/aarch64-linux-gnu/crt1.o
/usr/lib/aarch64-linux-gnu/crti.o
/usr/lib/aarch64-linux-gnu/crtn.o
/usr/lib/aarch64-linux-gnu/gcrt1.o
/usr/lib/aarch64-linux-gnu/grcrt1.o
/usr/lib/aarch64-linux-gnu/rcrt1.o
-lgcc_eh -lc#
[-l namespec|--library=namespec]: Add the archive or object file specified by namespec to the list of files to link.
-lgcc/-lgcc_eh: linkgcc-lc: linkglibc
Previously, we've explored the libgcc and glibc libraries in GCC Compilation Quick Tour - dynamic.
$ find /usr/lib/ -not \( -path "*/gcc-cross" -prune \) -type f -regextype egrep -iregex ".*libgcc(_s|_eh)?\..*"
/usr/lib/gcc/aarch64-linux-gnu/11/libgcc_s.so
/usr/lib/gcc/aarch64-linux-gnu/11/libgcc_eh.a
/usr/lib/gcc/aarch64-linux-gnu/11/libgcc.a
/usr/lib/aarch64-linux-gnu/libgcc_s.so.1
$ find /usr/lib/ -not \( -path "*/python3" -prune \) -type f -regextype egrep -iregex ".*libc\..*"
/usr/lib/aarch64-linux-gnu/libc.so.6
/usr/lib/aarch64-linux-gnu/libc.a
/usr/lib/aarch64-linux-gnu/libc.so
The ldd command can print out shared object dependencies, adding -v for verbose details.
When linking dynamically, we can guess from the name -lgcc_s / libgcc_s.so that the suffix _s means "shared". But what does the suffix _eh in libgcc_eh.a mean?
tech-pkg: Re: What does libgcc_eh.a do?
"eh" - sounds like exception handling (C++/Java support) to me.
Why does static-gnulib include -lgcc_eh?
Avoid use of libgcc_s and libgcc_eh when building glibc
Why are libgcc.a and libgcc_eh.a compiled with -fvisibility=hidden? - H.J. Lu - Re
The reason why libgcc_eh.a is split from libgcc.a is that the unwinder really should be just one in the whole process, especially when one had to register shared libraries/binaries with the unwinder it was very important. So, libgcc_eh.a is meant for -static linking only, where you really can't link libgcc_s.so.1, for all other uses of the unwinder you really should link the shared libgcc_s.so.1 instead. Note e.g. glibc internally dlopens libgcc_s.so.1.
Richard Henderson - Re: RFC: Always build libgcc_eh.a
In the --enable-shared configuration we have:
libgcc.a: Support routines, not including EHlibgcc_eh.a: EH(Exception Handling) support routineslibgcc_s.so: Support routines, including EH
In the --disable-shared configuration we have:
libgcc.a: That's all you get, folks - all routines
Restore exclusion of "gcc_eh" from implicit link libraries (!1460) · Merge requests
That's only partially true: Whether libgcc_eh or libgcc_s is used depends on -static-libgcc parameter. libgcc_s should be a shared object and is fine to link then. libgcc_eh on the other hand is the exception handling part split off from libgcc and is always a static library. Linking libgcc_eh into a shared library would be an issue, but it only appears if -static or -static-libgcc is being used.
outcome#
When the link editor processes an archive library, it extracts library members and copies them into the output object file. These statically linked services are available during execution without involving the dynamic linker.
As we give -o to specify output filename for the product, the outcome executable filename is b.out. As per the -static link policy, it will assemble glibc(libc.a) into the final product b.out.
b.out swells macroscopically and enormously in size in contrast to a.out.
$ ls -l .
-rw-rw-r-- 1 pifan pifan 137 5月 27 17:56 0701.c
-rwxrwxr-x 1 pifan pifan 8872 5月 27 21:47 a.out
-rwxrwxr-x 1 pifan pifan 650752 5月 27 22:20 b.out
$ ls -hl .
-rw-rw-r-- 1 pifan pifan 137 5月 27 17:56 0701.c
-rwxrwxr-x 1 pifan pifan 8.7K 5月 28 11:39 a.out
-rwxrwxr-x 1 pifan pifan 636K 5月 27 22:20 b.out