Introduction to ARM64v8
Рівні винятків - EL (ARM64v8)
У архітектурі ARMv8 рівні виконання, відомі як Рівні Винятків (EL), визначають рівень привілегій та можливості середовища виконання. Існує чотири рівні винятків, від EL0 до EL3, кожен служить різним цілям:
EL0 - Режим користувача:
Це найменш привілейований рівень і використовується для виконання звичайного програмного коду.
Додатки, що працюють на рівні EL0, ізольовані одне від одного та від системного програмного забезпечення, що підвищує безпеку та стабільність.
EL1 - Режим ядра операційної системи:
Більшість ядер операційних систем працюють на цьому рівні.
EL1 має більше привілеїв, ніж EL0 та може отримувати доступ до ресурсів системи, але з деякими обмеженнями для забезпечення цілісності системи.
EL2 - Режим гіпервізора:
Цей рівень використовується для віртуалізації. Гіпервізор, що працює на рівні EL2, може керувати кількома операційними системами (кожна у власному EL1), що працюють на одному фізичному обладнанні.
EL2 надає можливості для ізоляції та керування віртуалізованими середовищами.
EL3 - Режим монітора безпеки:
Це найбільш привілейований рівень і часто використовується для безпечного завантаження та довірених середовищ виконання.
EL3 може керувати та контролювати доступи між безпечними та небезпечними станами (наприклад, безпечний запуск, довірена ОС тощо).
Використання цих рівнів дозволяє структурованим та безпечним способом керувати різними аспектами системи, від користувацьких додатків до найбільш привілейованого системного програмного забезпечення. Підхід ARMv8 до рівнів привілеїв допомагає ефективно ізолювати різні компоненти системи, тим самим підвищуючи безпеку та надійність системи.
Регістри (ARM64v8)
У ARM64 є 31 регістр загального призначення, позначених як x0
до x30
. Кожен може зберігати значення 64 біт (8 байт). Для операцій, які вимагають лише значень 32 біт, до тих же регістрів можна отримати доступ у режимі 32 біт за допомогою імен w0 до w30.
x0
доx7
- Зазвичай використовуються як регістри-запаси та для передачі параметрів у підпрограми.
x0
також містить дані повернення функції.
x8
- У ядрі Linux,x8
використовується як номер системного виклику для інструкціїsvc
. У macOS використовується x16!x9
доx15
- Додаткові тимчасові регістри, часто використовуються для локальних змінних.x16
таx17
- Регістри внутрішньопроцедурного виклику. Тимчасові регістри для негайних значень. Їх також використовують для непрямих викликів функцій та заготовок PLT (Procedure Linkage Table).
x16
використовується як номер системного виклику для інструкціїsvc
в macOS.
x18
- Регістр платформи. Його можна використовувати як регістр загального призначення, але на деяких платформах цей регістр зарезервований для платформено-специфічних використань: Вказівник на поточний блок середовища потоку в Windows або вказівник на поточну структуру завдання, що виконується в ядрі Linux.x19
доx28
- Це регістри, які зберігаються викликачем. Функція повинна зберігати значення цих регістрів для свого викликача, тому вони зберігаються в стеку та відновлюються перед поверненням до викликача.x29
- Вказівник рамки для відстеження стекової рамки. Коли створюється нова стекова рамка через виклик функції, регістрx29
зберігається в стеку, а адреса нової рамки (адресаsp
) зберігається в цьому реєстрі.
Цей регістр також може використовуватися як регістр загального призначення, хоча зазвичай використовується як посилання на локальні змінні.
x30
абоlr
- Регістр посилання. Він містить адресу повернення, коли виконується інструкціяBL
(Branch with Link) абоBLR
(Branch with Link to Register), зберігаючи значенняpc
в цьому регістрі.
Його також можна використовувати як будь-який інший регістр.
Якщо поточна функція збирається викликати нову функцію і, отже, перезаписати
lr
, вона збереже його в стеку на початку, це епілог (stp x29, x30 , [sp, #-48]; mov x29, sp
-> Зберегтиfp
таlr
, створити простір та отримати новийfp
) та відновить його в кінці, це пролог (ldp x29, x30, [sp], #48; ret
-> Відновитиfp
таlr
та повернутися).
sp
- Вказівник стеку, використовується для відстеження верхушки стеку.
значення
sp
завжди повинно бути збережено принаймні з вирівнанням квадратного слова, або може виникнути виняток вирівнювання.
pc
- Лічильник програми, який вказує на наступну інструкцію. Цей регістр можна оновлювати лише через генерації винятків, повернення винятків та гілки. Єдині звичайні інструкції, які можуть читати цей регістр, це інструкції гілки з посиланням (BL, BLR) для збереження адресиpc
вlr
(Регістр посилання).xzr
- Регістр нуля. Також називаєтьсяwzr
у формі регістра 32 біт. Може використовуватися для отримання нульового значення легко (загальна операція) або для виконання порівнянь за допомогоюsubs
якsubs XZR, Xn, #10
зберігаючи отримані дані нікуди (уxzr
).
Регістри Wn
є версією 32 біт регістра Xn
.
Регістри SIMD та з плаваючою комою
Крім того, є ще 32 регістри довжиною 128 біт, які можна використовувати в оптимізованих операціях одночасного виконання кількох даних (SIMD) та для виконання операцій з плаваючою комою. Їх називають регістрами Vn, хоча вони також можуть працювати в режимах 64-біт, 32-біт, 16-біт та 8-біт, і тоді їх називають Qn
, Dn
, Sn
, Hn
та Bn
.
Реєстри системи
Існує сотні реєстрів системи, також називаних реєстрами спеціального призначення (SPR), які використовуються для моніторингу та контролю поведінки процесорів.
Їх можна читати або встановлювати лише за допомогою спеціальної інструкції mrs
та msr
.
Спеціальні реєстри TPIDR_EL0
та TPIDDR_EL0
часто зустрічаються при зворотньому проектуванні. Суфікс EL0
вказує на мінімальне виключення, з якого можна отримати доступ до реєстру (у цьому випадку EL0 - це звичайний рівень виключення (привілей), на якому працюють звичайні програми).
Їх часто використовують для зберігання базової адреси області пам'яті локального сховища потоку. Зазвичай перший може бути прочитаний та записаний програмами, що працюють на EL0, але другий може бути прочитаний з EL0 та записаний з EL1 (як ядро).
mrs x0, TPIDR_EL0 ; Прочитати TPIDR_EL0 у x0
msr TPIDR_EL0, X0 ; Записати x0 у TPIDR_EL0
PSTATE
PSTATE містить кілька компонентів процесу, серіалізованих у видимий для операційної системи спеціальний реєстр SPSR_ELx
, де X - рівень дозволу спрацьованого виключення (це дозволяє відновити стан процесу після завершення виключення).
Це доступні поля:
Прапорці умов
N
,Z
,C
таV
:N
означає, що операція дала від'ємний результатZ
означає, що операція дала нульC
означає, що операція виконанаV
означає, що операція дала підписане переповнення:Сума двох позитивних чисел дає від'ємний результат.
Сума двох від'ємних чисел дає позитивний результат.
У відніманні, коли від меншого позитивного числа віднімається велике від'ємне число (або навпаки), і результат не може бути представлений у межах заданого розміру біта.
Очевидно, що процесор не знає, чи операція підписана чи ні, тому він буде перевіряти C та V у операціях та вказувати, чи відбувся перенос у випадку, якщо він був підписаний або непідписаний.
Не всі інструкції оновлюють ці прапорці. Деякі, наприклад, CMP
або TST
, роблять це, а інші, які мають суфікс s, наприклад ADDS
, також роблять це.
Прапор ширини поточного реєстра (
nRW
): Якщо прапорець має значення 0, програма буде працювати в стані виконання AArch64 після відновлення.Поточний рівень виключення (
EL
): Звичайна програма, що працює на EL0, матиме значення 0Прапор поступового виконання (
SS
): Використовується відладчиками для поетапного виконання, встановлюючи прапорець SS на 1 всерединіSPSR_ELx
через виключення. Програма виконає крок і видасть виняток поетапного виконання.Прапор стану недопустимого виключення (
IL
): Використовується для позначення, коли привілейоване програмне забезпечення виконує недійсний перехід рівня виключення, цей прапорець встановлюється на 1, і процесор викликає незаконне виключення стану.Прапори
DAIF
: Ці прапорці дозволяють привілейованій програмі вибірково маскувати певні зовнішні виключення.Якщо
A
дорівнює 1, це означає, що будуть викликані асинхронні відмови.I
налаштовує відповідь на зовнішні запити переривань (IRQ), а F пов'язаний з швидкими запитами переривань (FIR).Прапори вибору вказівника стеку (
SPS
): Привілейовані програми, що працюють на EL1 та вище, можуть перемикатися між використанням власного реєстра вказівника стеку та реєстра користувача (наприклад, міжSP_EL1
таEL0
). Це перемикання виконується шляхом запису в спеціальний реєстрSPSel
. Це не можна зробити з EL0.
Конвенція виклику (ARM64v8)
Конвенція виклику ARM64 вказує, що перші вісім параметрів функції передаються в реєстрах x0
до x7
. Додаткові параметри передаються на стек. Результат повертається в реєстрі x0
, або також в x1
, якщо він має довжину 128 біт. Реєстри x19
до x30
та sp
повинні бути збережені під час викликів функцій.
При читанні функції в асемблері шукайте пролог та епілог функції. Пролог зазвичай включає збереження вказівника кадру (x29
), створення нового вказівника кадру та виділення місця стеку. Епілог зазвичай включає відновлення збереженого вказівника кадру та повернення з функції.
Конвенція виклику в Swift
У Swift є власна конвенція виклику, яку можна знайти за посиланням https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64
Загальні інструкції (ARM64v8)
Інструкції ARM64 зазвичай мають формат opcode dst, src1, src2
, де opcode
- це операція, яку потрібно виконати (наприклад, add
, sub
, mov
, тощо), dst
- це реєстр призначення, де буде збережено результат, а src1
та src2
- джерела реєстрів. Також можна використовувати негайні значення замість джерелних реєстрів.
mov
: Перемістити значення з одного реєстра в інший.Приклад:
mov x0, x1
— Це переміщує значення зx1
вx0
.ldr
: Завантажити значення з пам'яті в реєстр.Приклад:
ldr x0, [x1]
— Це завантажує значення з місця пам'яті, на яке вказуєx1
, вx0
.Режим зміщення: Вказується зміщення, яке впливає на вказівник абоїну, наприклад:
ldr x2, [x1, #8]
, це завантажить в x2 значення з x1 + 8ldr x2, [x0, x1, lsl #2]
, це завантажить в x2 об'єкт з масиву x0, з позиції x1 (індекс) * 4Режим передварительного індексування: Це застосовує обчислення до вихідного значення, отримує результат і також зберігає нове вихідне значення в вихідному.
ldr x2, [x1, #8]!
, це завантажитьx1 + 8
вx2
і збереже в x1 результатx1 + 8
str lr, [sp, #-4]!
, Зберегти вказівник ланцюга в sp та оновити реєстр spРежим післяіндексації: Це схоже на попереднє, але спочатку звертається до адреси пам'яті, а потім обчислюється та зберігається зміщення.
ldr x0, [x1], #8
, завантажитиx1
вx0
та оновити x1 наx1 + 8
Адресування відносно PC: У цьому випадку адреса для завантаження обчислюється відносно реєстра PC
ldr x1, =_start
, Це завантажить адресу, де починається символ_start
, в x1 відносно поточного PC.str
: Зберегти значення з реєстра в пам'ять.Приклад:
str x0, [x1]
— Це зберігає значення вx0
в місце пам'яті, на яке вказуєx1
.ldp
: Завантажити пару реєстрів. Ця інструкція завантажує два реєстри з послідовних місць пам'яті. Адреса пам'яті зазвичай формується додаванням зміщення до значення в іншому реєстрі.Приклад:
ldp x0, x1, [x2]
— Це завантажуєx0
таx1
з місць пам'яті вx2
таx2 + 8
, відповідно.stp
: Зберегти пару реєстрів. Ця інструкція зберігає два реєстри в послідовних місцях пам'яті. Адреса пам'яті зазвичай формується додаванням зміщення до значення в іншому реєстрі.Приклад:
stp x0, x1, [sp]
— Це зберігаєx0
таx1
в місцях пам'яті вsp
таsp + 8
, відповідно.stp x0, x1, [sp, #16]!
— Це зберігаєx0
таx1
в місцях пам'яті вsp+16
таsp + 24
, відповідно, та оновлюєsp
наsp+16
.add
: Додати значення двох реєстрів та зберегти результат в реєстрі.Синтаксис: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]
Xn1 -> Призначення
Xn2 -> Операнд 1
Xn3 | #imm -> Операнд 2 (регістр або негайний)
[shift #N | RRX] -> Виконати зсув або викликати RRX
Приклад:
add x0, x1, x2
— Це додає значення вx1
таx2
разом і зберігає результат вx0
.add x5, x5, #1, lsl #12
— Це дорівнює 4096 (1 зсув 12 разів) -> 1 0000 0000 0000 0000adds
Це виконуєadd
та оновлює прапорціsub
: Віднімання значень двох регістрів та збереження результату в регістрі.Перевірте синтаксис
add
.Приклад:
sub x0, x1, x2
— Це віднімає значення вx2
відx1
та зберігає результат вx0
.subs
Це схоже на віднімання, але оновлює прапорціmul
: Помножити значення двох регістрів та зберегти результат в регістрі.Приклад:
mul x0, x1, x2
— Це множить значення вx1
таx2
та зберігає результат вx0
.div
: Розділити значення одного регістра на інший та зберегти результат в регістрі.Приклад:
div x0, x1, x2
— Це ділить значення вx1
наx2
та зберігає результат вx0
.lsl
,lsr
,asr
,ror
,rrx
:Логічний зсув вліво: Додавання 0 з кінця, переміщаючи інші біти вперед (множення на n-рази 2)
Логічний зсув вправо: Додавання 1 з початку, переміщаючи інші біти назад (ділення на n-рази 2 у беззнаковому вигляді)
Арифметичний зсув вправо: Подібно до
lsr
, але замість додавання 0, якщо найбільш значущий біт - 1, **додаються 1 (**ділення на n-рази 2 у знаковому вигляді)Поворот вправо: Подібно до
lsr
, але все, що видаляється зправа, додається зліваПоворот вправо з розширенням: Подібно до
ror
, але з прапорцем переносу як "найбільш значущим бітом". Таким чином, прапорець переносу переміщується на біт 31, а видалений біт - у прапорець переносу.bfm
: Переміщення бітів поля, ці операції копіюють біти0...n
зі значення та розміщують їх у позиціяхm..m+n
.#s
вказує найлівіший біт позиції, а#r
- кількість правих зсувів.Переміщення бітів поля:
BFM Xd, Xn, #r
Підписане переміщення бітів поля:
SBFM Xd, Xn, #r, #s
Непідписане переміщення бітів поля:
UBFM Xd, Xn, #r, #s
Вилучення та вставка бітів поля: Копіювання бітового поля з регістра та копіювання його в інший регістр.
BFI X1, X2, #3, #4
Вставити 4 біти з X2 з 3-го біту X1BFXIL X1, X2, #3, #4
Витягнути з 3-го біту X2 чотири біти та скопіювати їх в X1SBFIZ X1, X2, #3, #4
Розширити знак 4 біти з X2 та вставити їх в X1, починаючи з позиції біта 3, обнуляючи праві бітиSBFX X1, X2, #3, #4
Витягує 4 біти, починаючи з біта 3 з X2, розширює знак та поміщає результат в X1UBFIZ X1, X2, #3, #4
Розширити нулями 4 біти з X2 та вставити їх в X1, починаючи з позиції біта 3, обнуляючи праві бітиUBFX X1, X2, #3, #4
Витягує 4 біти, починаючи з біта 3 з X2 та поміщає результат з розширенням нулями в X1.Розширення знаку до X: Розширює знак (або додає просто 0 у беззнаковій версії) значення для можливості виконання операцій з ним:
SXTB X1, W2
Розширює знак байта з W2 до X1 (W2
- це половинаX2
) для заповнення 64 бітівSXTH X1, W2
Розширює знак 16-бітного числа з W2 до X1 для заповнення 64 бітівSXTW X1, W2
Розширює знак байта з W2 до X1 для заповнення 64 бітівUXTB X1, W2
Додає 0 (беззнакове) до байта з W2 до X1 для заповнення 64 бітівextr
: Витягує біти з вказаної пари конкатенованих регістрів.Приклад:
EXTR W3, W2, W1, #3
Це конкатенує W1+W2 та отримує від біта 3 W2 до біта 3 W1 та зберігає це в W3.cmp
: Порівняти два регістри та встановити умовні прапорці. Це псевдонім дляsubs
, встановлюючи регістр призначення на нульовий регістр. Корисно знати, якщоm == n
.Підтримує той самий синтаксис, що й
subs
Приклад:
cmp x0, x1
— Це порівнює значення вx0
таx1
та встановлює умовні прапорці відповідно.cmn
: Порівняти від'ємний операнд. У цьому випадку це псевдонім дляadds
та підтримує той самий синтаксис. Корисно знати, якщоm == -n
.ccmp
: Умовне порівняння, це порівняння, яке буде виконано лише у випадку, якщо попереднє порівняння було істинним та спеціально встановить біти nzcv.cmp x1, x2; ccmp x3, x4, 0, NE; blt _func
-> якщо x1 != x2 та x3 < x4, перейти до funcЦе тому, що
ccmp
буде виконано лише у випадку, якщо попереднійcmp
бувNE
, якщо цього не було, бітиnzcv
будуть встановлені на 0 (що не задовольнить порівнянняblt
).Це також може бути використано як
ccmn
(таке саме, але від'ємне, якcmp
протиcmn
).tst
: Він перевіряє, чи обидва значення порівняння рівні 1 (працює як та І без збереження результату десь). Корисно перевірити реєстр зі значенням та перевірити, чи будь-які біти реєстра, вказані в значенні, рівні 1.Приклад:
tst X1, #7
Перевірити, чи будь-які останні 3 біти X1 рівні 1teq
: Операція XOR з відкиданням результатуb
: Безумовний перехідПриклад:
b myFunction
Зауважте, що це не заповнить регістр посилання адресою повернення (не підходить для викликів підпрограм, які потрібно повертатися назад)
bl
: Перехід з посиланням, використовується для виклику підпрограми. Зберігає адресу повернення вx30
.Приклад:
bl myFunction
— Це викликає функціюmyFunction
та зберігає адресу повернення вx30
.Зауважте, що це не заповнить регістр посилання адресою повернення (не підходить для викликів підпрограм, які потрібно повертатися назад)
blr
: Перехід з посиланням на регістр, використовується для виклику підпрограми, де ціль вказана в регістрі. Зберігає адресу повернення вx30
. (ЦеПриклад:
blr x1
— Це викликає функцію, адреса якої міститься вx1
, та зберігає адресу повернення вx30
.ret
: Повернення з підпрограми, зазвичай використовуючи адресу вx30
.Приклад:
ret
— Це повертається з поточної підпрограми, використовуючи адресу повернення вx30
.b.<cond>
: Умовні переходиb.eq
: Перехід, якщо рівно, на основі попередньої інструкціїcmp
.Приклад:
b.eq label
— Якщо попередня інструкціяcmp
знайшла два рівні значення, це переходить наlabel
.b.ne
: Гілка, якщо не рівно. Ця інструкція перевіряє умовні прапорці (які були встановлені попередньою інструкцією порівняння), і якщо порівнювані значення не рівні, вона переходить на мітку або адресу.Приклад: Після інструкції
cmp x0, x1
,b.ne label
— Якщо значення вx0
таx1
не рівні, це переходить наlabel
.cbz
: Порівняти та перейти на нуль. Ця інструкція порівнює регістр з нулем, і якщо вони рівні, вона переходить на мітку або адресу.Приклад:
cbz x0, label
— Якщо значення вx0
дорівнює нулю, це переходить наlabel
.cbnz
: Порівняти та перейти на ненуль. Ця інструкція порівнює регістр з нулем, і якщо вони не рівні, вона переходить на мітку або адресу.Приклад:
cbnz x0, label
— Якщо значення вx0
не дорівнює нулю, це переходить наlabel
.tbnz
: Тест біту та перехід на ненульПриклад:
tbnz x0, #8, label
tbz
: Тест біту та перехід на нульПриклад:
tbz x0, #8, label
Умовні операції вибору: Це операції, поведінка яких змінюється в залежності від умовних бітів.
csel Xd, Xn, Xm, cond
->csel X0, X1, X2, EQ
-> Якщо істина, X0 = X1, якщо хиба, X0 = X2csinc Xd, Xn, Xm, cond
-> Якщо істина, Xd = Xn, якщо хиба, Xd = Xm + 1cinc Xd, Xn, cond
-> Якщо істина, Xd = Xn + 1, якщо хиба, Xd = Xncsinv Xd, Xn, Xm, cond
-> Якщо істина, Xd = Xn, якщо хиба, Xd = NOT(Xm)cinv Xd, Xn, cond
-> Якщо істина, Xd = NOT(Xn), якщо хиба, Xd = Xncsneg Xd, Xn, Xm, cond
-> Якщо істина, Xd = Xn, якщо хиба, Xd = - Xmcneg Xd, Xn, cond
-> Якщо істина, Xd = - Xn, якщо хиба, Xd = Xncset Xd, Xn, Xm, cond
-> Якщо істина, Xd = 1, якщо хиба, Xd = 0csetm Xd, Xn, Xm, cond
-> Якщо істина, Xd = <всі 1>, якщо хиба, Xd = 0adrp
: Обчислити адресу сторінки символу та зберегти її в регістрі.Приклад:
adrp x0, symbol
— Це обчислює адресу сторінкиsymbol
та зберігає її вx0
.ldrsw
: Завантажити знакове 32-бітне значення з пам'яті та розширити його до 64 бітів.Приклад:
ldrsw x0, [x1]
— Це завантажує знакове 32-бітне значення з місця пам'яті, на яке вказуєx1
, розширює його до 64 бітів та зберігає його вx0
.stur
: Зберегти значення регістра в місце пам'яті, використовуючи зсув від іншого регістра.Приклад:
stur x0, [x1, #4]
— Це зберігає значення вx0
в місце пам'яті, яке на 4 байти більше від адреси, що знаходиться в даний момент вx1
.svc
: Здійснити системний виклик. Він означає "Виклик наглядача". Коли процесор виконує цю інструкцію, він переходить з режиму користувача в режим ядра та переходить до конкретного місця в пам'яті, де знаходиться код обробки системного виклику ядра.Приклад:
Пролог функції
Зберегти регістр ланцюга та вказівника на фрейм в стек:
Встановіть новий вказівник рамки:
mov x29, sp
(встановлює новий вказівник рамки для поточної функції)Виділіть місце в стеку для локальних змінних (якщо потрібно):
sub sp, sp, <size>
(де<size>
- це кількість байтів, необхідних)
Епілог функції
Звільніть локальні змінні (якщо були виділені):
add sp, sp, <size>
Відновіть регістр посилання та вказівник рамки:
Повернення:
ret
(повертає управління викликачу, використовуючи адресу в регістрі посилань)
Стан виконання AARCH32
Armv8-A підтримує виконання програм 32-біт. AArch32 може працювати в одному з двох наборів інструкцій: A32
та T32
і може перемикатися між ними за допомогою взаємодії
.
Привілейовані 64-бітні програми можуть планувати виконання 32-бітних програм, виконуючи перехід рівня винятку до менш привілейованого 32-бітного.
Зверніть увагу, що перехід з 64-бітного на 32-бітний відбувається з меншим рівнем винятку (наприклад, 64-бітна програма в EL1 спричиняє виконання програми в EL0). Це виконується шляхом встановлення біту 4 спеціального регістра SPSR_ELx
на 1, коли потік обробки AArch32
готовий до виконання, а решта SPSR_ELx
зберігає програми AArch32
CPSR. Потім привілейований процес викликає інструкцію ERET
, щоб процесор перейшов до AArch32
, увійшовши в A32 або T32 в залежності від CPSR**.**
Взаємодія
відбувається за допомогою бітів J та T CPSR. J=0
та T=0
означає A32
, а J=0
та T=1
означає T32. Це в основному означає встановлення найнижчого біту на 1, щоб показати, що набір інструкцій - T32.
Це встановлюється під час інструкцій гілки взаємодії, але також може бути встановлено безпосередньо іншими інструкціями, коли PC встановлено як регістр призначення. Приклад:
Інший приклад:
Реєстри
Є 16 регістрів по 32 біти (r0-r15). Від r0 до r14 їх можна використовувати для будь-якої операції, проте деякі з них зазвичай зарезервовані:
r15
: Лічильник програми (завжди). Містить адресу наступної інструкції. У режимі A32 поточний + 8, у режимі T32, поточний + 4.r11
: Вказівник рамкиr12
: Регістр внутрішньопроцедурного викликуr13
: Вказівник стекуr14
: Регістр посилання
Крім того, регістри резервуються в банківських реєстрах
. Це місця, які зберігають значення регістрів, що дозволяє виконувати швидку зміну контексту при обробці винятків та привілейованих операцій, щоб уникнути необхідності вручну зберігати та відновлювати регістри кожного разу.
Це виконується шляхом збереження стану процесора від CPSR
до SPSR
режиму процесора, до якого взято виняток. Під час повернення з винятку, CPSR
відновлюється з SPSR
.
CPSR - Регістр поточного стану програми
У AArch32 CPSR працює аналогічно до PSTATE
в AArch64 і також зберігається в SPSR_ELx
під час винятку для подальшого відновлення виконання:
Поля поділені на деякі групи:
Регістр статусу програми застосування (APSR): Арифметичні прапорці та доступні з EL0
Регістри стану виконання: Поведінка процесу (керована ОС).
Регістр статусу програми застосування (APSR)
Прапорці
N
,Z
,C
,V
(так само, як у AArch64)Прапорець
Q
: Встановлюється в 1, коли відбувається насичення цілих чисел під час виконання спеціалізованої насиченої арифметичної інструкції. Як тільки він встановлений в1
, він буде зберігати значення до тих пір, поки його не буде вручну встановлено на 0. Крім того, немає жодної інструкції, яка перевіряє його значення неявно, це потрібно зробити, читаючи його вручну.GE
(Більше або дорівнює) Прапорці: Використовується в операціях SIMD (Одна Інструкція, Багато Даних), таких як "паралельне додавання" та "паралельне віднімання". Ці операції дозволяють обробляти кілька точок даних в одній інструкції.
Наприклад, інструкція UADD8
додає чотири пари байтів (з двох 32-бітних операндів) паралельно і зберігає результати в 32-бітному регістрі. Потім встановлює прапорці GE
в APSR
на основі цих результатів. Кожен прапорець GE відповідає одному з додавань байтів, вказуючи, чи відбулося переповнення додавання для цієї пари байтів.
Інструкція SEL
використовує ці прапорці GE для виконання умовних дій.
Регістри стану виконання
Біти
J
таT
:J
повинен бути 0, і якщоT
дорівнює 0, використовується набір інструкцій A32, а якщо 1 - T32.Регістр стану блоку IT (
ITSTATE
): Це біти з 10-15 та 25-26. Вони зберігають умови для інструкцій всередині групи з префіксомIT
.Біт
E
: Вказує на порядок байтів.Біти режиму та маски винятків (0-4): Вони визначають поточний стан виконання. П'ятий вказує, що програма працює як 32-бітна (1) або 64-бітна (0). Інші 4 представляють режим винятка, який в даний момент використовується (коли виникає виняток і його обробляють). Число встановлює поточний пріоритет, у разі виникнення іншого винятка під час обробки цього.
AIF
: Деякі винятки можна вимкнути, використовуючи бітиA
,I
,F
. ЯкщоA
дорівнює 1, це означає, що будуть викликані асинхронні відмови.I
налаштовує відповідь на зовнішні апаратні запити переривань (IRQ), а F пов'язаний з швидкими запитами переривань (FIR).
Іноді легше перевірити декомпільований код з libsystem_kernel.dylib
ніж перевіряти вихідний код, оскільки код декількох системних викликів (BSD та Mach) генерується за допомогою скриптів (перевірте коментарі в вихідному коді), тоді як у dylib ви можете знайти, що викликається.
виклики machdep
XNU підтримує ще один тип викликів, які називаються залежними від машини. Кількість цих викликів залежить від архітектури, і ані виклики, ані номери не гарантовано залишатимуться постійними.
сторінка comm
Це сторінка пам'яті власника ядра, яка відображена в адресному просторі кожного користувацького процесу. Це призначено для прискорення переходу з режиму користувача в простір ядра швидше, ніж використання системних викликів для ядерних служб, які використовуються настільки часто, що цей перехід був би дуже неефективним.
Наприклад, виклик gettimeofdate
читає значення timeval
безпосередньо зі сторінки comm.
objc_msgSend
Дуже поширено зустрічати цю функцію в програмах Objective-C або Swift. Ця функція дозволяє викликати метод об'єкта Objective-C.
Параметри (додаткова інформація в документації):
x0: self -> Вказівник на екземпляр
x1: op -> Селектор методу
x2... -> Решта аргументів викликаного методу
Таким чином, якщо ви встановите точку зупинки перед гілкою до цієї функції, ви легко зможете знайти, що викликається в lldb за допомогою (у цьому прикладі об'єкт викликає об'єкт з NSConcreteTask
, який виконає команду):
Шеллкоди
Для компіляції:
Для видобуття байтів:
Last updated