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
-static
option to enable full static linking. - use the
-Wl,-Bstatic
,-Wl,-Bdynamic
options 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_s
for dynamic,-lgcc_eh
for 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
/.fini
sections.
However, the version of crt1, crtbegin/crtend is different.
- /usr/lib/aarch64-linux-gnu/
crt1.o
: This object is expected to contain the_start
symbol which takes care of bootstrapping the initial execution of the program. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtbeginT.o
: Used in place ofcrtbegin.o
when 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.o
when generating PIEs. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtbeginS.o
: Used in place ofcrtbegin.o
when generating shared objects/PIEs. - /usr/lib/gcc/aarch64-linux-gnu/11/
crtendS.o
: Used in place ofcrtend.o
when 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