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 (Таблиця зв'язку процедур).
x16
використовується як номер системного виклику для інструкціїsvc
в macOS.
x18
- Регістр платформи. Його можна використовувати як регістр загального призначення, але на деяких платформах цей регістр зарезервований для платформено-специфічних використань: Вказівник на поточний блок середовища потоку в Windows або вказівник на поточну структуру завдання в ядрі Linux.x19
доx28
- Це регістри, які зберігаються викликачем. Функція повинна зберігати значення цих регістрів для свого викликача, тому вони зберігаються в стеку та відновлюються перед поверненням до викликача.x29
- Вказівник рамки для відстеження стекової рамки. Коли створюється нова стекова рамка через виклик функції, регістрx29
зберігається в стеці, а адреса нової рамки (адресаsp
) зберігається в цьому реєстрі.
Цей регістр також може використовуватися як регістр загального призначення, хоча зазвичай використовується як посилання на локальні змінні.
x30
абоlr
- Регістр посилання. Він містить адресу повернення, коли виконується інструкціяBL
(Гілка з посиланням) абоBLR
(Гілка з посиланням на реєстр), зберігаючи значення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, [зсув #N | RRX]
Xn1 -> Призначення
Xn2 -> Операнд 1
Xn3 | #imm -> Операнд 2 (регістр або негайний)
[зсув #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 (працює як і ANDS без збереження результату десь). Корисно перевірити реєстр зі значенням та перевірити, чи будь-які біти реєстра, вказані в значенні, рівні 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).
macOS
BSD системні виклики
Перевірте syscalls.master. BSD системні виклики матимуть x16 > 0.
Mach Traps
Перевірте в syscall_sw.c mach_trap_table
та в mach_traps.h прототипи. Максимальна кількість Mach traps - MACH_TRAP_TABLE_COUNT
= 128. Mach traps матимуть x16 < 0, тому потрібно викликати номери з попереднього списку з мінусом: _kernelrpc_mach_vm_allocate_trap
це -10
.
Ви також можете перевірити libsystem_kernel.dylib
у дизасемблері, щоб дізнатися, як викликати ці (і BSD) системні виклики:
Іноді легше перевірити декомпільований код з 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
, який виконає команду):
Встановлення змінної середовища NSObjCMessageLoggingEnabled=1
дозволяє реєструвати, коли ця функція викликається в файлі, наприклад, /tmp/msgSends-pid
.
Крім того, встановлення OBJC_HELP=1
і виклик будь-якого виконуваного файлу дозволяє переглянути інші змінні середовища, які можна використовувати для реєстрації випадків виконання певних дій Objc-C.
Коли ця функція викликається, потрібно знайти викликаний метод вказаного екземпляру, для цього виконуються різні пошуки:
Виконати оптимістичний пошук в кеші:
Якщо успішно, завершено
Отримати runtimeLock (читання)
Якщо (реалізувати && !cls->realized) реалізувати клас
Якщо (ініціалізувати && !cls->initialized) ініціалізувати клас
Спробувати власний кеш класу:
Якщо успішно, завершено
Спробувати список методів класу:
Якщо знайдено, заповнити кеш і завершено
Спробувати кеш батьківського класу:
Якщо успішно, завершено
Спробувати список методів батьківського класу:
Якщо знайдено, заповнити кеш і завершено
Якщо (розв'язувач) спробувати розв'язувач методу і повторити з пошуку класу
Якщо все ще тут (= все інше не вдалося), спробувати пересилальник
Коди оболонки
Для компіляції:
Для видобутку байтів:
Для новіших версій macOS: