REX
AMDは、64 ビット x86 拡張機能を開発したときに、プレフィックス用の新しいオペコードやその他の新しい命令を追加する余地が必要でした。一部のオペコードの意味を新しい命令に変更しました。
命令のいくつかは、既存の命令の単なる短縮形であるか、そうでなければ必要ではありませんでした。PUSHA
被害者の一人でした。なぜ禁止されたのかは明らかPUSHA
ではありませんが、新しい命令オペコードと重複しているようには見えません。おそらく、それらは将来の使用のために予約されているPUSHA
オペコードPOPA
です。これらは完全に冗長であり、これ以上高速ではなく、重要なほどコード内で頻繁に発生しないためです。
の順序は、PUSHA
命令エンコードの順序でした: eax
, ecx
, edx
, ebx
, esp
, ebp
, . 重複してプッシュしたことに注意してください! プッシュされたデータを見つけるために知っておく必要があります!esi
edi
esp
esp
コードを 64 ビットから変換している場合、PUSHA
コードはとにかく良くないため、更新して新しいレジスタをプッシュする必要がありr8
ますr15
。また、はるかに大きな SSE 状態を保存して復元する必要がありxmm8
ますxmm15
。あなたがそれらを破壊しようとしていると仮定します。
割り込みハンドラ コードが C コードに転送される単なるスタブである場合、すべてのレジスタを保存する必要はありません。rbx
C コンパイラは、rbp
、rsi
、rdi
、およびr12
thruを保持するコードを生成すると想定できますr15
。rax
、、、およびthrurcx
を保存して復元するだけで済みます。(注: Linux またはその他の System V ABI プラットフォームでは、コンパイラは, , -を保持します。rdx
r8
r11
rbx
rbp
r12
r15
rsi
rdi
セグメント レジスタはロング モードでは値を保持しません (中断されたスレッドが 32 ビット互換モードで実行されている場合は、セグメント レジスタを保存する必要があります。ughoavgfhw に感謝します)。実際には、ロング モードでのセグメンテーションの大部分を取り除きましたがFS
、オペレーティング システムがスレッド ローカル データのベース アドレスとして使用するために予約されています。レジスタ値自体は重要ではなく、 と のベースはMSRFS
とGS
によって設定され0xC0000100
ます0xC0000101
。C コードによってアクセスされるスレッド ローカル データは、任意のスレッドの TLS を使用している可能性があることをFS
覚えておいてください。C ランタイム ライブラリは一部の機能に TLS を使用するため、注意してください (例: 通常、strtok は TLS を使用します)。
FS
またはGS
(ユーザー モードであっても) に値をロードすると、FSBASE
またはGSBASE
MSR が上書きされます。一部のオペレーティング システムは「プロセッサ ローカル」ストレージとして使用するため (各 CPU の構造体へのポインターを保持する方法が必要です)、ユーザー モードでのGS
読み込みによって破壊されない場所にそれを保持する必要があります。GS
この問題を解決するために、GSBASE
レジスター用に 2 つの MSR が予約されています。1 つはアクティブで、もう 1 つは非表示です。カーネル モードでは、カーネルGSBASE
は通常のGSBASE
MSR に保持され、ユーザー モード ベースは別の (非表示) にあります。GSBASE
MSR。カーネル モードからユーザー モード コンテキストにコンテキストを切り替えるとき、およびユーザー モード コンテキストを保存してカーネル モードに入るときに、コンテキスト切り替えコードは SWAPGS 命令を実行する必要があります。これにより、可視 MSR と非表示GSBASE
MSR の値が交換されます。カーネルGSBASE
はユーザー モードの他の MSR に安全に隠されているため、ユーザー モード コードはGSBASE
、値を にロードしてカーネルを上書きすることはできませんGS
。CPU がカーネル モードに再び入ると、コンテキスト保存コードが実行SWAPGS
され、カーネルのGSBASE
.