1. ホーム
  2. linux

[解決済み] i386およびx86-64におけるUNIXおよびLinuxシステムコール(およびユーザースペースファンクション)の呼び出し規約は?

2022-04-21 14:54:39

質問内容

以下のリンクは、UNIX (BSD flavor) & Linux の x86-32 システムコール規約を説明するものです。

しかし、UNIX & Linuxの両方におけるx86-64システムコールの規約はどうなっているのでしょうか?

解決方法は?

各トピックの詳細については、こちらをご覧ください。 Linuxシステムコールの決定版


LinuxのGNU Assembler(gas)で検証してみました。

カーネルインターフェース

x86-32 aka i386 Linux System Call convention:

x86-32では、Linuxシステムコールのパラメータはレジスタを使用して渡されます。 %eax には、syscall_number を指定します。 ebx, %ecx, %edx, %esi, %edi, %ebp はシステムコールに6つのパラメータを渡すために使用されます。

戻り値は %eax . 他のすべてのレジスタ(EFLAGS を含む)は int $0x80 .

次のスニペットを Linux アセンブリチュートリアル しかし、これには疑問があります。どなたか例を示していただけると幸いです。

<ブロッククオート

引数が6個以上ある場合。 %ebx を含む必要があります。 引数のリストがある場所 が格納されていますが、これは気にしないでください。 を使うことはまずないでしょうから。 6つ以上のシステムコールを持つ の引数を指定します。

例ともう少し読むには、以下を参照してください。 http://www.int80h.org/bsdasm/#alternate-calling-convention . を使った i386 Linux 用の Hello World の別の例です。 int 0x80 : Linuxのシステムコールを使ったアセンブリ言語によるHello, world?

32ビットシステムコールを行うには、より高速な方法があります。 sysenter . カーネルは、すべてのプロセス(vDSO)にメモリのページをマッピングし、ユーザースペース側の sysenter ダンスは、カーネルがリターン・アドレスを見つけることができるように協力する必要があります。 Argからレジスタへのマッピングは int $0x80 . を使用する代わりに、通常は vDSO にコールする必要があります。 sysenter を直接使用します。 (参照 Linuxシステムコールの決定版 については、vDSOへのリンクと呼び出しに関する情報、および sysenter その他、システムコールに関することなら何でも。)

x86-32 [Free|Open|Net|DragonFly]BSD UNIX System Call convention:

パラメータはスタック上に渡されます。パラメータをスタックにプッシュする(最後のパラメータが最初にプッシュされる)。その後、32ビットのダミーデータ(実際にはダミーデータではありません)を追加して、システムコール命令を実行します。 int $0x80

http://www.int80h.org/bsdasm/#default-calling-convention


x86-64 Linux システムコール規約。

(注 x86-64 Mac OS Xは似ているが異なる Linuxから。 TODO: *BSD がどうなっているかをチェックしてください)

