Проект выполнялся на Mac OS X 10.14.6
Узнать вашу версию можно командой
$ sw_vers
Или нажмите на яблоко в верхнем левом углу экрана и выберете About This Mac
.
Для компиляции я использовал NASM. Установка:
brew install nasm
Если нет brew, выполните:
curl -fsSL https://rawgit.com/kube/42homebrew/master/install.sh | zsh
Еще необходим gcc, подразумевается, что он есть. Чтобы проверить, что у вас все работает, склонируйте репозиторий, перейдите в него и выполните:
./check.sh
Если вы увидели поздравление, значит всё работает.
Разберем код поздравления.
1 global _main ; точка входа в программу, будет вызвана процедура _main
2
3 section .text ; в секции text пишется код программы
4 _main: ; объявляем процедуру _main
5 mov rax, 0x02000004 ; помещаем в регистр rax номер системного вызова - write
6 mov rdi, 1 ; помещаем в регистр rdi файловый дескриптор - stdout
7 lea rsi, message ; помещаем в регистр rsi сообщение для вывода
8 mov rdx, len ; помещаем в регистр rdx длину сообщения
9 syscall ; просим ОС выполнить то, что мы закодировали выше
10 call _exit ; вызываем процедуру _exit
11
12 _exit: ; объявляем процедуру _exit
13 mov rax, 0x02000001 ; помещаем в регистр rax номер системного вызова - exit
14 xor rdi, rdi ; помещаем в регистр rdi код ошибки - 0
15 syscall ; просим ОС выполнить то, что мы закодировали выше
16
17 section .data ; в секции data объявляются "переменные"
18 message default rel ; объявляем message и помещаем туда сообщение
19 db "Congrats! Everything is working OK!", 10, 0
20 len equ $-message ; объявляем len и помещаем туда длину сообщения
Для работы нашей программы ОС выделяет память, которая будет поделена на:
- Код программы (
section .text
) - фиксированный размер - Данные программы
section .data
- здесь содержатся данные программы, которые не могут быть изменены. Фиксированный размер.bss
- буфер для данных, которые будут изменены или объявлены позже в программе, фиксированый размер. Буфер изначально заполнен нулями
- Стэк - область памяти для передачи данных процедурам в программе
Иногда вместо section
можно увидеть segment
, это синонимы, можно использовать любое слово. Код будет работать,
если не указывать их вообще, но они упрощают чтение кода.
Коды системных вызовов можно найти в файле syscall.h
. Воспользуйтесь поиском, чтобы посмотреть его.
При передаче аргументов в функции, они помещаются в rdi
, rsi
, rdx
, rcx
, r8
, r9
соответственно
rax
- регистр "обмена", через него осуществляется передача значений между функциями.
xor rdi, rdi
равносильно mov rdi, 0
, но эффективней по памяти и скорости.
Для работы со структурой важно понимать как она хранится в памяти. Для доступа к элементам необходимо указывать смещение. В случае с t_list
, который содержит два указателя, получаем:
- размер 16 байт
- смещение 8 байт
typedef struct s_list
{
void *content;
struct s_list *next;
} t_list;
size of t_list - 16 bytes
offset - 8 bytes
0 8 16
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| content | next |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
поэтому, если адрес t_list
в rdi
, то:
- для доступа в
content
используем[rdi]
- доступ в
next
-[rdi + 8]