Introduction to ARM64v8
例外レベル - EL(ARM64v8)
ARMv8アーキテクチャでは、実行レベルである例外レベル(EL)が、実行環境の特権レベルと機能を定義します。EL0からEL3までの4つの例外レベルがあり、それぞれ異なる目的で使用されます:
EL0 - ユーザーモード:
これは最も特権のないレベルであり、通常のアプリケーションコードの実行に使用されます。
EL0で実行されるアプリケーションは、お互いやシステムソフトウェアから分離されており、セキュリティと安定性が向上しています。
EL1 - オペレーティングシステムカーネルモード:
ほとんどのオペレーティングシステムカーネルはこのレベルで実行されます。
EL1はEL0よりも特権があり、システムリソースにアクセスできますが、システムの整合性を確保するためにいくつかの制限があります。
EL2 - ハイパーバイザーモード:
このレベルは仮想化に使用されます。EL2で実行されるハイパーバイザーは、同じ物理ハードウェア上で実行されている複数のオペレーティングシステム(それぞれが独自のEL1で)を管理できます。
EL2には仮想化環境の分離と制御の機能が備わっています。
EL3 - セキュアモニターモード:
これは最も特権のあるレベルであり、セキュアブートや信頼された実行環境によく使用されます。
EL3はセキュア状態と非セキュア状態の間のアクセスを管理および制御できます(セキュアブート、信頼されたOSなど)。
これらのレベルの使用により、ユーザーアプリケーションから最も特権のあるシステムソフトウェアまで、システムのさまざまな側面を構造化して安全に管理する方法が提供されます。ARMv8の特権レベルへのアプローチは、異なるシステムコンポーネントを効果的に分離することで、システムのセキュリティと堅牢性を向上させます。
レジスタ(ARM64v8)
ARM64には31個の汎用レジスタがあり、x0
からx30
までラベル付けされています。それぞれが64ビット(8バイト)の値を格納できます。32ビットの値のみを必要とする操作の場合、同じレジスタにはw0
からw30
という名前で32ビットモードでアクセスできます。
x0
からx7
- これらは通常、スクラッチレジスタとサブルーチンにパラメータを渡すために使用されます。x0
は関数の戻りデータも保持します。
x8
- Linuxカーネルでは、x8
はsvc
命令のシステムコール番号として使用されます。macOSではx16が使用されます!x9
からx15
- さらなる一時レジスタであり、ローカル変数によく使用されます。x16
とx17
- 手続き内呼び出しレジスタ。即値のための一時レジスタ。間接関数呼び出しやPLT(手続きリンクテーブル)スタブにも使用されます。x16
はmacOSで**svc
命令のシステムコール番号**として使用されます。
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
- プログラムカウンタ。次の命令を指すレジスタ。このレジスタは例外生成、例外リターン、およびブランチを介してのみ更新できます。このレジスタを読み取ることができる通常の命令は、pc
アドレスをlr
(リンクレジスタ)に格納するためのリンク付きブランチ命令(BL、BLR)だけです。xzr
- ゼロレジスタ。32ビットレジスタ形式では**wzr
とも呼ばれます。ゼロ値を簡単に取得するために使用できます(一般的な操作)またはsubs
を使用して比較を行うために使用できます。例:subs XZR, Xn, #10
**(結果のデータをどこにも保存しない)。
**Wn
レジスタはXn
**レジスタの32ビットバージョンです。
SIMDおよび浮動小数点レジスタ
さらに、最適化された単一命令複数データ(SIMD)操作や浮動小数点演算を実行するために使用できる、長さが128ビットの別の32個のレジスタがあります。これらはVnレジスタと呼ばれますが、64ビット、32ビット、16ビット、8ビットで動作することもでき、その場合は**Qn
、Dn
、Sn
、Hn
、Bn
**と呼ばれます。
システムレジスタ
数百のシステムレジスタ、または特殊用途レジスタ(SPR)は、プロセッサの動作を監視および制御するために使用されます。
これらは、専用の特殊命令 mrs
と msr
を使用してのみ読み取りまたは設定できます。
特殊レジスタ TPIDR_EL0
と TPIDDR_EL0
は、リバースエンジニアリング時に一般的に見られます。EL0
接尾辞は、レジスタにアクセスできる最小例外を示します(この場合、EL0は通常の例外(特権)レベルで一般プログラムが実行されます)。
これらは通常、メモリのスレッドローカルストレージ領域のベースアドレスを格納するために使用されます。通常、最初のものはEL0で実行されるプログラムに対して読み書き可能ですが、2番目はEL0から読み取り、カーネルから書き込むことができます。
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
は操作が符号オーバーフローを生じたことを意味します:2つの正の数の合計は負の結果を生じます。
2つの負の数の合計は正の結果を生じます。
減算では、大きな負の数が小さな正の数から減算される場合(またはその逆)、結果が与えられたビットサイズの範囲内に表現できない場合。
明らかに、プロセッサは操作が符号付きかどうかを知らないため、符号が付いているかどうかをチェックし、符号が付いている場合にキャリーが発生したかどうかを示します。
すべての命令がこれらのフラグを更新するわけではありません。CMP
や TST
のような一部の命令は、および ADDS
のような s 接尾辞を持つ他の命令もそれを行います。
現在のレジスタ幅 (
nRW
) フラグ: フラグが値 0 を保持している場合、プログラムは再開後に AArch64 実行状態で実行されます。現在の例外レベル (
EL
): EL0 で実行される通常のプログラムは値 0 を持ちますシングルステップ フラグ (
SS
): デバッガが SS フラグをSPSR_ELx
で 1 に設定してシングルステップ例外を発生させることで、ステップごとに単一ステップ例外を発行します。不正例外 状態フラグ (
IL
): 特権ソフトウェアが無効な例外レベル転送を実行するときにマークされ、このフラグが 1 に設定され、プロセッサが不正な状態例外をトリガーします。DAIF
フラグ: これらのフラグにより、特権プログラムは特定の外部例外を選択的にマスクできます。A
が 1 の場合、非同期中断 がトリガーされます。I
は外部ハードウェア 割り込みリクエスト(IRQs)に応答するように構成され、F は Fast Interrupt Requests(FIRs)に関連しています。スタックポインタ選択 フラグ (
SPS
): EL1 およびそれ以上で実行される特権プログラムは、自分自身のスタックポインタレジスタとユーザーモデルのレジスタ(たとえばSP_EL1
とEL0
の間)を切り替えることができます。この切り替えは、SPSel
特殊レジスタに書き込むことによって実行されます。これは EL0 からは行えません。
呼び出し規約(ARM64v8)
ARM64 呼び出し規約では、関数への最初の 8 つのパラメータはレジスタ x0
から x7
に渡されます。追加のパラメータはスタックに渡されます。戻り値はレジスタ x0
に返されるか、128 ビットの場合は x1
にも返されます。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
: 1 つのレジスタから別のレジスタに値を移動します。例:
mov x0, x1
— これはx1
からx0
に値を移動します。ldr
: メモリから値をレジスタにロードします。例:
ldr x0, [x1]
— これはx1
が指すメモリ位置からx0
に値をロードします。オフセットモード: オリンポインタに影響を与えるオフセットが示されます。たとえば:
ldr x2, [x1, #8]
は、x1 + 8 の値を x2 にロードしますldr x2, [x0, x1, lsl #2]
は、x0 の配列から、位置 x1(インデックス) * 4 のオブジェクトを x2 にロードしますプリインデックスモード: これは、計算をオリジンに適用し、結果を取得して新しいオリジンをオリジンに格納します。
ldr x2, [x1, #8]!
は、x1 + 8
をx2
にロードし、x1 + 8
の結果をx1
に格納しますstr lr, [sp, #-4]!
は、リンクレジスタを sp に格納し、レジスタ sp を更新しますポストインデックスモード: これは前のモードと同様ですが、メモリアドレスにアクセスしてからオフセットを計算して格納します。
ldr x0, [x1], #8
は、x1
をx0
にロードし、x1 + 8
で x1 を更新しますPC相対アドレッシング: この場合、ロードするアドレスは PC レジスタに対して相対的に計算されます
ldr x1, =_start
は、_start
シンボルが開始するアドレスを、現在の PC に関連して x1 にロードします。str
: メモリにあるレジスタから値を格納します。例:
str x0, [x1]
— これはx0
の値をx1
が指すメモリ位置に格納します。ldp
: 2 つのレジスタを連続するメモリ位置からロードします。メモリアドレスは通常、別のレジスタの値にオフセットを追加して形成されます。例:
ldp x0, x1, [x2]
は、それぞれx2
とx2 + 8
のメモリ位置からx0
とx1
をロードします。stp
: 2 つのレジスタを連続するメモリ位置に格納します。メモリアドレスは通常、別のレジスタの値にオフセットを追加して形成されます。例:
stp x0, x1, [sp]
は、それぞれsp
とsp + 8
のメモリ位置にx0
とx1
を格納します。stp x0, x1, [sp, #16]!
は、それぞれsp+16
とsp + 24
のメモリ位置にx0
とx1
を格納し、sp
をsp+16
に更新します。add
: 2 つのレジスタの値を加算し、結果をレジスタに格納します。構文: 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
: 2つのレジスタの値を引き算し、結果をレジスタに格納します。add
構文を確認してください。例:
sub x0, x1, x2
— これはx1
からx2
の値を引き、結果をx0
に格納します。subs
これはsubと同じですが、フラグを更新しますmul
: 2つのレジスタの値を掛け算し、結果をレジスタに格納します。例:
mul x0, x1, x2
— これはx1
とx2
の値を掛け算し、結果をx0
に格納します。div
: 1つのレジスタの値をもう1つで割り、結果をレジスタに格納します。例:
div x0, x1, x2
— これはx1
をx2
で割り、結果をx0
に格納します。lsl
,lsr
,asr
,ror
,rrx
:論理左シフト: 末尾に0を追加し、他のビットを前に移動します(n回2倍)
論理右シフト: 先頭に1を追加し、他のビットを後ろに移動します(符号なしでn回2で割る)
算術右シフト: **
lsr
**と同様ですが、最上位ビットが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
X2からX1の3番目のビットに4ビットを挿入しますBFXIL X1, X2, #3, #4
X2の3番目のビットから4ビットを抽出し、それらをX1にコピーしますSBFIZ X1, X2, #3, #4
X2から4ビットを符号拡張し、X1の3ビット目から挿入し、右側のビットをゼロにしますSBFX X1, X2, #3, #4
X2から3ビット目から4ビットを抽出し、符号拡張し、結果をX1に配置しますUBFIZ X1, X2, #3, #4
X2から4ビットをゼロ拡張し、X1の3ビット目から挿入し、右側のビットをゼロにしますUBFX X1, X2, #3, #4
X2から3ビット目から4ビットを抽出し、ゼロ拡張された結果を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を連結し、W2のビット3からW1のビット3までを取得してW3に格納します。cmp
: 2つのレジスタを比較し、条件フラグを設定します。これは、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にジャンプしますこれは前の
cmp
がNE
だった場合にのみccmp
が実行されるためであり、そうでない場合はビットnzcv
が0に設定されます(これはblt
の比較を満たしません)。これは
ccmn
としても使用できます(cmp
とcmn
のように)。tst
: 比較の値のいずれかが1であるかどうかをチェックします(結果をどこにも保存せずにANDSのように機能します)。指定された値のレジスタをチェックし、その値で指定されたレジスタのビットのいずれかが1であるかどうかを確認するのに便利です。例:
tst X1, #7
X1の最後の3ビットのいずれかが1であるかどうかをチェックしますteq
: 結果を破棄する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
命令で2つの等しい値が見つかった場合、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 = <all 1>、偽の場合、Xd = 0adrp
: シンボルのページアドレスを計算してレジスタに格納します。例:
adrp x0, symbol
—symbol
のページアドレスを計算してx0
に格納します。ldrsw
: メモリから符号付き32ビット値を64ビットに拡張してロードします。例:
ldrsw x0, [x1]
—x1
が指すメモリ位置から符号付き32ビット値をロードし、64ビットに拡張してx0
に格納します。stur
: レジスタの値をメモリ位置にストアし、別のレジスタからのオフセットを使用します。例:
stur x0, [x1, #4]
—x1
に現在格納されているアドレスよりも4バイト大きいアドレスにx0
の値を格納します。svc
: システムコールを行います。"Supervisor Call" の略です。プロセッサがこの命令を実行すると、ユーザーモードからカーネルモードに切り替わり、カーネルのシステムコール処理コードが格納されているメモリ内の特定の場所にジャンプします。例:
関数プロローグ
リンクレジスタとフレームポインタをスタックに保存:
新しいフレームポインタを設定する:
mov x29, sp
(現在の関数のための新しいフレームポインタを設定する)ローカル変数用のスタック上のスペースを割り当てる(必要な場合):
sub sp, sp, <size>
(ここで<size>
は必要なバイト数です)
関数エピローグ
ローカル変数を解放する(割り当てられている場合):
add sp, sp, <size>
リンクレジスタとフレームポインタを復元する:
Return:
ret
(リンクレジスタ内のアドレスを使用して呼び出し元に制御を返します)
AARCH32 実行状態
Armv8-A は 32 ビットプログラムの実行をサポートします。AArch32 は A32
と T32
の 2 つの命令セットのいずれかで実行でき、interworking
を介してそれらの間を切り替えることができます。
特権を持つ 64 ビットプログラムは、32 ビットの低特権レベルに例外レベル転送を実行することで、32 ビットプログラムの実行をスケジュールできます。
64 ビットから 32 ビットへの移行は、例外レベルの低下によって行われます(たとえば、EL1 での 64 ビットプログラムが EL0 でのプログラムをトリガーする)。これは、AArch32
プロセススレッドが実行される準備ができているときに、SPSR_ELx
特殊レジスタの ビット 4 を 1 に設定することで行われ、SPSR_ELx
の残りの部分は AArch32
プログラムの CPSR を保存します。その後、特権プロセスは ERET
命令を呼び出してプロセッサが AArch32
に遷移し、CPSR に応じて A32 または T32 に入ります**。**
interworking
は CPSR の J ビットと T ビットを使用して行われます。J=0
かつ T=0
は A32
を意味し、J=0
かつ T=1
は T32 を意味します。これは基本的に、命令セットが T32 であることを示すために 最下位ビットを 1 に設定することを意味します。
これは interworking 分岐命令で設定されますが、PC が宛先レジスタとして設定されているときに他の命令で直接設定することもできます。例:
別の例:
レジスタ
32ビットのレジスタが16個あります(r0-r15)。r0からr14まで、どんな操作にも使用できますが、一部は通常予約されています:
r15
:プログラムカウンタ(常に)。次の命令のアドレスが格納されています。A32では現在+8、T32では現在+4。r11
:フレームポインタr12
:手続き内呼び出しレジスタr13
:スタックポインタr14
:リンクレジスタ
さらに、レジスタは**バンク付きレジスタ
にバックアップされます。これは、例外処理や特権操作で高速なコンテキスト切り替え**を実行するために、レジスタの値を保存しておく場所です。これにより、毎回レジスタを手動で保存および復元する必要がなくなります。
これは、例外が発生したプロセッサモードのCPSR
からプロセッサのSPSR
にプロセッサ状態を保存することで行われます。例外が返されると、CPSR
はSPSR
から復元されます。
CPSR - 現在のプログラムステータスレジスタ
AArch32では、CPSRはAArch64の**PSTATE
と同様に機能し、例外が発生すると後で実行を復元するためにSPSR_ELx
**にも保存されます:
フィールドはいくつかのグループに分かれています:
アプリケーションプログラムステータスレジスタ(APSR):算術フラグで、EL0からアクセス可能
実行状態レジスタ:プロセスの動作(OSによって管理)
アプリケーションプログラムステータスレジスタ(APSR)
N
、Z
、C
、V
フラグ(AArch64と同様)Q
フラグ:専用の飽和算術命令の実行中に整数の飽和が発生すると、このフラグが1に設定されます。一度**1
**に設定されると、手動で0に設定されるまで値が維持されます。さらに、その値を暗黙的にチェックする命令は存在せず、値を読んで手動でチェックする必要があります。GE
(以上または等しい)フラグ:これはSIMD(Single Instruction, Multiple Data)操作で使用され、"parallel add"や"parallel subtract"などの操作に使用されます。これらの操作は、1つの命令で複数のデータポイントを処理できます。
たとえば、UADD8
命令は、並列に4組のバイト(2つの32ビットオペランドから)を追加し、結果を32ビットレジスタに格納します。次に、これらの結果に基づいて、APSR
内のGE
フラグが設定されます。各GEフラグは1つのバイトの追加に対応し、そのバイトのペアの追加がオーバーフローしたかどうかを示します。
SEL
命令はこれらのGEフラグを使用して条件付きアクションを実行します。
実行状態レジスタ
J
およびT
ビット:J
は0である必要があり、T
が0の場合はA32命令セットが使用され、1の場合はT32が使用されます。ITブロックステートレジスタ(
ITSTATE
):これらは10-15および25-26のビットです。IT
で接頭辞が付いたグループ内の命令の条件を格納します。E
ビット:エンディアンネスを示します。モードおよび例外マスクビット(0-4):現在の実行状態を決定します。5番目はプログラムが32ビット(1)または64ビット(0)で実行されているかを示します。他の4つは、使用中の例外モード(例外が発生し処理中の場合)を表します。設定された数値は、これが処理中に別の例外が発生した場合の現在の優先度を示します。
AIF
:特定の例外は、A
、I
、F
ビットを使用して無効にできます。A
が1の場合、非同期中断がトリガーされます。I
は外部ハードウェアの割り込みリクエスト(IRQ)に応答するように構成され、Fはファスト割り込みリクエスト(FIR)に関連しています。
時々、libsystem_kernel.dylib
からの**decompiled
**コードをチェックする方が、複数のシステムコール(BSDとMach)のコードがスクリプト経由で生成されるため、ソースコードをチェックするよりも簡単です(ソースコードのコメントをチェックしてください)。dylibでは、呼び出されている内容を見つけることができます。
machdep calls
XNUは、マシン依存の呼び出しと呼ばれる別のタイプの呼び出しをサポートしています。これらの呼び出しの数はアーキテクチャに依存し、呼び出しまたは数値が一定であることは保証されていません。
comm page
これは、すべてのユーザープロセスのアドレス空間にマップされるカーネル所有のメモリページです。ユーザーモードからカーネルスペースへの移行を、システムコールを使用するよりも効率的に行うために使用されます。カーネルサービスのためにシステムコールを使用すると非常に効率が悪いためです。
たとえば、gettimeofdate
呼び出しは、timeval
の値をcommページから直接読み取ります。
objc_msgSend
Objective-CやSwiftプログラムでよく見られる関数です。この関数を使用すると、Objective-Cオブジェクトのメソッドを呼び出すことができます。
パラメータ(詳細はドキュメントを参照):
x0: self -> インスタンスへのポインタ
x1: op -> メソッドのセレクタ
x2... -> 呼び出されるメソッドの残りの引数
したがって、この関数への分岐前にブレークポイントを設定すると、lldbで呼び出される内容を簡単に見つけることができます(この例では、オブジェクトがNSConcreteTask
からのオブジェクトを呼び出し、コマンドを実行します)。
NSObjCMessageLoggingEnabled=1
環境変数を設定すると、この関数が呼び出されたときに /tmp/msgSends-pid
のようなファイルにログを記録することができます。
シェルコード
コンパイルするには:
バイトを抽出するには:
新しい macOS 向け:
Last updated