Из этого доклада вы узнаете, как разбирать различные ошибки сборки C++, как их исправлять и что для этого нужно знать.
Речь пойдет о стадиях сборки программы на C++ и о том, как устроены объектные файлы (на примере формата ELF), о работе компилятора и компоновщика, а также особенностях, связанных со спецификой языка. В заключительной части доклада будут разобраны принципы работы препроцессора C++ и способы его отладки.
3. Содержание
Сборка программы
Как устроен процесс сборки
Какие файлы создаются, как они устроены, что содержат
Особенности сборки C и C++
Использующиеся программы
Препроцессор и его отладка
Детали языка (inline, static и так далее)
Стандартная библиотека
3
8. Объектные файлы
Содержимое
Машинный код
Секции дополнительных данных
– От чего зависит и какие зависимости предоставляет
– Как загружать
– Как запускать
Использование
Промежуточные объектные файлы
– Код отдельных частей программы
Исполняемый файл
8
11. Процесс
Компиляция (compile)
C++ → машинный код
Один исходный файл → один объектный файл
Компоновка (link)
Объектные файлы → исполняемый файл
Сборка (build)
Всё вместе
11
15. Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS main.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 6
6: 00000000 0 SECTION LOCAL DEFAULT 7
7: 00000000 0 SECTION LOCAL DEFAULT 5
8: 00000000 33 FUNC GLOBAL DEFAULT 1 main
9: 00000000 0 NOTYPE GLOBAL DEFAULT UND sum
readelf -s main.o
15
16. Символы
Имя — произвольная строка
Значение — 64-битное число (адрес, смещение и т. д.)
Свойства
Тип
Видимость
– локальный символ
– глобальный символ
– внешний символ
16
17. Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS main.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 6
6: 00000000 0 SECTION LOCAL DEFAULT 7
7: 00000000 0 SECTION LOCAL DEFAULT 5
8: 00000000 33 FUNC GLOBAL DEFAULT 1 main
9: 00000000 0 NOTYPE GLOBAL DEFAULT UND sum
readelf -s main.o
17
20. Секции
Именованный блок данных в объектном файле
.text — код программы
.data — статически инициализированные данные
.symtab — таблица символов
Тип секции
Код
Статические данные
Таблицы символов
Таблицы строк
20
30. System V AMD64 ABI
Первые 6 целочисленных аргументов и указателей
rdi, rsi, rdx, rcx, r8, r9
Типы с плавающей точкой
xmm0-xmm7
Остальное на стеке
Возвращаемое значение
rax
30
32. int sum(int a, int b);
int main() {
int result = sum(1, 2);
return result * 2;
}
main.c
32
int sum(int a, int b) {
return a + b;
}
sum.c
33. #include "sum.h"
int main() {
int result = sum(1, 2);
return result * 2;
}
main.c
33
int sum(int a, int b);
sum.h
34. # 1 "main.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.c"
# 1 "sum.h" 1
int sum(int a, int b);
# 2 "main.c" 2
int main() {
int result = sum(1, 2);
return result * 2;
}
gcc -E main.c
34
35. Препроцессор
Обрабатывает текст
#include — подстановка содержимого другого файла
– расширение не важно — хоть *.txt, хоть *.exe
– *.h — удобное соглашение
#ifdef/#endif — выборочное включение/исключение текста
35
43. #include "sum.h"
int main() {
int result = sum(1, 2);
return result * 2;
}
main.c
43
#include <sum.h>
int main() {
int result = sum(1, 2);
return result * 2;
}
main.c
44. "sum.h" vs <sum.h>
Разные списки директорий, в которых препроцессор ищет файл
Если файл "sum.h" не найден, он ищется как <sum.h>
Соглашение
"sum.h" — локальный файл
– Сначала ищется в текущей директории
<sum.h> — системный файл
44
46. Опции препроцессора
Директории
-Idir — добавить dir в начало обоих списков
-iquotedir — добавить dir в начало списка для #include
"file"
Отладка
-H — вывести дерево #include
-dM — вывести #define для всех предопределённых
переменных
-dD — оставить директивы #define в обработанном файле
-dI — оставить директивы #include в обработанном файле
-P — не генерировать номера строк
46
51. Ошибки сборки
Ошибки
multiple definition of `sum'
undefined reference to `sum'
too many arguments to function ‘sum’
Поиск причины
gcc -v — что компилируется и компонуется
gcc -E — результат препроцессинга, какие сигнатуры
используются
readelf -s — какие символы в объектном файле, какие
символы — внешние
objdump -d -r — где используются внешние символы
51
59. inline
int sum(int a, int b) {
return a + b;
}
int main() {
int result = sum(1, 2);
return result * 2;
}
main.cpp
59
inline
int sum(int a, int b) {
return a + b;
}
int calc() {
return sum(2, 3);
}
sum.cpp
65. Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS sum.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 2
4: 00000000 0 SECTION LOCAL DEFAULT 3
5: 00000000 20 FUNC LOCAL DEFAULT 1 sum
6: 00000000 0 SECTION LOCAL DEFAULT 5
7: 00000000 0 SECTION LOCAL DEFAULT 6
8: 00000000 0 SECTION LOCAL DEFAULT 4
readelf -s sum.o
65
66. main.o: In function `main':
main.c:(.text+0x13): undefined reference to `sum'
collect2: error: ld returned 1 exit status
gcc -o program sum.o main.o
66
81. Сборка
Препроцессинг — сс1, cc1plus
C++ с директивами препроцессора → C++
Компиляция — cc1, cc1plus, as
C++ → ассемблер → машинный код
Один исходный файл → один объектный файл
Компоновка — ld (collect2)
Объектные файлы → исполняемый файл
Сборка — gcc
Всё вместе
81
82. $ gcc -o program main.cpp sum.cpp
$ ls
main.cpp program sum.cpp
Всё вместе
82
84. За кадром
Другие компиляторы
LLVM + Clang (clang)
Microsoft Visual C++ (cl)
Другие ОС
Windows
– Portable Executable (PE)
– Common Object File Format (COFF)
Mac OS X
– Mach Object (Mach-O)
84