content/blog/2018-11-28-cpp-compiler.org

0b2895a90c7751ce06e4b26d4db41005ea2f40a9
cleberg.net/content/blog/2018-11-28-cpp-compiler.org rendered · source · history · blame · raw

130 lines · 5323 bytes

  1#+date:        <2018-11-28 Wed 00:00:00>
  2#+title:       C++ Compilation Process: Stepwise Transformation from Source to Executable
  3#+description: Systematic presentation of the C++ compilation phases including preprocessing, compilation, assembly, and linking, with technical insights into each stage’s function.
  4#+slug:        cpp-compiler
  5#+filetags:    :cpp:compilation:programming:
  6
  7* A Brief Introduction
  8
  9[[https://en.wikipedia.org/wiki/C%2B%2B][C++]] is a general-purpose programming language with object-oriented, generic, and
 10functional features in addition to facilities for low-level memory manipulation.
 11
 12The source code, shown in the snippet below, must be compiled before it can be
 13executed. There are many steps and intricacies to the compilation process, and
 14this post was a personal exercise to learn and remember as much information as I
 15can.
 16
 17#+begin_src cpp
 18#include <iostream>
 19
 20int main()
 21{
 22    std::cout << "Hello, world!\n";
 23}
 24#+end_src
 25
 26** Compilation Process
 27
 28*** An Overview
 29
 30Compiling C++ projects is a frustrating task most days. Seemingly nonexistent
 31errors keeping your program from successfully compiling can be annoying
 32(especially since you know you wrote it perfectly the first time, right?).
 33
 34I'm learning more and more about C++ these days and decided to write this
 35concept down so that I can cement it even further in my own head. However, C++
 36is not the only compiled language. Check out [[https://en.wikipedia.org/wiki/Compiled_language][the Wikipedia entry for compiled
 37languages]] for more examples of compiled languages.
 38
 39I'll start with a wonderful, graphical way to conceptualize the C++ compiler.
 40View [[https://web.archive.org/web/20190419035048/http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html][The C++ Compilation Process]] by Kurt MacMahon, an NIU professor, to see the
 41graphic and an explanation. The goal of the compilation process is to take the
 42C++ code and produce a shared library, dynamic library, or an executable file.
 43
 44** Compilation Phases
 45
 46Let's break down the compilation process. There are four major steps to
 47compiling C++ code.
 48
 49*** Step 1
 50
 51The first step is to expand the source code file to meet all dependencies. The
 52C++ preprocessor includes the code from all the header files, such as =#include
 53<iostream>=. Now, what does that mean? The previous example includes the
 54=iostream= header. This tells the computer that you want to use the =iostream=
 55standard library, which contains classes and functions written in the core
 56language. This specific header allows you to manipulate input/output streams.
 57After all this, you'll end up which a temporary file that contains the expanded
 58source code.
 59
 60In the example of the C++ code above, the =iostream= class would be included in
 61the expanded code.
 62
 63*** Step 2
 64
 65After the code is expanded, the compiler comes into play. The compiler takes the
 66C++ code and converts this code into the assembly language, understood by the
 67platform. You can see this in action if you head over to the [[https://godbolt.org][GodBolt Compiler
 68Explorer]], which shows C++ being converted into assembly dynamically.
 69
 70For example, the =Hello, world!= code snippet above compiles into the following
 71assembly code:
 72
 73#+begin_src asm
 74.LC0:
 75        .string "Hello, world!\n"
 76main:
 77        push    rbp
 78        mov     rbp, rsp
 79        mov     esi, OFFSET FLAT:.LC0
 80        mov     edi, OFFSET FLAT:_ZSt4cout
 81        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
 82        mov     eax, 0
 83        pop     rbp
 84        ret
 85__static_initialization_and_destruction_0(int, int):
 86        push    rbp
 87        mov     rbp, rsp
 88        sub     rsp, 16
 89        mov     DWORD PTR [rbp-4], edi
 90        mov     DWORD PTR [rbp-8], esi
 91        cmp     DWORD PTR [rbp-4], 1
 92        jne     .L5
 93        cmp     DWORD PTR [rbp-8], 65535
 94        jne     .L5
 95        mov     edi, OFFSET FLAT:_ZStL8__ioinit
 96        call    std::ios_base::Init::Init() [complete object constructor]
 97        mov     edx, OFFSET FLAT:__dso_handle
 98        mov     esi, OFFSET FLAT:_ZStL8__ioinit
 99        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
100        call    __cxa_atexit
101.L5:
102        nop
103        leave
104        ret
105_GLOBAL__sub_I_main:
106        push    rbp
107        mov     rbp, rsp
108        mov     esi, 65535
109        mov     edi, 1
110        call    __static_initialization_and_destruction_0(int, int)
111        pop     rbp
112        ret
113#+end_src
114
115*** Step 3
116
117Third, the assembly code generated by the compiler is assembled into the object
118code for the platform. Essentially, this is when the compiler takes the assembly
119code and assembles it into machine code in a binary format. After researching
120this online, I figured out that a lot of compilers will allow you to stop
121compilation at this step. This would be useful for compiling each source code
122file separately. This saves time later if a single file changes; only that file
123needs to be recompiled.
124
125*** Step 4
126
127Finally, the object code file generated by the assembler is linked together with
128the object code files for any library functions used to produce a shared
129library, dynamic library, or an executable file. It replaces all references to
130undefined symbols with the correct addresses.