参照:「A.2 AMD64 リナックス の「カーネル規約」(Kernel Conventions") を参照してください。 System V Application Binary Interface AMD64 Architecture Processor Supplement (英語) . i386 および x86-64 System V psABI の最新バージョンは、以下の URL でご覧になれます。 このページからリンクされている ABI メンテナレポジトリの . (また x86 タグの wiki には最新の ABI リンクやその他 x86 asm に関する良い情報がたくさんあります)。

このセクションのスニペットを紹介します。

  1. ユーザレベルのアプリケーションは、整数レジスタとして、以下のものを渡すために使用します。 シーケンス %rdi、%rsi、%rdx、%rcx。 r8と%r9があります。 カーネル・インターフェースは、%rdi, %rsi, %rdx, %r10, %r8, %r9 を使用します。
  2. システムコールは syscall 命令 . この は、%rcx と %r11 を抑制します。 と%raxの戻り値だけでなく、他のレジスタは保存されます。
  3. システムコールの番号は、レジスタ %rax に渡さなければなりません。
  4. システムコールの引数は6個までで、引数が渡されることはありません。 を直接スタックに載せる。
  5. システムコールから戻ると、レジスタ %rax にその結果が格納されます。 システムコールの 4095から-1の範囲の値は エラーであれば -errno .
  6. クラスINTEGERまたはクラスMEMORYの値のみがカーネルに渡されます。

これはABIのLinux固有の付録であり、Linuxであっても規範的なものではなく、参考となるものであることを忘れないでください。 (しかし、実際には正確です)。

この32ビット int $0x80 ABI 64ビットコードで使用可能です(ただし、強くは推奨しません)。 64ビットコードで32ビットint 0x80 Linux ABIを使用した場合どうなりますか? 入力は32ビットに切り捨てられるので、ポインタには適さず、r8-r11がゼロになります。

ユーザーインターフェース:関数の呼び出し

x86-32 関数呼び出しの規約。

x86-32では、パラメータはスタック上で渡されました。最後のパラメータは、すべてのパラメータが終了するまで、最初にスタックにプッシュされ、その後 call という命令が実行されました。これは、LinuxでCライブラリ(libc)の関数をアセンブリから呼び出すときに使用されます。

Linux で使用されている i386 System V ABI の最新バージョンでは、16 バイトのアライメントが必要です。 %esp の前に call x86-64 System V ABI が常に要求しているようなものです。 Callees はそれを前提として、SSE の 16 バイトのロード/ストアを使用し、アラインメント無しでフォールトすることが許されています。 しかし、歴史的に見ると、Linux は 4 バイトのスタックアライメントしか必要としないので、8 バイトの double などがあります。

他の最新の32ビットシステムの中には、いまだに4バイト以上のスタックアライメントを必要としないものもあります。


x86-64 System V ユーザースペースの関数呼び出しの規則。

x86-64 System V は、i386 System V のスタック引数規約よりも効率的な、レジスターでの引数渡しを採用しています。 これは i386 System V のスタック引数規約よりも効率的で、引数をメモリ (キャッシュ) に格納してから呼び出し側で再びロードするというレイテンシと余分な命令を回避することができます。 これは利用可能なレジスタが多いためうまく機能し、レイテンシとアウトオブオーダー実行が重要な最近の高性能CPUに適しています。 (i386 ABIは非常に古い)。

この中で 新しい という機構があります。まず、パラメータはクラスに分けられます。各パラメータのクラスによって、呼び出される関数に渡される方法が決まります。

の「3.2 関数呼び出しのシーケンス」を参照してください。 System V Application Binary Interface AMD64 Architecture Processor Supplement (英語) という文章があり、その一部をご紹介します。

引数が分類されると、レジスタが割り当てられます(順番に)。 左から右の順番で)次のように渡します。

  1. クラスがMEMORYの場合、スタック上の引数を渡します。
  2. クラスが INTEGER の場合、次に利用可能なレジスタを選択します。 rdi, %rsi, %rdx, %rcx, %r8 および %r9 のシーケンスが使用されます。

そこで %rdi, %rsi, %rdx, %rcx, %r8 and %r9 はレジスタ 順番に アセンブリから libc の関数に整数/ポインタ (すなわち INTEGER クラス) のパラメータを渡すために使用します。最初の INTEGER パラメータには %rdi が使われます。 2番目は%rsi、3番目は%rdxといった具合です。次に call 命令を与える必要があります。 スタック( %rsp の場合、16Bアラインでなければなりません。 call が実行されます。

INTEGERパラメータが6個以上ある場合、7個目以降のINTEGERパラメータはスタックに渡される。 (呼び出し側がポップします。x86-32と同じです。)

最初の 8 個の浮動小数点引数は %xmm0-7 で渡され、後でスタックに積まれます。 コールプリザーブドベクトルレジスタはありません。 (FP と整数の引数を混在させた関数は、合計 8 個以上のレジスタ引数を持つことができます)。

可変個体関数( のように printf が常に必要です。 %al = FPレジスタの引数の数。

構造体をいつレジスタに詰めるかについては、ルールがあります ( rdx:rax を使用する場合と、メモリ上に置く場合があります。 詳細はABIを参照してください。また、コンパイラの出力をチェックして、コードがどのように受け渡しされるべきかについてコンパイラと一致することを確認してください。


注意事項 Windows x64の関数呼び出し規則 は、x86-64 System V とは複数の重要な違いがあります。 が必要です。 また、xmm6-xmm15 は呼び出し側で予約されています。 また、どの arg をどのレジスタに入れるかについてのルールも大きく異なっています。