2022-01-13
|~5 min read
|869 words
How do you actually compile a C program? What are the steps to go from authoring a program in C to running it?
Steps:
The authoring step is where you actually write your program.
This can be as big or small as you want.
Let’s write a small “Hello, World!”1
// program that prints Hello, World!
#include<stdio.h>
int main()
{
printf("Hello, world!\n");
}
The compilation step is actually an aggregate of several sub-steps.
In one step, we can use the gcc
compiler:
% gcc -Wall hello-world.c -o hello-world
This compilation has two flags:
Wall
which enables warnings about constructiono
which allows us to specify the output file.To learn about other flags, check out the compiler’s manual.
One way to see the intermediate steps is to use the save-temps
flag:
% gcc -save-temps -Wall hello-world.c -o hello-world
Now, instead of just the compiled file, we can see the steps that the compiler goes through.
% gcc -save-temps -Wall hello-world.c -o hello-world
% ls -la
total 200
drwxr-xr-x 9 stephen.weiss staff 288 Jan 13 14:22 .
drwxr-xr-x+ 85 stephen.weiss staff 2720 Jan 13 14:22 ..
-rwxr-xr-x 1 stephen.weiss staff 49424 Jan 13 14:22 hello-world
-rw-r--r-- 1 stephen.weiss staff 3168 Jan 13 14:22 hello-world.bc
-rw-r--r-- 1 stephen.weiss staff 101 Jan 13 14:07 hello-world.c
-rw-r--r-- 1 stephen.weiss staff 28130 Jan 13 14:22 hello-world.i
-rw-r--r-- 1 stephen.weiss staff 760 Jan 13 14:22 hello-world.o
-rw-r--r-- 1 stephen.weiss staff 666 Jan 13 14:22 hello-world.s
-rw-r--r-- 1 stephen.weiss staff 101 Jan 13 14:08 pbcopy
My initial understanding for this process and the details for the steps comes from Vikash Kumar’s write up for Geeks for Geeks and Wendy Leung’s How the Compilation Process Works for C Programs
This is a preparation step. In this step, the compiler looks for directives (indicated by the #
) and removes comments and expands macros / included files. It will also replace symbolic constants defined using the #define
directive with the appropriate value.
In our hello-world.c
, we included the #include
directive.
In our case, we told the compiler to include the stdio
header file. During this step, the compiler will go and find that header file.
If compiled with the -save-temps
flag, the pre-process file is the .i
.2
The next step in the process is the compile step which receives as input the output of the preprocessor step and outputs assembly language.
If compiled with the -save-temps
flag, the compiled file is the .s
.3
Following the compile step is the assemble step. In this step, the compiler converts the assembly code into binary / machine code.
If compiled with the -save-temps
flag, the compiled file is the .o
.4
The final step in the process is the linking. In this step the linker merges all of the output modules into a single module.
If libraries are used, the linker will link the code to the library’s function code.
There are two types of linking: static and dyanmic. In static linking, all referenced code is copied. In dynamic linking, the code is not copied but is instead linked through a reference to the library in the binary output.
Now that we have a compiled C program, we can run it:
% ./hello-world
Hello, world!
This is not the most in-depth article. There’s plenty more to be said about how each of the steps work when it comes to compiling a C program, but this gives me a basis from which to expand. I know the parts and the mechanisms.
1 C programmers may object to this file as it has a return type of an int
but no return statement. It would be more appropriate to do so, for example:
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
return 0;
}
2 The file in full at this point looks like the following:
# 1 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_stdio.h" 1 3 4
# 31 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_stdio.h" 3 4
# 1 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_common.h" 1 3 4
# 32 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_stdio.h" 2 3 4
# 42 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_stdio.h" 3 4
extern int __sprintf_chk (char * restrict, int, size_t,
const char * restrict, ...);
# 52 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_stdio.h" 3 4
extern int __snprintf_chk (char * restrict, size_t, int, size_t,
const char * restrict, ...);
extern int \_\_vsprintf_chk (char _ restrict, int, size_t,
const char _ restrict, va_list);
extern int \_\_vsnprintf_chk (char _ restrict, size_t, int, size_t,
const char _ restrict, va_list);
# 400 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdio.h" 2 3 4
# 3 "hello-world.c" 2
int main()
{
printf("Hello, world!\n");
}
3 The file in full at this point looks like the following:
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 12, 1
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
leaq L_.str(%rip), %rdi
movb $0, %al
callq _printf
xorl %eax, %eax
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "Hello, world!\n"
.subsections_via_symbols
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!