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.
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:
- Source files are compiled to object files.
- 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
- C or C++ source code file(s)
- GCC installed on the system
Procedure
- Change to the directory containing the source code file(s).
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 insource.o
.NoteWith C++ source code, replace the
gcc
command withg++
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
- Section 20.1, “Enabling Debugging with Debugging Information”
- Using the GNU Compiler Collection (GCC) — 3.10 Options for Debugging Your Program
- Debugging with GDB — 18.3 Debugging Information in Separate Files
The GCC manual page:
$ man gcc
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.
Level | Description |
---|---|
| Optimize for compilation speed - no code optimization (default) |
| Increasing optimization effort for code execution speed |
| Optimize for resulting file size |
| Level 3 plus disregard for strict standards compliance to allow for additional optimizations |
| 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
- Using GNU Compiler Collection — 3.11 Options That Control Optimization
Linux manual page for GCC:
$ man gcc
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
- Defensive Coding Guide
- Red Hat Developer blog post — Memory Error Detection Using GCC
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
- One or more object files
- GCC is installed on the system
Procedure
- Change to the directory containing the object code files.
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.
NoteWith C++ source code, replace the
gcc
command withg++
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
- Application Compatibility GUIDE
- Knowledge base solution — What gcc versions are available in Red Hat Enterprise Linux?
- Red Hat Developer Toolset User Guide — C++ Compatibility
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
Create a directory
hello-c
and change to its location:$ mkdir hello-c $ cd hello-c
Create a file
hello.c
with the following contents:#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
Compile the code with GCC:
$ gcc -c hello.c
The object file
hello.o
is created.Link an executable file
helloworld
from the object file:$ gcc hello.o -o helloworld
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
andg++
Procedure
Create a directory
hello-cpp
and change to its location:$ mkdir hello-cpp $ cd hello-cpp
Create a file
hello.cpp
with the following contents:#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
Compile the code with
g++
:$ g++ -c hello.cpp
The object file
hello.o
is created.Link an executable file
helloworld
from the object file:$ g++ hello.o -o helloworld
Run the resulting executable file:
$ ./helloworld Hello, World!