forked from mizabrik/acos-notes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathasm.tex
More file actions
90 lines (79 loc) · 6.67 KB
/
asm.tex
File metadata and controls
90 lines (79 loc) · 6.67 KB
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
\documentclass[main]{subfiles}
\begin{document}
Возвращаемся к вопросу, что такое ассемблерная программа.
Ассемблерная программа --- это текстовый файл с расширением .s (.asm в Windows).
В этом файле содержатся команды в некотором синтаксисе, мы будем
работать с синтаксисом AT\&T. Его генерирует gcc и соответствующий ему
ассемблер --- это gas. Вот основные соглашения в AT\&T синтаксисе:
\begin{itemize}
\item Все регистры именуются \%ИМЯ\_РЕГИСТРА
\item Всё, что начинается с доллара --- числа (такая же запись, как в C: десятичная
\$10, шестнадцатеричная \$OxFF и восьмеричная \$011)
\item Просто текст --- имя команды
\item Служебные слова начинаются с точки (макросы, управление компиляцией)
\item Результат записывается в последний аргумент
\end{itemize}
Программа разбита на секции:
\begin{description}
\item[.text] --- команды
\item[.rodata] (обычно примыкает к секции .text) --- константы: их
можно объявлять и устанавливать им значение, но их нельзя изменять
\item[.data] --- переменные, которыми будет пользоваться программа
\item[.bss] --- статические переменные. Память под них не отводится в объектном файле,
но компилятор/линкер должен знать размер этой секции, который здесь и прописывается.
\end{description}
NB: для 64-битных чисел используется суффикс u.
.int --- резервирует четыре байта там, где она встретилась.
Можно добавить число, тогда это --- количество участков. Есть
также .int, .long, .short, .byte. Присутствует понятие строк:
.string "строка". Как описать переменную? Во-первых, выделить память для неё,
во-вторых, нужно дать ей имя. Для этого в ассемблере используют метки,
их можно использовать как адрес для перехода или как аргумент.
Чтобы задать метку, нужно написать ИМЯ\_МЕТКИ: в начале строки.
a: .int 50 --- выделит 4 * 50 байт.
Для работы с ними нужно использовать круглые скобки:
movl \%eax, (a) переместит значение из регистра в первый элемент массива a.
movl \%eax, +40(a) --- в сорок первый элемент.
Пример простейшей программы (инклюды опущены):
\begin{lstlisting}[numbers=left]
int func(long int a, long int b, long int c,
long int d, long int e, long int f) {
int result;
result = a + b + c + d +e +f;
printf("result = %d\n", result);
return 10;
}
int main() {
func(1, 2, 3, 4, 5, 6);
return 0;
}
\end{lstlisting}
В результате компиляции без каких-либо ключей, мы получим такой код на
языке ассемблера:
\lstinputlisting[multicols=2,numbers=left]{asm-example.s}
В данном случае, происходит вообще бессмысленная деятельность: из-за несогласованности
частей компилятора, данные, переданные через регистры, перемещаются на стек.
Для этого также стек увеличивается на 64 байта.
В тексте программы был упомянут символ printf. Откуда же он берётся?
Этим занимается линковщик.
Статическая линковка: есть набор объектных файлов, они предоставляют и требуют
некоторые символы. Линковщик создаёт новый файл, добавляет в его начало
некоторый заголовок (зависит от ОС) и собирает секции .text из всех
файлов в одну, аналогично --- для .data. Статическая библиотека (*.a в Linux ---
архив секций, *.lib в Windows) таким же образом добавляется сюда же.
Далее линковщик подставляет вместо символов конкретные адреса относительно
начала файла. Также выполняются некоторые другие действия.
Например, prog зависит от a, а a зависит от b. Что примечательно,
требуется линковать их в определённом порядке: для оптимизации ненужные
символы отбрасываются, поэтому правильной последовательностью линковки
будет prog, a, b.
При статической линковке программа сильно растёт в размере, а некоторые
библиотеки используются более, чем одной программой. Для экономии памяти
используется динамическая линковка: в тело программы включается код
линковщика (ld.so для Linux), но как узнать адрес функции, ведь он может
зависеть от запуска? Для этого в секции .data заводится таблица динамических
функций (заполняется при запуске), и перед вызовом call эта функция ищется
в этой таблице. В gcc динамическая линковка включается ключом -shared,
ключ -pic говорит, что нужно генерировать позиционно-независимый код, т. к.
библиотека может лежать по разным адресам оперативной памяти.
\end{document}