Типы данных ARM и регистры.
Это вторая часть учебного пособия по основам ARM, охватывающая типы данных и регистры.
Подобно языкам высокого уровня, ARM поддерживает операции с различными типами данных.
Типы данных, которые мы можем загрузить (или сохранить), могут быть словами со знаком и без знака, полусловами или байтами. Расширения для этих типов данных: -h или -sh для полуслов, -b или -sb для байтов и без расширения для слов. Разница между подписанным и неподписанным типами данных:
Типы данных со знаком могут содержать как положительные, так и отрицательные значения и, следовательно, имеют меньший диапазон.Типы данных без знака могут содержать большие положительные значения (включая «Ноль»), но не могут содержать отрицательные значения и поэтому имеют более широкий диапазон.
Вот несколько примеров того, как эти типы данных могут использоваться с инструкциями Load и Store:
Порядок байтов.
Существует два основных способа просмотра байтов в памяти: Little-Endian (LE) или Big-Endian (BE). Разница заключается в порядке байтов, в котором каждый байт объекта хранится в памяти. В первом варианте значительный байт хранится в наименьшем адресе (адрес ближайший к нулю). Во втором значительные байты хранятся на самом низком адресе. До версии 3 архитектура ARM была с прямым порядком байтов, с тех пор она является двоичным, что означает, что она имеет параметр, который допускает переключаемую последовательность. Например, в ARMv6 инструкции являются фиксированными с прямым порядком байтов, и доступ к данным может быть либо с порядком байтов Little-Endian, либо с прямым порядком байтов, как управляется битом 9, битом E регистра состояния программы (CPSR).
Регистры ARM.
Количество регистров зависит от версии ARM. Согласно Справочному руководству ARM, существует 30 32-разрядных регистров общего назначения , за исключением процессоров на базе ARMv6-M и ARMv7-M. Первые 16 регистров доступны в режиме пользовательского уровня, дополнительные регистры доступны при выполнении привилегированного программного обеспечения (за исключением ARMv6-M и ARMv7-M). В этой серии руководств мы будем работать с регистрами, доступными в привилегированном режиме: r0-15. Эти 16 регистров можно разделить на две группы: регистры общего и специального назначения.
В следующей таблице приведен краткий обзор того, как регистры ARM могут взаимодействовать с регистрами в процессорах Intel.
R0-R12 : может использоваться во время обычных операций для хранения временных значений, указателей (местоположений в памяти) и т. Д. R0, например, может упоминаться как аккумулятор во время арифметических операций или для сохранения результата ранее вызванной функции. R7 становится полезным при работе с системными вызовами, поскольку он хранит номер системного вызова, а R11 помогает нам отслеживать границы в стеке, служащем указателем кадра (будет рассмотрено позже). Кроме того, соглашение о вызове функции в ARM указывает, что первые четыре аргумента функции хранятся в регистрах r0-r3.
R13: SP (указатель стека). Указатель стека указывает на вершину стека. Стек - это область памяти, используемая для хранения функции, которая восстанавливается при возврате функции. Поэтому указатель стека используется для выделения места в стеке путем вычитания значения (в байтах), которое мы хотим выделить из указателя стека. Другими словами, если мы хотим выделить 32-битное значение, мы вычитаем 4 из указателя стека.
R14: LR (Link Link). При выполнении вызова функции регистр связи обновляется с помощью адреса памяти, ссылающегося на следующую инструкцию, из которой была инициирована функция. Это позволяет программе вернуться к функции «parent», которая инициировала вызов функции «child» после завершения функции «child».
R15: ПК (программный счетчик). Счетчик программ автоматически увеличивается на размер выполняемой команды. Этот размер всегда составляет 4 байта в состоянии ARM и 2 байта в режиме THUMB. Когда выполняется инструкция перехода, ПК содержит адрес назначения. Во время выполнения ПК сохраняет адрес текущей инструкции плюс 8 (две инструкции ARM) в состоянии ARM, а текущую инструкцию плюс 4 (две инструкции Thumb) в состоянии большого пальца (v1). Это отличается от x86, где ПК всегда указывает на следующую инструкцию, которая будет выполнена.
Давайте посмотрим, как ПК ведет себя в отладчике. Мы используем следующую программу, чтобы сохранить адрес pc в r0 и включить две случайные инструкции. Давай посмотрим что происходит.
В GDB мы устанавливаем Breakpoint на _start и запускаем ее:
А вот и выведенный код:
Мы видим, что ПК содержит адрес (0x8054) следующей инструкции (mov r0, pc), которая будет выполнена. Теперь давайте выполним следующую инструкцию, после которой R0 должен содержать адрес ПК (0x8054), верно?
…правильно? Неправильно. Посмотрите на адрес R0.Пока мы ожидали, что R0 будет содержать ранее прочитанное значение ПК (0x8054), оно вместо этого содержит значение, которое на две инструкции впереди ПК, которое мы ранее прочитали (0x805c). Из этого примера вы можете видеть, что когда мы непосредственно читаем PC, это следует из определения, что PC указывает на следующую инструкцию; но при отладке ПК указывает две инструкции перед текущим значением ПК (0x8054 + 8 = 0x805C). Это связано с тем, что старые процессоры ARM всегда извлекали две инструкции перед выполняемыми в данный момент инструкциями. Причина, по которой ARM сохраняет это определение, заключается в обеспечении совместимости с более ранними процессорами.
Регистр текущего состояния программы
Когда вы откладываете двоичный файл ARM с помощью gdb, вы видите нечто, называемое Flags:
Регистр $ cpsr показывает значение регистра текущего состояния программы (CPSR), под которым вы можете видеть флаги, прерывания, переполнения, переносы, нули и минусы. Эти флаги представляют определенные биты в регистре CPSR и устанавливаются в соответствии со значением CPSR и становятся полужирными при активации. Биты N, Z, C и V идентичны битам SF, ZF, CF и OF в регистре EFLAG на x86. Эти биты используются для поддержки условного выполнения в условных выражениях и циклах на уровне сборки. Мы рассмотрим коды условий, использованные в части 6: условное выполнение и ветвление .
На рисунке выше показана схема 32-разрядного регистра (CPSR), где левая (<-) сторона содержит младшие значащие биты, а правая (->) - младшие значащие биты. Каждая ячейка (кроме секции GE и M вместе с пустыми) имеет размер один бит. Эти однобитовые разделы определяют различные свойства текущего состояния программы.
Давайте предположим, что мы будем использовать инструкцию CMP для сравнения чисел 1 и 2. Результат будет «отрицательным», потому что 1 - 2 = -1. Когда мы сравниваем два равных числа, например, 2 против 2, устанавливается флаг Z (ноль), потому что 2 - 2 = 0. Имейте в виду, что регистры, используемые с инструкцией CMP, не будут изменены, только CPSR будет изменен на основе результата сравнения этих регистров друг с другом.
Вот как это выглядит в GDB (с установленным GEF): В этом примере мы сравниваем регистры r1 и r0, где r1 = 4 и r0 = 2. Вот как выглядят флаги после выполнения операции cmp r1, r0:
Флаг переноса установлен, потому что мы используем cmp r1, r0 для сравнения 4 с 2 (4 - 2). Напротив, Отрицательный флаг (N) устанавливается, если мы используем cmp r0, r1 для сравнения меньшего числа (2) с большим числом (4).
Вот выдержка из инфоцентра ARM :
APSR содержит следующие флаги состояния ALU:
N - установить, когда результат операции был отрицательным.
Z - установить, когда результатом операции был ноль.
C - Установите, когда операция привела к переносу.
V - Установите, когда операция вызвала oVerflow.
Перенос происходит:
Спасибо за прочтение данной статьи и хорошего дня
neopaket
Оригинальная статья: https://azeria-labs.com/arm-data-types-and-registers-part-2/
Это вторая часть учебного пособия по основам ARM, охватывающая типы данных и регистры.
Подобно языкам высокого уровня, ARM поддерживает операции с различными типами данных.
Типы данных, которые мы можем загрузить (или сохранить), могут быть словами со знаком и без знака, полусловами или байтами. Расширения для этих типов данных: -h или -sh для полуслов, -b или -sb для байтов и без расширения для слов. Разница между подписанным и неподписанным типами данных:
Типы данных со знаком могут содержать как положительные, так и отрицательные значения и, следовательно, имеют меньший диапазон.Типы данных без знака могут содержать большие положительные значения (включая «Ноль»), но не могут содержать отрицательные значения и поэтому имеют более широкий диапазон.
Вот несколько примеров того, как эти типы данных могут использоваться с инструкциями Load и Store:
Код:
ldr = Load Word
ldrh = Load unsigned Half Word
ldrsh = Load signed Half Word
ldrb = Load unsigned Byte
ldrsb = Load signed Bytes
str = Store Word
strh = Store unsigned Half Word
strsh = Store signed Half Word
strb = Store unsigned Byte
strsb = Store signed Byte
Порядок байтов.
Существует два основных способа просмотра байтов в памяти: Little-Endian (LE) или Big-Endian (BE). Разница заключается в порядке байтов, в котором каждый байт объекта хранится в памяти. В первом варианте значительный байт хранится в наименьшем адресе (адрес ближайший к нулю). Во втором значительные байты хранятся на самом низком адресе. До версии 3 архитектура ARM была с прямым порядком байтов, с тех пор она является двоичным, что означает, что она имеет параметр, который допускает переключаемую последовательность. Например, в ARMv6 инструкции являются фиксированными с прямым порядком байтов, и доступ к данным может быть либо с порядком байтов Little-Endian, либо с прямым порядком байтов, как управляется битом 9, битом E регистра состояния программы (CPSR).
Регистры ARM.
Количество регистров зависит от версии ARM. Согласно Справочному руководству ARM, существует 30 32-разрядных регистров общего назначения , за исключением процессоров на базе ARMv6-M и ARMv7-M. Первые 16 регистров доступны в режиме пользовательского уровня, дополнительные регистры доступны при выполнении привилегированного программного обеспечения (за исключением ARMv6-M и ARMv7-M). В этой серии руководств мы будем работать с регистрами, доступными в привилегированном режиме: r0-15. Эти 16 регистров можно разделить на две группы: регистры общего и специального назначения.
# | Alias | Цель |
| R0 | - | Общее назначение |
| R1 | - | Общее назначение |
| R2 | - | Общее назначение |
| R3 | - | Общее назначение |
| R4 | - | Общее назначение |
| R5 | - | Общее назначение |
| R6 | - | Общее назначение |
| R7 | - | Удержание системного вызова |
| R8 | - | Общее назначение |
| R9 | - | Общее назначение |
| R10 | - | Общее назначение |
| R11 | FP | Указание кадра |
Регистры специального назначения | ||
| R12 | IP | Внутрепроцедурный вызов |
| R13 | SP | Указатель стека |
| R14 | LR | Регистр ссылок |
| R15 | PC | Счетчик команд |
| CPSR | - | Регистр текущего состояния программы |
В следующей таблице приведен краткий обзор того, как регистры ARM могут взаимодействовать с регистрами в процессорах Intel.
ARM | Описание | x86 |
| R0 | Общее назначение | EAX |
| R1-R5, | Общее назначение | EBX, ECX, EDX, ESI, EDI |
| R6-R10 | Общее назначение | - |
| R11 (FP) | Указатель кадров | EBP |
| R12 | Внутрипроцедурный вызов | - |
| R13 (SP) | Указатель стека | ESP |
| R14 (LR) | Регистр ссылок | - |
| R15 (PC) | <- Счетчик программ / Указатель команд -> | EIP |
| CPSR | Cодержит информацию о состоянии процессора и управляющую информацию | EFLAGS |
R0-R12 : может использоваться во время обычных операций для хранения временных значений, указателей (местоположений в памяти) и т. Д. R0, например, может упоминаться как аккумулятор во время арифметических операций или для сохранения результата ранее вызванной функции. R7 становится полезным при работе с системными вызовами, поскольку он хранит номер системного вызова, а R11 помогает нам отслеживать границы в стеке, служащем указателем кадра (будет рассмотрено позже). Кроме того, соглашение о вызове функции в ARM указывает, что первые четыре аргумента функции хранятся в регистрах r0-r3.
R13: SP (указатель стека). Указатель стека указывает на вершину стека. Стек - это область памяти, используемая для хранения функции, которая восстанавливается при возврате функции. Поэтому указатель стека используется для выделения места в стеке путем вычитания значения (в байтах), которое мы хотим выделить из указателя стека. Другими словами, если мы хотим выделить 32-битное значение, мы вычитаем 4 из указателя стека.
R14: LR (Link Link). При выполнении вызова функции регистр связи обновляется с помощью адреса памяти, ссылающегося на следующую инструкцию, из которой была инициирована функция. Это позволяет программе вернуться к функции «parent», которая инициировала вызов функции «child» после завершения функции «child».
R15: ПК (программный счетчик). Счетчик программ автоматически увеличивается на размер выполняемой команды. Этот размер всегда составляет 4 байта в состоянии ARM и 2 байта в режиме THUMB. Когда выполняется инструкция перехода, ПК содержит адрес назначения. Во время выполнения ПК сохраняет адрес текущей инструкции плюс 8 (две инструкции ARM) в состоянии ARM, а текущую инструкцию плюс 4 (две инструкции Thumb) в состоянии большого пальца (v1). Это отличается от x86, где ПК всегда указывает на следующую инструкцию, которая будет выполнена.
Давайте посмотрим, как ПК ведет себя в отладчике. Мы используем следующую программу, чтобы сохранить адрес pc в r0 и включить две случайные инструкции. Давай посмотрим что происходит.
Код:
.section .text
.global _start
_start:
mov r0, pc
mov r1, #2
add r2, r1, r1
bkpt
В GDB мы устанавливаем Breakpoint на _start и запускаем ее:
Код:
gef> br _start
Breakpoint 1 at 0x8054
gef> run
А вот и выведенный код:
Код:
$r0 0x00000000 $r1 0x00000000 $r2 0x00000000 $r3 0x00000000
$r4 0x00000000 $r5 0x00000000 $r6 0x00000000 $r7 0x00000000
$r8 0x00000000 $r9 0x00000000 $r10 0x00000000 $r11 0x00000000
$r12 0x00000000 $sp 0xbefff7e0 $lr 0x00000000 $pc 0x00008054
$cpsr 0x00000010
0x8054 <_start> mov r0, pc <- $pc
0x8058 <_start+4> mov r0, #2
0x805c <_start+8> add r1, r0, r0
0x8060 <_start+12> bkpt 0x0000
0x8064 andeq r1, r0, r1, asr #10
0x8068 cmnvs r5, r0, lsl #2
0x806c tsteq r0, r2, ror #18
0x8070 andeq r0, r0, r11
0x8074 tsteq r8, r6, lsl #6
Мы видим, что ПК содержит адрес (0x8054) следующей инструкции (mov r0, pc), которая будет выполнена. Теперь давайте выполним следующую инструкцию, после которой R0 должен содержать адрес ПК (0x8054), верно?
Код:
$r0 0x0000805c $r1 0x00000000 $r2 0x00000000 $r3 0x00000000
$r4 0x00000000 $r5 0x00000000 $r6 0x00000000 $r7 0x00000000
$r8 0x00000000 $r9 0x00000000 $r10 0x00000000 $r11 0x00000000
$r12 0x00000000 $sp 0xbefff7e0 $lr 0x00000000 $pc 0x00008058
$cpsr 0x00000010
0x8058 <_start+4> mov r0, #2 <- $pc
0x805c <_start+8> add r1, r0, r0
0x8060 <_start+12> bkpt 0x0000
0x8064 andeq r1, r0, r1, asr #10
0x8068 cmnvs r5, r0, lsl #2
0x806c tsteq r0, r2, ror #18
0x8070 andeq r0, r0, r11
0x8074 tsteq r8, r6, lsl #6
0x8078 adfcssp f0, f0, #4.0
…правильно? Неправильно. Посмотрите на адрес R0.Пока мы ожидали, что R0 будет содержать ранее прочитанное значение ПК (0x8054), оно вместо этого содержит значение, которое на две инструкции впереди ПК, которое мы ранее прочитали (0x805c). Из этого примера вы можете видеть, что когда мы непосредственно читаем PC, это следует из определения, что PC указывает на следующую инструкцию; но при отладке ПК указывает две инструкции перед текущим значением ПК (0x8054 + 8 = 0x805C). Это связано с тем, что старые процессоры ARM всегда извлекали две инструкции перед выполняемыми в данный момент инструкциями. Причина, по которой ARM сохраняет это определение, заключается в обеспечении совместимости с более ранними процессорами.
Регистр текущего состояния программы
Когда вы откладываете двоичный файл ARM с помощью gdb, вы видите нечто, называемое Flags:
Регистр $ cpsr показывает значение регистра текущего состояния программы (CPSR), под которым вы можете видеть флаги, прерывания, переполнения, переносы, нули и минусы. Эти флаги представляют определенные биты в регистре CPSR и устанавливаются в соответствии со значением CPSR и становятся полужирными при активации. Биты N, Z, C и V идентичны битам SF, ZF, CF и OF в регистре EFLAG на x86. Эти биты используются для поддержки условного выполнения в условных выражениях и циклах на уровне сборки. Мы рассмотрим коды условий, использованные в части 6: условное выполнение и ветвление .
На рисунке выше показана схема 32-разрядного регистра (CPSR), где левая (<-) сторона содержит младшие значащие биты, а правая (->) - младшие значащие биты. Каждая ячейка (кроме секции GE и M вместе с пустыми) имеет размер один бит. Эти однобитовые разделы определяют различные свойства текущего состояния программы.
| Флаг | Описание |
| N (отрицательный) | Включено, если в результате выполнения инструкции получено отрицательное число. |
| Z (ноль) | Включено, если результат инструкции дает нулевое значение. |
| C (Carry) | Включается, если результат инструкции дает значение, которое требует 33-го бита для полного представления. |
| V (переполнение) | Включается, если в результате выполнения инструкции получено значение, которое не может быть представлено в дополнении 32-битного второго. |
| E (Endian-бит) | ARM может работать с прямым или прямым порядком байтов. Этот бит установлен в 0 для младшего порядка или в 1 для старшего порядка. |
| T ("большой палец") | Этот бит устанавливается, если вы находитесь в состоянии "большого пальца", и отключается, когда вы находитесь в состоянии ARM. |
| М (биты режима) | Эти биты определяют текущий режим привилегий (USR, SVC и т. Д.). |
| J (Джазель) | Третье состояние выполнения, которое позволяет некоторым процессорам ARM выполнять аппаратный байт-код Java. |
Давайте предположим, что мы будем использовать инструкцию CMP для сравнения чисел 1 и 2. Результат будет «отрицательным», потому что 1 - 2 = -1. Когда мы сравниваем два равных числа, например, 2 против 2, устанавливается флаг Z (ноль), потому что 2 - 2 = 0. Имейте в виду, что регистры, используемые с инструкцией CMP, не будут изменены, только CPSR будет изменен на основе результата сравнения этих регистров друг с другом.
Вот как это выглядит в GDB (с установленным GEF): В этом примере мы сравниваем регистры r1 и r0, где r1 = 4 и r0 = 2. Вот как выглядят флаги после выполнения операции cmp r1, r0:
Флаг переноса установлен, потому что мы используем cmp r1, r0 для сравнения 4 с 2 (4 - 2). Напротив, Отрицательный флаг (N) устанавливается, если мы используем cmp r0, r1 для сравнения меньшего числа (2) с большим числом (4).
Вот выдержка из инфоцентра ARM :
APSR содержит следующие флаги состояния ALU:
N - установить, когда результат операции был отрицательным.
Z - установить, когда результатом операции был ноль.
C - Установите, когда операция привела к переносу.
V - Установите, когда операция вызвала oVerflow.
Перенос происходит:
- если результат сложения больше или равен 2^32 (^ - показатель степени)
- если результат вычитания положительный или ноль
- в результате встроенной операции смены ствола в движении или логической инструкции.
Спасибо за прочтение данной статьи и хорошего дня
neopaket
Оригинальная статья: https://azeria-labs.com/arm-data-types-and-registers-part-2/
Последнее редактирование модератором: