Приветствую всех на моем канале Old Programmer.
Все разделы моего канала здесь.
- Книга "Ассемблер для Linux"
- Список разделов канала Old Programmer, канала о программировании и программистах
На моем канале довольно много материалов по программированию на языке ассемблера (Linux, x86-64). Данной статьей я начинаю новую серию статей. Я бы сказал, что это ассемблер 2.0. Начал вплотную заниматься книгой по ассемблеру под предварительным названием Ассемблер для Linux. Буду публиковать часть материалов из книги с минимальными изменениями или совсем без них. Да в чем-то они будут повторять те материалы, которые здесь имеются. Но они будут более подробно описывать те или иные вопросы и должны охватить более широкий круг тем. Сегодня первый материал. Да, материалы, можно сказать, еще в 0-м приближении, и будут меняться, возможно и с вашей помощью.
Параграф 1.4.
Первые программы для Linux на GNU Assembler
В данном параграфе, не вдаваясь в детали, мы рассмотрим простую ассемблерную программу, ее трансляцию и исполнение. Но прежде вспомним как происходит компилирование программ, написанных на языках высокого уровня.
В листинге 2 представлена простая программа на языке C.
Для компиляции программы используем универсальный модуль gcc для вызова компилятора из GNU Compiler Collection.GCC - стандартный набор компиляторов, разрабатываемых в рамках проекта GNU.
gcc -ol2 l2.c
В результате выполнения на диске появляется двоичный исполняемый модуль l2, который может быть запущен командой ./l2. Программа gcc является управляющей. Она в частности определяет какой из компиляторов должен быть запущен. В данном случае запускается компилятор классического языка C. При необходимости также запускается транслятор с языка ассемблера (программа as) и компоновщик (программа ld).
При желании можно получить промежуточный модуль на языке ассемблера командой
gcc -Sl2.c
В результате ее выполнения появляется файл l2.s, содержащий ассемблерный код. Мы не будем пока касаться содержимого этого файла, нам важно было подчеркнуть, что ассемблер является промежуточным звеном трансляции программы с языков высокого уровня. Это важный момент, в дальнейшем мы увидим что язык ассемблера довольно просто можно использовать совместно с языками высокого уровня.
В листинге 3 представлена простая программа на языке ассемблера, которая ничего не делает. При запуске она сразу же выполняет команду выхода.
Откомпилировать программу, т.е. получить исполняемый модуль можно с помощью последовательности следующих команд.
as --64l3.s -ol3.o
ld -sl3.o -ol3
Как видим трансляция происходит в два этапа. При выполнении первой команды (программа as) на диске появляется файл l3.o. Такие файлы называются объектными. В дальнейшем они сыграют важную роль при рассмотрении вопроса многомодульных программ и интеграции ассемблера с другими языками программирования (раздельная компиляция). Второй этап называется компоновкой и выполняется он с помощью программы-компоновщика ld. В результате на диске в текущем каталоге появляется исполняемый двоичный модуль l3, который можно запустить на выполнение командой ./l3.
Процесс компоновки называется также редактированием связей и соответственно утилита ld редактором связей.
Обратимся теперь к листингу 3. Данный листинг в значительной степени представляет из себя каркас, который мы в дальнейшем, будем использовать для написания программ на ассемблере, возможно несколько дополняя или видоизменяя его. Остановимся подробнее на всех элементах представленного шаблона программы.
1. Для написания однострочного комментария используется символ '# '. Можно также использовать комментарии многострочные в стиле языка C: /* */.
2. Заметим, что некоторые строки в программе начинаются с точки. Это директивы ассемблера, которые влияют на компиляцию программы, используются для задания данных. Директива .text определяет так называемую секцию программы. В нашем случае в программе всего одна секция. В дальнейшем мы обычно будем добавлять еще секцию для хранения данных.
3. В программе есть директива, после которой стоит двоеточие. Это _start. Это так называемые метки. Метка указывает на конкретное место в программе. После компиляции она превращается в конкретный адрес. Метка указывает на команду или данное. Формально между меткой, которая указывает на команду или данное нет никакой разницы. Разница существует на уровне логики выполнения программы.
4. Директива .globl указывает компилятору as, что указанная в метка является глобальной и она будет представлена в объектном модуле. В данном случае мы говорим, что глобальной меткой будет _start. Вы можете посмотреть объектный модуль l3.o и действительно в нем присутствует слово _start. Для компоновщика ld метка _start по умолчанию является местом, с которого начинается выполняться программа. Если убрать строку .globl _start, то компоновщик не будет знать с какого адреса должна начинать выполняться программа и выдаст предупреждение. При этом исполняемый бинарный файл будет создан на основе предположения компоновщика об адреса, с которого должна выполняться программа. Кстати, метка начала выполнения программы не обязательно должна называться _start. С помощью опции -e можно указать компоновщику другую существующую метку. Например так
ld -s -e main l3.o -ol3
В данном случае компоновщик указывает, что адресом запуска для программы будет метка main.
5. Последние три строки программы являются реальными командами процессора. В дальнейшем мы подробно будем говорить о командах x86-64. mov это команда пересылки данных. В нашем случае она засылает число 60 в регистр процессора rax (не большая память процессора состоит из регистров). Далее команда xor обнуляет содержимое регистра rdi. Данная команда равносильна команде mov $0, %rdi. Команда syscall основная команда, которую мы будем использовать для вызова системных функций ядра операционной системы. При программировании на ассемблере обращение к функциям ядра дело повседневное. При этом регистр rax должен содержать номер функции ядра. В нашем случае мы используем функцию с номером 60, которая позволяет закончить выполнение программы. Регистр rdi, для данной функции он должен содержать код выхода, который можно отловить, средствами запускающего процесса. Обычно 0 означает, что программа закончила свою работу без ошибок.
--> Глава 1. Параграф 1.5. <--Глава 1. Параграф 1.3.
Подписываемся на мой канал Old Programmer и пишем свои комментарии.