Хабрахабр

[Перевод] Пишем Android-приложение на ассемблере

image

Одно дело — установка Android Studio и написание «Hello, World» на Java или Kotlin. Эта рассказ о нестандартном подходе к разработке Android-приложений. Но я покажу, как эту же задачу можно выполнить иначе.

Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Skillbox рекомендует: Образовательный онлайн-курс «Профессия Java-разработчик».

Как работает мой смартфон с Android OS?

Сначала небольшая предыстория. Однажды вечером мне позвонила знакомая по имени Ариэлла. Она спросила меня: «Слушай, а как работает мой смартфон? Что у него внутри? Как электрическая энергия и обычные единицы и нули позволяют всему этому функционировать?»

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

Далее мы вышли на более высокий уровень: я ей показал, как можно создавать логические вентили, к примеру NAND (логическое И) плюс NOR (логическое ИЛИ) c использованием специфической комбинации транзисторов. Затем мы работали пару недель вместе, поскольку Ариэлла захотела узнать, как работают кирпичики электронной техники, то есть полупроводниковые элементы, включая транзисторы.

Когда все прояснилось, начали разрабатывать простой процессор (воображаемый), в котором было два регистра общего назначения и две простые инструкции (добавление этих регистров). Мы исследовали логические элементы разных видов, объединяли их для выполнения вычислений (например, добавления двух бинарных чисел) и ячеек памяти (триггеров). Мы даже написали простую программу, которая умножает эти два числа.

Здесь объясняется практически все, с самых основ. Кстати, если вас интересует эта тема, то прочитайте инструкцию по созданию 8-битного компьютера с нуля. Хотел бы я прочитать это раньше!

Hello, Android!

После завершения всех этапов изучения мне показалось, что у Ариэллы хватит знаний, чтобы понять, как работает процессор смартфона. Ее смартфон — Galaxy S6 Edge, база которого — архитектура ARM (как, собственно, и у большинства смартфонов). Мы решили написать «Hello, World»-приложение для Android, но на ассемблере.

.text
.globl _start _start: mov %r0, $1 // file descriptor number 1 (stdout) ldr %r1, =message mov %r2, $message_len mov %r7, $4 // syscall 4 (write) swi $0 mov %r0, $0 // exit status 0 (ok) mov %r7, $1 // syscall 1 (exit) swi $0 .data
message: .ascii "Hello, World\n"
message_len = . - message

Если вы никогда раньше не сталкивались с кодом ассемблера, то этот блок может вас напугать. Но ничего страшного, давайте разберем код вместе.

Первая — это текст с инструкциями машинного кода, и вторая — переменные, строки и другая информация (начиная со строки 15). Итак, наша программа состоит из двух частей. Раздел .text обычно доступен лишь для чтения, а .data — также и для записи.

Она представляет собой точку входа в приложение. В строке 2 мы определяем глобальную функцию с названием _start. Определение функции объявлено в строке 4. ОС начинает выполнять код именно с этой точки.

В строках 5–9 сообщение выводится на экран, в строках 11–13 программа завершается. Кроме того, функция выполняет еще две вещи. Тем не менее выход не будет корректным, поскольку программа завершится с ошибкой. Даже если удалить 11–13 строки, программа выведет нашу строку «Hello, World» и завершится. Без строк 11–13 приложение попытается выполнить недопустимую инструкцию.

В приложении мы вызываем функцию write(). Печать на экран выводится при помощи системной функции «Системный вызов» операционной системы. Далее выполняется инструкция swi $=0 (строка 9), где идет переход прямо в Linux-ядро, который является основой Android. Ее мы указываем, когда загружаем значение 4 в регистр процессора с названием r7 (строка 8).

Например, r0 показывает номер дескриптора файла, который нам необходимо напечатать. Что касается параметров для системного вызова, то они передаются через другие регистры. Мы помещаем туда значение 1 (строка 5), указывающее стандартный вывод (stdout), то есть вывод на экран.

