Jump to content

GCC

From MukeWiki

GNU Compiler Collection

package (rpm) description contents
gcc GNU Compiler Collection (including C support) gcc front ends for C language (cc is symbolic link to gcc)
glibc GNU implementation of C Standard Library libc.so.6 and other standard libraries
gcc-c++ GNU C++ support for GCC g++ front ends for C++ language (c++ exactly to same file as g++)
libstdc++ GNU implementation of C++ Standard Library libstdc++.so.6
gcc-gfortran GNU Fortran support for GCC gfortran front ends for Fortran language
cpp GNU C Preprocessor cpp (don't confuse with C++)
binutils GNU collection of Binary Utilities as (assembler), ar (archives), ld (linker), etc.

Documentation


gcc 5 2015-04 default mode for C is now -std=gnu11 instead of -std=gnu89 gcc [5, 6) gnu11, gnu++98
gcc 6 2016-04 default mode for C++ is now -std=gnu++14 instead of -std=gnu++98 gcc [6, 8) gnu11, gnu++14
gcc 8 2018-05 default mode for C is now -std=gnu17 instead of -std=gnu11 gcc [8, 11) gnu17, gnu++14
gcc 11 2021-04 default mode for C++ is now -std=gnu++17 instead of -std=gnu++14 gcc [11, 15) gnu17, gnu++17
gcc 15 2025-04 default mode for C is now -std=gnu23 instead of -std=gnu17 gcc [15] gnu23, gnu++17

General

C source code file (main)
simple.c
#include <stdio.h>

int main()
{
  printf("Hello\n");
  return 0;
}
C source code files (library + main)
muke.h muke.c program.c
#ifndef MUKE_H
#define MUKE_H

void print_hello(void);

#endif
#include <stdio.h>
#include "muke.h"

void print_hello(void)
{
  printf("Hello from muke\n");
}
#include "muke.h"

int main()
{
  print_hello();
  return 0;
}

You can download all files from here muke_gcc.tar.gz.

When you invoke gcc(1), it normally does preprocessing, compilation, assembly and linking (always in that order). File name suffix determines what kind of compilation is done (only C language for example): .c C source code that must be preprocessed and .h C (or C++) header file to be turned into a precompiled header.

# "all in one" building C program (preprocessing, compilation, assembly and linking)
$ gcc simple.c             # created a.out file

# store the usual "temporary" intermediate files permanently (try with -v option, aka verbose)
$ gcc -save-temps simple.c # created a-simple.i, a-simple.s, a-simple.o and a.out files (in this order)
  1. C source file simple.c is preprocessed to a-simple.i (preprocessed C source code file).
  2. Preprocessed source file a-simple.i is compiled to a-simple.s (assembler source code file).
  3. Assembler source file a-simple.s is assembled to a-simple.o (object file, ELF LSB relocatable).
  4. Object file a-simple.o is linked to a.out (final executable program file, ELF LSB executable, dynamically linked).
# "manually" invoking each step
# try with -v option; print the commands executed to run the stages of compilation.

$ gcc -E simple.c > simple.i   # standard output redirect to simple.i file
# 1. '-E' Stop after the preprocessing stage; do not run the compiler. The output is preprocessed source code.

$ gcc -S simple.c              # created simple.s file
# 2. '-S' Stop after the stage of compilation; do not assemble. The output is an assembler code file.

$ gcc -c simple.c              # created simple.o file
# 3. '-c' Compile or assemble the source files, but do not link. The output is an object file.

$ gcc simple.o                 # created a.out file
# 4.      Object file is linked to final executable program file.
$ file simple.i simple.s simple.o a.out
simple.i: C source, ASCII text
simple.s: assembler source, ASCII text
simple.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
a.out:    ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7160d69f809ef189994bd519e400d1df81b1e1b8, for GNU/Linux 3.2.0, not stripped

Linux tools for analyzing the contents of Executable and Linkable Format (ELF) object files: nm(1), objdump(1) and readelf(1) (all provided by binutils package) or elfutils purely for Linux (written by Ulrich Drepper from RedHat). More info about GNU Binutils. See also The 101 of ELF files on Linux: Understanding and Analysis.

gcc with -x option can specify explicitly the language for the following input files (rather than letting the compiler choose a default based on the file name suffix). See also What is the difference between g++ and gcc?.

gfortran => gcc -x f95 -l gfortran -shared-libgcc program.f   -v
g++      => gcc -x c++ -l stdc++   -shared-libgcc program.cpp -v

Libraries

Object code (.o extension) is created by compiling the source code with a compiler. This is an intermediate form. Executable code (without extension) is created by linking object code and libraries with a linker. It is possible to run gcc so that it performs only compiling, only linking, or both compiling and linking in a single step.

# compile and linking in one step
$ gcc -o program0 program.c muke.c # created program0 file

# compiling and linking in two steps
$ gcc -c program.c muke.c          # created muke.o and program.o files, compiling the source with a compiler
$ gcc -o program1 program.o muke.c # created program1 file, linking object code and libraries with a linker

# files program0 and program1 are absolutely identical, see also #build-id section about comparing programs

In this example program use C Standard Library function printf(3) (in muke.c source file) provided by libc.so shared (object) library. Linux tool ldd(1) print shared object (shared libraries) dependencies.
NOTE ldd can execution of the program (not safe) and therefore never employ ldd on an untrusted executable. A safer alternative when dealing with untrusted executables is $ objdump -p /path/to/program | grep NEEDED. However, that this alternative shows only the direct dependencies of the executable, while ldd shows the entire dependency tree of the executable.
NOTE libtree(1) print shared object dependencies as a tree (provided by libtree-ldd RPM package).

$ ldd program0
    linux-vdso.so.1 (0x00007ffecaf83000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f679241d000)     # function printf(3) provided by libc.so.6
    /lib64/ld-linux-x86-64.so.2 (0x00007f6792614000)
$ objdump -p program0 | grep NEEDED
  NEEDED               libc.so.6

$ file program0
program0: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID, for GNU/Linux 3.2.0, not stripped

Normally when we compile a program, we would have one object file per piece of source code, and then these would be linked into a single executable binary. While this is a perfectly valid way to compile programs, some issues can come into play when compiling large programs, or when reusing functions in multiple programs, which we can manage this with libraries.

We have a choice of using dynamic or static linking when building applications with fully compiled languages. Dynamic libraries are available as shared objects (.so extension) for dynamic linking. They are a form of an executable file. Static libraries are available as archive files (.a extension) for static linking. They contain a group of object files. Static linking means that the library code is copied into your executable file at compile time, while dynamic linking means that the library code is loaded into memory at run time. Static linking makes libraries part of the resulting executable file. Dynamic linking keeps these libraries as separate files. Static linking is not recommended.

# creating static library (archive file libmuke.a) for static linking, not recommended
$ gcc -c muke.c         # created muke.o file
$ ar rcs libmuke.a muke.o   # ar(1)
$ file libmuke.a
libmuke.a: current ar archive
$ ldd libmuke.a
    not a dynamic executable
$ nm libmuke.a              # or try: ar tv libmuke.a

muke.o:
0000000000000000 T print_hello
                 U puts            # NOTE gcc (by default) optimize function printf(3) into puts(3)

# static linking
$ gcc -static -o program2 program.c libmuke.a   # static library libc.a is provided by glibc-static RPM package
$ file program2
program2: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID, for GNU/Linux 3.2.0, not stripped, too many notes (256)
$ ldd program2
    not a dynamic executable
# program2 statically linked with libmuke.a and libc.a, useful only in specific cases
# static archive library (libmuke.a) is just a collection of *.o files which were compiled separately (muke.o)
$ gcc -o program3 program.c libmuke.a
$ file program3
program3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID, for GNU/Linux 3.2.0, not stripped
$ ldd program3
    linux-vdso.so.1 (0x00007fff8b3db000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fd7617c5000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fd7619c8000)
# program3 with libmuke.a which contains object code muke.o and which is dynamically linked with libc.so
# files program3 and program0 (or program1) are absolutely identical
# creating dynamic library (shared object libmuke.so) for dynamic linking
$ gcc -c -fPIC muke.c   # created muke.o (position-independent code) file
$ gcc -shared -o libmuke.so muke.o
$ file libmuke.so
libmuke.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID, not stripped
$ ldd libmuke.so
    linux-vdso.so.1 (0x00007ffeb6a9d000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f9d16c08000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f9d16e10000)

# dynamic linking
$ gcc -o program4 program.c libmuke.so
$ file program4
program4: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID, for GNU/Linux 3.2.0, not stripped
$ ldd program4
    linux-vdso.so.1 (0x00007ffc5a3c3000)
    libmuke.so => not found                              # why 'not found' see below
    libc.so.6 => /lib64/libc.so.6 (0x00007fa32c8b5000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fa32cab8000)

Using a dynamic library

Dynamic libraries are available as standalone executable files, required at both linking time and run time. They stay independent of your application executable file.

# linking with a dynamic (shared) library

$ gcc program.c -lmuke       # -l search the library named muke (libmuke.{so,a}) when linking
usr/bin/ld: cannot find -lmuke: No such file or directory
collect2: error: ld returned 1 exit status
$ gcc program.c -L. -lmuke   # -L add directory . (current) to the list of directories to be searched for -l

gcc recognizes both dynamic and static libraries and (by default) prefers dynamic linking. When the -lmuke option is encountered, gcc will first attempt to locate a shared object (libmuke.so file) containing a dynamically linked version of the muke library, and then look for the archive file (libmuke.a file) containing a static version of the library.

$ ./a.out
./a.out: error while loading shared libraries: libmuke.so: cannot open shared object file: No such file or directory
$ ldd a.out | grep muke
    libmuke.so => not found

Dynamic loader cannot find the shared library (file libmuke.so is not in the standard location). When a program is linked against a dynamic library, the resulting program must always load the library at run time. There are many options for locating (available at run time) the library:

  • using rpath value stored in the executable file
    The rpath is a special value saved as a part of an executable file when it is being linked. Later, when the program is loaded from its executable file, the runtime linker will use the rpath value to locate the library files. More info about rpath.
$ gcc program.c -L. -lmuke -Wl,-rpath=.
#        gcc link option: -Wl,option pass option as an option to the linker (gcc invokes ld linker on the fly)
#                                      do not add a space after the comma in the -Wl,option
#     ld (linker) option:     -rpath add directory . (current) to the runtime library search path
$ ldd a.out | grep muke
    libmuke.so => ./libmuke.so (0x00007f8edab00000)
  • using LD_LIBRARY_PATH environment variable
    If no rpath is found in the program's executable file, the runtime linker will use the LD_LIBRARY_PATH environment variable. This value should represent the path where the shared library objects are located.
$ gcc program.c -L. -lmuke
$ ldd a.out | grep muke
    libmuke.so => not found
$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
$ ldd a.out | grep muke
    libmuke.so => ./libmuke.so (0x00007f5af34af000)

$ unset LD_LIBRARY_PATH
$ ldd a.out | grep muke
    libmuke.so => not found
$ cp libmuke.so /tmp
$ export LD_LIBRARY_PATH=/tmp:$LD_LIBRARY_PATH
$ ldd a.out | grep muke
    libmuke.so => /tmp/libmuke.so (0x00007fbf468d4000)
$ ld --verbose | grep -i SEARCH
SEARCH_DIR("=/usr/x86_64-redhat-linux/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/x86_64-redhat-linux/lib"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");

# libraries recognized by the dynamic linker (file /etc/ld.so.conf and dir /etc/ld.so.conf.d/)
$ ldconfig -v   # or -p

Others

build-id

build-id Unique Identification of Binaries

Each executable or shared library built with gcc is assigned a unique identification 160-bit SHA-1 string, generated as a checksum of selected parts of the binary. This allows two builds of the same program on the same host to always produce consistent build-ids and binary content.

gcc (by default) invokes linker (ld over gcc internal collect2 utility) with --build-id option (try gcc -v to see). Option --build-id request the creation of a .note.gnu.build-id ELF note section, an identifier (by default 160-bit SHA1 hash) that uniquely identifies the output file.

$ file /usr/bin/bash
/usr/bin/bash: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e3b0fdf454b9c1914e86f32d900a6ee5daa185f1, for GNU/Linux 3.2.0, stripped
$ readelf --notes /usr/bin/bash                           # readelf    from binutils
$ eu-readelf --notes='.note.gnu.build-id' /usr/bin/bash   # eu-readelf from elfutis (more featureful)
Note section [ 3] '.note.gnu.build-id' of 36 bytes at offset 0x388:
  Owner          Data size  Type
  GNU                   20  GNU_BUILD_ID
    Build ID: e3b0fdf454b9c1914e86f32d900a6ee5daa185f1
$ gcc -o programAB program.c muke.c
$ gcc -o programBA muke.c program.c 
# file size of programAB and programBA is identical, but files are different (i.a. different build-id)
$ eu-readelf --notes='.note.gnu.build-id' programAB | grep 'Build ID'
    Build ID: 83c61d5f9739752fcfadcf80199a271b919f5b69
$ eu-readelf --notes='.note.gnu.build-id' programBA | grep 'Build ID'
    Build ID: 4eec135d9fe7c70d29d9203bcd9f0855d17edaca
# SHA-1 checksum of the binaries is different, object code parts (program.o and muke.o) in different order

GCC discovery options

discovery options
$ echo 'int main(){return 0;}' | gcc -x c -v -     # -Q

$ gcc -Q --help=common
$ diff <(gcc -Q --help=optimizers) <(gcc -Q --help=optimizers -O2)   # NOTE instead diff try sdiff -s
$ diff <(gcc -Q --help=warnings) <(gcc -Q --help=warnings -Wextra)
preprocessor macros

See also GCC dump preprocessor defines.

$ gcc -E -dM -x c /dev/null   # or to same: gcc -E -dM - < /dev/null   # or: echo | gcc -dM -E -
# is to same as
$ cpp -dM /dev/null           # or to same: cpp -dM < /dev/null        # or: echo | cpp -dM

$ echo '#include <unistd.h>'  | gcc -E -dM -x c - | grep -i POSIX_VERSION
#define _POSIX_VERSION 200809L
$ echo '#include <sqlite3.h>' | gcc -E -dM -x c - | grep -i SQLITE_VERSION
#define SQLITE_VERSION_NUMBER 3045001
#define SQLITE_VERSION "3.45.1"
$ echo '#include <limits.h>'  | gcc -E -dM -x c - | grep -i PATH_MAX
#define PATH_MAX 4096
#define _POSIX_PATH_MAX 256

$ gcc -E -dM -x c               /dev/null | grep -i BYTE_ORDER
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
$ gcc -E -dM -x c               /dev/null | grep -i CHAR_BIT
#define __CHAR_BIT__ 8
$ gcc -E -dM -x c               /dev/null | grep -i STDC_IEC_559   # since C23 deprecated
#define __STDC_IEC_559__ 1           # IEC 60559 floating point arithmetic (IEEE 754 standard)
#define __STDC_IEC_559_COMPLEX__ 1   # IEC 60559 compatible complex arithmetic
$ gcc -E -dM -x c               /dev/null | grep -i STDC_VERSION
#define __STDC_VERSION__ 201710L
$ gcc -E -dM -x c++             /dev/null | grep -i cplusplus
#define __cplusplus 201703L
$ gcc -E -dM -x c++ --std=c++14 /dev/null | grep -i cplusplus
#define __cplusplus 201402L

NOTE GNU gcc uses GNU dialects (extensions) of ISO standards as default standard:

  • gcc default for C code is -std=gnu17. This is GNU dialect of ISO C17 -std=c17.
  • g++ default for C++ code is -std=gnu++17. This is GNU dialect of 2017 ISO C++ standard -std=c++17.
  • gfortran default for Fortran code is -std=gnu. This is GNU Fortran dialect, which specifies a superset of the latest Fortran standard.
dependencies

See also How to print dependencies from llvm / clang (equivalent of gcc -MD)?

$ gcc -M simple.c
$ g++ -M simple.cxx

x86 Options

-march=cpu-type generate instructions for the machine type cpu-type. Specifying -march=cpu-type implies -mtune=cpu-type, except where noted otherwise.

  • cpu-type=x86-64 is generic CPU with 64-bit extensions (on the x86 family of computers).
  • cpu-type=native selects the CPU to generate code for at compilation time by determining the processor type of the compiling machine. Using -march=native enables all instruction subsets supported by the local machine (hence the result might not run on different machines). Using -mtune=native produces code optimized for the local machine under the constraints of the selected instruction set.

On Fedora gcc (by default) configured (compiled) with: --with-tune=generic --with-arch_32=i686 which means
gcc = gcc -march=x86-64 -mtune=generic. For better performance (but running only on current machine) use gcc -march=native.

$ cat /proc/cpuinfo | grep -m1 'model name'
model name	: AMD Ryzen 5 5600G with Radeon Graphics

$ gcc --help=target -Q | grep -E 'march=|mtune='
 -march=                     		x86-64
 -mtune=                     		generic

$ diff <(gcc --help=target -Q) <(gcc --help=target -Q -march=native) | grep -E 'arch|tune'
<   -march=                     		x86-64
>   -march=                     		znver3
<   -mtune=                     		generic
>   -mtune=                     		znver3

pkgconf

pkgconf is a program which helps to configure compiler and linker flags for development libraries. It is similar to pkg-config from freedesktop.org without depending on GLib. On Fedora/RHEL pkgconf(1) fully replaced pkg-config(1).

$ pkgconf --list-all
$ pkgconf --path fuse   # fuse simply as an example
/usr/lib64/pkgconfig/fuse.pc
$ pkgconf --cflags --libs fuse
-I/usr/include/fuse -D_FILE_OFFSET_BITS=64 -lfuse -pthread
$ gcc fuse_example.c $(pkgconf --cflags --libs fuse)

Code Checkers

  • CodeChecker all in one solution (source on GitHub). Not necessary if you use individual commands/programs separately.

Notes

docasne na tomto mieste, neskor presunut na Cpp, resp. C wiki stranku