The mysterious abilities of the GCC command

Octave Collombel
3 min readFeb 5, 2021

In C, unlike other programming languages than you might be familiar with (like ruby or python), code needs to be compiled, if you’re not familiar with that term, it means transforming Source Code (which is human readable) into machine code (which is able to be executed by a computer).

This is a multi-step translation process, which, on Unix-like operating system, is handled by default by a program called GCC(GNU Compiler Collection), it is an integrated distribution of compilers for several major programming languages, including the one which we are using today.

In this article, I will break down step by step the compilation process of this basic c program:

#include <stdio.h>
/**
* main - This function prints "Hello, World!"
*/
int main(void)
{
printf("Hello, World!\d");
return (0);
}

STEP 1: Preprocessing

In the context of our program, this first step does only really one major thing, and that is to interpret the first line of our file (the one starting with “#”) as a preprocessing directive, more specifically “#include <stdio.h>”, which will make it so a header file called stdio.h will be included in our source file.

Said file is what will defines most of the input/output functions (C has to abstracts all file operations into operations on streams of bytes, which may be “input streams” or “output streams”.) of our C program.

On a more minor note, this step will also remove comments (which is something a computer has no need for, as they are meant for human readers).

Below is a an exemple of how, by passing the “-E” argument to the gcc command, you are able save the output of the preprocessing to a file called “HelloWorldPreprocessed” (the “-o” argument being what allows us to name the output file).

gcc -E HelloWorld.c -o HelloWorldPreprocessed
An excerpt of the end of the “HelloWorldPreprocessed” file

STEP 2: Compilation

This second step is where the compilation process actually begins, taking the preprocessed file and using it as input to to transform our file into assembly code.
As you can see below, this process is accomplished through use of the argument “-S”.

gcc -S HelloWorld.c -o HelloWorldCompilled.s
An excerpt of HelloWorldCompilled.s

STEP 3: Assembly

Now that we have assembly code, it’s only logical we put it through gcc’s assembler, by passing the “-c” argument to the gcc command;

gcc -c HelloWorld.c -o HelloWorldBinary.o

We’ll be able to transform our code into binary, which will be basically unreadable by humans, but very digestible for computers.

An excerpt of HelloWorldBinary.o

PART 4: Linking

Finally, now that our code is fully machine-readable, it’s time to link function calls with their definitions, by that I mean that it’s at this stage that functions like printf() are implemented, and that standard routines that are needed for the program to run are added, turning our file into an executable.
(We don’t need to pass an argument to the gcc command for this step to work)

gcc -o HelloWorldFinal HelloWorld.c
The result of all our hard work

This executable can then be run by simply typing “./HelloWorldFinal”, allowing us to finally print our string in the terminal.

With our objective achieved, I thank you for reading, and bid you farewell.

--

--