Ассемблер GAS. Начало работы на Linux

Последнее обновление: 23.09.2023

В этой статье мы рассмотрим начало работы и первую программу на GNU ассемблере или сокращенно GAS. В рамках данного руководства будет преимущество применяться ОС Linux, как самый распространенный вариант для работы с ассемблером GAS.

Но перед началом работы на необходимо установить требуемый инструментарий. GNU ассемблер распространяется как часть набора компиляторов и прочиз программ для создания приложений, который называется GCC. Поэтому в начале нам надо установить GCC.

В Linux, в частности, в Ubuntu, для этого можно установить пакет build-essential. Для его установки сначала обновим репозиторий пакетов с помощью команды:

sudo apt update

Затем собственно установим пакет build-essential с помощью команды:

sudo apt install build-essential

После установки пакета мы можем проверить установленную версию GCC:

gcc --version

Аналогично можно проверить версию идущего в комплекте ассемблера:

eugene@Eugene:~# as --version
GNU assembler (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `x86_64-linux-gnu'.
eugene@Eugene:~#

Первая программа на GAS

Определим в файловой системе папку для хранения файлов. Например, пусть она будет называться asm. В этой папке создадим новый файл, который пусть будет называться hello.s. Обычно файлы с кодом ассемблера имеют расширение *.asm или .s. Для GAS чаще используются файлы с расширением .s, соответственно в нашем случае файл кода также будет помещаться в файлы с расширением .s

В файле hello.s определим следующий код:

# Первая программа
.globl _start

.section .text
_start:
    movq $60, %rax
    movq $22, %rdi
    syscall

Рассмотрим код программы построчно. Первой строкой идет комментарий:

# Первая программа

Комментарий предваряется символом решетки #. При компиляции комментарии игнорируются. Тем не менее на уровне кода комментарии важны, так как позволяют понять, что делает та или иная строка или блок кода.

Дальше идет директива .globl

.globl _start

Все операторы, который начинаются с точки, представляют директивы. Директива .globl позволяет сделать определенную метку видимой вне программы. Так, при компиляции нам надо чтобы программа компоновщика увидела стартовую точку программы - место откуда начинается программа. В нашем случае стартовой точкой программы является метка _start. И благодаря выражению .globl _start мы делаем видимой метку _start вне текущей программы.

Далее идет применение еще одной директивы - .section

.section .text

Директива .section указывает, что дальше идет определенная секция/раздел программы. В данном случае мы определяем секцию .text. Данная секция предназначена для инструкций программы. Стоит отметить, что использование директивы .section здесь необязательно: мы могли бы просто написать .text, и ассемблер знал, что дальше идет секция кода программы.

Затем определяем стартовую точку программы в виде метки _start:

_start:

После этой метки располагаются собственно инструкции программы, который определяют ее действия. По сути метка в ассемблере - это произвольный идентификатор, после которого идет двоеточие :. А все последующие выражения проецируются на эту метку.

Далее располагается первая инструкция программы:

movq $60, %rax

Инструкция movq фактически является сокращением от "move quadword" (поместить четверичное слово). Четверичное слово в ассемблере Itel x86-64 представляет некоторое число размером в 64 бита (8 байт). Мнструкция принимает два операнда. Первый операнд - что помещаем, второй операнд - куда помещаем. В данном случае помещаем число 60 в регистр RAX. Обратите внимание, что перед число указывается символ доллара $, а перед названием регистра - символ %. Символ доллара перед числом - $60 указывает, что мы рассматриваем это значение именно как число (иначе говоря непосредственный операнд или immediate operand), а не адрес или что-то еще.

Почему именно число 60? Число 60 - это номер системного вызова Linux завершения программы. То есть если программа хочет завершиться, она говорит операционной системе выполнить системный вызов с номером 60. Но чтобы операционная система получила эту команду, число надо поместить в регистр RAX.

Следующая инструкция аналогичным образом помещает в регистр RDI число 22:

movq $22, %rdi

То есть здесь та же сама инструкция, только число 22, а регистр - RDI. Число 22 здесь произвольное - это то число, которое будет считаться статусным кодом программы. Когда программа завершается, то с помощью статусного кода она может уведомить систему о некотором результате. В данном случае конкретное значение статусного кода нам не важно, поэтому можно выбрать любое или как здесь число 22. Однако чтобы операционная система получила при завершении программы ее статусной код результата, его надо поместить в регистр RDI.

Наконец последняя инструкция

syscall

Эта инструкция собственно и выполняет системный вызов, номер которого (номер 60) помещен в регистр RAX. Таким образом, суммирую, программа выполняет системный вызов 60, то есть просто завершается, и при завершении посылает системе статусный код 22.

Компиляция и выполнение программы

Для компиляции программы на ассемблере выполним следующую команду:

as hello.s -o hello.o

Программа ассемблера as принимает файл кода hello.s и компилирует его в машинноый код, который помещается в объектный файл с именем hello.o. Имя объектного файла указывается после опции -o. Объектный файл обычно имеет расширение .o. Если явным образом не указать имя объектного файла, то код программы компилируется в файл, который называется a.out.

Объектный файл не является исполняемым файлом. Чтобы программу можно было выполнять, объектный файл надо скомпоновать/слинковать в исполняемый файл с помощью программы компоновщика (также называемого линковщиком/линкером или linker). Для этого выполним следующую команду:

ld hello.o -o hello

Программе компоновщика/линковщика ld передается объектный файл. А после опции -o указывается имя генерируемого исполняемого файла. Таким образом, после выполнения этой команды в папке с исходным кодом у нас появится исполняемый файл hello, который мы можем запускать.

Запустим созданный файл hello:

./hello

В результате выполнения программы мы ничего не увидим на консоли. Тем не менее программа все-таки выполняет некоторые действия. И как минимум делает одну вещь - завершает работу и возвращает статусный код выполнения. Получим этот статусный код, выполнив в консоли следующую команду:

echo $?

В результате выполнения этой команды нам должно отобразиться число 22 - наш статусный код, который мы возвращаем из программы.

Консольный вывод полностью:

eugene@Eugene:~# cd asm
eugene@Eugene:~/asm# as hello.s -o hello.o
eugene@Eugene:~/asm# ld hello.o -o hello
eugene@Eugene:~/asm# ./hello
eugene@Eugene:~/asm# echo $?
22
eugene@Eugene:~/asm#
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850