Red Hat Training

A Red Hat training course is available for Red Hat Enterprise Linux

Chapter 15. Building Code with GCC

This chapter describes situations where source code must be transformed into executable code.

15.1. Relationship between Code Forms

Prerequisites

  • Understanding of the concepts of compiling and linking

Possible Code Forms

When using the C and C++ languages, there are three forms of code:

  • Source code written in the C or C++ language, present as plain text files.

    The files typically use extensions such as .c, .cc, .cpp, .h, .hpp, .i, .inc. For a complete list of supported extensions and their interpretation, see the gcc manual pages:

    $ man gcc
  • Object code, created by compiling the source code with a compiler. This is an intermediate form.

    The object code files use the .o extension.

  • Executable code, created by linking object code with a linker.

    Linux application executable files do not use any file name extension. Shared object (library) executable files use the .so file name extension.

Note

Library archive files for static linking also exist. This is a variant of object code which uses the .a file name extension. Static linking is not recommended. See Section 16.2, “Static and dynamic linking”.

Handling of Code Forms in GCC

Producing executable code from source code requires two steps, which require different applications or tools. GCC can be used as an intelligent driver for both compilers and linkers. This allows you to use a single command gcc for any of the required actions. GCC automatically selects the actions required (compiling and linking), as well as their sequence:

  1. Source files are compiled to object files.
  2. Object files and libraries are linked (including the previously compiled sources).

It is possible to run GCC such that only step 1 happens, only step 2 happens, or both steps 1 and 2 happen. This is determined by the types of inputs and requested types of output.

Because larger projects require a build system which usually runs GCC separately for each action, it is helpful to always consider compilation and linking as two distinct actions, even if GCC can perform both at once.

Additional Resources

15.2. Compiling Source Files to Object Code

To create object code files from source files and not an executable file immediately, GCC must be instructed to create only object code files as its output. This action represents the basic operation of the build process for larger projects.

Prerequisites

Procedure

  1. Change to the directory containing the source code file(s).
  2. Run gcc with the -c option:

    $ gcc -c source.c another_source.c

    Object files are created, with their file names reflecting the original source code files: source.c results in source.o.

    Note

    With C++ source code, replace the gcc command with g++ for convenient handling of C++ Standard Library dependencies.

Additional Resources

15.3. Enabling Debugging of C and C++ Applications with GCC

Because debugging information is large, it is not included in executable files by default. To enable debugging of your C and C++ applications with it, you must explicitly instruct the compiler to create debugging information.

Enabling the Creation of Debugging Information with GCC

To enable the creation of debugging information with GCC when compiling and linking code, use the -g option:

$ gcc ... -g ...
  • Optimizations performed by the compiler and linker can result in executable code which is hard to relate to the original source code: variables may be optimized out, loops unrolled, operations merged into the surrounding ones etc. This affects debugging negatively. For an improved debugging experience, consider setting the optimization with the -Og option. However, changing the optimization level changes the executable code and may change the actual behaviour so as to remove some bugs.
  • The -fcompare-debug GCC option tests code compiled by GCC with debug information and without debug information. The test passes if the resulting two binary files are identical. This test ensures that executable code is not affected by any debugging options, which further ensures that there are no hidden bugs in the debug code. Note that using the -fcompare-debug option significantly increases compilation time. See the GCC manual page for details about this option.

Additional Resources

15.4. Code Optimization with GCC

A single program can be transformed into more than one sequence of machine instructions. An optimal result can be achieved if more resources are allocated for analysis of the code during compilation.

Code Optimization with GCC

With GCC, it is possible to set the optimization level using the -Olevel option. This option accepts a set of values in place of the level.

LevelDescription

0

Optimize for compilation speed - no code optimization (default)

1, 2, 3

Increasing optimization effort for code execution speed

s

Optimize for resulting file size

fast

Level 3 plus disregard for strict standards compliance to allow for additional optimizations

g

Optimize for debugging experience

For release builds, the optimization option -O2 is recommended.

During development, the -Og option is more useful for debugging the program or library in some situations. Because some bugs manifest only with certain optimization levels, ensure to test the program or library with the release optimization level.

GCC offers a large number of options to enable individual optimizations. For more information, see the following Additional Resources.

