Chapter 2. Building Code with GCC

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

2.1. Relationship between code forms

Prerequisites

  • Understanding 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 and uses the .a file name extension. Static linking is not recommended. See Section 3.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 type of output(s).

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

2.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

  • C or C++ source code file(s)
  • GCC installed on the system

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.

2.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 it.

To enable 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 improved debuging 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.
  • To include also macro definitions in the debug information, use the -g3 option instead of -g.
  • 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

2.4. Code optimization with GCC

A single program can be transformed into more than one sequence of machine instructions. You can achieve a more optimal result if you allocate more resources to analyzing the code during compilation.

With GCC, you can 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

Optimize to increase code execution speed (the larger the number, the greater the speed).

s

Optimize for file size.

fast

Same as a level 3 setting, plus fast disregards strict standards compliance to allow for additional optimizations.

g

Optimize for debugging experience.

For release builds, use the optimization option -O2.

During development, the -Og option is useful for debugging the program or library in some situations. Because some bugs manifest only with certain optimization levels, 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

2.5. Options for hardening code with GCC

When the compiler transforms source code to object code, it can add various checks to prevent commonly exploited situations and increase security. Choosing the right set of compiler options can help produce more secure programs and libraries, without having to change 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 -fstack-clash-protection -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

Use the following options 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

2.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

  • One or more object file(s)
  • GCC must be installed on the system

Procedure

  1. Change to the directory containing the object code file(s).
  2. Run gcc:

    $ gcc ... objfile.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 after the list of object files.

    Note

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

2.7. Example: Building a C program with GCC

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

Prerequisites

  • You must understand how to use GCC.

Procedure

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

    $ mkdir hello-c
    $ cd hello-c
  2. Create 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!

2.8. Example: Building a C++ program with GCC

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

Prerequisites

  • You must understand the difference between gcc and g++.

Procedure

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

    $ mkdir hello-cpp
    $ cd hello-cpp
  2. Create 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!