その理由は、 RCX がシステム コールにパラメーターを渡すために使用されず、64 ビット モードで R10 に置き換えられた理由と同じです。つまり、指示に関するインテルのドキュメントから:sysenter
sysexit
sysexit
SYSEXIT を実行する前に、ソフトウェアは次の MSR および汎用レジスタに値を書き込むことによって、特権レベル 3 コード セグメントとコード エントリ ポイント、および特権レベル 3 スタック セグメントとスタック ポインタを指定する必要があります。
• IA32_SYSENTER_CS (MSR アドレス 174H) — 特権レベル 3 コードおよびスタック セグメントのセグメント セレクターを決定するために使用される 32 ビット値が含まれます (「操作」セクションを参照)。
• RDX — このレジスタの標準アドレスは RIP にロードされます (したがって、この値はユーザー コードで実行される最初の命令を参照します)。戻りが 64 ビット モードでない場合、ビット 31:0 のみがロードされます。
• ECX — このレジスタの標準アドレスは RSP にロードされます (したがって、この値には特権レベル 3 スタックのスタック ポインタが含まれます)。戻りが 64 ビット モードでない場合、ビット 31:0 のみがロードされます。
したがって、 rdx
( edx
) とrcx
( ecx
) は命令によって予約されています。今はどうebp
ですか?さて、sysenter
指示に関するドキュメントから:
SYSENTER 命令と SYSEXIT 命令は対になる命令ですが、コールとリターンのペアを構成するものではありません。SYSENTER 命令を実行すると、プロセッサはユーザー コードの状態情報 (命令ポインタなど) を保存せず、SYSENTER 命令も SYSEXIT 命令もスタック上でのパラメータの受け渡しをサポートしません。
これは、 onにRSP
置き換えられたという事実から明らかであるため、OS はユーザー空間スタックがどこにあるべきかさえ知りません。少なくとも、これを学ぶのは簡単ではありません。そのため、 Linuxはまさにこの目的のために予約しています。つまり、OS にユーザー スタックを提供することです。実行する前に で上書きする必要があるため、呼び出し元は保存する必要があります。IA32_SYSENTER_ESP
sysenter
ebp
ebp
esp
sysenter
Linuxがスタック ポインターを渡す目的で、edx
または専用にしなかったのはなぜですか。これらの 2 つのレジスターは上書きされません。速度のためだと思います。通常の呼び出しでパラメーターを渡すために使用される場合、可能な最後の(6番目の)パラメーターです。syscall が 5 つ以上のパラメーターを必要とすることはめったにないため、ほぼすべてのシステム コール (またはがスタック ポインターに使用された場合) に対してユーザー空間スタックを読み取る代わりに、Linux は 6 つのパラメーターを使用するシステム コールに対してのみこれを行う必要があります。(実行する前に最後にプッシュする必要があることに注意してください。これは、カーネルが 6 番目のパラメーターの場所を認識している必要があるためです)。ecx
sysenter
ebp
int 0x80
edx
ecx
ebp
sysenter
これはすべて Linux ソースにまとめられていますarch/x86/entry/vdso/vdso32/sysenter.S
。
/*
* The caller puts arg2 in %ecx, which gets pushed. The kernel will use
* %ecx itself for arg2. The pushing is because the sysexit instruction
* (found in entry.S) requires that we clobber %ecx with the desired %esp.
* User code might expect that %ecx is unclobbered though, as it would be
* for returning via the iret instruction, so we must push and pop.
*
* The caller puts arg3 in %edx, which the sysexit instruction requires
* for %eip. Thus, exactly as for arg2, we must push and pop.
*
* Arg6 is different. The caller puts arg6 in %ebp. Since the sysenter
* instruction clobbers %esp, the user's %esp won't even survive entry
* into the kernel. We store %esp in %ebp. Code in entry.S must fetch
* arg6 from the stack.
*
* You can not use this vsyscall for the clone() syscall because the
* three words on the parent stack do not get copied to the child.
*/