Additional Resources

15.5. Hardening Code with GCC

When the compiler transforms source code to object code, it can add various checks to prevent commonly exploited situations and thus increase security. Choosing the right set of compiler options can help produce more secure programs and libraries, without changes to the source code.

Release Version Options

The following list of options is the recommended minimum for developers targeting Red Hat Enterprise Linux:

$ gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -D_FORTIFY_SOURCE=2 ...
  • For programs, add the -fPIE and -pie Position Independent Executable options.
  • For dynamically linked libraries, the mandatory -fPIC (Position Independent Code) option indirectly increases security.

Development Options

The following options are recommended to detect security flaws during development. Use these options in conjunction with the options for the release version:

$ gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...

Additional Resources

15.6. Linking Code to Create Executable Files

Linking is the final step when building a C or C++ application. Linking combines all object files and libraries into an executable file.

Prerequisites

Procedure

  1. Change to the directory containing the object code files.
  2. Run gcc:

    $ gcc ... objectfile.o another_object.o ... -o executable-file

    An executable file named executable-file is created from the supplied object files and libraries.

    To link additional libraries, add the required options before the list of object files. See Chapter 16, Using Libraries with GCC.

    Note

    With C++ source code, replace the gcc command with g++ for convenient handling of C++ Standard Library dependencies.

Additional Resources

15.7. C++ Compatibility of Various Red Hat Products

The Red Hat ecosystem includes several versions of the GCC compiler and linker, provided in Red Hat Enterprise Linux and Red Hat Developer Toolset. The C++ ABI compatibility between these is as follows:

  • The system compiler based on GCC 4.8 and provided directly as part of Red Hat Enterprise Linux 7 supports only compiling and linking the C++98 standard (also known as C++03), and its variant with GNU extensions.
  • Any C++98-compliant binaries or libraries built explicitly with the options -std=c++98 or -std=gnu++98 can be freely mixed, regardless of the version of the compiler used.
  • Using and mixing the C++11 and C++14 language versions is supported only when using compilers from Red Hat Developer Toolset and only when all C++ objects compiled with the respective flag have been built using the same major version of GCC.
  • When linking C++ files built with both Red Hat Developer Toolset and Red Hat Enterprise Linux toolchain, prefer the Red Hat Developer Toolset version of the compiler and linker.
  • The default setting for compilers in Red Hat Enterprise Linux 6 and 7 and Red Hat Developer Toolset up to 4.1 is -std=gnu++98. That is, C++98 with GNU extensions.
  • The default setting for compilers in Red Hat Developer Toolset 6, 6.1, 7, 7.1, 8.0, 8.1, 9.0, 9.1, and 10 is -std=gnu++14. That is, C++14 with GNU extensions.

Additional Resources

15.8. Example: Building a C Program with GCC

This example shows the exact steps to build a sample minimal C program.

Prerequisites

  • Understanding the use of GCC

Steps

  1. Create a directory hello-c and change to its location:

    $ mkdir hello-c
    $ cd hello-c
  2. Create a file hello.c with the following contents:

    #include <stdio.h>
    
    int main() {
      printf("Hello, World!\n");
      return 0;
    }
  3. Compile the code with GCC:

    $ gcc -c hello.c

    The object file hello.o is created.

  4. Link an executable file helloworld from the object file:

    $ gcc hello.o -o helloworld
  5. Run the resulting executable file:

    $ ./helloworld
    Hello, World!

Additional Resources

15.9. Example: Building a C++ Program with GCC

This example shows the exact steps to build a sample minimal C++ program.

Prerequisites

  • Understanding the use of GCC
  • Understanding the difference between gcc and g++

Procedure

  1. Create a directory hello-cpp and change to its location:

    $ mkdir hello-cpp
    $ cd hello-cpp
  2. Create a file hello.cpp with the following contents:

    #include <iostream>
    
    int main() {
      std::cout << "Hello, World!\n";
      return 0;
    }
  3. Compile the code with g++:

    $ g++ -c hello.cpp

    The object file hello.o is created.

  4. Link an executable file helloworld from the object file:

    $ g++ hello.o -o helloworld
  5. Run the resulting executable file:

    $ ./helloworld
    Hello, World!