forked from sorokin/cpp-notes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
compilation.tex
34 lines (25 loc) · 5.15 KB
/
compilation.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
\section{Процесс компиляции программ}
Очень небольшое число программ написаны в одном файле. Исходный код большинства программ разбит на множество файлов. Например ядро Linux версии 3.2 содержит 37626 файлов.
Программы на C++ состоят из файлов двух видов, одни файлы имеют расширение .h, другие --- .cpp. Файлы имеющие расширение .h называются хедерами (англ. header). Для получения исполняемого файла из исходного кода выполняется процесс, называемый компиляция, состоящий из следующих стадий:
\begin{enumerate}
\item Препроцессирование --- применяется к каждому .cpp файлу. В случаях, когда результат этой операции сохраняют на диск, файлу дают расширение .i.
\item Трансляция --- применяется к результату препроцессирования, результатом этой операции является код на ассемблере. Файлы такого типа имеют расширение .s.
\item Ассемблирование --- переводит код на ассемблере в машинный код, такие файлы имеют расширение .o и называются объектными файлами.
\item Линковка --- процесс получающий на вход множество объектных файлов и выдающий на выходе единый исполняемый файл.
\end{enumerate}
В первых компиляторах все эти стадии выполнялись отдельными программами, а результат работы этих стадий выписывался в явном виде. Сейчас, в силу разных соображений, некоторые стадии выполняются вместе, без явного выписывания промежуточного результата. Например, в подавляющем большинстве компиляторов препроцессирование и трансляция делаются вместе, без явного выписывания результата препроцессирования на диск. Это связано с тем, что как правило результат препроцессирования большой и его сериализия/десериализация создает лишние накладные расходы замедляющие компиляцию. Компилятор clang умеет работать в режиме integrated-as (встроенных ассемблер), когда результат трансляции не выписывается в виде ассемблерного текста, а сразу преобразуется в машинный код. Это позволило увеличить скорость компиляции и улучшить сообщения об ошибках при использовании asm-вставок.
Для компиляции программ, используется команда {\bf g++}. На самом деле, программа {\bf g++} не делает ничего сама, она всего лишь вызывает другие программы в правильном порядке. Например, при вызове {\bf g++ helloworld.cpp} выполняются следующие команды:
\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{sh}
# препроцессирование и трансляция
cc1plus -quiet -D_GNU_SOURCE helloworld.cpp -quiet -dumpbase helloworld.cpp -mtune=generic -march=x86-64 -auxbase helloworld -version -o /tmp/ccSpnAlT.s
# ассемблирование
as --64 -o /tmp/ccW7yOj1.o /tmp/ccSpnAlT.s
# линковка
ld --eh-frame-hdr -m elf_x86_64 /lib/crt1.o /lib/crti.o /lib/crtbegin.o -L/lib -dynamic-linker /lib/ld-linux-x86-64.so.2 /tmp/ccW7yOj1.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /lib/crtend.o /lib/crtn.o
\end{minted}
Команда {\bf g++}, вызывающая другие команды, называется драйвером. Как видно внутренние команды получают достаточно много опций, чтобы их вызывать напрямую. К счастью, драйвер позволяет выполнить каждую стадию компиляции отдельно:
\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{sh}
g++ -S -o helloworld.s helloworld.cpp # вызывает cc1plus
g++ -c -o helloworld.o helloworld.s # вызывает as
g++ helloworld.o # вызывает ld
\end{minted}