Добро пожаловать обратно, кибервоины!
В предыдущей статье мы рассмотрели некоторые команды ассемблера ARM. Сегодня мы углубимся в практическое применение инструкции ADD. Используя возможности отладчика GNU (GDB), мы рассмотрим, как анализировать и обрабатывать эту инструкцию, чтобы глубже понять архитектуру ARM.
Чтобы проверить, работает ли ваша система в 32-разрядном пользовательском пространстве, выполните:
raspberrypi> getconf LONG_BIT
Далее проверьте архитектуру ваших двоичных файлов:
raspberrypi> файл /bin/bash
В приведенном выше примере вы видите довольно распространённую проблему современных Raspberry Pi: Raspbian OS — 32-битная система, но использует 64-битное ядро. Это оптимальный вариант установки, поскольку он обеспечивает совместимость с 32-битной версией для всех ваших приложений и библиотек, а также лучшую поддержку оборудования благодаря 64-битному ядру.
Синтаксис следующий:
<strong>ADD{S}{<c>}{<q>} {<Rd>,} <Rn>, #<const></strong>
Где
S – если присутствует, инструкция обновляет флаги. Мы поговорим о флагах позже;
<Rd> – регистр назначения;
<Rn> – первый операнд;
<const> – непосредственное значение, которое нужно добавить к значению, полученному из <Rn>;
<c> и <q> – необязательные поля ассемблера.
Перейдём к практическому этапу и напишем код. Я создам файл instructions.s и открою его в Vim.
Начало файла, как обычно, — глобальное объявление значения «_start». Я подробно объяснил этот шаг в следующей статье . Также для удобства изучения я добавлю комментарий с синтаксисом инструкции add.
Прежде всего, нам нужен регистр (<Rn>), который будет прибавляться к нашему константному значению (#<const>). Мы создадим регистр общего назначения с помощью инструкции mov.
Как вы, возможно, помните из моей предыдущей статьи, регистры общего назначения — это r0-r12.
Чтобы настроить регистр общего назначения со значением по нашему выбору, мы можем использовать следующую команду:
mov r0, #7
Где
mov – инструкция скопировать значение в регистр;
r0 – регистр назначения, в котором мы будем хранить временное значение;
#7 – знак решетки означает, что следующее значение является константой. В этом примере я использовал число 7; вы можете выбрать любое другое.
После этого мы готовы приступить к выполнению инструкции по добавлению.
add r1, r0, #3
Где
r1 — регистр назначения, в котором мы собираемся сохранить сумму 7 + 3
r0 — наш первый операнд со значением 7.
#3 – константа, которая будет добавлена к r0. Я использовал значение 3.
На этом этапе давайте соберем этот код и посмотрим в gdb (отладчике GNU), что происходит.
Для сборки я буду использовать GCC:
gcc -g -nostdlib -static -o instructions instructions.s
Где
-g – Включить отладочную информацию
-nostdlib – Не компоновать со стандартной библиотекой (так как мы ее не используем)
-static – Создать статический исполняемый файл
Теперь мы можем открыть исполняемый файл с помощью GDB, но перед этим я установлю GEF (GDB Enhanced Features), который обеспечивает автоматический мониторинг регистров, вывод цветового кода и многое другое.
Чтобы установить GEF, выполните:
raspberrypi> bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
Теперь запустим GDB:
gdb ./instructions
Прежде всего, я собираюсь отключить смещенное пошаговое выполнение, чтобы избежать возможных ошибок в GDB.
(gdb) set displaced-stepping off
После этого мы можем установить точку останова на метке _start, чтобы остановить выполнение там:
(gdb) break _start
Запускаем нашу программу:
(gdb) run
Здесь мы видим, что программа начала выполнение, но остановилась на _start из-за точки останова.
Давайте проверим значение всех регистров:
(gdb) info registers
На этом этапе они пустые. Давайте рассмотрим одну инструкцию по сборке:
(gdb) stepi
И проверьте значение только регистров r0 и r1.
(gdb) info registers r0 r1
И здесь мы видим, что регистр r0 уже хранит значение 0x7 или 7 в десятичной системе счисления.
Если мы выполним следующую инструкцию ассемблера и снова проверим значение регистра с помощью тех же команд, мы увидим значение регистра r1.
Значение r1 равно 0xa или 10 в десятичной системе счисления, как мы и запрограммировали.
В предыдущей статье мы рассмотрели некоторые команды ассемблера ARM. Сегодня мы углубимся в практическое применение инструкции ADD. Используя возможности отладчика GNU (GDB), мы рассмотрим, как анализировать и обрабатывать эту инструкцию, чтобы глубже понять архитектуру ARM.
Подготовьте среду
Прежде чем приступить к изучению ассемблера, нам следует подготовить среду. О возможных способах подготовки можно прочитать в этой статье . Я буду использовать Raspberry Pi с 32-битной ОС Raspbian.Чтобы проверить, работает ли ваша система в 32-разрядном пользовательском пространстве, выполните:
raspberrypi> getconf LONG_BIT
Далее проверьте архитектуру ваших двоичных файлов:
raspberrypi> файл /bin/bash
В приведенном выше примере вы видите довольно распространённую проблему современных Raspberry Pi: Raspbian OS — 32-битная система, но использует 64-битное ядро. Это оптимальный вариант установки, поскольку он обеспечивает совместимость с 32-битной версией для всех ваших приложений и библиотек, а также лучшую поддержку оборудования благодаря 64-битному ядру.
Инструкция ДОБАВИТЬ
Эта инструкция добавляет немедленное значение к значению регистра и записывает результат в регистр назначения.Синтаксис следующий:
<strong>ADD{S}{<c>}{<q>} {<Rd>,} <Rn>, #<const></strong>
Где
S – если присутствует, инструкция обновляет флаги. Мы поговорим о флагах позже;
<Rd> – регистр назначения;
<Rn> – первый операнд;
<const> – непосредственное значение, которое нужно добавить к значению, полученному из <Rn>;
<c> и <q> – необязательные поля ассемблера.
Перейдём к практическому этапу и напишем код. Я создам файл instructions.s и открою его в Vim.
Начало файла, как обычно, — глобальное объявление значения «_start». Я подробно объяснил этот шаг в следующей статье . Также для удобства изучения я добавлю комментарий с синтаксисом инструкции add.
Прежде всего, нам нужен регистр (<Rn>), который будет прибавляться к нашему константному значению (#<const>). Мы создадим регистр общего назначения с помощью инструкции mov.
Как вы, возможно, помните из моей предыдущей статьи, регистры общего назначения — это r0-r12.
Чтобы настроить регистр общего назначения со значением по нашему выбору, мы можем использовать следующую команду:
mov r0, #7
Где
mov – инструкция скопировать значение в регистр;
r0 – регистр назначения, в котором мы будем хранить временное значение;
#7 – знак решетки означает, что следующее значение является константой. В этом примере я использовал число 7; вы можете выбрать любое другое.
После этого мы готовы приступить к выполнению инструкции по добавлению.
add r1, r0, #3
Где
r1 — регистр назначения, в котором мы собираемся сохранить сумму 7 + 3
r0 — наш первый операнд со значением 7.
#3 – константа, которая будет добавлена к r0. Я использовал значение 3.
На этом этапе давайте соберем этот код и посмотрим в gdb (отладчике GNU), что происходит.
Для сборки я буду использовать GCC:
gcc -g -nostdlib -static -o instructions instructions.s
Где
-g – Включить отладочную информацию
-nostdlib – Не компоновать со стандартной библиотекой (так как мы ее не используем)
-static – Создать статический исполняемый файл
Теперь мы можем открыть исполняемый файл с помощью GDB, но перед этим я установлю GEF (GDB Enhanced Features), который обеспечивает автоматический мониторинг регистров, вывод цветового кода и многое другое.
Чтобы установить GEF, выполните:
raspberrypi> bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
Теперь запустим GDB:
gdb ./instructions
Прежде всего, я собираюсь отключить смещенное пошаговое выполнение, чтобы избежать возможных ошибок в GDB.
(gdb) set displaced-stepping off
После этого мы можем установить точку останова на метке _start, чтобы остановить выполнение там:
(gdb) break _start
Запускаем нашу программу:
(gdb) run
Здесь мы видим, что программа начала выполнение, но остановилась на _start из-за точки останова.
Давайте проверим значение всех регистров:
(gdb) info registers
На этом этапе они пустые. Давайте рассмотрим одну инструкцию по сборке:
(gdb) stepi
И проверьте значение только регистров r0 и r1.
(gdb) info registers r0 r1
И здесь мы видим, что регистр r0 уже хранит значение 0x7 или 7 в десятичной системе счисления.
Если мы выполним следующую инструкцию ассемблера и снова проверим значение регистра с помощью тех же команд, мы увидим значение регистра r1.
Значение r1 равно 0xa или 10 в десятичной системе счисления, как мы и запрограммировали.