Первая программа на Linux ARM64

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

Рассмотрим создание первой программы на ассемблере на Linux ARM64 (обращаю внимание, что речь идет именно о компиляции на машине на платформе ARM64, а не Intel x86-64). Для компиляции программы на ассемблере под Linux ARM64 можно использовать компилятор GAS, который идет в составе набора компиляторов GCC GNU.

Для использования GAS вначале установим пакет GCC командой:

sudo apt-get update
sudo apt-get install gcc

Поскольку наша ОС работает на ARM64, то по умолчанию компилятор будет компилировать файлы именно для этой платформы.

Итак, после установки нам станет доступно приложение as, которое представляет компилятор GAS. Кроме того, также будет доступен линкер.компоновщик в виде приложения ld. Например, проверим версию компилятора gas:

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 `aarch64-linux-gnu'.
eugene@Eugene:~$

Создание первой программы

Теперь напишем первую простейшую программу, которая просто будет выводить на консоль некоторую строку. Для этого создадим в каталоге пользователя какой-нибудь каталог, например, arm. Создадим в этого каталоге новый файл hello.s (обычно файлы с кодом ассемблера arm имеют расширение .s). Определим в этом файл следующий код:

.global _start          // устанавливаем стартовый адрес программы

_start: 
    mov X0, #1          // 1 = StdOut - поток вывода
    ldr X1, =message             // строка для вывода на экран
    mov X2, #19                // длина строки
    mov X8, #64                // устанавливаем функцию Linux
    svc 0                      // вызываем функцию Linux для вывода строки

    mov X0, #0                 // Устанавливаем 0 как код возврата
    mov X8, #93                // код 93 представляет завершение программы
    svc 0                      // вызываем функцию Linux для выхода из программы

.data
message: .ascii "Hello METANIT.COM!\n"    // данные для вывода

Для большего понимания я снабдил программу комментариями. GNU Assembler использует тот же самый синтаксис комментариев, что и C/C++ и другие си-подобные языки: одиночный комментарий начинается с двойного слеша //. Также можно использовать многострочный комментарий с помощью символов /∗ и ∗/, между которыми помещается текст комментария /* текст комментария */

Вначале надо указать линкеру (в нашем случае программа ld) стартовую точку программы. В данной программе стартовая точка программы проецируется на метку _start. И чтобы линкер получил к ней доступ, определяет _start в качестве глобальной переменной с помощью оператора global.

.global _start

Одна программа может состоять из множества файлов, но только один из них может иметь точку входа в программу.

Далее идут собственно действия программы. Вначале вызывается инструкция mov, которая помещает данные в регистр.

mov X0, #1

Значения X0-X2 представляют регистры для параметров функции в Linux. В данном случае помещаем в регистр X0 значение "#1". Операнды начинаются со знака "#" Число 1 представляет номер стандартного потока вывода "StdOut".

Далее загружаем в регистр X1 адрес строки для вывода на экран с помощью инструкции ldr

ldr X1, =message

Затем также с помощью инструкции mov помещаем в регистр X2 длину выводимой строки

mov X2, #19

Для любого системного вызова в Linux параметры помещаются в регистры X0–X7 в зависимости от количества. Затем в регистр X0 помещается код возврата. А сам системный вызов определяется номером функции из регистра X8. Здесь помещаем в X8 функцию с номеро 64 (функция write)

mov X8, #64

Далее выполняем системный вызов с помощью оператора svc

svc 0 

Операционная система, используя параметры в регистрах и номер функции, выведет строку на экран.

После этого нам надо выйти из программы. Для этого помещаем в регистр X0 число 0

mov X0, #0

А в регистр X8 передаем число 93 - номер функции для выхода из программы (функция exit)

mov X8, #93

И с помощью svc также выполняем функции. После этого программа должна завершить выполнение.

В самом конце программы размещена секция данных

.data
message: .ascii "Hello METANIT.COM!\n"    // данные для вывода

Директива .data определяет секцию данных. Выражение .ascii выделяет память и помещает в нее указанную далее строку.

Строка завершается символом перевода строки "\n", чтобы не надо было нажимать на Return, чтобы увидеть текст в окне терминала.

Компиляция приложения

Для компиляции приложения откроем терминал и командой cd перейдем к папке, где расположен файл hello.s с исходным кодом программы. И для компиляции выполним команду:

as hello.s -o hello.o

Компилятору as в качестве параметра передается файл с исходным кодом hello.s. А параметр -o указывает, в какой файл будет компилироваться программа - в данном случае в файл hello.o. Соответственно в папке программы появится файл hello.o

Затем нам нужно скомпоновать программу с исполняемый файл с помощью программы линкера.компоновщика ld командой:

ld hello.o -o hello

После этого в папке программы появится исполняемый файл hello, который мы можем запускать на устройстве с архитектурой ARM64 под управлением Linux. Полный вывод консоли:

eugene@Eugene:~$ cd arm
eugene@Eugene:~/arm$ as hello.s -o hello.o
eugene@Eugene:~/arm$ ld hello.o -o hello
eugene@Eugene:~/arm$ ./hello
Hello METANIT.COM!
eugene@Eugene:~/arm$

Установка Arm GNU Toolchain

Выше рассматривалась компиляция программы, которая выполняется напосредственно на Linux (на arm-архитектуре). Но кроме этого, мы можем использовать непривязанные к определенной системе пакеты (то что называется bare metall). Одним из наиболее популярных подобных пакетов является комплект инструметов Arm GNU Toolchain, который можно найти на странице https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads. Здесь представлены поседние версии Arm GNU Toolchain для разных архитектур.

Установка GNU Arm Embedded Toolchain для bare metal ARM64 ассемблера на Linux

Для Linux ARM64 доступны следующие пакеты:

  • AArch32 bare-metal target (arm-none-eabi): для компиляции программ под 32-битные архитектуры без привязки к конкретной операционной системе

  • AArch32 GNU/Linux target with hard float (arm-none-linux-gnueabihf): для компиляции программ под 32-битную ОС Linux

  • AArch64 bare-metal target (aarch64-none-elf): для компиляции программ под 64-битные архитектуры без привязки к конкретной операционной системе

Поскольку в нашем случае мы собираемся компилировать под bare metal на ARM64, то нас интересует пакет AArch64 bare-metal target (aarch64-none-elf), который не привязан к определенной ОС. Загрузим данный архив.

После загрузки сначала создадим папку, где будут располагаться все загруженные инструменты:

sudo mkdir /opt/aarch64-none-elf

Далее для распаковки архива в созданный каталог "opt/aarch64-none-elf" выполним следующую команду:

sudo tar -xvf ~/arm-gnu-toolchain-12.3.rel1-aarch64-aarch64-none-elf.tar.xz --strip-components=1 -C /opt/aarch64-none-elf

В данном случае предполагается, что архив загружен в папку текущего пользователя. После этого в каталоге opt/aarch64-none-elf/bin мы найдем файлы aarch64-none-elf-as и aarch64-none-elf-ld - компилятор и линкер соответственно.

eugene@Eugene:~$ cd ../../opt/aarch64-none-elf/bin
eugene@Eugene:/opt/aarch64-none-elf/bin$ ls -l
total 231252
-rwxr-xr-x 1 802 802   1382256 Jun 27 17:20 aarch64-none-elf-addr2line
-rwxr-xr-x 2 802 802   1416584 Jun 27 17:20 aarch64-none-elf-ar
-rwxr-xr-x 2 802 802   2598160 Jun 27 17:20 aarch64-none-elf-as
-rwxr-xr-x 2 802 802   1466592 Jun 27 17:39 aarch64-none-elf-c++
-rwxr-xr-x 1 802 802   1381216 Jun 27 17:20 aarch64-none-elf-c++filt
-rwxr-xr-x 1 802 802   1462448 Jun 27 17:39 aarch64-none-elf-cpp
-rwxr-xr-x 1 802 802     43832 Jun 27 17:20 aarch64-none-elf-elfedit
-rwxr-xr-x 2 802 802   1466592 Jun 27 17:39 aarch64-none-elf-g++
-rwxr-xr-x 2 802 802   1462352 Jun 27 17:39 aarch64-none-elf-gcc
-rwxr-xr-x 2 802 802   1462352 Jun 27 17:39 aarch64-none-elf-gcc-12.3.1
-rwxr-xr-x 1 802 802     36784 Jun 27 17:39 aarch64-none-elf-gcc-ar
-rwxr-xr-x 1 802 802     36784 Jun 27 17:39 aarch64-none-elf-gcc-nm
-rwxr-xr-x 1 802 802     36784 Jun 27 17:39 aarch64-none-elf-gcc-ranlib
-rwxr-xr-x 1 802 802    949984 Jun 27 17:39 aarch64-none-elf-gcov
-rwxr-xr-x 1 802 802    646008 Jun 27 17:39 aarch64-none-elf-gcov-dump
-rwxr-xr-x 1 802 802    672520 Jun 27 17:39 aarch64-none-elf-gcov-tool
-rwxr-xr-x 1 802 802 162324640 Jun 27 17:24 aarch64-none-elf-gdb
-rwxr-xr-x 1 802 802      4627 Jun 27 17:24 aarch64-none-elf-gdb-add-index
-rwxr-xr-x 1 802 802   1471128 Jun 27 17:39 aarch64-none-elf-gfortran
-rwxr-xr-x 1 802 802   1455296 Jun 27 17:20 aarch64-none-elf-gprof
-rwxr-xr-x 4 802 802   3552152 Jun 27 17:20 aarch64-none-elf-ld
-rwxr-xr-x 4 802 802   3552152 Jun 27 17:20 aarch64-none-elf-ld.bfd
-rwxr-xr-x 1 802 802  35199688 Jun 27 17:39 aarch64-none-elf-lto-dump
-rwxr-xr-x 2 802 802   1406592 Jun 27 17:20 aarch64-none-elf-nm
-rwxr-xr-x 2 802 802   1530920 Jun 27 17:20 aarch64-none-elf-objcopy
-rwxr-xr-x 2 802 802   2967960 Jun 27 17:20 aarch64-none-elf-objdump
-rwxr-xr-x 2 802 802   1416592 Jun 27 17:20 aarch64-none-elf-ranlib
-rwxr-xr-x 2 802 802   1027840 Jun 27 17:20 aarch64-none-elf-readelf
-rwxr-xr-x 1 802 802   1386568 Jun 27 17:20 aarch64-none-elf-size
-rwxr-xr-x 1 802 802   1386464 Jun 27 17:20 aarch64-none-elf-strings
-rwxr-xr-x 2 802 802   1530920 Jun 27 17:20 aarch64-none-elf-strip
eugene@Eugene:/opt/aarch64-none-elf/bin$

Добавим в переменную PATH путь к компилятору и линкеру в папке bin. Для этого откроем выполним следующую команду:

echo 'export PATH=$PATH:/opt/aarch64-none-elf/bin' | sudo tee -a /etc/profile.d/aarch64-none-elf.sh

Чтобы изменения вступили в силу без необходимости перезагрузки, выполним команду

source /etc/profile

Проверим установку, выведя на консоль версию с помощью следующей команды:

aarch64-none-elf-as --version

Должно отобразиться что-то типа следующего:

eugene@Eugene:~$ aarch64-none-elf-as --version
GNU assembler (Arm GNU Toolchain 12.3.Rel1 (Build arm-12.35)) 2.40.0.20230627
Copyright (C) 2023 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 `aarch64-none-elf'.
eugene@Eugene:~$

Весь остальной процесс будет аналогичен то, что был описан ранее. Так, переходим в папку с файлом с исходным кодом и для создания объектного файла выполняем команду

aarch64-none-elf-as hello.s -o hello.o

Затем нам нужно скомпновать программу с исполняемый файл с помощью линкера aarch64-none-elf-ld командой:

aarch64-none-elf-ld hello.o -o hello
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850