В нашей программе для него установлено значение message_len (строка 7), вычисляемое в строке 18 с использованием специального синтаксиса: символ точки обозначает текущий адрес памяти. r1 указывает на адрес памяти данных, которые мы хотим записать, поэтому мы просто загружаем в эту область адрес строки «Hello, World» (строка 6), а регистр r2 показывает, сколько байтов мы хотим записать. — message обозначает текущий адрес памяти минус адрес message. По этой причине. Ну а поскольку мы заявляем message_len сразу же после message, то все это вычисляется как длина message.

Если записать код строк 5–9 при помощи языка С, получится следующее:

#define message "Hello, World\n"
write(1, message, strlen(message));

Завершить работу программы несколько проще. Для этого мы просто прописываем код выхода в регистр r0 (строка 11), после чего добавляем значение 1, являющееся номером вызова системной функции exit(), в r7 (строка 12), затем снова вызываем ядро (строка 13).

Также там есть и реализация write() и exit(), вызывающих соответствующие системные функции. Полный список системных вызовов Android и их номеров можно найти в исходном коде операционной системы.

Собираем программу

Для того чтобы скомпилировать наш проект, понадобится Android NDK (Native Development Kit). Он содержит набор компиляторов и инструментов сборки для ARM-платформы. Загрузить его можно с официального сайта, установить — например, через Android Studio.

Если вы произвели загрузку через Android Studio, то поищите его в папке Android SDK. После того как NDK установлен, нам понадобится файл arm-linux-androideabi-as, это ассемблер для ARM. Обычно ее расположение —

9\prebuilt\windows-x86_64\bin. ndk-bundle\toolchains\arm-linux-androideabi-4.

После того как ассемблер найден, сохраните написанное в файл с названием hello.s, после чего выполните следующую команду для преобразования его в машинный код:

arm-linux-androideabi-as -o hello.o hello.s

Для того чтобы преобразовать его в двоичный файл, который может работать на вашем девайсе, вызовите компоновщик: Эта операция позволяет создать объектный ELF-файл с именем hello.o.

arm-linux-androideabi-ld -o hello hello.o

Теперь у нас есть файл hello, который содержит программу, вполне готовую к использованию.

Запускаем приложение на своем девайсе

Приложения для Android обычно распространяются в .apk-формате. Это особый вид сжатого файла, который включает классы Java (да, можно писать и при помощи С / С++, но точкой входа должна быть Java).

После этого пускаем в ход adb shell для того, чтобы запустить приложение и оценить результат: Для того чтобы избежать проблем при запуске приложения, в примере был использован adb, что позволило скопировать его во временную папку нашего устройства Android.

adb push hello /data/local/tmp/hello
adb shell chmod +x /data/local/tmp/hello

И, наконец, запускаем приложение:

adb shell /data/local/tmp/hello

А что напишете вы?

Сейчас у вас есть рабочее окружение, похожее на то, которое было у Ариэллы. Она потратила на изучение ARM-ассемблера несколько дней, придумав затем несложный проект — это игра Sven Boom (разновидность Fizz Buzz родом из Израиля). Игроки считают по очереди и каждый раз, когда число делится на 7 или содержит число 7, они должны сказать «бум» (отсюда и название игры).

Ариэлла написала целый метод, который выводит на экран числа, по одной цифре за раз. Стоит отметить, что программа по игре — не такая уж и простая задача. Поскольку она писала все на ассемблере без вызова стандартных функций библиотеки С, на решение пришлось потратить несколько дней.

Кстати, некоторые идентификаторы кода в приложении — на самом деле еврейские слова (например, _sifra_ahrona). Первая программа Ариэллы для Adnroid размещена вот здесь.

Я предлагаю вам заняться этим вплотную и попробовать создать небольшое приложение на ассемблере для вашего устройства. Написание Android-приложения на ассемблере — хороший способ познакомиться поближе с архитектурой ARM, а также лучше понять внутреннюю кухню гаджета, используемого вами ежедневно. Это может быть простая игра или что-нибудь еще.

Skillbox рекомендует:

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть